1 /* 2 * Copyright (C) 2017 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 com.android.setupwizardlib.view; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.util.AttributeSet; 22 import android.view.View; 23 import android.widget.FrameLayout; 24 25 import com.android.setupwizardlib.R; 26 27 /** 28 * A layout that will measure its children size based on the space it is given, by using its 29 * {@code android:minWidth}, {@code android:minHeight}, {@code android:maxWidth}, and 30 * {@code android:maxHeight} values. 31 * 32 * <p>Typically this is used to show an illustration image or video on the screen. For optimal UX, 33 * those assets typically want to occupy the remaining space available on screen within a certain 34 * range, and then stop scaling beyond the min/max size attributes. Therefore this view is typically 35 * used inside a ScrollView with {@code fillViewport} set to true, together with a linear layout 36 * weight or relative layout to fill the remaining space visible on screen. 37 * 38 * <p>When measuring, this view ignores its children and simply layout according to the minWidth / 39 * minHeight given. Therefore it is common for children of this layout to have width / height set to 40 * {@code match_parent}. The maxWidth / maxHeight values will then be applied to the children to 41 * make sure they are not too big. 42 */ 43 public class FillContentLayout extends FrameLayout { 44 45 private int mMaxWidth; 46 private int mMaxHeight; 47 48 public FillContentLayout(Context context) { 49 this(context, null); 50 } 51 52 public FillContentLayout(Context context, AttributeSet attrs) { 53 this(context, attrs, R.attr.suwFillContentLayoutStyle); 54 } 55 56 public FillContentLayout(Context context, AttributeSet attrs, int defStyleAttr) { 57 super(context, attrs, defStyleAttr); 58 init(context, attrs, defStyleAttr); 59 } 60 61 private void init(Context context, AttributeSet attrs, int defStyleAttr) { 62 TypedArray a = context.obtainStyledAttributes( 63 attrs, 64 R.styleable.SuwFillContentLayout, 65 defStyleAttr, 66 0); 67 68 mMaxHeight = a.getDimensionPixelSize( 69 R.styleable.SuwFillContentLayout_android_maxHeight, -1); 70 mMaxWidth = a.getDimensionPixelSize(R.styleable.SuwFillContentLayout_android_maxWidth, -1); 71 72 a.recycle(); 73 } 74 75 @Override 76 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 77 // Measure this view with the minWidth and minHeight, without asking the children. 78 // (Children size is the drawable's intrinsic size, and we don't want that to influence 79 // the size of the illustration). 80 setMeasuredDimension( 81 getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 82 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 83 84 int childCount = getChildCount(); 85 for (int i = 0; i < childCount; i++) { 86 measureIllustrationChild(getChildAt(i), getMeasuredWidth(), getMeasuredHeight()); 87 } 88 } 89 90 private void measureIllustrationChild(View child, int parentWidth, int parentHeight) { 91 // Modified from ViewGroup#measureChildWithMargins 92 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 93 94 // Create measure specs that are no bigger than min(parentSize, maxSize) 95 96 int childWidthMeasureSpec = getMaxSizeMeasureSpec( 97 Math.min(mMaxWidth, parentWidth), 98 getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, 99 lp.width); 100 int childHeightMeasureSpec = getMaxSizeMeasureSpec( 101 Math.min(mMaxHeight, parentHeight), 102 getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, 103 lp.height); 104 105 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 106 } 107 108 private static int getMaxSizeMeasureSpec(int maxSize, int padding, int childDimension) { 109 // Modified from ViewGroup#getChildMeasureSpec 110 int size = Math.max(0, maxSize - padding); 111 112 if (childDimension >= 0) { 113 // Child wants a specific size... so be it 114 return MeasureSpec.makeMeasureSpec(childDimension, MeasureSpec.EXACTLY); 115 } else if (childDimension == LayoutParams.MATCH_PARENT) { 116 // Child wants to be our size. So be it. 117 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); 118 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 119 // Child wants to determine its own size. It can't be 120 // bigger than us. 121 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST); 122 } 123 return 0; 124 } 125 } 126