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 17 package androidx.media.widget; 18 19 import android.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.os.Build; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 import androidx.annotation.AttrRes; 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 import androidx.annotation.RequiresApi; 30 import androidx.annotation.StyleRes; 31 32 import java.util.ArrayList; 33 34 class BaseLayout extends ViewGroup { 35 private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1); 36 BaseLayout(@onNull Context context)37 BaseLayout(@NonNull Context context) { 38 super(context); 39 } 40 BaseLayout(@onNull Context context, @Nullable AttributeSet attrs)41 BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs) { 42 super(context, attrs); 43 } 44 BaseLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr)45 BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs, 46 @AttrRes int defStyleAttr) { 47 super(context, attrs, defStyleAttr); 48 } 49 50 @RequiresApi(21) BaseLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)51 BaseLayout(@NonNull Context context, @Nullable AttributeSet attrs, 52 @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { 53 super(context, attrs, defStyleAttr, defStyleRes); 54 } 55 56 @Override checkLayoutParams(LayoutParams p)57 public boolean checkLayoutParams(LayoutParams p) { 58 return p instanceof MarginLayoutParams; 59 } 60 61 @Override generateDefaultLayoutParams()62 public LayoutParams generateDefaultLayoutParams() { 63 return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 64 } 65 66 @Override generateLayoutParams(AttributeSet attrs)67 public LayoutParams generateLayoutParams(AttributeSet attrs) { 68 return new MarginLayoutParams(getContext(), attrs); 69 } 70 71 @Override generateLayoutParams(LayoutParams lp)72 public LayoutParams generateLayoutParams(LayoutParams lp) { 73 if (lp instanceof MarginLayoutParams) { 74 return lp; 75 } 76 return new MarginLayoutParams(lp); 77 } 78 79 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)80 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 81 int count = getChildCount(); 82 83 final boolean measureMatchParentChildren = 84 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY 85 || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; 86 mMatchParentChildren.clear(); 87 88 int maxHeight = 0; 89 int maxWidth = 0; 90 int childState = 0; 91 92 for (int i = 0; i < count; i++) { 93 final View child = getChildAt(i); 94 if (child.getVisibility() != View.GONE) { 95 measureChildWithMargins( 96 child, widthMeasureSpec, 0, heightMeasureSpec, 0); 97 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 98 maxWidth = Math.max(maxWidth, 99 child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); 100 maxHeight = Math.max(maxHeight, 101 child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); 102 childState = childState | child.getMeasuredState(); 103 if (measureMatchParentChildren) { 104 if (lp.width == LayoutParams.MATCH_PARENT 105 || lp.height == LayoutParams.MATCH_PARENT) { 106 mMatchParentChildren.add(child); 107 } 108 } 109 } 110 } 111 112 // Account for padding too 113 maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground(); 114 maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground(); 115 116 // Check against our minimum height and width 117 maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); 118 maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); 119 120 if (Build.VERSION.SDK_INT >= 23) { 121 // Check against our foreground's minimum height and width 122 final Drawable drawable = getForeground(); 123 if (drawable != null) { 124 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight()); 125 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth()); 126 } 127 } 128 129 setMeasuredDimension( 130 resolveSizeAndState(maxWidth, widthMeasureSpec, childState), 131 resolveSizeAndState(maxHeight, heightMeasureSpec, 132 childState << View.MEASURED_HEIGHT_STATE_SHIFT)); 133 134 count = mMatchParentChildren.size(); 135 if (count > 1) { 136 for (int i = 0; i < count; i++) { 137 final View child = mMatchParentChildren.get(i); 138 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 139 140 final int childWidthMeasureSpec; 141 if (lp.width == LayoutParams.MATCH_PARENT) { 142 final int width = Math.max(0, getMeasuredWidth() 143 - getPaddingLeftWithForeground() - getPaddingRightWithForeground() 144 - lp.leftMargin - lp.rightMargin); 145 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( 146 width, MeasureSpec.EXACTLY); 147 } else { 148 childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 149 getPaddingLeftWithForeground() + getPaddingRightWithForeground() 150 + lp.leftMargin + lp.rightMargin, lp.width); 151 } 152 153 final int childHeightMeasureSpec; 154 if (lp.height == LayoutParams.MATCH_PARENT) { 155 final int height = Math.max(0, getMeasuredHeight() 156 - getPaddingTopWithForeground() - getPaddingBottomWithForeground() 157 - lp.topMargin - lp.bottomMargin); 158 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( 159 height, MeasureSpec.EXACTLY); 160 } else { 161 childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 162 getPaddingTopWithForeground() + getPaddingBottomWithForeground() 163 + lp.topMargin + lp.bottomMargin, lp.height); 164 } 165 166 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 167 } 168 } 169 } 170 171 @Override onLayout(boolean changed, int left, int top, int right, int bottom)172 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 173 final int count = getChildCount(); 174 175 final int parentLeft = getPaddingLeftWithForeground(); 176 final int parentRight = right - left - getPaddingRightWithForeground(); 177 178 final int parentTop = getPaddingTopWithForeground(); 179 final int parentBottom = bottom - top - getPaddingBottomWithForeground(); 180 181 for (int i = 0; i < count; i++) { 182 final View child = getChildAt(i); 183 if (child.getVisibility() != View.GONE) { 184 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 185 186 final int width = child.getMeasuredWidth(); 187 final int height = child.getMeasuredHeight(); 188 189 int childLeft; 190 int childTop; 191 192 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 193 + lp.leftMargin - lp.rightMargin; 194 195 childTop = parentTop + (parentBottom - parentTop - height) / 2 196 + lp.topMargin - lp.bottomMargin; 197 198 child.layout(childLeft, childTop, childLeft + width, childTop + height); 199 } 200 } 201 } 202 203 @Override shouldDelayChildPressedState()204 public boolean shouldDelayChildPressedState() { 205 return false; 206 } 207 getPaddingLeftWithForeground()208 private int getPaddingLeftWithForeground() { 209 return isForegroundInsidePadding() ? Math.max(getPaddingLeft(), 0) : 210 getPaddingLeft() + 0; 211 } 212 getPaddingRightWithForeground()213 private int getPaddingRightWithForeground() { 214 return isForegroundInsidePadding() ? Math.max(getPaddingRight(), 0) : 215 getPaddingRight() + 0; 216 } 217 getPaddingTopWithForeground()218 private int getPaddingTopWithForeground() { 219 return isForegroundInsidePadding() ? Math.max(getPaddingTop(), 0) : 220 getPaddingTop() + 0; 221 } 222 getPaddingBottomWithForeground()223 private int getPaddingBottomWithForeground() { 224 return isForegroundInsidePadding() ? Math.max(getPaddingBottom(), 0) : 225 getPaddingBottom() + 0; 226 } 227 228 // A stub method for View's isForegroundInsidePadding() which is hidden. 229 // Always returns true for now, since the default value is true. 230 // See View's isForegroundInsidePadding method. isForegroundInsidePadding()231 private boolean isForegroundInsidePadding() { 232 return true; 233 } 234 } 235