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 */ 14 package android.support.v17.leanback.widget; 15 16 import android.content.Context; 17 import android.graphics.drawable.Drawable; 18 import android.util.AttributeSet; 19 import android.view.Gravity; 20 import android.view.View; 21 import android.widget.FrameLayout; 22 23 /** 24 * Subclass of FrameLayout that support scale layout area size for children. 25 * @hide 26 */ 27 public 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 ScaleFrameLayout(Context context)34 public ScaleFrameLayout(Context context) { 35 this(context ,null); 36 } 37 ScaleFrameLayout(Context context, AttributeSet attrs)38 public ScaleFrameLayout(Context context, AttributeSet attrs) { 39 this(context, attrs, 0); 40 } 41 ScaleFrameLayout(Context context, AttributeSet attrs, int defStyle)42 public ScaleFrameLayout(Context context, AttributeSet attrs, 43 int defStyle) { 44 super(context, attrs, defStyle); 45 } 46 setLayoutScaleX(float scaleX)47 public void setLayoutScaleX(float scaleX) { 48 if (scaleX != mLayoutScaleX) { 49 mLayoutScaleX = scaleX; 50 requestLayout(); 51 } 52 } 53 setLayoutScaleY(float scaleY)54 public void setLayoutScaleY(float scaleY) { 55 if (scaleY != mLayoutScaleY) { 56 mLayoutScaleY = scaleY; 57 requestLayout(); 58 } 59 } 60 61 @Override onLayout(boolean changed, int left, int top, int right, int bottom)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 int layoutDirection = getLayoutDirection(); 67 final float pivotX = (layoutDirection == View.LAYOUT_DIRECTION_RTL) ? 68 getWidth() - getPivotX() : 69 getPivotX(); 70 if (mLayoutScaleX != 1f) { 71 parentLeft = getPaddingLeft() + (int)(pivotX - pivotX / mLayoutScaleX + 0.5f); 72 parentRight = (int)(pivotX + (right - left - pivotX) / mLayoutScaleX + 0.5f) 73 - getPaddingRight(); 74 } else { 75 parentLeft = getPaddingLeft(); 76 parentRight = right - left - getPaddingRight(); 77 } 78 79 final int parentTop, parentBottom; 80 final float pivotY = getPivotY(); 81 if (mLayoutScaleY != 1f) { 82 parentTop = getPaddingTop() + (int)(pivotY - pivotY / mLayoutScaleY + 0.5f); 83 parentBottom = (int)(pivotY + (bottom - top - pivotY) / mLayoutScaleY + 0.5f) 84 - getPaddingBottom(); 85 } else { 86 parentTop = getPaddingTop(); 87 parentBottom = bottom - top - getPaddingBottom(); 88 } 89 90 for (int i = 0; i < count; i++) { 91 final View child = getChildAt(i); 92 if (child.getVisibility() != GONE) { 93 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 94 95 final int width = child.getMeasuredWidth(); 96 final int height = child.getMeasuredHeight(); 97 98 int childLeft; 99 int childTop; 100 101 int gravity = lp.gravity; 102 if (gravity == -1) { 103 gravity = DEFAULT_CHILD_GRAVITY; 104 } 105 106 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 107 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 108 109 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 110 case Gravity.CENTER_HORIZONTAL: 111 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 112 lp.leftMargin - lp.rightMargin; 113 break; 114 case Gravity.RIGHT: 115 childLeft = parentRight - width - lp.rightMargin; 116 break; 117 case Gravity.LEFT: 118 default: 119 childLeft = parentLeft + lp.leftMargin; 120 } 121 122 switch (verticalGravity) { 123 case Gravity.TOP: 124 childTop = parentTop + lp.topMargin; 125 break; 126 case Gravity.CENTER_VERTICAL: 127 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 128 lp.topMargin - lp.bottomMargin; 129 break; 130 case Gravity.BOTTOM: 131 childTop = parentBottom - height - lp.bottomMargin; 132 break; 133 default: 134 childTop = parentTop + lp.topMargin; 135 } 136 137 child.layout(childLeft, childTop, childLeft + width, childTop + height); 138 // synchronize child pivot to be same as ScaleFrameLayout's pivot 139 child.setPivotX(pivotX - childLeft); 140 child.setPivotY(pivotY - childTop); 141 } 142 } 143 } 144 getScaledMeasureSpec(int measureSpec, float scale)145 private static int getScaledMeasureSpec(int measureSpec, float scale) { 146 return scale == 1f ? measureSpec : MeasureSpec.makeMeasureSpec( 147 (int) (MeasureSpec.getSize(measureSpec) / scale + 0.5f), 148 MeasureSpec.getMode(measureSpec)); 149 } 150 151 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)152 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 153 if (mLayoutScaleX != 1f || mLayoutScaleY != 1f) { 154 final int scaledWidthMeasureSpec = 155 getScaledMeasureSpec(widthMeasureSpec, mLayoutScaleX); 156 final int scaledHeightMeasureSpec = 157 getScaledMeasureSpec(heightMeasureSpec, mLayoutScaleY); 158 super.onMeasure(scaledWidthMeasureSpec, scaledHeightMeasureSpec); 159 setMeasuredDimension((int)(getMeasuredWidth() * mLayoutScaleX + 0.5f), 160 (int)(getMeasuredHeight() * mLayoutScaleY + 0.5f)); 161 } else { 162 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 163 } 164 } 165 166 /** 167 * setForeground() is not supported, throws UnsupportedOperationException() when called. 168 */ 169 @Override setForeground(Drawable d)170 public void setForeground(Drawable d) { 171 throw new UnsupportedOperationException(); 172 } 173 } 174