1/* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.widget; 18 19import android.graphics.drawable.Drawable; 20import android.media.update.ViewGroupProvider; 21import android.util.AttributeSet; 22import android.view.View; 23import android.view.ViewGroup; 24import android.view.ViewGroup.LayoutParams; 25import android.view.ViewGroup.MarginLayoutParams; 26 27import java.util.ArrayList; 28 29public class BaseLayout extends ViewGroupImpl { 30 private final ViewGroup mInstance; 31 private final ViewGroupProvider mSuperProvider; 32 private final ViewGroupProvider mPrivateProvider; 33 34 private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); 35 36 public BaseLayout(ViewGroup instance, 37 ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { 38 super(instance, superProvider, privateProvider); 39 mInstance = instance; 40 mSuperProvider = superProvider; 41 mPrivateProvider = privateProvider; 42 } 43 44 @Override 45 public boolean checkLayoutParams_impl(LayoutParams p) { 46 return p instanceof MarginLayoutParams; 47 } 48 49 @Override 50 public LayoutParams generateDefaultLayoutParams_impl() { 51 return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 52 } 53 54 @Override 55 public LayoutParams generateLayoutParams_impl(AttributeSet attrs) { 56 return new MarginLayoutParams(mInstance.getContext(), attrs); 57 } 58 59 @Override 60 public LayoutParams generateLayoutParams_impl(LayoutParams lp) { 61 if (lp instanceof MarginLayoutParams) { 62 return lp; 63 } 64 return new MarginLayoutParams(lp); 65 } 66 67 @Override 68 public void onMeasure_impl(int widthMeasureSpec, int heightMeasureSpec) { 69 int count = mInstance.getChildCount(); 70 71 final boolean measureMatchParentChildren = 72 View.MeasureSpec.getMode(widthMeasureSpec) != View.MeasureSpec.EXACTLY || 73 View.MeasureSpec.getMode(heightMeasureSpec) != View.MeasureSpec.EXACTLY; 74 mMatchParentChildren.clear(); 75 76 int maxHeight = 0; 77 int maxWidth = 0; 78 int childState = 0; 79 80 for (int i = 0; i < count; i++) { 81 final View child = mInstance.getChildAt(i); 82 if (child.getVisibility() != View.GONE) { 83 mPrivateProvider.measureChildWithMargins_impl( 84 child, widthMeasureSpec, 0, heightMeasureSpec, 0); 85 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 86 maxWidth = Math.max(maxWidth, 87 child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 88 maxHeight = Math.max(maxHeight, 89 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 90 childState = childState | child.getMeasuredState(); 91 if (measureMatchParentChildren) { 92 if (lp.width == LayoutParams.MATCH_PARENT || 93 lp.height == LayoutParams.MATCH_PARENT) { 94 mMatchParentChildren.add(child); 95 } 96 } 97 } 98 } 99 100 // Account for padding too 101 maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); 102 maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); 103 104 // Check against our minimum height and width 105 maxHeight = Math.max(maxHeight, mPrivateProvider.getSuggestedMinimumHeight_impl()); 106 maxWidth = Math.max(maxWidth, mPrivateProvider.getSuggestedMinimumWidth_impl()); 107 108 // Check against our foreground's minimum height and width 109 final Drawable drawable = mInstance.getForeground(); 110 if (drawable != null) { 111 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); 112 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); 113 } 114 115 mPrivateProvider.setMeasuredDimension_impl( 116 mInstance.resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 117 mInstance.resolveSizeAndState(maxHeight, heightMeasureSpec, 118 childState << View.MEASURED_HEIGHT_STATE_SHIFT)); 119 120 count = mMatchParentChildren.size(); 121 if (count > 1) { 122 for (int i = 0; i < count; i++) { 123 final View child = mMatchParentChildren.get(i); 124 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 125 126 final int childWidthMeasureSpec; 127 if (lp.width == LayoutParams.MATCH_PARENT) { 128 final int width = Math.max(0, mInstance.getMeasuredWidth() 129 - getPaddingLeftWithForeground() - getPaddingRightWithForeground() 130 - lp.leftMargin - lp.rightMargin); 131 childWidthMeasureSpec = View.MeasureSpec.makeMeasureSpec( 132 width, View.MeasureSpec.EXACTLY); 133 } else { 134 childWidthMeasureSpec = mInstance.getChildMeasureSpec(widthMeasureSpec, 135 getPaddingLeftWithForeground() + getPaddingRightWithForeground() + 136 lp.leftMargin + lp.rightMargin, 137 lp.width); 138 } 139 140 final int childHeightMeasureSpec; 141 if (lp.height == LayoutParams.MATCH_PARENT) { 142 final int height = Math.max(0, mInstance.getMeasuredHeight() 143 - getPaddingTopWithForeground() - getPaddingBottomWithForeground() 144 - lp.topMargin - lp.bottomMargin); 145 childHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec( 146 height, View.MeasureSpec.EXACTLY); 147 } else { 148 childHeightMeasureSpec = mInstance.getChildMeasureSpec(heightMeasureSpec, 149 getPaddingTopWithForeground() + getPaddingBottomWithForeground() + 150 lp.topMargin + lp.bottomMargin, 151 lp.height); 152 } 153 154 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 155 } 156 } 157 } 158 159 @Override 160 public void onLayout_impl(boolean changed, int left, int top, int right, int bottom) { 161 final int count = mInstance.getChildCount(); 162 163 final int parentLeft = getPaddingLeftWithForeground(); 164 final int parentRight = right - left - getPaddingRightWithForeground(); 165 166 final int parentTop = getPaddingTopWithForeground(); 167 final int parentBottom = bottom - top - getPaddingBottomWithForeground(); 168 169 for (int i = 0; i < count; i++) { 170 final View child = mInstance.getChildAt(i); 171 if (child.getVisibility() != View.GONE) { 172 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 173 174 final int width = child.getMeasuredWidth(); 175 final int height = child.getMeasuredHeight(); 176 177 int childLeft; 178 int childTop; 179 180 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 181 lp.leftMargin - lp.rightMargin; 182 183 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 184 lp.topMargin - lp.bottomMargin; 185 186 child.layout(childLeft, childTop, childLeft + width, childTop + height); 187 } 188 } 189 } 190 191 @Override 192 public boolean shouldDelayChildPressedState_impl() { 193 return false; 194 } 195 196 private int getPaddingLeftWithForeground() { 197 return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingLeft(), 0) : 198 mInstance.getPaddingLeft() + 0; 199 } 200 201 private int getPaddingRightWithForeground() { 202 return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingRight(), 0) : 203 mInstance.getPaddingRight() + 0; 204 } 205 206 private int getPaddingTopWithForeground() { 207 return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingTop(), 0) : 208 mInstance.getPaddingTop() + 0; 209 } 210 211 private int getPaddingBottomWithForeground() { 212 return mInstance.isForegroundInsidePadding() ? Math.max(mInstance.getPaddingBottom(), 0) : 213 mInstance.getPaddingBottom() + 0; 214 } 215} 216