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

Basic Tutorials concerning: GUI, Views, Activites, XML, Layouts, Intents, ...

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

Postby mmin18 » Fri Apr 18, 2008 9:53 am

This is a keypad used in handyCalc:
Image
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 files/handycalc_library_155.zip and import to eclipse to run it yourself.

Image

Syntax: [ Download ] [ Hide ]
Using xml Syntax Highlighting
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <org.mmin.handycalc.MyGridLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.         xmlns:handy="http://schemas.android.com/apk/res/org.mmin.handycalc"
  4.         handy:rows="5"
  5.         handy:columns="5"
  6.         handy:preferredCellWidth="36px"
  7.         handy:preferredCellHeight="36px"
  8.         android:layout_width="fill_parent"
  9.         android:layout_height="wrap_content"
  10.         >
  11. <!--
  12.         preferredCellWidth and preferredCellHeight is useless if you explicit a width or height.
  13.         but in this case, i let the layout to measure it self, so it's measured height equals preferredCellHeight*rows
  14. -->
  15.        
  16.         <Button android:text="(0,0)" handy:layout_columnStart="0" handy:layout_rowStart="0" />
  17.         <Button android:text="(1,0)-(2,3)" handy:layout_columnStart="1" handy:layout_rowStart="0" handy:layout_columnEnd="2" handy:layout_rowEnd="3" />
  18.         <Button android:text="(2,1)-(5,2)" handy:layout_columnStart="2" handy:layout_rowStart="1" handy:layout_columnEnd="5" handy:layout_rowEnd="2" />
  19.         <Button android:text="(1,3)-(5,5)" handy:layout_columnStart="1" handy:layout_rowStart="3" handy:layout_columnEnd="5" handy:layout_rowEnd="5" />
  20.        
  21. </org.mmin.handycalc.MyGridLayout>
Parsed in 0.004 seconds, using GeSHi 1.0.8.4
Attachments
GridLayout.png
GridLayout demo
GridLayout.png (30.41 KiB) Viewed 18448 times
handyCalc Keypad.PNG
handyCalc keypad
handyCalc Keypad.PNG (37.76 KiB) Viewed 18450 times
mmin18
Junior Developer
Junior Developer
 
Posts: 20
Joined: Sun Feb 03, 2008 2:33 pm
Location: China

Top

