| /* |
| * 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 |
| * |
| * http://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 androidx.widget; |
| |
| import android.content.Context; |
| import android.graphics.drawable.Drawable; |
| import android.os.Build; |
| import android.util.AttributeSet; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.ViewGroup.MarginLayoutParams; |
| |
| import androidx.annotation.AttrRes; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.RequiresApi; |
| import androidx.annotation.StyleRes; |
| |
| import java.util.ArrayList; |
| |
| class BaseLayout extends ViewGroup { |
| private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); |
| |
| BaseLayout(@NonNull Context context) { |
| super(context); |
| } |
| |
| BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs) { |
| this(context, attrs, 0); |
| } |
| |
| BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs, |
| @AttrRes int defStyleAttr) { |
| super(context, attrs, defStyleAttr); |
| } |
| |
| @RequiresApi(21) |
| BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs, |
| @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { |
| super(context, attrs, defStyleAttr, defStyleRes); |
| } |
| |
| @Override |
| public boolean checkLayoutParams(LayoutParams p) { |
| return p instanceof MarginLayoutParams; |
| } |
| |
| @Override |
| public LayoutParams generateDefaultLayoutParams() { |
| return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); |
| } |
| |
| @Override |
| public LayoutParams generateLayoutParams(AttributeSet attrs) { |
| return new MarginLayoutParams(getContext(), attrs); |
| } |
| |
| @Override |
| public LayoutParams generateLayoutParams(LayoutParams lp) { |
| if (lp instanceof MarginLayoutParams) { |
| return lp; |
| } |
| return new MarginLayoutParams(lp); |
| } |
| |
| @Override |
| public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| int count = getChildCount(); |
| |
| final boolean measureMatchParentChildren = |
| View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY |
| || View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY; |
| mMatchParentChildren.clear(); |
| |
| int maxHeight = 0; |
| int maxWidth = 0; |
| int childState = 0; |
| |
| for (int i = 0; i < count; i++) { |
| final View child = getChildAt(i); |
| if (child.getVisibility() != View.GONE) { |
| measureChildWithMargins( |
| child, widthMeasureSpec, 0, heightMeasureSpec, 0); |
| final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); |
| maxWidth = Math.max(maxWidth, |
| child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); |
| maxHeight = Math.max(maxHeight, |
| child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); |
| childState = childState | child.getMeasuredState(); |
| if (measureMatchParentChildren) { |
| if (lp.width == LayoutParams.MATCH_PARENT |
| || lp.height == LayoutParams.MATCH_PARENT) { |
| mMatchParentChildren.add(child); |
| } |
| } |
| } |
| } |
| |
| // Account for padding too |
| maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); |
| maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); |
| |
| // Check against our minimum height and width |
| maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); |
| maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); |
| |
| if (Build.VERSION.SDK_INT >= 23) { |
| // Check against our foreground's minimum height and width |
| final Drawable drawable = getForeground(); |
| if (drawable != null) { |
| maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); |
| maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); |
| } |
| } |
| |
| setMeasuredDimension( |
| resolveSizeAndState(maxWidth, widthMeasureSpec, childState), |
| resolveSizeAndState(maxHeight, heightMeasureSpec, |
| childState << View.MEASURED_HEIGHT_STATE_SHIFT)); |
| |
| count = mMatchParentChildren.size(); |
| if (count > 1) { |
| for (int i = 0; i < count; i++) { |
| final View child = mMatchParentChildren.get(i); |
| final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); |
| |
| final int childWidthMeasureSpec; |
| if (lp.width == LayoutParams.MATCH_PARENT) { |
| final int width = Math.max(0, getMeasuredWidth() |
| - getPaddingLeftWithForeground() - getPaddingRightWithForeground() |
| - lp.leftMargin - lp.rightMargin); |
| childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec( |
| width, View.MeasureSpec.EXACTLY); |
| } else { |
| childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, |
| getPaddingLeftWithForeground() + getPaddingRightWithForeground() |
| + lp.leftMargin + lp.rightMargin, lp.width); |
| } |
| |
| final int childHeightMeasureSpec; |
| if (lp.height == LayoutParams.MATCH_PARENT) { |
| final int height = Math.max(0, getMeasuredHeight() |
| - getPaddingTopWithForeground() - getPaddingBottomWithForeground() |
| - lp.topMargin - lp.bottomMargin); |
| childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec( |
| height, View.MeasureSpec.EXACTLY); |
| } else { |
| childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, |
| getPaddingTopWithForeground() + getPaddingBottomWithForeground() |
| + lp.topMargin + lp.bottomMargin, lp.height); |
| } |
| |
| child.measure(childWidthMeasureSpec, childHeightMeasureSpec); |
| } |
| } |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| final int count = getChildCount(); |
| |
| final int parentLeft = getPaddingLeftWithForeground(); |
| final int parentRight = right - left - getPaddingRightWithForeground(); |
| |
| final int parentTop = getPaddingTopWithForeground(); |
| final int parentBottom = bottom - top - getPaddingBottomWithForeground(); |
| |
| for (int i = 0; i < count; i++) { |
| final View child = getChildAt(i); |
| if (child.getVisibility() != View.GONE) { |
| final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); |
| |
| final int width = child.getMeasuredWidth(); |
| final int height = child.getMeasuredHeight(); |
| |
| int childLeft; |
| int childTop; |
| |
| childLeft = parentLeft + (parentRight - parentLeft - width) / 2 |
| + lp.leftMargin - lp.rightMargin; |
| |
| childTop = parentTop + (parentBottom - parentTop - height) / 2 |
| + lp.topMargin - lp.bottomMargin; |
| |
| child.layout(childLeft, childTop, childLeft + width, childTop + height); |
| } |
| } |
| } |
| |
| @Override |
| public boolean shouldDelayChildPressedState() { |
| return false; |
| } |
| |
| private int getPaddingLeftWithForeground() { |
| return isForegroundInsidePadding() ? Math.max(getPaddingLeft(), 0) : |
| getPaddingLeft() + 0; |
| } |
| |
| private int getPaddingRightWithForeground() { |
| return isForegroundInsidePadding() ? Math.max(getPaddingRight(), 0) : |
| getPaddingRight() + 0; |
| } |
| |
| private int getPaddingTopWithForeground() { |
| return isForegroundInsidePadding() ? Math.max(getPaddingTop(), 0) : |
| getPaddingTop() + 0; |
| } |
| |
| private int getPaddingBottomWithForeground() { |
| return isForegroundInsidePadding() ? Math.max(getPaddingBottom(), 0) : |
| getPaddingBottom() + 0; |
| } |
| |
| // A stub method for View's isForegroundInsidePadding() which is hidden. |
| // Always returns true for now, since the default value is true. |
| // See View's isForegroundInsidePadding method. |
| private boolean isForegroundInsidePadding() { |
| return true; |
| } |
| } |