OSDN Git Service

Tweaked the dialer screen to match the designer's PPL.
authorNicolas Catania <niko@google.com>
Tue, 29 Sep 2009 06:03:33 +0000 (23:03 -0700)
committerNicolas Catania <niko@google.com>
Tue, 29 Sep 2009 20:20:16 +0000 (13:20 -0700)
Created a layout-long-finger version of the dialpad for wvga devices.
For these devices, the buttons are taller than for hvga and
the margin around the elements are also different.

Extracted the bar with the 3 buttons in a separate file that gets
included in both portrait and landscape modes.

Simplified the ButtonGridLayout. Now runs 6% faster (3 runs w/ traceview).
I hardcoded the size of the button grid because the number of columns was
already hardcoded, so why not fix everything?
Use some member to cache the calculation made during onMeasure to be reused
in onLayout.

Tested on dream and sh.... in portrait and landscape modes.

Bug:2104523

res/layout-finger/dialpad.xml
res/layout-finger/twelve_key_dialer.xml
res/layout-finger/voicemail_dial_delete.xml [new file with mode: 0644]
res/layout-land-finger/twelve_key_dialer.xml
res/layout-long-finger/dialpad.xml [new file with mode: 0644]
res/layout-long-finger/twelve_key_dialer.xml [new file with mode: 0644]
res/layout-long-finger/voicemail_dial_delete.xml [new file with mode: 0644]
res/layout-long-land-finger/twelve_key_dialer.xml [new file with mode: 0644]
src/com/android/contacts/ButtonGridLayout.java

index 0acb721..82179db 100644 (file)
     android:id="@+id/dialpad"
     android:paddingLeft="7dp"
     android:paddingRight="7dp"
-    android:layout_width="fill_parent"
+    android:paddingTop="6dp"
+    android:paddingBottom="6dp"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
 >
         <ImageButton android:id="@+id/one"
             android:layout_width="88dp"
index 018bee8..83eb2cf 100644 (file)
@@ -25,9 +25,8 @@
     <!-- TODO: Use a textAppearance to control the display of the number -->
     <EditText android:id="@+id/digits"
         android:layout_width="fill_parent"
-        android:layout_height="66dip"
+        android:layout_height="67dip"
         android:layout_marginBottom="6dip"
-        android:layout_marginTop="1dip"
         android:gravity="center"
         android:maxLines="1"
         android:scrollHorizontally="true"
     <include layout="@layout/dialpad" />
 
     <!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
-    <LinearLayout android:id="@+id/voicemailAndDialAndDelete"
-        android:layout_width="wrap_content"
-        android:layout_height="fill_parent"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginBottom="6dip"
-        android:orientation="horizontal">
-
-        <!-- Onscreen "Voicemail" button -->
-        <ImageButton android:id="@+id/voicemailButton"
-            android:layout_width="90dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_voicemail"
-            android:src="@drawable/ic_dial_action_voice_mail" />
-
-        <!-- Onscreen "Dial" button, used on all platforms by
-             default. Its usage can be disabled using resources (see
-             config.xml.) -->
-        <ImageButton android:id="@+id/dialButton"
-            android:layout_width="116dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_action"
-            android:src="@drawable/ic_dial_action_call" />
-
-        <!-- Onscreen "Backspace/Delete" button -->
-        <ImageButton android:id="@+id/deleteButton"
-            android:layout_width="90dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_delete"
-            android:src="@drawable/ic_dial_action_delete" />
-    </LinearLayout>
+    <include layout="@layout/voicemail_dial_delete" />
 
     <!-- "Dialpad chooser" UI, shown only when the user brings up the
          Dialer while a call is already in progress.
