Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8047253
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 5, 20262026-06-05T06:02:41+00:00 2026-06-05T06:02:41+00:00

As per the Google design patterns I have been implementing the dashboard layout by

  • 0

As per the Google design patterns I have been implementing the dashboard layout by using the DashboardLayout.java file used by Google in there Google IO app. This has been working fine when using buttons, but as soon as I add a custom view the grid view produced by the DashboardLayout.java file falls apart:

Working without custom view:

enter image description here

Not working with custom view:

enter image description here

The code for the 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 now = new DateTime(); 

    Period p = new Period(now, expiry, PeriodType.dayTime());

    canvas.drawColor(Color.TRANSPARENT);

    if(flag)
    {
        // To ensure the rectangles will be wide enough for all numbers we cheat and initially set the 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) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = measureSpec;
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by 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) {
        // We were told how big to be
        result = specSize;
    } else {
        // Measure the text (beware: ascent is a negative number)
        result = viewWidth;
        /*if (specMode == MeasureSpec.AT_MOST) {
            // Respect AT_MOST value if that was what is called for by measureSpec
            result = Math.min(result, specSize);
        }*/
    }
    return result;
}
}

The DashboardLayout code that I am using:

/**
 * Custom layout that arranges children in a grid-like manner, optimizing for even   horizontal and
* 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 to find the 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();
        for (int i = 0; i < 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 for each child to be exactly the same size.

        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildWidth, MeasureSpec.EXACTLY);
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                mMaxChildHeight, MeasureSpec.EXACTLY);

        for (int i = 0; i < 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 the number of visible children.
    int visibleCount = 0;
    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (child.getVisibility() == GONE) {
            continue;
        }
        ++visibleCount;
    }

    if (visibleCount == 0) {
        return;
    }

    // Calculate what number of rows and columns will optimize for even horizontal and
    // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on.
    int bestSpaceDifference = Integer.MAX_VALUE;
    int spaceDifference;

    // Horizontal and 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 a better whitespace squareness/ratio
            bestSpaceDifference = spaceDifference;

            // If we found a better whitespace squareness and there's only 1 row, this is
            // the best we can do.
            if (rows == 1) {
                break;
            }
        } else {
            // This is a worse whitespace ratio, use the previous value of cols and 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 and cols.

    // If we chose a layout that has negative horizontal or vertical space, force it to zero.
    hSpace = Math.max(0, hSpace);
    vSpace = Math.max(0, vSpace);

    // Re-use width/height variables to be child width/height.
    width = (width - hSpace * (cols + 1)) / cols;
    height = (height - vSpace * (rows + 1)) / rows;

    int left, top;
    int col, row;
    int visibleIndex = 0;
    for (int i = 0; i < 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 but not least the 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">

    <!-- The custom view that once un-commented cause the 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 over the amount of code posted, but I hope it makes it easier to see the issue(s).

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-05T06:02:42+00:00Added an answer on June 5, 2026 at 6:02 am

    I have since discovered the bug in the my onMeasureWidth function in the 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) {
            // We were told how big to be
            result = specSize;
        } else {
            // Measure the text
            result = measureSpec;
            if (specMode == MeasureSpec.AT_MOST) {
                // Respect AT_MOST value if that was what is called for by 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) {
                // We were told how big to be
                result = specSize;
            } else {
                // Measure the text
                result = viewWidth;
    
            }
    
        return result;
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have been using the Google Websearch API for over 1 year now. The
Using Visual Studio 2005 As per the title; MSDN and google can't tell me,
I've been tasked with implementing Google Analytics inside our (ASP.NET) application. Here is the
I have built a service with ServiceStack (customer example) as per this link: https://docs.google.com/present/view?id=dg3mcfb_213gsvvmmfk
It's possible to suppress warnings on a per-file basis with Google's Closure Compiler via
I am developing an application on the Google App Engine using Python. I have
Average time spent per visit as reported by Google Analytics does not match my
Per a great answer from another question I have begun mounting global resources (css/js/images)
Amazon Integration I have my own CMS which has a file manager. A lot
I created a google maps store locator and im using a jquery pagination to

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.