andbook!.pdf - Learning Android Get an anddev.org - Android-Shirt Back to index
anddev.org Header Logo
FAQ Search Top rated articles Browse Feeds anddev.org - Authors Contact Details Register Log in

[Custom Widget] - MyGridLayout: Divide to grid and layout it


 
       anddev.org - Android Development Community | Android Tutorials | Index -> Novice Tutorials
Author Message
mmin18
Junior Developer
Junior Developer


Joined: 03 Feb 2008
Posts: 19
Location: China

PostPosted: Fri Apr 18, 2008 9:53 am    Post subject: [Custom Widget] - MyGridLayout: Divide to grid and layout it Reply with quote

This is a keypad used in handyCalc:

It is make up with MyGridLayout. It's divided into a 5x5 grid and layout each button on it.
Of course you can make it with other layouts like LinearLayout or RelativeLayout. but the measure method costs too much time and the I use some LinearLayout to combine a keypad like that and it delay the UI for about 3 seconds on my poor DELL D600 laptop which is not acceptable.

With a MyGridLayout, you can divide it to 100x100 and use your widgets as a 100% percentage.

This is an sample of MyGridLayout. you can download a sample project http://www.anddev.org/files/handycalc_library_155.zip and import to eclipse to run it yourself.



XML:
<?xml version="1.0" encoding="utf-8"?>
<org.mmin.handycalc.MyGridLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:handy="http://schemas.android.com/apk/res/org.mmin.handycalc"
     handy:rows="5"
     handy:columns="5"
     handy:preferredCellWidth="36px"
     handy:preferredCellHeight="36px"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     >

<!--
     preferredCellWidth and preferredCellHeight is useless if you explicit a width or height.
     but in this case, i let the layout to measure it self, so it's measured height equals preferredCellHeight*rows
 -->

     
     <Button android:text="(0,0)" handy:layout_columnStart="0" handy:layout_rowStart="0" />
     <Button android:text="(1,0)-(2,3)" handy:layout_columnStart="1" handy:layout_rowStart="0" handy:layout_columnEnd="2" handy:layout_rowEnd="3" />
     <Button android:text="(2,1)-(5,2)" handy:layout_columnStart="2" handy:layout_rowStart="1" handy:layout_columnEnd="5" handy:layout_rowEnd="2" />
     <Button android:text="(1,3)-(5,5)" handy:layout_columnStart="1" handy:layout_rowStart="3" handy:layout_columnEnd="5" handy:layout_rowEnd="5" />
     
</org.mmin.handycalc.MyGridLayout>



GridLayout.png
 Description:
GridLayout demo
 Filesize:  30.41 KB
 Viewed:  2637 Time(s)

GridLayout.png



handyCalc Keypad.PNG
 Description:
handyCalc keypad
 Filesize:  37.76 KB
 Viewed:  2639 Time(s)

handyCalc Keypad.PNG


Back to top
View user's profile Send private message Send e-mail
mmin18
Junior Developer
Junior Developer


Joined: 03 Feb 2008
Posts: 19
Location: China

PostPosted: Fri Apr 18, 2008 10:01 am    Post subject: Reply with quote

This is the java class for MyGridLayout. I hide most of the code just leave the most essential methods to make it work.

Java:
package org.mmin.handycalc;

import ...;

public class MyGridLayout extends ViewGroup {

// To view the full code, download the archive file http://www.anddev.org/files/handycalc_library_155.zip and import it in eclipse.

     public static class LayoutParams extends
               android.view.ViewGroup.LayoutParams {
// ...
          public LayoutParams(Context c, AttributeSet attrs) {
               super(10, 10);
               // get the layout attributes of it's child view from the xml layout file.
               android.content.Resources.StyledAttributes a = c
                         .obtainStyledAttributes(attrs,
                                   R.styleable.MyGridLayout_Layout);
               this.columnStart = a.getInt(
                         R.styleable.MyGridLayout_Layout_layout_columnStart, 0);
               this.rowStart = a.getInt(
                         R.styleable.MyGridLayout_Layout_layout_rowStart, 0);
               this.columnEnd = a.getInt(
                         R.styleable.MyGridLayout_Layout_layout_columnEnd,
                         this.columnStart + 1);
               this.rowEnd = a.getInt(
                         R.styleable.MyGridLayout_Layout_layout_rowEnd,
                         this.rowStart + 1);
               a.recycle();
          }
     }