Postby mmin18 » Fri Apr 18, 2008 10:01 am

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

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package org.mmin.handycalc;
  2.  
  3.  
  4.  
  5. import ...;
  6.  
  7.  
  8.  
  9. public class MyGridLayout extends ViewGroup {
  10.  
  11.  
  12.  
  13. // To view the full code, download the archive file http://www.anddev.org/files/handycalc_library_155.zip and import it in eclipse.
  14.  
  15.  
  16.  
  17.         public static class LayoutParams extends
  18.  
  19.                         android.view.ViewGroup.LayoutParams {
  20.  
  21. // ...
  22.  
  23.                 public LayoutParams(Context c, AttributeSet attrs) {
  24.  
  25.                         super(10, 10);
  26.  
  27.                         // get the layout attributes of it's child view from the xml layout file.
  28.  
  29.                         android.content.Resources.StyledAttributes a = c
  30.  
  31.                                         .obtainStyledAttributes(attrs,
  32.  
  33.                                                         R.styleable.MyGridLayout_Layout);
  34.  
  35.                         this.columnStart = a.getInt(
  36.  
  37.                                         R.styleable.MyGridLayout_Layout_layout_columnStart, 0);
  38.  
  39.                         this.rowStart = a.getInt(
  40.  
  41.                                         R.styleable.MyGridLayout_Layout_layout_rowStart, 0);
  42.  
  43.                         this.columnEnd = a.getInt(
  44.  
  45.                                         R.styleable.MyGridLayout_Layout_layout_columnEnd,
  46.  
  47.                                         this.columnStart + 1);
  48.  
  49.                         this.rowEnd = a.getInt(
  50.  
  51.                                         R.styleable.MyGridLayout_Layout_layout_rowEnd,
  52.  
  53.                                         this.rowStart + 1);
  54.  
  55.                         a.recycle();
  56.  
  57.                 }
  58.  
  59.         }
  60.  
  61.  
  62.  
  63.         public MyGridLayout(Context context, AttributeSet attrs, Map inflateParams) {
  64.  
  65.                 super(context, attrs, inflateParams);
  66.  
  67.                 readAttr(context, attrs);
  68.  
  69.         }
  70.  
  71.  
  72.  
  73.         public MyGridLayout(Context context, AttributeSet attrs, Map inflateParams,
  74.  
  75.                         int defStyle) {
  76.  
  77.                 super(context, attrs, inflateParams, defStyle);
  78.  
  79.                 readAttr(context, attrs);
  80.  
  81.         }
  82.  
  83.  
  84.  
  85.         private void readAttr(Context c, AttributeSet attrs) {
  86.  
  87.                 android.content.Resources.StyledAttributes a = c
  88.  
  89.                                 .obtainStyledAttributes(attrs, R.styleable.MyGridLayout);
  90.  
  91.                 this.rows = a.getInt(R.styleable.MyGridLayout_rows, 1);
  92.  
  93.                 this.columns = a.getInt(R.styleable.MyGridLayout_columns, 1);
  94.  
  95.                 this.preferredCellWidth = a.getDimension(
  96.  
  97.                                 R.styleable.MyGridLayout_preferredCellWidth, 1);
  98.  
  99.                 this.preferredCellHeight = a.getDimension(
  100.  
  101.                                 R.styleable.MyGridLayout_preferredCellHeight, 1);
  102.  
  103.                 a.recycle();
  104.  
  105.         }
  106.  
  107.  
  108.  
  109.         private int rows;
  110.  
  111.         private int columns;
  112.  
  113.         private float preferredCellWidth;
  114.  
  115.         private float preferredCellHeight;
  116.  
  117.  
  118.  
  119.         @Override
  120.  
  121.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  122.  
  123.                 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  124.  
  125.                 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  126.  
  127.                 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  128.  
  129.                 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  130.  
  131.  
  132.  
  133.                 float cellWidth = preferredCellWidth(), cellHeight = preferredCellHeight();
  134.  
  135.                 if (widthMode == MeasureSpec.EXACTLY
  136.  
  137.                                 || widthMode == MeasureSpec.AT_MOST) {
  138.  
  139.                         float width = widthSize;
  140.  
  141.                         width = width - mPaddingLeft - mPaddingRight;
  142.  
  143.                         width /= columns();
  144.  
  145.                         if (widthMode == MeasureSpec.EXACTLY)
  146.  
  147.                                 cellWidth = width;
  148.  
  149.                         else
  150.  
  151.                                 cellWidth = Math.min(cellWidth, width);
  152.  
  153.                 }
  154.  
  155.                 if (heightMode == MeasureSpec.EXACTLY
  156.  
  157.                                 || heightMode == MeasureSpec.AT_MOST) {
  158.  
  159.                         float height = heightSize;
  160.  
  161.                         height = height - mPaddingTop - mPaddingBottom;
  162.  
  163.                         height /= rows();
  164.  
  165.                         if (heightMode == MeasureSpec.EXACTLY)
  166.  
  167.                                 cellHeight = height;
  168.  
  169.                         else
  170.  
  171.                                 cellHeight = Math.min(cellHeight, height);
  172.  
  173.                 }
  174.  
  175.  
  176.  
  177.                 int count = getChildCount();
  178.  
  179.                 for (int i = 0; i < count; i++) {
  180.  
  181.                         View child = getChildAt(i);
  182.  
  183.                         if (child.getVisibility() != GONE) {
  184.  
  185.                                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
  186.  
  187.                                 int width = (int) Math.round(cellWidth * lp.width());
  188.  
  189.                                 int height = (int) Math.round(cellHeight * lp.height());
  190.  
  191.                                 child.measure(MeasureSpec.makeMeasureSpec(width,
  192.  
  193.                                                 MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
  194.  
  195.                                                 height, MeasureSpec.EXACTLY));
  196.  
  197.                         }
  198.  
  199.                 }
  200.  
  201.  
  202.  
  203.                 setMeasuredDimension((int) Math.round(cellWidth * columns()
  204.  
  205.                                 + mPaddingLeft + mPaddingRight), (int) Math.round(cellHeight
  206.  
  207.                                 * rows() + mPaddingTop + mPaddingBottom));
  208.  
  209.         }
  210.  
  211.  
  212.  
  213.         private int lastWidth, lastHeight;
  214.  
  215.  
  216.  
  217.         @Override
  218.  
  219.         protected void onLayout(boolean changed, int l, int t, int r, int b) {
  220.  
  221.                 int width = r - l;
  222.  
  223.                 int height = b - t;
  224.  
  225.                 if (lastWidth == width && lastHeight == height)
  226.  
  227.                         return;
  228.  
  229.                 float cellWidth = width, cellHeight = height;
  230.  
  231.                 cellWidth = cellWidth - mPaddingLeft - mPaddingRight;
  232.  
  233.                 cellHeight = cellHeight - mPaddingTop - mPaddingBottom;
  234.  
  235.                 cellWidth /= columns();
  236.  
  237.                 cellHeight /= rows();
  238.  
  239.                 int count = getChildCount();
  240.  
  241.                 for (int i = 0; i < count; i++) {
  242.  
  243.                         View child = getChildAt(i);
  244.  
  245.                         if (child.getVisibility() != GONE) {
  246.  
  247.                                 LayoutParams lp = (LayoutParams) child.getLayoutParams();
  248.  
  249.                                 int cl = (int) Math.round(mPaddingLeft + lp.columnStart
  250.  
  251.                                                 * cellWidth);
  252.  
  253.                                 int ct = (int) Math.round(mPaddingTop + lp.rowStart
  254.  
  255.                                                 * cellHeight);
  256.  
  257.                                 int cr = (int) Math.round(mPaddingLeft + lp.columnEnd
  258.  
  259.                                                 * cellWidth);
  260.  
  261.                                 int cb = (int) Math.round(mPaddingTop + lp.rowEnd * cellHeight);
  262.  
  263.                                 child.layout(cl, ct, cr, cb);
  264.  
  265.                         }
  266.  
  267.                 }
  268.  
  269.                 lastWidth = width;
  270.  
  271.                 lastHeight = height;
  272.  
  273.         }
  274.  
  275.  
  276.  
  277.         public android.view.ViewGroup.LayoutParams generateLayoutParams(
  278.  
  279.                         AttributeSet attrs) {
  280.  
  281.                 // let the MyGridLayout.LayoutParams to read attributes of layout instead of ViewGroup.LayoutParams
  282.  
  283.                 return new LayoutParams(getContext(), attrs);
  284.  
  285.         }
  286.  
  287.  
  288.  
  289.         protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams p) {
  290.  
  291.                 // if the layout params is invalid, the android will throw a runtime exception.
  292.  
  293.                 if (p instanceof LayoutParams) {
  294.  
  295.                         int columns = columns(), rows = rows();
  296.  
  297.                         LayoutParams lp = (LayoutParams) p;
  298.  
  299.                         if (lp.columnEnd > columns || lp.columnStart < 0)
  300.  
  301.                                 return false;
  302.  
  303.                         if (lp.rowEnd > rows || lp.rowStart < 0)
  304.  
  305.                                 return false;
  306.  
  307.                         return lp.columnEnd > lp.columnStart && lp.rowEnd > lp.rowStart;
  308.  
  309.                 } else
  310.  
  311.                         return false;
  312.  
  313.         }
  314.  
  315.  
  316.  
  317. }
  318.  
  319.  