diff --git a/res/layout-finger/voicemail_dial_delete.xml b/res/layout-finger/voicemail_dial_delete.xml
new file mode 100644 (file)
index 0000000..1aa2ac4
--- /dev/null
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/voicemailAndDialAndDelete"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:layout_marginTop="6dip"
+    android:orientation="horizontal">
+
+    <!-- Onscreen "Voicemail" button.
+         The width is 75 (from the mocks) + 12 of padding from the
+         9patch, total is 87.
+    -->
+    <ImageButton android:id="@+id/voicemailButton"
+        android:layout_width="87dip"
+        android:layout_height="50dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_voicemail"
+        android:src="@drawable/ic_dial_action_voice_mail" />
+
+    <!-- Onscreen "Dial" button, used on all platforms by
+         default. Its usage can be disabled using resources (see
+         config.xml.) -->
+    <ImageButton android:id="@+id/dialButton"
+        android:layout_width="116dip"
+        android:layout_height="50dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_action"
+        android:src="@drawable/ic_dial_action_call" />
+
+    <!-- Onscreen "Backspace/Delete" button
+         The width is 75 (from the mocks) + 12 of padding from the
+         9patch, total is 87.
+    -->
+    <ImageButton android:id="@+id/deleteButton"
+        android:layout_width="87dip"
+        android:layout_height="50dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_delete"
+        android:src="@drawable/ic_dial_action_delete" />
+</LinearLayout>
+
index 11ec8d6..8c66ff3 100644 (file)
@@ -26,7 +26,7 @@
     <EditText android:id="@+id/digits"
         android:layout_width="fill_parent"
         android:layout_height="66dip"
-        android:layout_marginBottom="6dip"
+        android:layout_marginBottom="50dip"
         android:layout_marginTop="1dip"
         android:gravity="center"
         android:maxLines="1"
      />
 
     <!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
-    <LinearLayout android:id="@+id/voicemailAndDialAndDelete"
-        android:layout_width="wrap_content"
-        android:layout_height="fill_parent"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginBottom="6dip"
-        android:orientation="horizontal">
-
-        <!-- Onscreen "Voicemail" button -->
-        <ImageButton android:id="@+id/voicemailButton"
-            android:layout_width="90dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_voicemail"
-            android:src="@drawable/ic_dial_action_voice_mail" />
-
-        <!-- Onscreen "Dial" button, used on all platforms by
-             default. Its usage can be disabled using resources (see
-             config.xml.) -->
-        <ImageButton android:id="@+id/dialButton"
-            android:layout_width="116dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_action"
-            android:src="@drawable/ic_dial_action_call" />
-
-        <!-- Onscreen "Backspace/Delete" button -->
-        <ImageButton android:id="@+id/deleteButton"
-            android:layout_width="90dip"
-            android:layout_height="52dip"
-            android:layout_gravity="center_vertical"
-            android:state_enabled="false"
-            android:background="@drawable/btn_dial_delete"
-            android:src="@drawable/ic_dial_action_delete" />
-    </LinearLayout>
+    <include layout="@layout/voicemail_dial_delete" />
 
     <!-- "Dialpad chooser" UI, shown only when the user brings up the
          Dialer while a call is already in progress.
