12c96011dee46e09d6ab766a42a98b299927d633bChris Banes/* 22c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Copyright (C) 2016 The Android Open Source Project 32c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 42c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 52c96011dee46e09d6ab766a42a98b299927d633bChris Banes * you may not use this file except in compliance with the License. 62c96011dee46e09d6ab766a42a98b299927d633bChris Banes * You may obtain a copy of the License at 72c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 82c96011dee46e09d6ab766a42a98b299927d633bChris Banes * http://www.apache.org/licenses/LICENSE-2.0 92c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 102c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Unless required by applicable law or agreed to in writing, software 112c96011dee46e09d6ab766a42a98b299927d633bChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 122c96011dee46e09d6ab766a42a98b299927d633bChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132c96011dee46e09d6ab766a42a98b299927d633bChris Banes * See the License for the specific language governing permissions and 142c96011dee46e09d6ab766a42a98b299927d633bChris Banes * limitations under the License. 152c96011dee46e09d6ab766a42a98b299927d633bChris Banes */ 162c96011dee46e09d6ab766a42a98b299927d633bChris Banes 172c96011dee46e09d6ab766a42a98b299927d633bChris Banespackage android.support.v7.widget; 182c96011dee46e09d6ab766a42a98b299927d633bChris Banes 198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 202c96011dee46e09d6ab766a42a98b299927d633bChris Banes 212c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.content.Context; 222c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.graphics.drawable.Drawable; 232c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.annotation.Nullable; 242c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.annotation.RestrictTo; 252c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v4.view.GravityCompat; 262c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v4.view.ViewCompat; 272c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v7.appcompat.R; 282c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.util.AttributeSet; 292c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.Gravity; 302c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.View; 312c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.ViewGroup; 322c96011dee46e09d6ab766a42a98b299927d633bChris Banes 332c96011dee46e09d6ab766a42a98b299927d633bChris Banes/** 342c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Special implementation of linear layout that's capable of laying out alert 352c96011dee46e09d6ab766a42a98b299927d633bChris Banes * dialog components. 362c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <p> 372c96011dee46e09d6ab766a42a98b299927d633bChris Banes * A dialog consists of up to three panels. All panels are optional, and a 382c96011dee46e09d6ab766a42a98b299927d633bChris Banes * dialog may contain only a single panel. The panels are laid out according 392c96011dee46e09d6ab766a42a98b299927d633bChris Banes * to the following guidelines: 402c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <ul> 412c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <li>topPanel: exactly wrap_content</li> 422c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <li>contentPanel OR customPanel: at most fill_parent, first priority for 432c96011dee46e09d6ab766a42a98b299927d633bChris Banes * extra space</li> 442c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <li>buttonPanel: at least minHeight, at most wrap_content, second 452c96011dee46e09d6ab766a42a98b299927d633bChris Banes * priority for extra space</li> 462c96011dee46e09d6ab766a42a98b299927d633bChris Banes * </ul> 472c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 482c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @hide 492c96011dee46e09d6ab766a42a98b299927d633bChris Banes */ 508e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP) 512c96011dee46e09d6ab766a42a98b299927d633bChris Banespublic class AlertDialogLayout extends LinearLayoutCompat { 522c96011dee46e09d6ab766a42a98b299927d633bChris Banes 532c96011dee46e09d6ab766a42a98b299927d633bChris Banes public AlertDialogLayout(@Nullable Context context) { 542c96011dee46e09d6ab766a42a98b299927d633bChris Banes super(context); 552c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 562c96011dee46e09d6ab766a42a98b299927d633bChris Banes 572c96011dee46e09d6ab766a42a98b299927d633bChris Banes public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs) { 582c96011dee46e09d6ab766a42a98b299927d633bChris Banes super(context, attrs); 592c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 602c96011dee46e09d6ab766a42a98b299927d633bChris Banes 612c96011dee46e09d6ab766a42a98b299927d633bChris Banes @Override 622c96011dee46e09d6ab766a42a98b299927d633bChris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 632c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (!tryOnMeasure(widthMeasureSpec, heightMeasureSpec)) { 642c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Failed to perform custom measurement, let superclass handle it. 652c96011dee46e09d6ab766a42a98b299927d633bChris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 662c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 672c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 682c96011dee46e09d6ab766a42a98b299927d633bChris Banes 692c96011dee46e09d6ab766a42a98b299927d633bChris Banes private boolean tryOnMeasure(int widthMeasureSpec, int heightMeasureSpec) { 702c96011dee46e09d6ab766a42a98b299927d633bChris Banes View topPanel = null; 712c96011dee46e09d6ab766a42a98b299927d633bChris Banes View buttonPanel = null; 722c96011dee46e09d6ab766a42a98b299927d633bChris Banes View middlePanel = null; 732c96011dee46e09d6ab766a42a98b299927d633bChris Banes 742c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int count = getChildCount(); 752c96011dee46e09d6ab766a42a98b299927d633bChris Banes for (int i = 0; i < count; i++) { 762c96011dee46e09d6ab766a42a98b299927d633bChris Banes final View child = getChildAt(i); 772c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (child.getVisibility() == View.GONE) { 782c96011dee46e09d6ab766a42a98b299927d633bChris Banes continue; 792c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 802c96011dee46e09d6ab766a42a98b299927d633bChris Banes 812c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int id = child.getId(); 822c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (id == R.id.topPanel) { 832c96011dee46e09d6ab766a42a98b299927d633bChris Banes topPanel = child; 842c96011dee46e09d6ab766a42a98b299927d633bChris Banes } else if (id == R.id.buttonPanel) { 852c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonPanel = child; 862c96011dee46e09d6ab766a42a98b299927d633bChris Banes } else if (id == R.id.contentPanel || id == R.id.customPanel) { 872c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (middlePanel != null) { 882c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Both the content and custom are visible. Abort! 892c96011dee46e09d6ab766a42a98b299927d633bChris Banes return false; 902c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 912c96011dee46e09d6ab766a42a98b299927d633bChris Banes middlePanel = child; 922c96011dee46e09d6ab766a42a98b299927d633bChris Banes } else { 932c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Unknown top-level child. Abort! 942c96011dee46e09d6ab766a42a98b299927d633bChris Banes return false; 952c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 962c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 972c96011dee46e09d6ab766a42a98b299927d633bChris Banes 982c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int heightMode = MeasureSpec.getMode(heightMeasureSpec); 992c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 1002c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 1012c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1022c96011dee46e09d6ab766a42a98b299927d633bChris Banes int childState = 0; 1032c96011dee46e09d6ab766a42a98b299927d633bChris Banes int usedHeight = getPaddingTop() + getPaddingBottom(); 1042c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1052c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (topPanel != null) { 1062c96011dee46e09d6ab766a42a98b299927d633bChris Banes topPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED); 1072c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1082c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight += topPanel.getMeasuredHeight(); 109fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas childState = View.combineMeasuredStates(childState, topPanel.getMeasuredState()); 1102c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1112c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1122c96011dee46e09d6ab766a42a98b299927d633bChris Banes int buttonHeight = 0; 1132c96011dee46e09d6ab766a42a98b299927d633bChris Banes int buttonWantsHeight = 0; 1142c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (buttonPanel != null) { 1152c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED); 1162c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonHeight = resolveMinimumHeight(buttonPanel); 1172c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonWantsHeight = buttonPanel.getMeasuredHeight() - buttonHeight; 1182c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1192c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight += buttonHeight; 120fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState()); 1212c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1222c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1232c96011dee46e09d6ab766a42a98b299927d633bChris Banes int middleHeight = 0; 1242c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (middlePanel != null) { 1252c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childHeightSpec; 1262c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (heightMode == MeasureSpec.UNSPECIFIED) { 1272c96011dee46e09d6ab766a42a98b299927d633bChris Banes childHeightSpec = MeasureSpec.UNSPECIFIED; 1282c96011dee46e09d6ab766a42a98b299927d633bChris Banes } else { 1292c96011dee46e09d6ab766a42a98b299927d633bChris Banes childHeightSpec = MeasureSpec.makeMeasureSpec( 1302c96011dee46e09d6ab766a42a98b299927d633bChris Banes Math.max(0, heightSize - usedHeight), heightMode); 1312c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1322c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1332c96011dee46e09d6ab766a42a98b299927d633bChris Banes middlePanel.measure(widthMeasureSpec, childHeightSpec); 1342c96011dee46e09d6ab766a42a98b299927d633bChris Banes middleHeight = middlePanel.getMeasuredHeight(); 1352c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1362c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight += middleHeight; 137fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState()); 1382c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1392c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1402c96011dee46e09d6ab766a42a98b299927d633bChris Banes int remainingHeight = heightSize - usedHeight; 1412c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1422c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Time for the "real" button measure pass. If we have remaining space, 1432c96011dee46e09d6ab766a42a98b299927d633bChris Banes // make the button pane bigger up to its target height. Otherwise, 1442c96011dee46e09d6ab766a42a98b299927d633bChris Banes // just remeasure the button at whatever height it needs. 1452c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (buttonPanel != null) { 1462c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight -= buttonHeight; 1472c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1482c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int heightToGive = Math.min(remainingHeight, buttonWantsHeight); 1492c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (heightToGive > 0) { 1502c96011dee46e09d6ab766a42a98b299927d633bChris Banes remainingHeight -= heightToGive; 1512c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonHeight += heightToGive; 1522c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1532c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1542c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childHeightSpec = MeasureSpec.makeMeasureSpec( 1552c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonHeight, MeasureSpec.EXACTLY); 1562c96011dee46e09d6ab766a42a98b299927d633bChris Banes buttonPanel.measure(widthMeasureSpec, childHeightSpec); 1572c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1582c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight += buttonPanel.getMeasuredHeight(); 159fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState()); 1602c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1612c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1622c96011dee46e09d6ab766a42a98b299927d633bChris Banes // If we still have remaining space, make the middle pane bigger up 1632c96011dee46e09d6ab766a42a98b299927d633bChris Banes // to the maximum height. 1642c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (middlePanel != null && remainingHeight > 0) { 1652c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight -= middleHeight; 1662c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1672c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int heightToGive = remainingHeight; 1682c96011dee46e09d6ab766a42a98b299927d633bChris Banes remainingHeight -= heightToGive; 1692c96011dee46e09d6ab766a42a98b299927d633bChris Banes middleHeight += heightToGive; 1702c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1712c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Pass the same height mode as we're using for the dialog itself. 1722c96011dee46e09d6ab766a42a98b299927d633bChris Banes // If it's EXACTLY, then the middle pane MUST use the entire 1732c96011dee46e09d6ab766a42a98b299927d633bChris Banes // height. 1742c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childHeightSpec = MeasureSpec.makeMeasureSpec( 1752c96011dee46e09d6ab766a42a98b299927d633bChris Banes middleHeight, heightMode); 1762c96011dee46e09d6ab766a42a98b299927d633bChris Banes middlePanel.measure(widthMeasureSpec, childHeightSpec); 1772c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1782c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight += middlePanel.getMeasuredHeight(); 179fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState()); 1802c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1812c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1822c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Compute desired width as maximum child width. 1832c96011dee46e09d6ab766a42a98b299927d633bChris Banes int maxWidth = 0; 1842c96011dee46e09d6ab766a42a98b299927d633bChris Banes for (int i = 0; i < count; i++) { 1852c96011dee46e09d6ab766a42a98b299927d633bChris Banes final View child = getChildAt(i); 1862c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (child.getVisibility() != View.GONE) { 1872c96011dee46e09d6ab766a42a98b299927d633bChris Banes maxWidth = Math.max(maxWidth, child.getMeasuredWidth()); 1882c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1892c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 1902c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1912c96011dee46e09d6ab766a42a98b299927d633bChris Banes maxWidth += getPaddingLeft() + getPaddingRight(); 1922c96011dee46e09d6ab766a42a98b299927d633bChris Banes 193fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas final int widthSizeAndState = View.resolveSizeAndState( 1942c96011dee46e09d6ab766a42a98b299927d633bChris Banes maxWidth, widthMeasureSpec, childState); 195fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas final int heightSizeAndState = View.resolveSizeAndState( 1962c96011dee46e09d6ab766a42a98b299927d633bChris Banes usedHeight, heightMeasureSpec, 0); 1972c96011dee46e09d6ab766a42a98b299927d633bChris Banes setMeasuredDimension(widthSizeAndState, heightSizeAndState); 1982c96011dee46e09d6ab766a42a98b299927d633bChris Banes 1992c96011dee46e09d6ab766a42a98b299927d633bChris Banes // If the children weren't already measured EXACTLY, we need to run 2002c96011dee46e09d6ab766a42a98b299927d633bChris Banes // another measure pass to for MATCH_PARENT widths. 2012c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (widthMode != MeasureSpec.EXACTLY) { 2022c96011dee46e09d6ab766a42a98b299927d633bChris Banes forceUniformWidth(count, heightMeasureSpec); 2032c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2042c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2052c96011dee46e09d6ab766a42a98b299927d633bChris Banes return true; 2062c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2072c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2082c96011dee46e09d6ab766a42a98b299927d633bChris Banes /** 2092c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Remeasures child views to exactly match the layout's measured width. 2102c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 2112c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @param count the number of child views 2122c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @param heightMeasureSpec the original height measure spec 2132c96011dee46e09d6ab766a42a98b299927d633bChris Banes */ 2142c96011dee46e09d6ab766a42a98b299927d633bChris Banes private void forceUniformWidth(int count, int heightMeasureSpec) { 2152c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Pretend that the linear layout has an exact size. 2162c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int uniformMeasureSpec = MeasureSpec.makeMeasureSpec( 2172c96011dee46e09d6ab766a42a98b299927d633bChris Banes getMeasuredWidth(), MeasureSpec.EXACTLY); 2182c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2192c96011dee46e09d6ab766a42a98b299927d633bChris Banes for (int i = 0; i < count; i++) { 2202c96011dee46e09d6ab766a42a98b299927d633bChris Banes final View child = getChildAt(i); 2212c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (child.getVisibility() != GONE) { 2222c96011dee46e09d6ab766a42a98b299927d633bChris Banes final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 2232c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (lp.width == LayoutParams.MATCH_PARENT) { 2242c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Temporarily force children to reuse their old measured 2252c96011dee46e09d6ab766a42a98b299927d633bChris Banes // height. 2262c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int oldHeight = lp.height; 2272c96011dee46e09d6ab766a42a98b299927d633bChris Banes lp.height = child.getMeasuredHeight(); 2282c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2292c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Remeasure with new dimensions. 2302c96011dee46e09d6ab766a42a98b299927d633bChris Banes measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0); 2312c96011dee46e09d6ab766a42a98b299927d633bChris Banes lp.height = oldHeight; 2322c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2332c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2342c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2352c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2362c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2372c96011dee46e09d6ab766a42a98b299927d633bChris Banes /** 2382c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Attempts to resolve the minimum height of a view. 2392c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <p> 2402c96011dee46e09d6ab766a42a98b299927d633bChris Banes * If the view doesn't have a minimum height set and only contains a single 2412c96011dee46e09d6ab766a42a98b299927d633bChris Banes * child, attempts to resolve the minimum height of the child view. 2422c96011dee46e09d6ab766a42a98b299927d633bChris Banes * 2432c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @param v the view whose minimum height to resolve 2442c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @return the minimum height 2452c96011dee46e09d6ab766a42a98b299927d633bChris Banes */ 2462c96011dee46e09d6ab766a42a98b299927d633bChris Banes private static int resolveMinimumHeight(View v) { 2472c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int minHeight = ViewCompat.getMinimumHeight(v); 2482c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (minHeight > 0) { 2492c96011dee46e09d6ab766a42a98b299927d633bChris Banes return minHeight; 2502c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2512c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2522c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (v instanceof ViewGroup) { 2532c96011dee46e09d6ab766a42a98b299927d633bChris Banes final ViewGroup vg = (ViewGroup) v; 2542c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (vg.getChildCount() == 1) { 2552c96011dee46e09d6ab766a42a98b299927d633bChris Banes return resolveMinimumHeight(vg.getChildAt(0)); 2562c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2572c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2582c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2592c96011dee46e09d6ab766a42a98b299927d633bChris Banes return 0; 2602c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2612c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2622c96011dee46e09d6ab766a42a98b299927d633bChris Banes @Override 2632c96011dee46e09d6ab766a42a98b299927d633bChris Banes protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 2642c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int paddingLeft = getPaddingLeft(); 2652c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2662c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Where right end of child should go 2672c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int width = right - left; 2682c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childRight = width - getPaddingRight(); 2692c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2702c96011dee46e09d6ab766a42a98b299927d633bChris Banes // Space available for child 2712c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childSpace = width - paddingLeft - getPaddingRight(); 2722c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2732c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int totalLength = getMeasuredHeight(); 2742c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int count = getChildCount(); 2752c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int gravity = getGravity(); 2762c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int majorGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 2772c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int minorGravity = gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK; 2782c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2792c96011dee46e09d6ab766a42a98b299927d633bChris Banes int childTop; 2802c96011dee46e09d6ab766a42a98b299927d633bChris Banes switch (majorGravity) { 2812c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.BOTTOM: 2822c96011dee46e09d6ab766a42a98b299927d633bChris Banes // totalLength contains the padding already 2832c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop = getPaddingTop() + bottom - top - totalLength; 2842c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 2852c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2862c96011dee46e09d6ab766a42a98b299927d633bChris Banes // totalLength contains the padding already 2872c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.CENTER_VERTICAL: 2882c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop = getPaddingTop() + (bottom - top - totalLength) / 2; 2892c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 2902c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2912c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.TOP: 2922c96011dee46e09d6ab766a42a98b299927d633bChris Banes default: 2932c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop = getPaddingTop(); 2942c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 2952c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 2962c96011dee46e09d6ab766a42a98b299927d633bChris Banes 2972c96011dee46e09d6ab766a42a98b299927d633bChris Banes final Drawable dividerDrawable = getDividerDrawable(); 2982c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int dividerHeight = dividerDrawable == null ? 2992c96011dee46e09d6ab766a42a98b299927d633bChris Banes 0 : dividerDrawable.getIntrinsicHeight(); 3002c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3012c96011dee46e09d6ab766a42a98b299927d633bChris Banes for (int i = 0; i < count; i++) { 3022c96011dee46e09d6ab766a42a98b299927d633bChris Banes final View child = getChildAt(i); 3032c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (child != null && child.getVisibility() != GONE) { 3042c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childWidth = child.getMeasuredWidth(); 3052c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childHeight = child.getMeasuredHeight(); 3062c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3072c96011dee46e09d6ab766a42a98b299927d633bChris Banes final LinearLayoutCompat.LayoutParams lp = 3082c96011dee46e09d6ab766a42a98b299927d633bChris Banes (LinearLayoutCompat.LayoutParams) child.getLayoutParams(); 3092c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3102c96011dee46e09d6ab766a42a98b299927d633bChris Banes int layoutGravity = lp.gravity; 3112c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (layoutGravity < 0) { 3122c96011dee46e09d6ab766a42a98b299927d633bChris Banes layoutGravity = minorGravity; 3132c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3142c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int layoutDirection = ViewCompat.getLayoutDirection(this); 3152c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int absoluteGravity = GravityCompat.getAbsoluteGravity( 3162c96011dee46e09d6ab766a42a98b299927d633bChris Banes layoutGravity, layoutDirection); 3172c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3182c96011dee46e09d6ab766a42a98b299927d633bChris Banes final int childLeft; 3192c96011dee46e09d6ab766a42a98b299927d633bChris Banes switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 3202c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.CENTER_HORIZONTAL: 3212c96011dee46e09d6ab766a42a98b299927d633bChris Banes childLeft = paddingLeft + ((childSpace - childWidth) / 2) 3222c96011dee46e09d6ab766a42a98b299927d633bChris Banes + lp.leftMargin - lp.rightMargin; 3232c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 3242c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3252c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.RIGHT: 3262c96011dee46e09d6ab766a42a98b299927d633bChris Banes childLeft = childRight - childWidth - lp.rightMargin; 3272c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 3282c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3292c96011dee46e09d6ab766a42a98b299927d633bChris Banes case Gravity.LEFT: 3302c96011dee46e09d6ab766a42a98b299927d633bChris Banes default: 3312c96011dee46e09d6ab766a42a98b299927d633bChris Banes childLeft = paddingLeft + lp.leftMargin; 3322c96011dee46e09d6ab766a42a98b299927d633bChris Banes break; 3332c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3342c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3352c96011dee46e09d6ab766a42a98b299927d633bChris Banes if (hasDividerBeforeChildAt(i)) { 3362c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop += dividerHeight; 3372c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3382c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3392c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop += lp.topMargin; 3402c96011dee46e09d6ab766a42a98b299927d633bChris Banes setChildFrame(child, childLeft, childTop, childWidth, childHeight); 3412c96011dee46e09d6ab766a42a98b299927d633bChris Banes childTop += childHeight + lp.bottomMargin; 3422c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3432c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3442c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3452c96011dee46e09d6ab766a42a98b299927d633bChris Banes 3462c96011dee46e09d6ab766a42a98b299927d633bChris Banes private void setChildFrame(View child, int left, int top, int width, int height) { 3472c96011dee46e09d6ab766a42a98b299927d633bChris Banes child.layout(left, top, left + width, top + height); 3482c96011dee46e09d6ab766a42a98b299927d633bChris Banes } 3492c96011dee46e09d6ab766a42a98b299927d633bChris Banes}