Parsed in 0.052 seconds, using GeSHi 1.0.8.4
mmin18
Junior Developer
Junior Developer
 
Posts: 20
Joined: Sun Feb 03, 2008 2:33 pm
Location: China

Postby GodsMoon » Fri Apr 18, 2008 3:22 pm

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?
GodsMoon
Developer
Developer
 
Posts: 26
Joined: Mon Dec 10, 2007 5:13 pm

Postby mmin18 » Fri Apr 18, 2008 3:38 pm

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?
mmin18
Junior Developer
Junior Developer
 
Posts: 20
Joined: Sun Feb 03, 2008 2:33 pm
Location: China

Postby duanng » Fri Apr 18, 2008 3:51 pm

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.
duanng
Freshman
Freshman
 
Posts: 8
Joined: Thu Apr 03, 2008 11:55 pm

Postby mmin18 » Fri Apr 18, 2008 3:53 pm

And this tutorial also helps you to understand how to build a custom layout widget.
mmin18
Junior Developer
Junior Developer
 
Posts: 20
Joined: Sun Feb 03, 2008 2:33 pm
Location: China

Top

Postby cabernet1976 » Wed Apr 30, 2008 4:07 am

Very nice tutorials :)
Upload2Flickr's blog: http://upload2flickr.blogspot.com
cabernet1976
Senior Developer
Senior Developer
 
