• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ButtonGridLayout(Context context)89     public ButtonGridLayout(Context context) {
90         super(context);
91     }
92 
ButtonGridLayout(Context context, AttributeSet attrs)93     public ButtonGridLayout(Context context, AttributeSet attrs) {
94         super(context, attrs);
95     }
96 
ButtonGridLayout(Context context, AttributeSet attrs, int defStyle)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
onFinishInflate()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      */
setChildrenBackgroundResource(int resid)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
onLayout(boolean changed, int left, int top, int right, int bottom)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
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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