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