mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-19 19:41:35 +08:00
1204 lines
38 KiB
Java
1204 lines
38 KiB
Java
/*
|
|
* Copyright 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.android.material.internal;
|
|
|
|
import com.google.android.material.R;
|
|
|
|
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.support.annotation.IntDef;
|
|
import android.support.annotation.Nullable;
|
|
import android.support.annotation.RestrictTo;
|
|
import android.support.v4.view.ViewCompat;
|
|
import android.util.AttributeSet;
|
|
import android.util.SparseIntArray;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.RelativeLayout;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* A layout that arranges its children in a way its attributes can be specified like the CSS
|
|
* Flexible Box Layout Module. This class extends the {@link ViewGroup} like other layout classes
|
|
* such as {@link LinearLayout} or {@link RelativeLayout}, the attributes can be specified from a
|
|
* layout XML or from code.
|
|
*
|
|
* <p>The supported attributes that you can use are:
|
|
*
|
|
* <ul>
|
|
* <li>{@code flexDirection}
|
|
* <li>{@code flexWrap}
|
|
* <li>{@code justifyContent}
|
|
* <li>{@code alignItems}
|
|
* <li>{@code alignContent}
|
|
* <li>{@code showDivider}
|
|
* <li>{@code showDividerHorizontal}
|
|
* <li>{@code showDividerVertical}
|
|
* <li>{@code dividerDrawable}
|
|
* <li>{@code dividerDrawableHorizontal}
|
|
* <li>{@code dividerDrawableVertical}
|
|
* </ul>
|
|
*
|
|
* for the FlexboxLayout.
|
|
*
|
|
* <p>And for the children of the FlexboxLayout, you can use:
|
|
*
|
|
* <ul>
|
|
* <li>{@code layout_order}
|
|
* <li>{@code layout_flexGrow}
|
|
* <li>{@code layout_flexShrink}
|
|
* <li>{@code layout_flexBasisPercent}
|
|
* <li>{@code layout_alignSelf}
|
|
* <li>{@code layout_minWidth}
|
|
* <li>{@code layout_minHeight}
|
|
* <li>{@code layout_maxWidth}
|
|
* <li>{@code layout_maxHeight}
|
|
* <li>{@code layout_wrapBefore}
|
|
* </ul>
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public class FlexboxLayout extends ViewGroup implements FlexContainer {
|
|
|
|
/**
|
|
* The current value of the {@link FlexWrap}, the default value is {@link FlexWrap#NOWRAP}.
|
|
*
|
|
* @see FlexWrap
|
|
*/
|
|
private int flexWrap;
|
|
|
|
/**
|
|
* The int definition to be used as the arguments for the {@link #setShowDivider(int)}, {@link
|
|
* #setShowDividerHorizontal(int)} or {@link #setShowDividerVertical(int)}. One or more of the
|
|
* values (such as {@link #SHOW_DIVIDER_BEGINNING} | {@link #SHOW_DIVIDER_MIDDLE}) can be passed
|
|
* to those set methods.
|
|
*/
|
|
@IntDef(
|
|
flag = true,
|
|
value = {SHOW_DIVIDER_NONE, SHOW_DIVIDER_BEGINNING, SHOW_DIVIDER_MIDDLE, SHOW_DIVIDER_END}
|
|
)
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@SuppressWarnings("WeakerAccess")
|
|
public @interface DividerMode {}
|
|
|
|
/** Constant to show no dividers */
|
|
public static final int SHOW_DIVIDER_NONE = 0;
|
|
|
|
/** Constant to show a divider at the beginning of the flex lines (or flex items). */
|
|
public static final int SHOW_DIVIDER_BEGINNING = 1;
|
|
|
|
/** Constant to show dividers between flex lines or flex items. */
|
|
public static final int SHOW_DIVIDER_MIDDLE = 1 << 1;
|
|
|
|
/** Constant to show a divider at the end of the flex lines or flex items. */
|
|
public static final int SHOW_DIVIDER_END = 1 << 2;
|
|
|
|
/** The drawable to be drawn for the horizontal dividers. */
|
|
@Nullable private Drawable dividerDrawableHorizontal;
|
|
|
|
/** The drawable to be drawn for the vertical dividers. */
|
|
@Nullable private Drawable dividerDrawableVertical;
|
|
|
|
/**
|
|
* Indicates the divider mode for the {@link #dividerDrawableHorizontal}. The value needs to be
|
|
* the combination of the value of {@link #SHOW_DIVIDER_NONE}, {@link #SHOW_DIVIDER_BEGINNING},
|
|
* {@link #SHOW_DIVIDER_MIDDLE} and {@link #SHOW_DIVIDER_END}
|
|
*/
|
|
private int showDividerHorizontal;
|
|
|
|
/**
|
|
* Indicates the divider mode for the {@link #dividerDrawableVertical}. The value needs to be the
|
|
* combination of the value of {@link #SHOW_DIVIDER_NONE}, {@link #SHOW_DIVIDER_BEGINNING}, {@link
|
|
* #SHOW_DIVIDER_MIDDLE} and {@link #SHOW_DIVIDER_END}
|
|
*/
|
|
private int showDividerVertical;
|
|
|
|
/** The height of the {@link #dividerDrawableHorizontal}. */
|
|
private int dividerHorizontalHeight;
|
|
|
|
/** The width of the {@link #dividerDrawableVertical}. */
|
|
private int dividerVerticalWidth;
|
|
|
|
/**
|
|
* Holds reordered indices, which {@link FlexItem#getOrder()} parameters are taken into account
|
|
*/
|
|
private int[] reorderedIndices;
|
|
|
|
/**
|
|
* Caches the {@link FlexItem#getOrder()} attributes for children views. Key: the index of the
|
|
* view reordered indices using the {@link FlexItem#getOrder()} isn't taken into account) Value:
|
|
* the value for the order attribute
|
|
*/
|
|
private SparseIntArray orderCache;
|
|
|
|
private FlexboxHelper flexboxHelper = new FlexboxHelper(this);
|
|
|
|
private List<FlexLine> flexLines = new ArrayList<>();
|
|
|
|
/**
|
|
* Used for receiving the calculation of the flex results to avoid creating a new instance every
|
|
* time flex lines are calculated.
|
|
*/
|
|
private FlexboxHelper.FlexLinesResult flexLinesResult = new FlexboxHelper.FlexLinesResult();
|
|
|
|
public FlexboxLayout(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public FlexboxLayout(Context context, AttributeSet attrs) {
|
|
this(context, attrs, 0);
|
|
}
|
|
|
|
public FlexboxLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
super(context, attrs, defStyleAttr);
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
if (orderCache == null) {
|
|
orderCache = new SparseIntArray(getChildCount());
|
|
}
|
|
if (flexboxHelper.isOrderChangedFromLastMeasurement(orderCache)) {
|
|
reorderedIndices = flexboxHelper.createReorderedIndices(orderCache);
|
|
}
|
|
|
|
// TODO: Only calculate the children views which are affected from the last measure.
|
|
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
|
|
}
|
|
|
|
@Override
|
|
public int getFlexItemCount() {
|
|
return getChildCount();
|
|
}
|
|
|
|
@Override
|
|
public View getFlexItemAt(int index) {
|
|
return getChildAt(index);
|
|
}
|
|
|
|
/**
|
|
* Returns a View, which is reordered by taking {@link LayoutParams#order} parameters into
|
|
* account.
|
|
*
|
|
* @param index the index of the view
|
|
* @return the reordered view, which {@link LayoutParams@order} is taken into account. If the
|
|
* index is negative or out of bounds of the number of contained views, returns {@code null}.
|
|
*/
|
|
public View getReorderedChildAt(int index) {
|
|
if (index < 0 || index >= reorderedIndices.length) {
|
|
return null;
|
|
}
|
|
return getChildAt(reorderedIndices[index]);
|
|
}
|
|
|
|
@Override
|
|
public View getReorderedFlexItemAt(int index) {
|
|
return getReorderedChildAt(index);
|
|
}
|
|
|
|
@Override
|
|
public void addView(View child, int index, ViewGroup.LayoutParams params) {
|
|
if (orderCache == null) {
|
|
orderCache = new SparseIntArray(getChildCount());
|
|
}
|
|
// Create an array for the reordered indices before the View is added in the parent
|
|
// ViewGroup since otherwise reordered indices won't be in effect before the
|
|
// FlexboxLayout's onMeasure is called.
|
|
// Because requestLayout is requested in the super.addView method.
|
|
reorderedIndices = flexboxHelper.createReorderedIndices(child, index, params, orderCache);
|
|
super.addView(child, index, params);
|
|
}
|
|
|
|
/**
|
|
* Sub method for {@link #onMeasure(int, int)}, when the main axis direction is horizontal (either
|
|
* left to right or right to left).
|
|
*
|
|
* @param widthMeasureSpec horizontal space requirements as imposed by the parent
|
|
* @param heightMeasureSpec vertical space requirements as imposed by the parent
|
|
* @see #onMeasure(int, int)
|
|
* @see #setFlexWrap(int)
|
|
*/
|
|
private void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
|
|
flexLines.clear();
|
|
|
|
flexLinesResult.reset();
|
|
flexboxHelper.calculateHorizontalFlexLines(
|
|
flexLinesResult, widthMeasureSpec, heightMeasureSpec);
|
|
flexLines = flexLinesResult.flexLines;
|
|
|
|
flexboxHelper.determineMainSize(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
flexboxHelper.determineCrossSize(heightMeasureSpec, getPaddingTop() + getPaddingBottom());
|
|
// Now cross size for each flex line is determined.
|
|
// Expand the views if alignItems (or mAlignSelf in each child view) is set to stretch
|
|
flexboxHelper.stretchViews();
|
|
setMeasuredDimensionForFlex(widthMeasureSpec, heightMeasureSpec, flexLinesResult.childState);
|
|
}
|
|
|
|
/**
|
|
* Set this FlexboxLayouts' width and height depending on the calculated size of main axis and
|
|
* cross axis.
|
|
*
|
|
* @param widthMeasureSpec horizontal space requirements as imposed by the parent
|
|
* @param heightMeasureSpec vertical space requirements as imposed by the parent
|
|
* @param childState the child state of the View
|
|
*/
|
|
private void setMeasuredDimensionForFlex(
|
|
int widthMeasureSpec, int heightMeasureSpec, int childState) {
|
|
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
|
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
|
int calculatedMaxHeight = getSumOfCrossSize() + getPaddingTop() + getPaddingBottom();
|
|
int calculatedMaxWidth = getLargestMainSize();
|
|
|
|
int widthSizeAndState;
|
|
switch (widthMode) {
|
|
case MeasureSpec.EXACTLY:
|
|
if (widthSize < calculatedMaxWidth) {
|
|
childState = View.combineMeasuredStates(childState, View.MEASURED_STATE_TOO_SMALL);
|
|
}
|
|
widthSizeAndState = View.resolveSizeAndState(widthSize, widthMeasureSpec, childState);
|
|
break;
|
|
case MeasureSpec.AT_MOST:
|
|
{
|
|
if (widthSize < calculatedMaxWidth) {
|
|
childState = View.combineMeasuredStates(childState, View.MEASURED_STATE_TOO_SMALL);
|
|
} else {
|
|
widthSize = calculatedMaxWidth;
|
|
}
|
|
widthSizeAndState = View.resolveSizeAndState(widthSize, widthMeasureSpec, childState);
|
|
break;
|
|
}
|
|
case MeasureSpec.UNSPECIFIED:
|
|
{
|
|
widthSizeAndState =
|
|
View.resolveSizeAndState(calculatedMaxWidth, widthMeasureSpec, childState);
|
|
break;
|
|
}
|
|
default:
|
|
throw new IllegalStateException("Unknown width mode is set: " + widthMode);
|
|
}
|
|
int heightSizeAndState;
|
|
switch (heightMode) {
|
|
case MeasureSpec.EXACTLY:
|
|
if (heightSize < calculatedMaxHeight) {
|
|
childState =
|
|
View.combineMeasuredStates(
|
|
childState, View.MEASURED_STATE_TOO_SMALL >> View.MEASURED_HEIGHT_STATE_SHIFT);
|
|
}
|
|
heightSizeAndState = View.resolveSizeAndState(heightSize, heightMeasureSpec, childState);
|
|
break;
|
|
case MeasureSpec.AT_MOST:
|
|
{
|
|
if (heightSize < calculatedMaxHeight) {
|
|
childState =
|
|
View.combineMeasuredStates(
|
|
childState, View.MEASURED_STATE_TOO_SMALL >> View.MEASURED_HEIGHT_STATE_SHIFT);
|
|
} else {
|
|
heightSize = calculatedMaxHeight;
|
|
}
|
|
heightSizeAndState = View.resolveSizeAndState(heightSize, heightMeasureSpec, childState);
|
|
break;
|
|
}
|
|
case MeasureSpec.UNSPECIFIED:
|
|
{
|
|
heightSizeAndState =
|
|
View.resolveSizeAndState(calculatedMaxHeight, heightMeasureSpec, childState);
|
|
break;
|
|
}
|
|
default:
|
|
throw new IllegalStateException("Unknown height mode is set: " + heightMode);
|
|
}
|
|
setMeasuredDimension(widthSizeAndState, heightSizeAndState);
|
|
}
|
|
|
|
@Override
|
|
public int getLargestMainSize() {
|
|
int largestSize = Integer.MIN_VALUE;
|
|
for (FlexLine flexLine : flexLines) {
|
|
largestSize = Math.max(largestSize, flexLine.mainSize);
|
|
}
|
|
return largestSize;
|
|
}
|
|
|
|
@Override
|
|
public int getSumOfCrossSize() {
|
|
int sum = 0;
|
|
for (int i = 0, size = flexLines.size(); i < size; i++) {
|
|
FlexLine flexLine = flexLines.get(i);
|
|
|
|
// Judge if the beginning or middle dividers are required
|
|
if (hasDividerBeforeFlexLine(i)) {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
sum += dividerHorizontalHeight;
|
|
} else {
|
|
sum += dividerVerticalWidth;
|
|
}
|
|
}
|
|
|
|
// Judge if the end divider is required
|
|
if (hasEndDividerAfterFlexLine(i)) {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
sum += dividerHorizontalHeight;
|
|
} else {
|
|
sum += dividerVerticalWidth;
|
|
}
|
|
}
|
|
sum += flexLine.crossSize;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
@Override
|
|
public boolean isMainAxisDirectionHorizontal() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
int layoutDirection = ViewCompat.getLayoutDirection(this);
|
|
boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
|
|
layoutHorizontal(isRtl, left, right);
|
|
}
|
|
|
|
/**
|
|
* Sub method for {@link #onLayout(boolean, int, int, int, int)}
|
|
*
|
|
* @param isRtl {@code true} if the horizontal layout direction is right to left, {@code false}
|
|
* otherwise.
|
|
* @param left the left position of this View
|
|
* @param right the right position of this View
|
|
* @see #getFlexWrap()
|
|
* @see #setFlexWrap(int)
|
|
*/
|
|
private void layoutHorizontal(boolean isRtl, int left, int right) {
|
|
int paddingLeft = getPaddingLeft();
|
|
int paddingRight = getPaddingRight();
|
|
float childLeft;
|
|
|
|
int width = right - left;
|
|
// childTop is used to align the vertical position of the children views.
|
|
int childTop = getPaddingTop();
|
|
|
|
// Used only for RTL layout
|
|
float childRight;
|
|
for (int i = 0, size = flexLines.size(); i < size; i++) {
|
|
FlexLine flexLine = flexLines.get(i);
|
|
if (hasDividerBeforeFlexLine(i)) {
|
|
childTop += dividerHorizontalHeight;
|
|
}
|
|
float spaceBetweenItem = 0f;
|
|
childLeft = paddingLeft;
|
|
childRight = width - paddingRight;
|
|
spaceBetweenItem = Math.max(spaceBetweenItem, 0);
|
|
|
|
for (int j = 0; j < flexLine.itemCount; j++) {
|
|
int index = flexLine.firstIndex + j;
|
|
View child = getReorderedChildAt(index);
|
|
if (child == null || child.getVisibility() == View.GONE) {
|
|
continue;
|
|
}
|
|
LayoutParams lp = ((LayoutParams) child.getLayoutParams());
|
|
childLeft += lp.leftMargin;
|
|
childRight -= lp.rightMargin;
|
|
int beforeDividerLength = 0;
|
|
int endDividerLength = 0;
|
|
if (hasDividerBeforeChildAtAlongMainAxis(index, j)) {
|
|
beforeDividerLength = dividerVerticalWidth;
|
|
childLeft += beforeDividerLength;
|
|
childRight -= beforeDividerLength;
|
|
}
|
|
if (j == flexLine.itemCount - 1 && (showDividerVertical & SHOW_DIVIDER_END) > 0) {
|
|
endDividerLength = dividerVerticalWidth;
|
|
}
|
|
|
|
if (isRtl) {
|
|
flexboxHelper.layoutSingleChildHorizontal(
|
|
child,
|
|
Math.round(childRight) - child.getMeasuredWidth(),
|
|
childTop,
|
|
Math.round(childRight),
|
|
childTop + child.getMeasuredHeight());
|
|
} else {
|
|
flexboxHelper.layoutSingleChildHorizontal(
|
|
child,
|
|
Math.round(childLeft),
|
|
childTop,
|
|
Math.round(childLeft) + child.getMeasuredWidth(),
|
|
childTop + child.getMeasuredHeight());
|
|
}
|
|
childLeft += child.getMeasuredWidth() + spaceBetweenItem + lp.rightMargin;
|
|
childRight -= child.getMeasuredWidth() + spaceBetweenItem + lp.leftMargin;
|
|
|
|
if (isRtl) {
|
|
flexLine.updatePositionFromView(
|
|
child, /*leftDecoration*/
|
|
endDividerLength,
|
|
0,
|
|
/*rightDecoration*/ beforeDividerLength,
|
|
0);
|
|
} else {
|
|
flexLine.updatePositionFromView(
|
|
child, /*leftDecoration*/
|
|
beforeDividerLength,
|
|
0,
|
|
/*rightDecoration*/ endDividerLength,
|
|
0);
|
|
}
|
|
}
|
|
childTop += flexLine.crossSize;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
if (dividerDrawableVertical == null && dividerDrawableHorizontal == null) {
|
|
return;
|
|
}
|
|
if (showDividerHorizontal == SHOW_DIVIDER_NONE && showDividerVertical == SHOW_DIVIDER_NONE) {
|
|
return;
|
|
}
|
|
|
|
int layoutDirection = ViewCompat.getLayoutDirection(this);
|
|
boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
|
|
drawDividersHorizontal(canvas, isRtl);
|
|
}
|
|
|
|
/**
|
|
* Sub method for {@link #onDraw(Canvas)}
|
|
*
|
|
* @param canvas the canvas on which the background will be drawn
|
|
* @param isRtl {@code true} when the horizontal layout direction is right to left, {@code false}
|
|
* otherwise
|
|
*/
|
|
private void drawDividersHorizontal(Canvas canvas, boolean isRtl) {
|
|
int paddingLeft = getPaddingLeft();
|
|
int paddingRight = getPaddingRight();
|
|
int horizontalDividerLength = Math.max(0, getWidth() - paddingRight - paddingLeft);
|
|
for (int i = 0, size = flexLines.size(); i < size; i++) {
|
|
FlexLine flexLine = flexLines.get(i);
|
|
for (int j = 0; j < flexLine.itemCount; j++) {
|
|
int viewIndex = flexLine.firstIndex + j;
|
|
View view = getReorderedChildAt(viewIndex);
|
|
if (view == null || view.getVisibility() == View.GONE) {
|
|
continue;
|
|
}
|
|
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
|
|
|
// Judge if the beginning or middle divider is needed
|
|
if (hasDividerBeforeChildAtAlongMainAxis(viewIndex, j)) {
|
|
int dividerLeft;
|
|
if (isRtl) {
|
|
dividerLeft = view.getRight() + lp.rightMargin;
|
|
} else {
|
|
dividerLeft = view.getLeft() - lp.leftMargin - dividerVerticalWidth;
|
|
}
|
|
|
|
drawVerticalDivider(canvas, dividerLeft, flexLine.top, flexLine.crossSize);
|
|
}
|
|
|
|
// Judge if the end divider is needed
|
|
if (j == flexLine.itemCount - 1) {
|
|
if ((showDividerVertical & SHOW_DIVIDER_END) > 0) {
|
|
int dividerLeft;
|
|
if (isRtl) {
|
|
dividerLeft = view.getLeft() - lp.leftMargin - dividerVerticalWidth;
|
|
} else {
|
|
dividerLeft = view.getRight() + lp.rightMargin;
|
|
}
|
|
|
|
drawVerticalDivider(canvas, dividerLeft, flexLine.top, flexLine.crossSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Judge if the beginning or middle dividers are needed before the flex line
|
|
if (hasDividerBeforeFlexLine(i)) {
|
|
int horizontalDividerTop = flexLine.top - dividerHorizontalHeight;
|
|
drawHorizontalDivider(canvas, paddingLeft, horizontalDividerTop, horizontalDividerLength);
|
|
}
|
|
// Judge if the end divider is needed before the flex line
|
|
if (hasEndDividerAfterFlexLine(i)) {
|
|
if ((showDividerHorizontal & SHOW_DIVIDER_END) > 0) {
|
|
int horizontalDividerTop = flexLine.bottom;
|
|
drawHorizontalDivider(canvas, paddingLeft, horizontalDividerTop, horizontalDividerLength);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void drawVerticalDivider(Canvas canvas, int left, int top, int length) {
|
|
if (dividerDrawableVertical == null) {
|
|
return;
|
|
}
|
|
dividerDrawableVertical.setBounds(left, top, left + dividerVerticalWidth, top + length);
|
|
dividerDrawableVertical.draw(canvas);
|
|
}
|
|
|
|
private void drawHorizontalDivider(Canvas canvas, int left, int top, int length) {
|
|
if (dividerDrawableHorizontal == null) {
|
|
return;
|
|
}
|
|
dividerDrawableHorizontal.setBounds(left, top, left + length, top + dividerHorizontalHeight);
|
|
dividerDrawableHorizontal.draw(canvas);
|
|
}
|
|
|
|
@Override
|
|
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
|
return p instanceof FlexboxLayout.LayoutParams;
|
|
}
|
|
|
|
@Override
|
|
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
|
return new FlexboxLayout.LayoutParams(getContext(), attrs);
|
|
}
|
|
|
|
@Override
|
|
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
|
|
if (lp instanceof FlexboxLayout.LayoutParams) {
|
|
return new FlexboxLayout.LayoutParams((FlexboxLayout.LayoutParams) lp);
|
|
} else if (lp instanceof MarginLayoutParams) {
|
|
return new FlexboxLayout.LayoutParams((MarginLayoutParams) lp);
|
|
}
|
|
return new LayoutParams(lp);
|
|
}
|
|
|
|
@FlexWrap
|
|
@Override
|
|
public int getFlexWrap() {
|
|
return flexWrap;
|
|
}
|
|
|
|
@Override
|
|
public void setFlexWrap(@FlexWrap int flexWrap) {
|
|
if (this.flexWrap != flexWrap) {
|
|
this.flexWrap = flexWrap;
|
|
requestLayout();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return the flex lines composing this flex container. This method returns a copy of the
|
|
* original list excluding a dummy flex line (flex line that doesn't have any flex items in it
|
|
* but used for the alignment along the cross axis). Thus any changes of the returned list are
|
|
* not reflected to the original list.
|
|
*/
|
|
@Override
|
|
public List<FlexLine> getFlexLines() {
|
|
List<FlexLine> result = new ArrayList<>(flexLines.size());
|
|
for (FlexLine flexLine : flexLines) {
|
|
if (flexLine.getItemCountNotGone() == 0) {
|
|
continue;
|
|
}
|
|
result.add(flexLine);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public int getDecorationLengthMainAxis(View view, int index, int indexInFlexLine) {
|
|
int decorationLength = 0;
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
if (hasDividerBeforeChildAtAlongMainAxis(index, indexInFlexLine)) {
|
|
decorationLength += dividerVerticalWidth;
|
|
}
|
|
if ((showDividerVertical & SHOW_DIVIDER_END) > 0) {
|
|
decorationLength += dividerVerticalWidth;
|
|
}
|
|
} else {
|
|
if (hasDividerBeforeChildAtAlongMainAxis(index, indexInFlexLine)) {
|
|
decorationLength += dividerHorizontalHeight;
|
|
}
|
|
if ((showDividerHorizontal & SHOW_DIVIDER_END) > 0) {
|
|
decorationLength += dividerHorizontalHeight;
|
|
}
|
|
}
|
|
return decorationLength;
|
|
}
|
|
|
|
@Override
|
|
public int getDecorationLengthCrossAxis(View view) {
|
|
// Decoration along the cross axis for an individual view is not supported in the
|
|
// FlexboxLayout.
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void onNewFlexLineAdded(FlexLine flexLine) {
|
|
// The size of the end divider isn't added until the flexLine is added to the flex container
|
|
// take the divider width (or height) into account when adding the flex line.
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
if ((showDividerVertical & SHOW_DIVIDER_END) > 0) {
|
|
flexLine.mainSize += dividerVerticalWidth;
|
|
flexLine.dividerLengthInMainSize += dividerVerticalWidth;
|
|
}
|
|
} else {
|
|
if ((showDividerHorizontal & SHOW_DIVIDER_END) > 0) {
|
|
flexLine.mainSize += dividerHorizontalHeight;
|
|
flexLine.dividerLengthInMainSize += dividerHorizontalHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getChildWidthMeasureSpec(int widthSpec, int padding, int childDimension) {
|
|
return getChildMeasureSpec(widthSpec, padding, childDimension);
|
|
}
|
|
|
|
@Override
|
|
public int getChildHeightMeasureSpec(int heightSpec, int padding, int childDimension) {
|
|
return getChildMeasureSpec(heightSpec, padding, childDimension);
|
|
}
|
|
|
|
@Override
|
|
public void onNewFlexItemAdded(View view, int index, int indexInFlexLine, FlexLine flexLine) {
|
|
// Check if the beginning or middle divider is required for the flex item
|
|
if (hasDividerBeforeChildAtAlongMainAxis(index, indexInFlexLine)) {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
flexLine.mainSize += dividerVerticalWidth;
|
|
flexLine.dividerLengthInMainSize += dividerVerticalWidth;
|
|
} else {
|
|
flexLine.mainSize += dividerHorizontalHeight;
|
|
flexLine.dividerLengthInMainSize += dividerHorizontalHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setFlexLines(List<FlexLine> flexLines) {
|
|
this.flexLines = flexLines;
|
|
}
|
|
|
|
@Override
|
|
public List<FlexLine> getFlexLinesInternal() {
|
|
return flexLines;
|
|
}
|
|
|
|
@Override
|
|
public void updateViewCache(int position, View view) {
|
|
// No op
|
|
}
|
|
|
|
/**
|
|
* @return the horizontal divider drawable that will divide each item.
|
|
* @see #setDividerDrawable(Drawable)
|
|
* @see #setDividerDrawableHorizontal(Drawable)
|
|
*/
|
|
@Nullable
|
|
@SuppressWarnings("UnusedDeclaration")
|
|
public Drawable getDividerDrawableHorizontal() {
|
|
return dividerDrawableHorizontal;
|
|
}
|
|
|
|
/**
|
|
* @return the vertical divider drawable that will divide each item.
|
|
* @see #setDividerDrawable(Drawable)
|
|
* @see #setDividerDrawableVertical(Drawable)
|
|
*/
|
|
@Nullable
|
|
@SuppressWarnings("UnusedDeclaration")
|
|
public Drawable getDividerDrawableVertical() {
|
|
return dividerDrawableVertical;
|
|
}
|
|
|
|
/**
|
|
* Set a drawable to be used as a divider between items. The drawable is used for both horizontal
|
|
* and vertical dividers.
|
|
*
|
|
* @param divider Drawable that will divide each item for both horizontally and vertically.
|
|
* @see #setShowDivider(int)
|
|
*/
|
|
public void setDividerDrawable(Drawable divider) {
|
|
setDividerDrawableHorizontal(divider);
|
|
setDividerDrawableVertical(divider);
|
|
}
|
|
|
|
/**
|
|
* Set a drawable to be used as a horizontal divider between items.
|
|
*
|
|
* @param divider Drawable that will divide each item.
|
|
* @see #setDividerDrawable(Drawable)
|
|
* @see #setShowDivider(int)
|
|
* @see #setShowDividerHorizontal(int)
|
|
*/
|
|
public void setDividerDrawableHorizontal(@Nullable Drawable divider) {
|
|
if (divider == dividerDrawableHorizontal) {
|
|
return;
|
|
}
|
|
dividerDrawableHorizontal = divider;
|
|
if (divider != null) {
|
|
dividerHorizontalHeight = divider.getIntrinsicHeight();
|
|
} else {
|
|
dividerHorizontalHeight = 0;
|
|
}
|
|
setWillNotDrawFlag();
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* Set a drawable to be used as a vertical divider between items.
|
|
*
|
|
* @param divider Drawable that will divide each item.
|
|
* @see #setDividerDrawable(Drawable)
|
|
* @see #setShowDivider(int)
|
|
* @see #setShowDividerVertical(int)
|
|
*/
|
|
public void setDividerDrawableVertical(@Nullable Drawable divider) {
|
|
if (divider == dividerDrawableVertical) {
|
|
return;
|
|
}
|
|
dividerDrawableVertical = divider;
|
|
if (divider != null) {
|
|
dividerVerticalWidth = divider.getIntrinsicWidth();
|
|
} else {
|
|
dividerVerticalWidth = 0;
|
|
}
|
|
setWillNotDrawFlag();
|
|
requestLayout();
|
|
}
|
|
|
|
/**
|
|
* Set how dividers should be shown between items in this layout. This method sets the divider
|
|
* mode for both horizontally and vertically.
|
|
*
|
|
* @param dividerMode One or more of {@link #SHOW_DIVIDER_BEGINNING}, {@link
|
|
* #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, or {@link #SHOW_DIVIDER_NONE} to show
|
|
* no dividers.
|
|
* @see #setShowDividerVertical(int)
|
|
* @see #setShowDividerHorizontal(int)
|
|
*/
|
|
public void setShowDivider(@DividerMode int dividerMode) {
|
|
setShowDividerVertical(dividerMode);
|
|
setShowDividerHorizontal(dividerMode);
|
|
}
|
|
|
|
/**
|
|
* Set how vertical dividers should be shown between items in this layout
|
|
*
|
|
* @param dividerMode One or more of {@link #SHOW_DIVIDER_BEGINNING}, {@link
|
|
* #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, or {@link #SHOW_DIVIDER_NONE} to show
|
|
* no dividers.
|
|
* @see #setShowDivider(int)
|
|
*/
|
|
public void setShowDividerVertical(@DividerMode int dividerMode) {
|
|
if (dividerMode != showDividerVertical) {
|
|
showDividerVertical = dividerMode;
|
|
requestLayout();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set how horizontal dividers should be shown between items in this layout.
|
|
*
|
|
* @param dividerMode One or more of {@link #SHOW_DIVIDER_BEGINNING}, {@link
|
|
* #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END}, or {@link #SHOW_DIVIDER_NONE} to show
|
|
* no dividers.
|
|
* @see #setShowDivider(int)
|
|
*/
|
|
public void setShowDividerHorizontal(@DividerMode int dividerMode) {
|
|
if (dividerMode != showDividerHorizontal) {
|
|
showDividerHorizontal = dividerMode;
|
|
requestLayout();
|
|
}
|
|
}
|
|
|
|
private void setWillNotDrawFlag() {
|
|
if (dividerDrawableHorizontal == null && dividerDrawableVertical == null) {
|
|
setWillNotDraw(true);
|
|
} else {
|
|
setWillNotDraw(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a divider is needed before the view whose indices are passed as arguments.
|
|
*
|
|
* @param index the absolute index of the view to be judged
|
|
* @param indexInFlexLine the relative index in the flex line where the view belongs
|
|
* @return {@code true} if a divider is needed, {@code false} otherwise
|
|
*/
|
|
private boolean hasDividerBeforeChildAtAlongMainAxis(int index, int indexInFlexLine) {
|
|
if (allViewsAreGoneBefore(index, indexInFlexLine)) {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
return (showDividerVertical & SHOW_DIVIDER_BEGINNING) != 0;
|
|
} else {
|
|
return (showDividerHorizontal & SHOW_DIVIDER_BEGINNING) != 0;
|
|
}
|
|
} else {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
return (showDividerVertical & SHOW_DIVIDER_MIDDLE) != 0;
|
|
} else {
|
|
return (showDividerHorizontal & SHOW_DIVIDER_MIDDLE) != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean allViewsAreGoneBefore(int index, int indexInFlexLine) {
|
|
for (int i = 1; i <= indexInFlexLine; i++) {
|
|
View view = getReorderedChildAt(index - i);
|
|
if (view != null && view.getVisibility() != View.GONE) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if a divider is needed before the flex line whose index is passed as an argument.
|
|
*
|
|
* @param flexLineIndex the index of the flex line to be checked
|
|
* @return {@code true} if a divider is needed, {@code false} otherwise
|
|
*/
|
|
private boolean hasDividerBeforeFlexLine(int flexLineIndex) {
|
|
if (flexLineIndex < 0 || flexLineIndex >= flexLines.size()) {
|
|
return false;
|
|
}
|
|
if (allFlexLinesAreDummyBefore(flexLineIndex)) {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
return (showDividerHorizontal & SHOW_DIVIDER_BEGINNING) != 0;
|
|
} else {
|
|
return (showDividerVertical & SHOW_DIVIDER_BEGINNING) != 0;
|
|
}
|
|
} else {
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
return (showDividerHorizontal & SHOW_DIVIDER_MIDDLE) != 0;
|
|
} else {
|
|
return (showDividerVertical & SHOW_DIVIDER_MIDDLE) != 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean allFlexLinesAreDummyBefore(int flexLineIndex) {
|
|
for (int i = 0; i < flexLineIndex; i++) {
|
|
if (flexLines.get(i).getItemCountNotGone() > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if a end divider is needed after the flex line whose index is passed as an argument.
|
|
*
|
|
* @param flexLineIndex the index of the flex line to be checked
|
|
* @return {@code true} if a divider is needed, {@code false} otherwise
|
|
*/
|
|
private boolean hasEndDividerAfterFlexLine(int flexLineIndex) {
|
|
if (flexLineIndex < 0 || flexLineIndex >= flexLines.size()) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = flexLineIndex + 1; i < flexLines.size(); i++) {
|
|
if (flexLines.get(i).getItemCountNotGone() > 0) {
|
|
return false;
|
|
}
|
|
}
|
|
if (isMainAxisDirectionHorizontal()) {
|
|
return (showDividerHorizontal & SHOW_DIVIDER_END) != 0;
|
|
} else {
|
|
return (showDividerVertical & SHOW_DIVIDER_END) != 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Per child parameters for children views of the {@link FlexboxLayout}.
|
|
*
|
|
* <p>Note that some parent fields (which are not primitive nor a class implements {@link
|
|
* Parcelable}) are not included as the stored/restored fields after this class is
|
|
* serialized/de-serialized as an {@link Parcelable}.
|
|
*/
|
|
public static class LayoutParams extends ViewGroup.MarginLayoutParams implements FlexItem {
|
|
|
|
/** @see FlexItem#getOrder() */
|
|
private int order = FlexItem.ORDER_DEFAULT;
|
|
|
|
/** @see FlexItem#getFlexGrow() */
|
|
private float flexGrow = FlexItem.FLEX_GROW_DEFAULT;
|
|
|
|
/** @see FlexItem#getFlexShrink() */
|
|
private float flexShrink = FlexItem.FLEX_SHRINK_DEFAULT;
|
|
|
|
/** @see FlexItem#getFlexBasisPercent() */
|
|
private float flexBasisPercent = FlexItem.FLEX_BASIS_PERCENT_DEFAULT;
|
|
|
|
/** @see FlexItem#getMinWidth() */
|
|
private int minWidth;
|
|
|
|
/** @see FlexItem#getMinHeight() */
|
|
private int minHeight;
|
|
|
|
/** @see FlexItem#getMaxWidth() */
|
|
private int maxWidth = MAX_SIZE;
|
|
|
|
/** @see FlexItem#getMaxHeight() */
|
|
private int maxHeight = MAX_SIZE;
|
|
|
|
/** @see FlexItem#isWrapBefore() */
|
|
private boolean wrapBefore;
|
|
|
|
public LayoutParams(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
|
|
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FlexboxLayout_Layout);
|
|
order = a.getInt(R.styleable.FlexboxLayout_Layout_layout_order, ORDER_DEFAULT);
|
|
flexGrow = a.getFloat(R.styleable.FlexboxLayout_Layout_layout_flexGrow, FLEX_GROW_DEFAULT);
|
|
flexShrink =
|
|
a.getFloat(R.styleable.FlexboxLayout_Layout_layout_flexShrink, FLEX_SHRINK_DEFAULT);
|
|
flexBasisPercent =
|
|
a.getFraction(
|
|
R.styleable.FlexboxLayout_Layout_layout_flexBasisPercent,
|
|
1,
|
|
1,
|
|
FLEX_BASIS_PERCENT_DEFAULT);
|
|
minWidth = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minWidth, 0);
|
|
minHeight = a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_minHeight, 0);
|
|
maxWidth =
|
|
a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxWidth, MAX_SIZE);
|
|
maxHeight =
|
|
a.getDimensionPixelSize(R.styleable.FlexboxLayout_Layout_layout_maxHeight, MAX_SIZE);
|
|
wrapBefore = a.getBoolean(R.styleable.FlexboxLayout_Layout_layout_wrapBefore, false);
|
|
a.recycle();
|
|
}
|
|
|
|
public LayoutParams(LayoutParams source) {
|
|
super(source);
|
|
|
|
order = source.order;
|
|
flexGrow = source.flexGrow;
|
|
flexShrink = source.flexShrink;
|
|
flexBasisPercent = source.flexBasisPercent;
|
|
minWidth = source.minWidth;
|
|
minHeight = source.minHeight;
|
|
maxWidth = source.maxWidth;
|
|
maxHeight = source.maxHeight;
|
|
wrapBefore = source.wrapBefore;
|
|
}
|
|
|
|
public LayoutParams(ViewGroup.LayoutParams source) {
|
|
super(source);
|
|
}
|
|
|
|
public LayoutParams(int width, int height) {
|
|
super(new ViewGroup.LayoutParams(width, height));
|
|
}
|
|
|
|
public LayoutParams(MarginLayoutParams source) {
|
|
super(source);
|
|
}
|
|
|
|
protected LayoutParams(Parcel in) {
|
|
// Passing a resolved value to resolve a lint warning
|
|
// height and width are set in this method anyway.
|
|
super(0, 0);
|
|
this.order = in.readInt();
|
|
this.flexGrow = in.readFloat();
|
|
this.flexShrink = in.readFloat();
|
|
this.flexBasisPercent = in.readFloat();
|
|
this.minWidth = in.readInt();
|
|
this.minHeight = in.readInt();
|
|
this.maxWidth = in.readInt();
|
|
this.maxHeight = in.readInt();
|
|
this.wrapBefore = in.readByte() != 0;
|
|
this.bottomMargin = in.readInt();
|
|
this.leftMargin = in.readInt();
|
|
this.rightMargin = in.readInt();
|
|
this.topMargin = in.readInt();
|
|
this.height = in.readInt();
|
|
this.width = in.readInt();
|
|
}
|
|
|
|
@Override
|
|
public int getWidth() {
|
|
return width;
|
|
}
|
|
|
|
@Override
|
|
public void setWidth(int width) {
|
|
this.width = width;
|
|
}
|
|
|
|
@Override
|
|
public int getHeight() {
|
|
return height;
|
|
}
|
|
|
|
@Override
|
|
public void setHeight(int height) {
|
|
this.height = height;
|
|
}
|
|
|
|
@Override
|
|
public int getOrder() {
|
|
return order;
|
|
}
|
|
|
|
@Override
|
|
public void setOrder(int order) {
|
|
this.order = order;
|
|
}
|
|
|
|
@Override
|
|
public float getFlexGrow() {
|
|
return flexGrow;
|
|
}
|
|
|
|
@Override
|
|
public void setFlexGrow(float flexGrow) {
|
|
this.flexGrow = flexGrow;
|
|
}
|
|
|
|
@Override
|
|
public float getFlexShrink() {
|
|
return flexShrink;
|
|
}
|
|
|
|
@Override
|
|
public void setFlexShrink(float flexShrink) {
|
|
this.flexShrink = flexShrink;
|
|
}
|
|
|
|
@Override
|
|
public int getMinWidth() {
|
|
return minWidth;
|
|
}
|
|
|
|
@Override
|
|
public void setMinWidth(int minWidth) {
|
|
this.minWidth = minWidth;
|
|
}
|
|
|
|
@Override
|
|
public int getMinHeight() {
|
|
return minHeight;
|
|
}
|
|
|
|
@Override
|
|
public void setMinHeight(int minHeight) {
|
|
this.minHeight = minHeight;
|
|
}
|
|
|
|
@Override
|
|
public int getMaxWidth() {
|
|
return maxWidth;
|
|
}
|
|
|
|
@Override
|
|
public void setMaxWidth(int maxWidth) {
|
|
this.maxWidth = maxWidth;
|
|
}
|
|
|
|
@Override
|
|
public int getMaxHeight() {
|
|
return maxHeight;
|
|
}
|
|
|
|
@Override
|
|
public void setMaxHeight(int maxHeight) {
|
|
this.maxHeight = maxHeight;
|
|
}
|
|
|
|
@Override
|
|
public boolean isWrapBefore() {
|
|
return wrapBefore;
|
|
}
|
|
|
|
@Override
|
|
public void setWrapBefore(boolean wrapBefore) {
|
|
this.wrapBefore = wrapBefore;
|
|
}
|
|
|
|
@Override
|
|
public float getFlexBasisPercent() {
|
|
return flexBasisPercent;
|
|
}
|
|
|
|
@Override
|
|
public void setFlexBasisPercent(float flexBasisPercent) {
|
|
this.flexBasisPercent = flexBasisPercent;
|
|
}
|
|
|
|
@Override
|
|
public int getMarginLeft() {
|
|
return leftMargin;
|
|
}
|
|
|
|
@Override
|
|
public int getMarginTop() {
|
|
return topMargin;
|
|
}
|
|
|
|
@Override
|
|
public int getMarginRight() {
|
|
return rightMargin;
|
|
}
|
|
|
|
@Override
|
|
public int getMarginBottom() {
|
|
return bottomMargin;
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
dest.writeInt(this.order);
|
|
dest.writeFloat(this.flexGrow);
|
|
dest.writeFloat(this.flexShrink);
|
|
dest.writeFloat(this.flexBasisPercent);
|
|
dest.writeInt(this.minWidth);
|
|
dest.writeInt(this.minHeight);
|
|
dest.writeInt(this.maxWidth);
|
|
dest.writeInt(this.maxHeight);
|
|
dest.writeByte(this.wrapBefore ? (byte) 1 : (byte) 0);
|
|
dest.writeInt(this.bottomMargin);
|
|
dest.writeInt(this.leftMargin);
|
|
dest.writeInt(this.rightMargin);
|
|
dest.writeInt(this.topMargin);
|
|
dest.writeInt(this.height);
|
|
dest.writeInt(this.width);
|
|
}
|
|
|
|
public static final Parcelable.Creator<LayoutParams> CREATOR =
|
|
new Parcelable.Creator<LayoutParams>() {
|
|
@Override
|
|
public LayoutParams createFromParcel(Parcel source) {
|
|
return new LayoutParams(source);
|
|
}
|
|
|
|
@Override
|
|
public LayoutParams[] newArray(int size) {
|
|
return new LayoutParams[size];
|
|
}
|
|
};
|
|
}
|
|
}
|