ScaleFrameLayout.java revision a9f6062bd2dd02b3de253b57c69302893bf1f2e3
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.view.ViewGroup; 22import android.widget.FrameLayout; 23 24/** 25 * Subclass of FrameLayout that support scale layout area size for children. 26 * @hide 27 */ 28public 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 void onLayout(boolean changed, int left, int top, int right, int bottom) { 83 final int count = getChildCount(); 84 85 final int parentLeft, parentRight; 86 final int layoutDirection = getLayoutDirection(); 87 final float pivotX = (layoutDirection == View.LAYOUT_DIRECTION_RTL) ? 88 getWidth() - getPivotX() : 89 getPivotX(); 90 if (mLayoutScaleX != 1f) { 91 parentLeft = getPaddingLeft() + (int)(pivotX - pivotX / mLayoutScaleX + 0.5f); 92 parentRight = (int)(pivotX + (right - left - pivotX) / mLayoutScaleX + 0.5f) 93 - getPaddingRight(); 94 } else { 95 parentLeft = getPaddingLeft(); 96 parentRight = right - left - getPaddingRight(); 97 } 98 99 final int parentTop, parentBottom; 100 final float pivotY = getPivotY(); 101 if (mLayoutScaleY != 1f) { 102 parentTop = getPaddingTop() + (int)(pivotY - pivotY / mLayoutScaleY + 0.5f); 103 parentBottom = (int)(pivotY + (bottom - top - pivotY) / mLayoutScaleY + 0.5f) 104 - getPaddingBottom(); 105 } else { 106 parentTop = getPaddingTop(); 107 parentBottom = bottom - top - getPaddingBottom(); 108 } 109 110 for (int i = 0; i < count; i++) { 111 final View child = getChildAt(i); 112 if (child.getVisibility() != GONE) { 113 final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 114 115 final int width = child.getMeasuredWidth(); 116 final int height = child.getMeasuredHeight(); 117 118 int childLeft; 119 int childTop; 120 121 int gravity = lp.gravity; 122 if (gravity == -1) { 123 gravity = DEFAULT_CHILD_GRAVITY; 124 } 125 126 final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); 127 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; 128 129 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 130 case Gravity.CENTER_HORIZONTAL: 131 childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + 132 lp.leftMargin - lp.rightMargin; 133 break; 134 case Gravity.RIGHT: 135 childLeft = parentRight - width - lp.rightMargin; 136 break; 137 case Gravity.LEFT: 138 default: 139 childLeft = parentLeft + lp.leftMargin; 140 } 141 142 switch (verticalGravity) { 143 case Gravity.TOP: 144 childTop = parentTop + lp.topMargin; 145 break; 146 case Gravity.CENTER_VERTICAL: 147 childTop = parentTop + (parentBottom - parentTop - height) / 2 + 148 lp.topMargin - lp.bottomMargin; 149 break; 150 case Gravity.BOTTOM: 151 childTop = parentBottom - height - lp.bottomMargin; 152 break; 153 default: 154 childTop = parentTop + lp.topMargin; 155 } 156 157 child.layout(childLeft, childTop, childLeft + width, childTop + height); 158 // synchronize child pivot to be same as ScaleFrameLayout's pivot 159 child.setPivotX(pivotX - childLeft); 160 child.setPivotY(pivotY - childTop); 161 } 162 } 163 } 164 165 private static int getScaledMeasureSpec(int measureSpec, float scale) { 166 return scale == 1f ? measureSpec : MeasureSpec.makeMeasureSpec( 167 (int) (MeasureSpec.getSize(measureSpec) / scale + 0.5f), 168 MeasureSpec.getMode(measureSpec)); 169 } 170 171 @Override 172 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 173 if (mLayoutScaleX != 1f || mLayoutScaleY != 1f) { 174 final int scaledWidthMeasureSpec = 175 getScaledMeasureSpec(widthMeasureSpec, mLayoutScaleX); 176 final int scaledHeightMeasureSpec = 177 getScaledMeasureSpec(heightMeasureSpec, mLayoutScaleY); 178 super.onMeasure(scaledWidthMeasureSpec, scaledHeightMeasureSpec); 179 setMeasuredDimension((int)(getMeasuredWidth() * mLayoutScaleX + 0.5f), 180 (int)(getMeasuredHeight() * mLayoutScaleY + 0.5f)); 181 } else { 182 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 183 } 184 } 185 186 /** 187 * setForeground() is not supported, throws UnsupportedOperationException() when called. 188 */ 189 @Override 190 public void setForeground(Drawable d) { 191 throw new UnsupportedOperationException(); 192 } 193 194} 195