1ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez/* 2ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * Copyright (C) 2015 The Android Open Source Project 3ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * 4ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * Licensed under the Apache License, Version 2.0 (the "License"); 5ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * you may not use this file except in compliance with the License. 6ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * You may obtain a copy of the License at 7ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * 8ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * http://www.apache.org/licenses/LICENSE-2.0 9ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * 10ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * Unless required by applicable law or agreed to in writing, software 11ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * distributed under the License is distributed on an "AS IS" BASIS, 12ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * See the License for the specific language governing permissions and 14ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * limitations under the License. 15ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez */ 16ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 17ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezpackage com.android.tools.idea.editors.theme.widgets; 18ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 19ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.content.Context; 20ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.util.AttributeSet; 21ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.util.DisplayMetrics; 22ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.util.TypedValue; 23ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.view.View; 24ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezimport android.view.ViewGroup; 25ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 26ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez/** 27ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * Custom layout used in the theme editor to display the component preview. It arranges the child 28ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * Views as a grid of cards. 29ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * <p/> 30ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * The Views are measured and the maximum width and height are used to dimension all the child 31ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * components. Any margin attributes from the children are ignored and only the item_margin element 32ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez * is used. 33ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez */ 34ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez@SuppressWarnings("unused") 35ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perezpublic class ThemePreviewLayout extends ViewGroup { 36ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private final int mMaxColumns; 37ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private final int mMaxColumnWidth; 38ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private final int mMinColumnWidth; 39ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private final int mItemHorizontalMargin; 40ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private final int mItemVerticalMargin; 41ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 42ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez /** Item width to use for every card component. This includes margins. */ 43ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private int mItemWidth; 44ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez /** Item height to use for every card component. This includes margins. */ 45ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private int mItemHeight; 46ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 47ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez /** Calculated number of columns */ 48ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez private int mNumColumns; 49ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 50ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez public ThemePreviewLayout(Context context) { 51ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez this(context, null); 52ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 53ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 54ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez public ThemePreviewLayout(Context context, AttributeSet attrs) { 55ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez this(context, attrs, 0); 56ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 57ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 58ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez public ThemePreviewLayout(Context context, AttributeSet attrs, int defStyleAttr) { 59ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez super(context, attrs, defStyleAttr); 60ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 61ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (attrs == null) { 62ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMaxColumnWidth = Integer.MAX_VALUE; 63ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMinColumnWidth = 0; 64ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMaxColumns = Integer.MAX_VALUE; 65ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemHorizontalMargin = 0; 66ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemVerticalMargin = 0; 67ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez return; 68ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 69ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 70ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez DisplayMetrics dm = getResources().getDisplayMetrics(); 71ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int maxColumnWidth = attrs.getAttributeIntValue(null, "max_column_width", Integer 72ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez .MAX_VALUE); 73ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int minColumnWidth = attrs.getAttributeIntValue(null, "min_column_width", 0); 74ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int itemHorizontalMargin = attrs.getAttributeIntValue(null, "item_horizontal_margin", 0); 75ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int itemVerticalMargin = attrs.getAttributeIntValue(null, "item_vertical_margin", 0); 76ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 77ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMaxColumnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 78ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez maxColumnWidth, 79ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez dm); 80ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMinColumnWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 81ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez minColumnWidth, 82ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez dm); 83ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemHorizontalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 84ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez itemHorizontalMargin, 85ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez dm); 86ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemVerticalMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 87ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez itemVerticalMargin, 88ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez dm); 89ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mMaxColumns = attrs.getAttributeIntValue(null, "max_columns", Integer.MAX_VALUE); 90ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 91ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 92ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez @Override 93ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 94ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Measure the column size. 95ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // The column has a minimum width that will be used to calculate the maximum number of 96ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // columns that we can fit in the available space. 97ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // 98ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Once we have the maximum number of columns, we will span all columns width evenly to fill 99ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // all the available space. 100ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int wSize = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight; 101ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 102ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Calculate the desired width of all columns and take the maximum. 103ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // This step can be skipped if we have a fixed column height so we do not have to 104ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // dynamically calculate it. 105ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int childWidthSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 106ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 107ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int itemWidth = 0; 108ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int itemHeight = 0; 109ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez for (int i = 0; i < getChildCount(); i++) { 110ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez View v = getChildAt(i); 111ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 112ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (v.getVisibility() == GONE) { 113ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez continue; 114ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 115ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 116ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez measureChild(v, childWidthSpec, childHeightSpec); 117ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 118ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez itemWidth = Math.max(itemWidth, v.getMeasuredWidth()); 119ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez itemHeight = Math.max(itemHeight, v.getMeasuredHeight()); 120ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 121ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 122ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez itemWidth = Math.min(Math.max(itemWidth, mMinColumnWidth), mMaxColumnWidth); 123ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mNumColumns = Math.min((int) Math.ceil((double) wSize / itemWidth), mMaxColumns); 124ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 125ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Check how much space this distribution would take taking into account the margins. 126ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // If it's bigger than what we have, remove one column. 127ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int wSizeNeeded = mNumColumns * itemWidth + (mNumColumns - 1) * mItemHorizontalMargin; 128ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (wSizeNeeded > wSize && mNumColumns > 1) { 129ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mNumColumns--; 130ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 131ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 132ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (getChildCount() < mNumColumns) { 133ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mNumColumns = getChildCount(); 134ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 135ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (mNumColumns == 0) { 136ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mNumColumns = 1; 137ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 138ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 139ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Inform each child of the measurement 140ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez childWidthSpec = MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY); 141ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez childHeightSpec = MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY); 142ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez for (int i = 0; i < getChildCount(); i++) { 143ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez View v = getChildAt(i); 144ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 145ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (v.getVisibility() == GONE) { 146ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez continue; 147ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 148ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 149ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez measureChild(v, childWidthSpec, childHeightSpec); 150ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 151ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 152ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Calculate the height of the first column to measure our own size 153ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int firstColumnItems = getChildCount() / mNumColumns + ((getChildCount() % mNumColumns) > 0 154ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez ? 1 : 0); 155ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 156ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int horizontalMarginsTotalWidth = (mNumColumns - 1) * mItemHorizontalMargin; 157ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int verticalMarginsTotalHeight = (firstColumnItems - 1) * mItemVerticalMargin; 158ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int totalWidth = mNumColumns * itemWidth + horizontalMarginsTotalWidth + 159ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mPaddingRight + mPaddingLeft; 160ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int totalHeight = firstColumnItems * itemHeight + verticalMarginsTotalHeight + 161ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mPaddingBottom + mPaddingTop; 162ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 163ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez setMeasuredDimension(resolveSize(totalWidth, widthMeasureSpec), 164ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez resolveSize(totalHeight, heightMeasureSpec)); 165ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 166ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemWidth = itemWidth; 167ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez mItemHeight = itemHeight; 168ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 169ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 170ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez @Override 171ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez protected void onLayout(boolean changed, int l, int t, int r, int b) { 172ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int itemsPerColumn = getChildCount() / mNumColumns; 173ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // The remainder items are distributed one per column. 174ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int remainderItems = getChildCount() % mNumColumns; 175ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 176ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int x = mPaddingLeft; 177ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int y = mPaddingTop; 178ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez int position = 1; 179ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez for (int i = 0; i < getChildCount(); i++) { 180ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez View v = getChildAt(i); 181ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez v.layout(x, 182ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez y, 183ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez x + mItemWidth, 184ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez y + mItemHeight); 185ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 186ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez if (position == itemsPerColumn + (remainderItems > 0 ? 1 : 0)) { 187ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez // Break column 188ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez position = 1; 189ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez remainderItems--; 190ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez x += mItemWidth + mItemHorizontalMargin; 191ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez y = mPaddingTop; 192ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } else { 193ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez position++; 194ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez y += mItemHeight + mItemVerticalMargin; 195ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 196ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 197ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez } 198ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez} 199ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 200ffb2906f2444d150246bc1215a3c8055001d81c8Diego Perez 201