     public MyGridLayout(Context context, AttributeSet attrs, Map inflateParams) {
          super(context, attrs, inflateParams);
          readAttr(context, attrs);
     }

     public MyGridLayout(Context context, AttributeSet attrs, Map inflateParams,
               int defStyle) {
          super(context, attrs, inflateParams, defStyle);
          readAttr(context, attrs);
     }

     private void readAttr(Context c, AttributeSet attrs) {
          android.content.Resources.StyledAttributes a = c
                    .obtainStyledAttributes(attrs, R.styleable.MyGridLayout);
          this.rows = a.getInt(R.styleable.MyGridLayout_rows, 1);
          this.columns = a.getInt(R.styleable.MyGridLayout_columns, 1);
          this.preferredCellWidth = a.getDimension(
                    R.styleable.MyGridLayout_preferredCellWidth, 1);
          this.preferredCellHeight = a.getDimension(
                    R.styleable.MyGridLayout_preferredCellHeight, 1);
          a.recycle();
     }

     private int rows;
     private int columns;
     private float preferredCellWidth;
     private float preferredCellHeight;

     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          int widthMode = MeasureSpec.getMode(widthMeasureSpec);
          int heightSize = MeasureSpec.getSize(heightMeasureSpec);
          int heightMode = MeasureSpec.getMode(heightMeasureSpec);

          float cellWidth = preferredCellWidth(), cellHeight = preferredCellHeight();
          if (widthMode == MeasureSpec.EXACTLY
                    || widthMode == MeasureSpec.AT_MOST) {
               float width = widthSize;
               width = width - mPaddingLeft - mPaddingRight;
               width /= columns();
               if (widthMode == MeasureSpec.EXACTLY)
                    cellWidth = width;
               else
                    cellWidth = Math.min(cellWidth, width);
          }
          if (heightMode == MeasureSpec.EXACTLY
                    || heightMode == MeasureSpec.AT_MOST) {
               float height = heightSize;
               height = height - mPaddingTop - mPaddingBottom;
               height /= rows();
               if (heightMode == MeasureSpec.EXACTLY)
                    cellHeight = height;
               else
                    cellHeight = Math.min(cellHeight, height);
          }

          int count = getChildCount();
          for (int i = 0; i < count; i++) {
               View child = getChildAt(i);
               if (child.getVisibility() != GONE) {
                    LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    int width = (int) Math.round(cellWidth * lp.width());
                    int height = (int) Math.round(cellHeight * lp.height());
                    child.measure(MeasureSpec.makeMeasureSpec(width,
                              MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                              height, MeasureSpec.EXACTLY));
               }
          }

