Home | History | Annotate | Download | only in widget
      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