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