diff --git a/res/layout-long-finger/dialpad.xml b/res/layout-long-finger/dialpad.xml
new file mode 100644 (file)
index 0000000..036c4fa
--- /dev/null
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Dialpad in the Contact app.
+     Tall screen version with taller buttons.
+ -->
+
+<com.android.contacts.ButtonGridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/dialpad"
+    android:paddingLeft="7dp"
+    android:paddingRight="7dp"
+    android:paddingTop="6dp"
+    android:paddingBottom="6dp"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+
+>
+        <ImageButton android:id="@+id/one"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_1_no_vm"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_one"
+        />
+
+        <ImageButton android:id="@+id/two"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_2"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_two"
+        />
+
+        <ImageButton android:id="@+id/three"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_3"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_three"
+        />
+
+        <ImageButton android:id="@+id/four"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_4"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_four"
+        />
+
+        <ImageButton android:id="@+id/five"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_5"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_five"
+        />
+
+        <ImageButton android:id="@+id/six"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_6"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_six"
+        />
+
+        <ImageButton android:id="@+id/seven"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_7"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_seven"
+        />
+
+        <ImageButton android:id="@+id/eight"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_8"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_eight"
+        />
+
+        <ImageButton android:id="@+id/nine"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_9"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_nine"
+        />
+
+        <ImageButton android:id="@+id/star"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_star"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_star"
+        />
+
+        <ImageButton android:id="@+id/zero"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_0"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_zero"
+        />
+
+        <ImageButton android:id="@+id/pound"
+            android:layout_width="88dp"
+            android:layout_height="58dp"
+            android:src="@drawable/dial_num_pound"
+            android:background="@drawable/btn_dial"
+            android:soundEffectsEnabled="false"
+            android:contentDescription="@string/description_image_button_pound"
+        />
+</com.android.contacts.ButtonGridLayout>
diff --git a/res/layout-long-finger/twelve_key_dialer.xml b/res/layout-long-finger/twelve_key_dialer.xml
new file mode 100644 (file)
index 0000000..77f471e
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+>
+
+    <!-- Text field above the keypad where the digits are displayed -->
+    <!-- TODO: Use a textAppearance to control the display of the number -->
+    <EditText android:id="@+id/digits"
+        android:layout_width="fill_parent"
+        android:layout_height="74dip"
+        android:layout_marginBottom="20dip"
+        android:gravity="center"
+        android:maxLines="1"
+        android:scrollHorizontally="true"
+        android:textSize="34sp"
+        android:freezesText="true"
+        android:background="@drawable/btn_dial_textfield"
+        android:textColor="@color/dialer_button_text"
+        android:focusableInTouchMode="false"
+    />
+
+    <!-- Keypad section -->
+    <include layout="@layout/dialpad" />
+
+    <!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
+    <include layout="@layout/voicemail_dial_delete" />
+
+    <!-- "Dialpad chooser" UI, shown only when the user brings up the
+         Dialer while a call is already in progress.
+         When this UI is visible, the other Dialer elements
+         (the textfield/button and the dialpad) are hidden. -->
+    <ListView android:id="@+id/dialpadChooser"
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:layout_weight="1"
+    />
+
+</LinearLayout>
diff --git a/res/layout-long-finger/voicemail_dial_delete.xml b/res/layout-long-finger/voicemail_dial_delete.xml
new file mode 100644 (file)
index 0000000..58c482b
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Horizontal row of buttons (Voicemail + DialButton + Delete.)
+     Tall screen version with taller buttons.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/voicemailAndDialAndDelete"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:layout_marginTop="24dip"
+    android:orientation="horizontal">
+
+    <!-- Onscreen "Voicemail" button -->
+    <ImageButton android:id="@+id/voicemailButton"
+        android:layout_width="87dip"
+        android:layout_height="58dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_voicemail"
+        android:src="@drawable/ic_dial_action_voice_mail" />
+
+    <!-- Onscreen "Dial" button, used on all platforms by
+         default. Its usage can be disabled using resources (see
+         config.xml.) -->
+    <ImageButton android:id="@+id/dialButton"
+        android:layout_width="116dip"
+        android:layout_height="58dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_action"
+        android:src="@drawable/ic_dial_action_call" />
+
+    <!-- Onscreen "Backspace/Delete" button -->
+    <ImageButton android:id="@+id/deleteButton"
+        android:layout_width="87dip"
+        android:layout_height="58dip"
+        android:layout_gravity="center_vertical"
+        android:state_enabled="false"
+        android:background="@drawable/btn_dial_delete"
+        android:src="@drawable/ic_dial_action_delete" />
+</LinearLayout>
+
diff --git a/res/layout-long-land-finger/twelve_key_dialer.xml b/res/layout-long-land-finger/twelve_key_dialer.xml
new file mode 100644 (file)
index 0000000..618792a
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/top"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical"
+>
+
+    <!-- Text field above the keypad where the digits are displayed -->
+    <!-- TODO: Use a textAppearance to control the display of the number -->
+    <EditText android:id="@+id/digits"
+        android:layout_width="fill_parent"
+        android:layout_height="74dip"
+        android:layout_marginBottom="30dip"
+        android:layout_marginTop="1dip"
+        android:gravity="center"
+        android:maxLines="1"
+        android:scrollHorizontally="true"
+        android:textSize="34sp"
+        android:freezesText="true"
+        android:background="@drawable/btn_dial_textfield"
+        android:textColor="@color/dialer_button_text"
+        android:hint="@string/dialerKeyboardHintText"
+     />
+
+    <!-- Horizontal row of buttons (Voicemail + DialButton + Delete.) -->
+    <include layout="@layout/voicemail_dial_delete" />
+
+    <!-- "Dialpad chooser" UI, shown only when the user brings up the
+         Dialer while a call is already in progress.
+         When this UI is visible, the other Dialer elements
+         (the textfield and button) are hidden. -->
+    <ListView android:id="@+id/dialpadChooser"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:footerDividersEnabled="true"
+    />
+
+</LinearLayout>
index 69eed97..c3fb1d9 100644 (file)
@@ -18,13 +18,40 @@ package com.android.contacts;
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.View.MeasureSpec;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
 