          setMeasuredDimension((int) Math.round(cellWidth * columns()
                    + mPaddingLeft + mPaddingRight), (int) Math.round(cellHeight
                    * rows() + mPaddingTop + mPaddingBottom));
     }

     private int lastWidth, lastHeight;

     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
          int width = r - l;
          int height = b - t;
          if (lastWidth == width && lastHeight == height)
               return;
          float cellWidth = width, cellHeight = height;
          cellWidth = cellWidth - mPaddingLeft - mPaddingRight;
          cellHeight = cellHeight - mPaddingTop - mPaddingBottom;
          cellWidth /= columns();
          cellHeight /= rows();
          int count = getChildCount();
          for (int i = 0; i < count; i++) {
               View child = getChildAt(i);
               if (child.getVisibility() != GONE) {
                    LayoutParams lp = (LayoutParams) child.getLayoutParams();
                    int cl = (int) Math.round(mPaddingLeft + lp.columnStart
                              * cellWidth);
                    int ct = (int) Math.round(mPaddingTop + lp.rowStart
                              * cellHeight);
                    int cr = (int) Math.round(mPaddingLeft + lp.columnEnd
                              * cellWidth);
                    int cb = (int) Math.round(mPaddingTop + lp.rowEnd * cellHeight);
                    child.layout(cl, ct, cr, cb);
               }
          }
          lastWidth = width;
          lastHeight = height;
     }

     public android.view.ViewGroup.LayoutParams generateLayoutParams(
               AttributeSet attrs) {
          // let the MyGridLayout.LayoutParams to read attributes of layout instead of ViewGroup.LayoutParams
          return new LayoutParams(getContext(), attrs);
     }

     protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
          // if the layout params is invalid, the android will throw a runtime exception.
          if (p instanceof LayoutParams) {
               int columns = columns(), rows = rows();
               LayoutParams lp = (LayoutParams) p;
               if (lp.columnEnd > columns || lp.columnStart < 0)
                    return false;
               if (lp.rowEnd > rows || lp.rowStart < 0)
                    return false;
               return lp.columnEnd > lp.columnStart && lp.rowEnd > lp.rowStart;
          } else
               return false;
     }

}
Back to top
View user's profile Send private message Send e-mail
GodsMoon
Junior Developer
Junior Developer


Joined: 10 Dec 2007
Posts: 23

PostPosted: Fri Apr 18, 2008 3:22 pm    Post subject: Reply with quote

Pardon my ignorance, but why is this method better than a table layout?
Looks like your layout would require 2 table layouts since you have a button that spans multiple rows but still...
Is the performance really that bad for a table layout?
Back to top
View user's profile Send private message Visit poster's website
mmin18
Junior Developer
Junior Developer


Joined: 03 Feb 2008
Posts: 19
Location: China

PostPosted: Fri Apr 18, 2008 3:38 pm    Post subject: Reply with quote

GodsMoon wrote:
Pardon my ignorance, but why is this method better than a table layout?
Looks like your layout would require 2 table layouts since you have a button that spans multiple rows but still...
Is the performance really that bad for a table layout?


TableLayout can span multiply columns but cannot span rows. it can let it's child view to decide how big a row or a colunm will take but a GridLayout cannot.

GridLayout's child view doesn't measure itself since it gives each child a explicit position according to the layout params.

They are used in different cases. But if you say you can use 2 table layouts to make that layout, i'll say why don't you try a AbsoluteLayout?
Back to top
View user's profile Send private message Send e-mail
duanng
Freshman
Freshman


Joined: 03 Apr 2008
Posts: 8

PostPosted: Fri Apr 18, 2008 3:51 pm    Post subject: Reply with quote

Yup, I agree to that. Think about this idea can be use to build a fast Grid Control. Where you do not want to stuffing 100x100 child controls to it and each controls render on themself.
Back to top
View user's profile Send private message
mmin18
Junior Developer
Junior Developer


Joined: 03 Feb 2008
Posts: 19
Location: China

PostPosted: Fri Apr 18, 2008 3:53 pm    Post subject: Reply with quote

And this tutorial also helps you to understand how to build a custom layout widget.
Back to top
View user's profile Send private message Send e-mail
cabernet1976
Senior Developer
Senior Developer


Joined: 16 Nov 2007
Posts: 147
Location: China

PostPosted: Wed Apr 30, 2008 4:07 am    Post subject: Reply with quote

Very nice tutorials Smile
_________________
EveryAlbum - http://www.anddev.org/viewtopic.php?p=7117#7117
Back to top
View user's profile Send private message Visit poster's website
Q_Ball
Once Poster
Once Poster


Joined: 03 Nov 2008
Posts: 1

PostPosted: Mon Nov 03, 2008 8:38 pm    Post subject: Reply with quote

This doesn't seem to work with 1.0, the constructor for ViewGroup seems to have changed.

Or maybe its just me, android.content.Resources cannot be resolved to a type...
Back to top
View user's profile Send private message
Display posts from previous:   
       anddev.org - Android Development Community | Android Tutorials | Index -> Novice Tutorials All times are GMT + 1 Hour
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


© 2007, Android Development Community
All rights reserved.
Powered by phpBB.