android - Google I/O apps 'DashboardLayout.java' code not working with custom view -
as per google design patterns have been implementing dashboard layout using dashboardlayout.java file used google in there google io app. has been working fine when using buttons, add custom view grid view produced dashboardlayout.java file falls apart:
working without custom view:

not working custom view:

the code custom view is:
public class countdown extends view { int viewwidth; int viewheight; paint textpaint; paint titlepaint; paint labelpaint; paint rectanglepaint; periodformatter daysformatter; periodformatter hoursformatter; periodformatter minutesformatter; periodformatter secondsformatter; datetimezone frenchtimezone; datetime expiry; context ctx; static int[] rectwidth; static int[] rectheight; boolean flag = true; public countdown(context context) { super(context); ctx = context; init(); } public countdown(context context, attributeset attrs) { super(context, attrs); ctx = context; init(); } public countdown(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); ctx = context; init(); } private void init() { rectwidth = new int[]{0,0,0,0}; rectheight = new int[]{0,0,0,0}; textpaint = new paint(); titlepaint = new paint(); labelpaint = new paint(); rectanglepaint = new paint(); frenchtimezone = datetimezone.forid("europe/paris"); expiry = new datetime(2012, 6, 17, 8, 30, frenchtimezone); //setup paints //turn antialiasing on textpaint.setantialias(true); int timerscaledsize = getresources().getdimensionpixelsize(r.dimen.text_size_dashboard_timer); textpaint.settextsize(timerscaledsize); textpaint.setcolor(color.white); textpaint.settextalign(align.center); labelpaint.setantialias(true); int labelscaledsize = getresources().getdimensionpixelsize(r.dimen.text_size_dashboard_timer_boxes_label); labelpaint.settextsize(labelscaledsize); labelpaint.setcolor(color.black); labelpaint.settextalign(align.center); labelpaint.settypeface(typeface.defaultfromstyle(typeface.bold)); titlepaint.setantialias(true); int titlescaledsize = getresources().getdimensionpixelsize(r.dimen.text_size_dashboard_title); titlepaint.settextsize(titlescaledsize); titlepaint.settypeface(typeface.defaultfromstyle(typeface.bold)); titlepaint.setcolor(color.white); rectanglepaint.setantialias(true); daysformatter = new periodformatterbuilder() .printzeroifsupported() .minimumprinteddigits(2) .appenddays() .toformatter(); hoursformatter = new periodformatterbuilder() .printzeroifsupported() .minimumprinteddigits(2) .appendhours() .toformatter(); minutesformatter = new periodformatterbuilder() .printzeroifsupported() .minimumprinteddigits(2) .appendminutes() .toformatter(); secondsformatter = new periodformatterbuilder() .printzeroifsupported() .minimumprinteddigits(2) .appendseconds() .toformatter(); } @override public void ondraw(canvas canvas) { datetime = new datetime(); period p = new period(now, expiry, periodtype.daytime()); canvas.drawcolor(color.transparent); if(flag) { // ensure rectangles wide enough numbers cheat , set width based upon 00. flag = false; drawtextrectangle(0, textpaint, labelpaint, canvas, "00", "", scalefordensity(20, ctx), scalefordensity(33, ctx)); drawtextrectangle(1, textpaint, labelpaint, canvas, "00", "", scalefordensity(53, ctx), scalefordensity(33, ctx)); drawtextrectangle(2, textpaint, labelpaint, canvas, "00", "", scalefordensity(87, ctx), scalefordensity(33, ctx)); drawtextrectangle(3, textpaint, labelpaint, canvas, "00", "", scalefordensity(120, ctx), scalefordensity(33, ctx)); } string title = "countdown"; float textwidth = titlepaint.measuretext(title); float titlestartpositionx = (viewwidth - textwidth) / 2; canvas.drawtext(title, titlestartpositionx, viewheight - scalefordensity(5, ctx), titlepaint); bitmap bitmap = bitmapfactory.decoderesource(getresources(), r.drawable.dashboard_counter); canvas.drawbitmap(bitmap, 0, 0, null); drawtextrectangle(0, textpaint, labelpaint, canvas, daysformatter.print(p), "days", scalefordensity(20, ctx), scalefordensity(33, ctx)); drawtextrectangle(1, textpaint, labelpaint, canvas, hoursformatter.print(p), "hrs", scalefordensity(53, ctx), scalefordensity(33, ctx)); drawtextrectangle(2, textpaint, labelpaint, canvas, minutesformatter.print(p), "mins", scalefordensity(87, ctx), scalefordensity(33, ctx)); drawtextrectangle(3, textpaint, labelpaint, canvas, secondsformatter.print(p), "secs", scalefordensity(120, ctx), scalefordensity(33, ctx)); invalidate(); } private void drawtextrectangle(int index, paint paint, paint labelpaint, canvas canvas, string text, string label, float x, float y) { paint.settextalign(align.center); rect bounds = new rect(); bounds = new rect(); paint.gettextbounds(text, 0, text.length(), bounds); if(rectwidth[index] == 0) { rectwidth[index] = math.abs(bounds.right - bounds.left); rectwidth[index] += scalefordensity(5, ctx); } if(rectheight[index] == 0) { rectheight[index] = math.abs(bounds.bottom - bounds.top); rectheight[index] += scalefordensity(5, ctx); } bounds.left = (int) (x - (rectwidth[index] / 2)); bounds.top = (int) (y - rectheight[index]); bounds.right = bounds.left + rectwidth[index]; bounds.bottom = (int) (bounds.top + rectheight[index] + scalefordensity(7, ctx)); paint rectanglepaint = new paint(); rectanglepaint.setantialias(true); rectanglepaint.setshader(new lineargradient(bounds.centerx(), bounds.top, bounds.centerx(), bounds.bottom, 0xff8ed8f8, 0xff207d94, tilemode.mirror)); rectf boundsf = new rectf(bounds); canvas.drawroundrect(boundsf, 2f, 2f, rectanglepaint); canvas.drawtext(text, x, y, paint); canvas.drawtext(label, x, y + rectheight[index], labelpaint); } public float scalefordensity(float px, context context) { resources resources = context.getresources(); displaymetrics metrics = resources.getdisplaymetrics(); return px * metrics.density + .5f; } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { int width = measurewidth(widthmeasurespec); int height = measureheight(heightmeasurespec, widthmeasurespec); viewwidth = width; viewheight = height; setmeasureddimension(width, height); } private int measurewidth(int measurespec) { int result = 0; int specmode = measurespec.getmode(measurespec); int specsize = measurespec.getsize(measurespec); if (specmode == measurespec.exactly) { // told how big result = specsize; } else { // measure text result = measurespec; if (specmode == measurespec.at_most) { // respect at_most value if called measurespec result = math.min(result, specsize); } } return result; } private int measureheight(int measurespecheight, int measurespecwidth) { int result = 0; int specmode = measurespec.getmode(measurespecheight); int specsize = measurespec.getsize(measurespecheight); if (specmode == measurespec.exactly) { // told how big result = specsize; } else { // measure text (beware: ascent negative number) result = viewwidth; /*if (specmode == measurespec.at_most) { // respect at_most value if called measurespec result = math.min(result, specsize); }*/ } return result; } } the dashboardlayout code using:
/** * custom layout arranges children in grid-like manner, optimizing horizontal , * vertical whitespace. */ public class dashboardlayout extends viewgroup { private static final int uneven_grid_penalty_multiplier = 10; boolean run = true; private int mmaxchildwidth = 0; private int mmaxchildheight = 0; public dashboardlayout(context context) { super(context, null); } public dashboardlayout(context context, attributeset attrs) { super(context, attrs, 0); } public dashboardlayout(context context, attributeset attrs, int defstyle) { super(context, attrs, defstyle); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { if(run) { run = false; mmaxchildwidth = 0; mmaxchildheight = 0; // measure once find maximum child size. int childwidthmeasurespec = measurespec.makemeasurespec( measurespec.getsize(widthmeasurespec), measurespec.at_most); int childheightmeasurespec = measurespec.makemeasurespec( measurespec.getsize(widthmeasurespec), measurespec.at_most); final int count = getchildcount(); (int = 0; < count; i++) { final view child = getchildat(i); if (child.getvisibility() == gone) { continue; } child.measure(childwidthmeasurespec, childheightmeasurespec); mmaxchildwidth = math.max(mmaxchildwidth, child.getmeasuredwidth()); mmaxchildheight = math.max(mmaxchildheight, child.getmeasuredheight()); } // measure again each child same size. childwidthmeasurespec = measurespec.makemeasurespec( mmaxchildwidth, measurespec.exactly); childheightmeasurespec = measurespec.makemeasurespec( mmaxchildheight, measurespec.exactly); (int = 0; < count; i++) { final view child = getchildat(i); if (child.getvisibility() == gone) { continue; } child.measure(childwidthmeasurespec, childheightmeasurespec); } } setmeasureddimension( resolvesize(mmaxchildwidth, widthmeasurespec), resolvesize(mmaxchildheight, heightmeasurespec)); } @override protected void onlayout(boolean changed, int l, int t, int r, int b) { int width = r - l; int height = b - t; final int count = getchildcount(); // calculate number of visible children. int visiblecount = 0; (int = 0; < count; i++) { final view child = getchildat(i); if (child.getvisibility() == gone) { continue; } ++visiblecount; } if (visiblecount == 0) { return; } // calculate number of rows , columns optimize horizontal , // vertical whitespace between items. start 1 x n grid, try 2 x n, , on. int bestspacedifference = integer.max_value; int spacedifference; // horizontal , vertical space between items int hspace = 0; int vspace = 0; int cols = 1; int rows; while (true) { rows = (visiblecount - 1) / cols + 1; hspace = ((width - mmaxchildwidth * cols) / (cols + 1)); vspace = ((height - mmaxchildheight * rows) / (rows + 1)); spacedifference = math.abs(vspace - hspace); if (rows * cols != visiblecount) { spacedifference *= uneven_grid_penalty_multiplier; } if (spacedifference < bestspacedifference) { // found better whitespace squareness/ratio bestspacedifference = spacedifference; // if found better whitespace squareness , there's 1 row, // best can do. if (rows == 1) { break; } } else { // worse whitespace ratio, use previous value of cols , exit. --cols; rows = (visiblecount - 1) / cols + 1; hspace = ((width - mmaxchildwidth * cols) / (cols + 1)); vspace = ((height - mmaxchildheight * rows) / (rows + 1)); break; } ++cols; } // lay out children based on calculated best-fit number of rows , cols. // if chose layout has negative horizontal or vertical space, force zero. hspace = math.max(0, hspace); vspace = math.max(0, vspace); // re-use width/height variables child width/height. width = (width - hspace * (cols + 1)) / cols; height = (height - vspace * (rows + 1)) / rows; int left, top; int col, row; int visibleindex = 0; (int = 0; < count; i++) { final view child = getchildat(i); if (child.getvisibility() == gone) { continue; } row = visibleindex / cols; col = visibleindex % cols; left = hspace * (col + 1) + width * col; top = vspace * (row + 1) + height * row; child.layout(left, top, (hspace == 0 && col == cols - 1) ? r : (left + width), (vspace == 0 && row == rows - 1) ? b : (top + height)); ++visibleindex; } } } and last not least layout:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <textview style="@style/headertextview" android:text="@string/header_dashboard" /> <view android:layout_width="fill_parent" android:layout_height="@dimen/content_divider_height" android:layout_marginleft="@dimen/content_divider_margin" android:layout_marginright="@dimen/content_divider_margin" android:background="@color/content_divider_colour" /> <com.a.b.ui.dashboardlayout android:layout_width="fill_parent" android:layout_height="fill_parent" style="@style/container"> <!-- custom view once un-commented cause problem --> <!-- <com.a.b.widget.countdown style="@style/dashboardbutton" /> --> <button android:id="@+id/home_btn_news" style="@style/dashboardbutton" android:text="a" android:drawabletop="@drawable/dashboard_counter" /> <button android:id="@+id/home_btn_feed" style="@style/dashboardbutton" android:text="b" android:drawabletop="@drawable/dashboard_counter" /> <button android:id="@+id/home_btn_guide" style="@style/dashboardbutton" android:text="c" android:drawabletop="@drawable/dashboard_counter" /> <button android:id="@+id/home_btn_sessions" style="@style/dashboardbutton" android:text="d" android:drawabletop="@drawable/dashboard_counter" /> <button android:id="@+id/home_btn_events" style="@style/dashboardbutton" android:text="e" android:drawabletop="@drawable/dashboard_counter" /> </com.a.b.ui.dashboardlayout> </linearlayout> apologies on amount of code posted, hope makes easier see issue(s).
i have since discovered bug in onmeasurewidth function in custom view. instead of:
private int measurewidth(int measurespec) { int result = 0; int specmode = measurespec.getmode(measurespec); int specsize = measurespec.getsize(measurespec); if (specmode == measurespec.exactly) { // told how big result = specsize; } else { // measure text result = measurespec; if (specmode == measurespec.at_most) { // respect at_most value if called measurespec result = math.min(result, specsize); } } return result; } it should be:
private int measurewidth(int measurespec) { int result = 0; int specmode = measurespec.getmode(measurespec); int specsize = measurespec.getsize(measurespec); if (specmode == measurespec.exactly) { // told how big result = specsize; } else { // measure text result = viewwidth; } return result; }
Comments
Post a Comment