ScaleFrameLayout.java revision 3595aa0cbdaa8e754365ca94a0b9eb8fc52b9796
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.content.Context;
17import android.graphics.drawable.Drawable;
18import android.util.AttributeSet;
19import android.view.Gravity;
20import android.view.View;
21import android.widget.FrameLayout;
22
23/**
24 * Subclass of FrameLayout that support scale layout area size for children.
25 * @hide
26 */
27public class ScaleFrameLayout extends FrameLayout {
28
29    private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
30
31    private float mLayoutScaleX = 1f;
32    private float mLayoutScaleY = 1f;
33
34    public ScaleFrameLayout(Context context) {
35        this(context ,null);
36    }
37
38    public ScaleFrameLayout(Context context, AttributeSet attrs) {
39        this(context, attrs, 0);
40    }
41
42    public ScaleFrameLayout(Context context, AttributeSet attrs,
43            int defStyle) {
44        super(context, attrs, defStyle);
45    }
46
47    public void setLayoutScaleX(float scaleX) {
48        if (scaleX != mLayoutScaleX) {
49            mLayoutScaleX = scaleX;
50            requestLayout();
51        }
52    }
53
54    public void setLayoutScaleY(float scaleY) {
55        if (scaleY != mLayoutScaleY) {
56            mLayoutScaleY = scaleY;
57            requestLayout();
58        }
59    }
60
61    @Override
62    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
63        final int count = getChildCount();
64
65        final int parentLeft, parentRight;
66        final float pivotX = getPivotX();
67        if (mLayoutScaleX != 1f) {
68            parentLeft = getPaddingLeft() + (int)(pivotX - pivotX / mLayoutScaleX + 0.5f);
69            parentRight = (int)(pivotX + (right - left - pivotX) / mLayoutScaleX + 0.5f)
70                    - getPaddingRight();
71        } else {
72            parentLeft = getPaddingLeft();
73            parentRight = right - left - getPaddingRight();
74        }
75
76        final int parentTop, parentBottom;
77        final float pivotY = getPivotY();
78        if (mLayoutScaleY != 1f) {
79            parentTop = getPaddingTop() + (int)(pivotY - pivotY / mLayoutScaleY + 0.5f);
80            parentBottom = (int)(pivotY + (bottom - top - pivotY) / mLayoutScaleY + 0.5f)
81                    - getPaddingBottom();
82        } else {
83            parentTop = getPaddingTop();
84            parentBottom = bottom - top - getPaddingBottom();
85        }
86
87        for (int i = 0; i < count; i++) {
88            final View child = getChildAt(i);
89            if (child.getVisibility() != GONE) {
90                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
91
92                final int width = child.getMeasuredWidth();
93                final int height = child.getMeasuredHeight();
94
95                int childLeft;
96                int childTop;
97
98                int gravity = lp.gravity;
99                if (gravity == -1) {
100                    gravity = DEFAULT_CHILD_GRAVITY;
101                }
102
103                final int layoutDirection = getLayoutDirection();
104                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
105                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
106
107                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
108                    case Gravity.CENTER_HORIZONTAL:
109                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
110                                lp.leftMargin - lp.rightMargin;
111                        break;
112                    case Gravity.RIGHT:
113                        childLeft = parentRight - width - lp.rightMargin;
114                        break;
115                    case Gravity.LEFT:
116                    default:
117                        childLeft = parentLeft + lp.leftMargin;
118                }
119
120                switch (verticalGravity) {
121                    case Gravity.TOP:
122                        childTop = parentTop + lp.topMargin;
123                        break;
124                    case Gravity.CENTER_VERTICAL:
125                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
126                                lp.topMargin - lp.bottomMargin;
127                        break;
128                    case Gravity.BOTTOM:
129                        childTop = parentBottom - height - lp.bottomMargin;
130                        break;
131                    default:
132                        childTop = parentTop + lp.topMargin;
133                }
134
135                child.layout(childLeft, childTop, childLeft + width, childTop + height);
136                // synchronize child pivot to be same as ScaleFrameLayout's pivot
137                child.setPivotX(pivotX - childLeft);
138                child.setPivotY(pivotY - childTop);
139            }
140        }
141    }
142
143    private static int getScaledMeasureSpec(int measureSpec, float scale) {
144        return scale == 1f ? measureSpec : MeasureSpec.makeMeasureSpec(
145                (int) (MeasureSpec.getSize(measureSpec) / scale + 0.5f),
146                MeasureSpec.getMode(measureSpec));
147    }
148
149    @Override
150    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
151        if (mLayoutScaleX != 1f || mLayoutScaleY != 1f) {
152            final int scaledWidthMeasureSpec =
153                    getScaledMeasureSpec(widthMeasureSpec, mLayoutScaleX);
154            final int scaledHeightMeasureSpec =
155                    getScaledMeasureSpec(heightMeasureSpec, mLayoutScaleY);
156            super.onMeasure(scaledWidthMeasureSpec, scaledHeightMeasureSpec);
157            setMeasuredDimension((int)(getMeasuredWidth() * mLayoutScaleX + 0.5f),
158                    (int)(getMeasuredHeight() * mLayoutScaleY + 0.5f));
159        } else {
160            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
161        }
162    }
163
164    /**
165     * setForeground() is not supported,  throws UnsupportedOperationException() when called.
166     */
167    @Override
168    public void setForeground(Drawable d) {
169        throw new UnsupportedOperationException();
170    }
171}
172