Posts: 154
Joined: Fri Nov 16, 2007 2:34 am
Location: China

Postby Q_Ball » Mon Nov 03, 2008 8:38 pm

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...
Q_Ball
Once Poster
Once Poster
 
Posts: 1
Joined: Mon Nov 03, 2008 2:19 pm

DOn't waste your time downloading this code

Postby bennyb » Sat Sep 26, 2009 10:05 pm

Don't waste your time downloading this code. It is incomplete, does not compile and useless.
bennyb
Junior Developer
Junior Developer
 
Posts: 13
Joined: Wed Sep 23, 2009 4:45 pm

Postby Fusion2k » Fri Apr 23, 2010 6:41 pm

Its a bit outdated, but if you change
Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. android.content.Resources.StyledAttributes a = c
  2.  
  3.                          .obtainStyledAttributes(attrs,
  4.  
  5.                                    R.styleable.MyGridLayout_Layout);
Parsed in 0.031 seconds, using GeSHi 1.0.8.4


to

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. android.content.res.TypedArray a = c
  2.  
  3.                          .obtainStyledAttributes(attrs,
  4.  
  5.                                    R.styleable.MyGridLayout_Layout);
  6.  
  7.  
Parsed in 0.034 seconds, using GeSHi 1.0.8.4


it should work

Have been looking for a layout like this, so thanx a bunch :)
Fusion2k
Freshman
Freshman
 
Posts: 7
Joined: Thu Apr 22, 2010 5:36 pm

Re: [Custom Widget] - MyGridLayout: Divide to grid and layou

Postby cody » Fri Oct 08, 2010 12:15 pm

Thanks,
this is a very useful tutorial. But I have problems downloading the example file ... ?

Regards,

cody
cody
Freshman
Freshman
 
Posts: 3
Joined: Thu Oct 07, 2010 10:09 pm

Re: [Custom Widget] - MyGridLayout: Divide to grid and layou

Postby ermalguni » Tue Dec 21, 2010 6:10 pm

I cannot access the zip sources file????
ermalguni
Once Poster
Once Poster
 
Posts: 1
Joined: Tue Dec 21, 2010 6:09 pm

Re: [Custom Widget] - MyGridLayout: Divide to grid and layou

Postby fabianosan » Fri Apr 08, 2011 3:32 am

Thank you. Nice example!
fabianosan
Once Poster
Once Poster
 
Posts: 1
Joined: Fri Apr 08, 2011 3:25 am

Top

Return to Novice Tutorials

Who is online

Users browsing this forum: No registered users and 12 guests