+/**
+ * Create a 4x3 grid of dial buttons.
+ *
+ * It was easier and more efficient to do it this way than use
+ * standard layouts. It's perfectly fine (and actually encouraged) to
+ * use custom layouts rather than piling up standard layouts.
+ *
+ * The horizontal and vertical spacings between buttons are controlled
+ * by the amount of padding (attributes on the ButtonGridLayout element):
+ *   - horizontal = left + right padding and
+ *   - vertical = top + bottom padding.
+ *
+ * This class assumes that all the buttons have the same size.
+ *
+ * Invocation: onMeasure is called first by the framework to know our
+ * size. Then onLayout is invoked to layout the buttons.
+ */
+// TODO: Blindly layout the buttons w/o checking if we overrun the
+// bottom-right corner.
 public class ButtonGridLayout extends ViewGroup {
+    private final int COLUMNS = 3;
+    private final int ROWS = 4;
+
+    // Width and height of a button
+    private int mButtonWidth;
+    private int mButtonHeight;
 
-    private final int mColumns = 3;
+    // Width and height of a button + padding.
+    private int mWidthInc;
+    private int mHeightInc;
 
     public ButtonGridLayout(Context context) {
         super(context);
@@ -40,64 +67,43 @@ public class ButtonGridLayout extends ViewGroup {
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        int i = 0;
         int y = mPaddingTop;
-        final int rows = getRows();
-        final View child0 = getChildAt(0);
-        final int yInc = (getHeight() - mPaddingTop - mPaddingBottom) / rows;
-        final int xInc = (getWidth() - mPaddingLeft - mPaddingRight) / mColumns;
-        final int childWidth = child0.getMeasuredWidth();
-        final int childHeight = child0.getMeasuredHeight();
-        final int xOffset = (xInc - childWidth) / 2;
-        final int yOffset = (yInc - childHeight) / 2;
-
-        for (int row = 0; row < rows; row++) {
+        for (int row = 0; row < ROWS; row++) {
             int x = mPaddingLeft;
-            for (int col = 0; col < mColumns; col++) {
-                int cell = row * mColumns + col;
-                if (cell >= getChildCount()) {
-                    break;
-                }
-                View child = getChildAt(cell);
-                child.layout(x + xOffset, y + yOffset,
-                        x + xOffset + childWidth,
-                        y + yOffset + childHeight);
-                x += xInc;
+            for (int col = 0; col < COLUMNS; col++) {
+                View child = getChildAt(i);
+
+                child.layout(x, y, x + mButtonWidth, y + mButtonHeight);
+
+                x += mWidthInc;
+                i++;
             }
-            y += yInc;
+            y += mHeightInc;
         }
-    }
-
-    private int getRows() {
-        return (getChildCount() + mColumns - 1) / mColumns;
-    }
+      }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = mPaddingLeft + mPaddingRight;
-        int height = mPaddingTop + mPaddingBottom;
-
-        // Measure the first child and get it's size
+          // Measure the first child and get it's size
         View child = getChildAt(0);
         child.measure(MeasureSpec.UNSPECIFIED , MeasureSpec.UNSPECIFIED);
-        int childWidth = child.getMeasuredWidth();
-        int childHeight = child.getMeasuredHeight();
+
         // Make sure the other children are measured as well, to initialize
         for (int i = 1; i < getChildCount(); i++) {
             getChildAt(i).measure(MeasureSpec.UNSPECIFIED , MeasureSpec.UNSPECIFIED);
         }
-        // All cells are going to be the size of the first child
-        width += mColumns * childWidth;
-        final int finalWidth = resolveSize(width, widthMeasureSpec);
 
-        // The vertical padding between button must be the same as the
-        // horizontal one. The cumulative horizontal padding is the
-        // difference between 'width' and 'finalWidth'.
-        final int padding = (finalWidth - width) / mColumns;
+        // Store these to be reused in onLayout.
+        mButtonWidth = child.getMeasuredWidth();
+        mButtonHeight = child.getMeasuredHeight();
+        mWidthInc = mButtonWidth + mPaddingLeft + mPaddingRight;
+        mHeightInc = mButtonHeight + mPaddingTop + mPaddingBottom;
 
-        height += getRows() * (childHeight + padding);
+        final int width = resolveSize(COLUMNS * mWidthInc, widthMeasureSpec);
+        final int height = resolveSize(ROWS * mHeightInc, heightMeasureSpec);
 
-        final int finalHeight = resolveSize(height, heightMeasureSpec);
-        setMeasuredDimension(finalWidth, finalHeight);
+        setMeasuredDimension(width, height);
     }
 
 }