Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2008 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.phone;
     18 
     19 import android.content.Context;
     20 import android.util.AttributeSet;
     21 import android.util.Log;
     22 import android.view.View.MeasureSpec;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 
     26 /**
     27  * Create a 4x3 grid of dial buttons.
     28  *
     29  * It was easier and more efficient to do it this way than use
     30  * standard layouts. It's perfectly fine (and actually encouraged) to
     31  * use custom layouts rather than piling up standard layouts.
     32  *
     33  * The horizontal and vertical spacings between buttons are controlled
     34  * by the amount of padding (attributes on the ButtonGridLayout element):
     35  *   - horizontal = left + right padding and
     36  *   - vertical = top + bottom padding.
     37  *
     38  * This class assumes that all the buttons have the same size.
     39  * The buttons will be bottom aligned in their view on layout.
     40  *
     41  * Invocation: onMeasure is called first by the framework to know our
     42  * size. Then onLayout is invoked to layout the buttons.
     43  */
     44 // TODO: Blindly layout the buttons w/o checking if we overrun the
     45 // bottom-right corner.
     46 
     47 public class ButtonGridLayout extends ViewGroup {
     48     static private final String TAG = "ButtonGridLayout";
     49     static private final int COLUMNS = 3;
     50     static private final int ROWS = 4;
     51     static private final int NUM_CHILDREN = ROWS * COLUMNS;
     52 
     53     private View[] mButtons = new View[NUM_CHILDREN];
     54 
     55     // This what the fields represent (height is similar):
     56     // PL: mPaddingLeft
     57     // BW: mButtonWidth
     58     // PR: mPaddingRight
     59     //
     60     //        mWidthInc
     61     // <-------------------->
     62     //   PL      BW      PR
     63     // <----><--------><---->
     64     //        --------
     65     //       |        |
     66     //       | button |
     67     //       |        |
     68     //        --------
     69     //
     70     // We assume mPaddingLeft == mPaddingRight == 1/2 padding between
     71     // buttons.
     72     //
     73     // mWidth == COLUMNS x mWidthInc
     74 
     75     // Width and height of a button
     76     private int mButtonWidth;
     77     private int mButtonHeight;
     78 
     79     // Width and height of a button + padding.
     80     private int mWidthInc;
     81     private int mHeightInc;
     82 
     83     // Height of the dialpad. Used to align it at the bottom of the
     84     // view.
     85     private int mWidth;
     86     private int mHeight;
     87 
     88 
     89     public ButtonGridLayout(Context context) {
     90         super(context);
     91     }
     92 
     93     public ButtonGridLayout(Context context, AttributeSet attrs) {
     94         super(context, attrs);
     95     }
     96 
     97     public ButtonGridLayout(Context context, AttributeSet attrs, int defStyle) {
     98         super(context, attrs, defStyle);
     99     }
    100 
    101     /**
    102      * Cache the buttons in a member array for faster access.  Compute
    103      * the measurements for the width/height of buttons.  The inflate
    104      * sequence is called right after the constructor and before the
    105      * measure/layout phase.
    106      */
    107     @Override
    108     protected void onFinishInflate () {
    109         super.onFinishInflate();
    110         final View[] buttons = mButtons;
    111         for (int i = 0; i < NUM_CHILDREN; i++) {
    112             buttons[i] = getChildAt(i);
    113             // Measure the button to get initialized.
    114             buttons[i].measure(MeasureSpec.UNSPECIFIED , MeasureSpec.UNSPECIFIED);
    115         }
    116 
    117         // Cache the measurements.
    118         final View child = buttons[0];
    119         mButtonWidth = child.getMeasuredWidth();
    120         mButtonHeight = child.getMeasuredHeight();
    121         mWidthInc = mButtonWidth + mPaddingLeft + mPaddingRight;
    122         mHeightInc = mButtonHeight + mPaddingTop + mPaddingBottom;
    123         mWidth = COLUMNS * mWidthInc;
    124         mHeight = ROWS * mHeightInc;
    125     }
    126 
    127     /**
    128      * Set the background of all the children. Typically a selector to
    129      * change the background based on some combination of the button's
    130      * attributes (e.g pressed, enabled...)
    131      * @param resid Is a resource id to be used for each button's background.
    132      */
    133     public void setChildrenBackgroundResource(int resid) {
    134         final View[] buttons = mButtons;
    135         for (int i = 0; i < NUM_CHILDREN; i++) {
    136             buttons[i].setBackgroundResource(resid);
    137         }
    138     }
    139 
    140     @Override
    141     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    142         final View[] buttons = mButtons;
    143         final int paddingLeft = mPaddingLeft;
    144         final int buttonWidth = mButtonWidth;
    145         final int buttonHeight = mButtonHeight;
    146         final int widthInc = mWidthInc;
    147         final int heightInc = mHeightInc;
    148 
    149         int i = 0;
    150         // The last row is bottom aligned.
    151         int y = (bottom - top) - mHeight + mPaddingTop;
    152         for (int row = 0; row < ROWS; row++) {
    153             int x = paddingLeft;
    154             for (int col = 0; col < COLUMNS; col++) {
    155                 buttons[i].layout(x, y, x + buttonWidth, y + buttonHeight);
    156                 x += widthInc;
    157                 i++;
    158             }
    159             y += heightInc;
    160         }
    161     }
    162 
    163     /**
    164      * This method is called twice in practice. The first time both
    165      * with and height are constraint by AT_MOST. The second time, the
    166      * width is still AT_MOST and the height is EXACTLY. Either way
    167      * the full width/height should be in mWidth and mHeight and we
    168      * use 'resolveSize' to do the right thing.
    169      */
    170     @Override
    171     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    172         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    173         final int width = resolveSize(mWidth, widthMeasureSpec);
    174         final int height = resolveSize(mHeight, heightMeasureSpec);
    175         setMeasuredDimension(width, height);
    176     }
    177 }
    178