/* * Copyright (C) 2011 The Android Open Source Project * * 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.android.contacts.widget; import com.android.contacts.R; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * Layout that calculates its height based on its width, or vice versa (depending on the set * {@link #setDirection(Direction)}. The factor is specified in {@link #setRatio(float)}. * <p>For {@link Direction#heightToWidth}: width := height * factor</p> * <p>For {@link Direction#widthToHeight}: height := width * factor</p> * <p>Only one child is allowed; if more are required, another ViewGroup can be used as the direct * child of this layout.</p> */ public class ProportionalLayout extends ViewGroup { /** Specifies whether the width should be calculated based on the height or vice-versa */ public enum Direction { widthToHeight("widthToHeight"), heightToWidth("heightToWidth"); public final String XmlName; private Direction(String xmlName) { XmlName = xmlName; } /** * Parses the given direction string and returns the Direction instance. This * should be used when inflating from xml */ public static Direction parse(String value) { if (widthToHeight.XmlName.equals(value)) { return Direction.widthToHeight; } else if (heightToWidth.XmlName.equals(value)) { return Direction.heightToWidth; } else { throw new IllegalStateException("direction must be either " + widthToHeight.XmlName + " or " + heightToWidth.XmlName); } } } private Direction mDirection; private float mRatio; public ProportionalLayout(Context context) { super(context); } public ProportionalLayout(Context context, AttributeSet attrs) { super(context, attrs); initFromAttributes(context, attrs); } public ProportionalLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initFromAttributes(context, attrs); } private void initFromAttributes(Context context, AttributeSet attrs) { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProportionalLayout); mDirection = Direction.parse(a.getString(R.styleable.ProportionalLayout_direction)); mRatio = a.getFloat(R.styleable.ProportionalLayout_ratio, 1.0f); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (getChildCount() != 1) { throw new IllegalStateException("ProportionalLayout requires exactly one child"); } final View child = getChildAt(0); // Do a first pass to get the optimal size measureChild(child, widthMeasureSpec, heightMeasureSpec); final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); final int width; final int height; if (mDirection == Direction.heightToWidth) { width = Math.round(childHeight * mRatio); height = childHeight; } else { width = childWidth; height = Math.round(childWidth * mRatio); } // Do a second pass so that all children are informed of the new size measureChild(child, MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); setMeasuredDimension( resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (getChildCount() != 1) { throw new IllegalStateException("ProportionalLayout requires exactly one child"); } final View child = getChildAt(0); child.layout(0, 0, right-left, bottom-top); } public Direction getDirection() { return mDirection; } public void setDirection(Direction direction) { mDirection = direction; } public float getRatio() { return mRatio; } public void setRatio(float ratio) { mRatio = ratio; } }