Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.contacts.widget;
     18 
     19 import android.content.Context;
     20 import android.content.res.TypedArray;
     21 import android.graphics.Rect;
     22 import android.util.AttributeSet;
     23 import android.view.Gravity;
     24 import android.view.View;
     25 import android.view.ViewGroup;
     26 import android.widget.LinearLayout;
     27 
     28 import com.android.contacts.R;
     29 
     30 /**
     31  * Layout similar to LinearLayout that allows a child to specify examples of
     32  * desired size depending on the parent size. For example if the widget wants to
     33  * be 100dip when parent is 200dip and 110dip when parent is 400dip, the layout
     34  * will ensure these requirements and interpolate for other parent sizes.
     35  * You can also specify minWidth for each child.  You can have at most one
     36  * child with layout_width="match_parent" - it will take the entire remaining
     37  * space.
     38  */
     39 public class InterpolatingLayout extends ViewGroup {
     40 
     41     private Rect mInRect = new Rect();
     42     private Rect mOutRect = new Rect();
     43 
     44     public InterpolatingLayout(Context context) {
     45         super(context);
     46     }
     47 
     48     public InterpolatingLayout(Context context, AttributeSet attrs) {
     49         super(context, attrs);
     50     }
     51 
     52     public InterpolatingLayout(Context context, AttributeSet attrs, int defStyle) {
     53         super(context, attrs, defStyle);
     54     }
     55 
     56     public final static class LayoutParams extends LinearLayout.LayoutParams {
     57 
     58         public int narrowParentWidth;
     59         public int narrowWidth;
     60         public int narrowMarginLeft;
     61         public int narrowPaddingLeft;
     62         public int narrowMarginRight;
     63         public int narrowPaddingRight;
     64         public int wideParentWidth;
     65         public int wideWidth;
     66         public int wideMarginLeft;
     67         public int widePaddingLeft;
     68         public int wideMarginRight;
     69         public int widePaddingRight;
     70         private float widthMultiplier;
     71         private int widthConstant;
     72         private float leftMarginMultiplier;
     73         private int leftMarginConstant;
     74         private float leftPaddingMultiplier;
     75         private int leftPaddingConstant;
     76         private float rightMarginMultiplier;
     77         private int rightMarginConstant;
     78         private float rightPaddingMultiplier;
     79         private int rightPaddingConstant;
     80 
     81         public LayoutParams(Context c, AttributeSet attrs) {
     82             super(c, attrs);
     83             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.InterpolatingLayout_Layout);
     84 
     85             narrowParentWidth = a.getDimensionPixelSize(
     86                     R.styleable.InterpolatingLayout_Layout_layout_narrowParentWidth, -1);
     87             narrowWidth = a.getDimensionPixelSize(
     88                     R.styleable.InterpolatingLayout_Layout_layout_narrowWidth, -1);
     89             narrowMarginLeft = a.getDimensionPixelSize(
     90                     R.styleable.InterpolatingLayout_Layout_layout_narrowMarginLeft, -1);
     91             narrowPaddingLeft = a.getDimensionPixelSize(
     92                     R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingLeft, -1);
     93             narrowMarginRight = a.getDimensionPixelSize(
     94                     R.styleable.InterpolatingLayout_Layout_layout_narrowMarginRight, -1);
     95             narrowPaddingRight = a.getDimensionPixelSize(
     96                     R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingRight, -1);
     97             wideParentWidth = a.getDimensionPixelSize(
     98                     R.styleable.InterpolatingLayout_Layout_layout_wideParentWidth, -1);
     99             wideWidth = a.getDimensionPixelSize(
    100                     R.styleable.InterpolatingLayout_Layout_layout_wideWidth, -1);
    101             wideMarginLeft = a.getDimensionPixelSize(
    102                     R.styleable.InterpolatingLayout_Layout_layout_wideMarginLeft, -1);
    103             widePaddingLeft = a.getDimensionPixelSize(
    104                     R.styleable.InterpolatingLayout_Layout_layout_widePaddingLeft, -1);
    105             wideMarginRight = a.getDimensionPixelSize(
    106                     R.styleable.InterpolatingLayout_Layout_layout_wideMarginRight, -1);
    107             widePaddingRight = a.getDimensionPixelSize(
    108                     R.styleable.InterpolatingLayout_Layout_layout_widePaddingRight, -1);
    109 
    110             a.recycle();
    111 
    112             if (narrowWidth != -1) {
    113                 widthMultiplier = (float) (wideWidth - narrowWidth)
    114                         / (wideParentWidth - narrowParentWidth);
    115                 widthConstant = (int) (narrowWidth - narrowParentWidth * widthMultiplier);
    116             }
    117 
    118             if (narrowMarginLeft != -1) {
    119                 leftMarginMultiplier = (float) (wideMarginLeft - narrowMarginLeft)
    120                         / (wideParentWidth - narrowParentWidth);
    121                 leftMarginConstant = (int) (narrowMarginLeft - narrowParentWidth
    122                         * leftMarginMultiplier);
    123             }
    124 
    125             if (narrowPaddingLeft != -1) {
    126                 leftPaddingMultiplier = (float) (widePaddingLeft - narrowPaddingLeft)
    127                         / (wideParentWidth - narrowParentWidth);
    128                 leftPaddingConstant = (int) (narrowPaddingLeft - narrowParentWidth
    129                         * leftPaddingMultiplier);
    130             }
    131 
    132             if (narrowMarginRight != -1) {
    133                 rightMarginMultiplier = (float) (wideMarginRight - narrowMarginRight)
    134                         / (wideParentWidth - narrowParentWidth);
    135                 rightMarginConstant = (int) (narrowMarginRight - narrowParentWidth
    136                         * rightMarginMultiplier);
    137             }
    138 
    139             if (narrowPaddingRight != -1) {
    140                 rightPaddingMultiplier = (float) (widePaddingRight - narrowPaddingRight)
    141                         / (wideParentWidth - narrowParentWidth);
    142                 rightPaddingConstant = (int) (narrowPaddingRight - narrowParentWidth
    143                         * rightPaddingMultiplier);
    144             }
    145         }
    146 
    147         public LayoutParams(int width, int height) {
    148             super(width, height);
    149         }
    150 
    151         public LayoutParams(MarginLayoutParams source) {
    152             super(source);
    153         }
    154 
    155         public int resolveWidth(int parentSize) {
    156             if (narrowWidth == -1) {
    157                 return width;
    158             } else {
    159                 int w = (int) (parentSize * widthMultiplier) + widthConstant;
    160                 return w <= 0 ? WRAP_CONTENT : w;
    161             }
    162         }
    163 
    164         public int resolveLeftMargin(int parentSize) {
    165             if (narrowMarginLeft == -1) {
    166                 return leftMargin;
    167             } else {
    168                 int w = (int) (parentSize * leftMarginMultiplier) + leftMarginConstant;
    169                 return w < 0 ? 0 : w;
    170             }
    171         }
    172 
    173         public int resolveLeftPadding(int parentSize) {
    174             int w = (int) (parentSize * leftPaddingMultiplier) + leftPaddingConstant;
    175             return w < 0 ? 0 : w;
    176         }
    177 
    178         public int resolveRightMargin(int parentSize) {
    179             if (narrowMarginRight == -1) {
    180                 return rightMargin;
    181             } else {
    182                 int w = (int) (parentSize * rightMarginMultiplier) + rightMarginConstant;
    183                 return w < 0 ? 0 : w;
    184             }
    185         }
    186 
    187         public int resolveRightPadding(int parentSize) {
    188             int w = (int) (parentSize * rightPaddingMultiplier) + rightPaddingConstant;
    189             return w < 0 ? 0 : w;
    190         }
    191     }
    192 
    193     @Override
    194     public LayoutParams generateLayoutParams(AttributeSet attrs) {
    195         return new LayoutParams(getContext(), attrs);
    196     }
    197 
    198     @Override
    199     protected LayoutParams generateDefaultLayoutParams() {
    200         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    201     }
    202 
    203     @Override
    204     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    205         int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
    206         int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
    207 
    208         int width = 0;
    209         int height = 0;
    210 
    211         View fillChild = null;
    212         int count = getChildCount();
    213         for (int i = 0; i < count; i++) {
    214             View child = getChildAt(i);
    215             if (child.getVisibility() == View.GONE) {
    216                 continue;
    217             }
    218 
    219             LayoutParams params = (LayoutParams) child.getLayoutParams();
    220             if (params.width == LayoutParams.MATCH_PARENT) {
    221                 if (fillChild != null) {
    222                     throw new RuntimeException(
    223                             "Interpolating layout allows at most one child"
    224                             + " with layout_width='match_parent'");
    225                 }
    226                 fillChild = child;
    227             } else {
    228                 int childWidth = params.resolveWidth(parentWidth);
    229                 int childWidthMeasureSpec;
    230                 switch (childWidth) {
    231                     case LayoutParams.WRAP_CONTENT:
    232                         childWidthMeasureSpec = MeasureSpec.UNSPECIFIED;
    233                         break;
    234                     default:
    235                         childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
    236                                 childWidth, MeasureSpec.EXACTLY);
    237                         break;
    238                 }
    239 
    240                 int childHeightMeasureSpec;
    241                 switch (params.height) {
    242                     case LayoutParams.WRAP_CONTENT:
    243                         childHeightMeasureSpec = MeasureSpec.UNSPECIFIED;
    244                         break;
    245                     case LayoutParams.MATCH_PARENT:
    246                         childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
    247                                 parentHeight - params.topMargin - params.bottomMargin,
    248                                 MeasureSpec.EXACTLY);
    249                         break;
    250                     default:
    251                         childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
    252                                 params.height, MeasureSpec.EXACTLY);
    253                         break;
    254                 }
    255 
    256                 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    257                 width += child.getMeasuredWidth();
    258                 height = Math.max(child.getMeasuredHeight(), height);
    259             }
    260 
    261             width += params.resolveLeftMargin(parentWidth) + params.resolveRightMargin(parentWidth);
    262         }
    263 
    264         if (fillChild != null) {
    265             int remainder = parentWidth - width;
    266             int childMeasureSpec = remainder > 0
    267                     ? MeasureSpec.makeMeasureSpec(remainder, MeasureSpec.EXACTLY)
    268                     : MeasureSpec.UNSPECIFIED;
    269             fillChild.measure(childMeasureSpec, heightMeasureSpec);
    270             width += fillChild.getMeasuredWidth();
    271             height = Math.max(fillChild.getMeasuredHeight(), height);
    272         }
    273 
    274         setMeasuredDimension(
    275                 resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
    276     }
    277 
    278     @Override
    279     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    280         int offset = 0;
    281         int width = right - left;
    282         int count = getChildCount();
    283         for (int i = 0; i < count; i++) {
    284             View child = getChildAt(i);
    285 
    286             if (child.getVisibility() == View.GONE) {
    287                 continue;
    288             }
    289 
    290             LayoutParams params = (LayoutParams) child.getLayoutParams();
    291             int gravity = params.gravity;
    292             if (gravity == -1) {
    293                 gravity = Gravity.START | Gravity.TOP;
    294             }
    295 
    296             if (params.narrowPaddingLeft != -1 || params.narrowPaddingRight != -1) {
    297                 int leftPadding = params.narrowPaddingLeft == -1 ? child.getPaddingLeft()
    298                         : params.resolveLeftPadding(width);
    299                 int rightPadding = params.narrowPaddingRight == -1 ? child.getPaddingRight()
    300                         : params.resolveRightPadding(width);
    301                 child.setPadding(
    302                         leftPadding, child.getPaddingTop(), rightPadding, child.getPaddingBottom());
    303             }
    304 
    305             int leftMargin = params.resolveLeftMargin(width);
    306             int rightMargin = params.resolveRightMargin(width);
    307 
    308             mInRect.set(offset + leftMargin, params.topMargin,
    309                     right - rightMargin, bottom - params.bottomMargin);
    310 
    311             Gravity.apply(gravity, child.getMeasuredWidth(), child.getMeasuredHeight(),
    312                     mInRect, mOutRect);
    313             child.layout(mOutRect.left, mOutRect.top, mOutRect.right, mOutRect.bottom);
    314 
    315             offset = mOutRect.right + rightMargin;
    316         }
    317     }
    318 }
    319