/* * Copyright (C) 2010 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.apps.tvremote.layout; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import com.google.android.apps.tvremote.R; /** * A FixedLayout positions its children in an array of rows of one component, * with percentage of total height specified. FixedLayout has to have known * dimensions. * */ public final class FixedLayout extends ViewGroup { /** * Array of heights of child views. */ private int[] mHeightArray; public FixedLayout(Context context) { this(context, null); } public FixedLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FixedLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * Fixed layout paramters. */ public static class FixedLayoutParams extends ViewGroup.LayoutParams { public static final float UNSPECIFIED = -1; public float percent; public FixedLayoutParams(Context context, AttributeSet attrs) { super(FILL_PARENT, WRAP_CONTENT); TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.FixedLayout); percent = array.getFloat(R.styleable.FixedLayout_layout_percent, UNSPECIFIED); array.recycle(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { throw new RuntimeException( "FixedLayout cannot have UNSPECIFIED dimensions"); } int width = widthSpecSize - getPaddingLeft() - getPaddingRight(); int height = heightSpecSize - getPaddingTop() - getPaddingBottom(); int count = getChildCount(); mHeightArray = new int[count]; if (count == 0) { return; } int remainingHeight = height; int unspecifiedCount = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); final ViewGroup.LayoutParams params = child.getLayoutParams(); final float currentPercent; if (params instanceof FixedLayoutParams) { currentPercent = ((FixedLayoutParams) params).percent; } else { currentPercent = FixedLayoutParams.UNSPECIFIED; } if (currentPercent > 0.0f) { mHeightArray[i] = (int) (height * currentPercent / 100.0f); remainingHeight -= mHeightArray[i]; } else { mHeightArray[i] = 0; } if (mHeightArray[i] < 0) { throw new IllegalStateException("Negative height of row: " + i); } if (mHeightArray[i] == 0) { unspecifiedCount++; } } if (remainingHeight < 0) { throw new IllegalStateException( "Remaining height < 0: " + remainingHeight); } if (unspecifiedCount > 0) { int i = 0; for (; unspecifiedCount > 0; unspecifiedCount--) { int currentHeight = remainingHeight / unspecifiedCount; for (; i < count && unspecifiedCount > 0; ++i) { if (mHeightArray[i] == 0) { mHeightArray[i] = currentHeight; remainingHeight -= currentHeight; break; } } } if (remainingHeight != 0) { throw new IllegalStateException( "Remaining height != 0: " + remainingHeight); } } else if (remainingHeight > 0) { mHeightArray[count - 1] += remainingHeight; } for (int i = 0; i < count; i++) { final View child = getChildAt(i); int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); int childheightSpec = MeasureSpec.makeMeasureSpec(mHeightArray[i], MeasureSpec.EXACTLY); child.measure(childWidthSpec, childheightSpec); } setMeasuredDimension(widthSpecSize, heightSpecSize); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int paddingLeft = getPaddingLeft(); final int paddingTop = getPaddingTop(); final int count = getChildCount(); int childTop = paddingTop; for (int i = 0; i < count; i++) { View child = getChildAt(i); if (child.getVisibility() != GONE) { int childLeft = paddingLeft; child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight()); } childTop += mHeightArray[i]; } } @Override public LayoutParams generateLayoutParams(AttributeSet attrs) { return new FixedLayoutParams(getContext(), attrs); } @Override protected boolean checkLayoutParams(LayoutParams p) { return (p instanceof FixedLayoutParams); } }