Wednesday 1 April 2015

Dashboard Design Tutorial For Android

Introduction :
Dashboard screen is an important component in android apps which provides easy navigation to functionalities of app. In this tutorial i am going to explain you how to build a dashboard screen for your app.
DashboardLayout is a ViewGroup extended class.

Reason for using DashboardLayout :
We can also achieve dashboard by using grid view in android. But by using DashboardLayout we can drop elements in a elegant manner. As i said in introduction DashboardLayout is a ViewGroup extended class it is capable of holding child Views (so DashboardLayout is a container like LinearLayout and RelativeLayout).
As we add child Views to the class the DashboardLayout measures the height and width of the individual child and the device height and width then the child views are aligned and scaled automatically.

The main goal is to achieve dashboard screen layout and provide navigation to related screens on selecting appropriate icon on the dashboard.

So let’s start by creating simple project.

As we are going to create an application which uses our own custom action bar we need to remove the default actionbar from the application. This is done by applying a custom theme to activity. Declare a theme named noTitleTheme inside the style.xml file. The code is as follows

 <style name = "noTitleTheme">
        <item name = "android:windowNoTitle">true</item>
 </style>

and use this theme in manifest of your application as  android:theme = "@style/noTitleTheme"
by doing this the application will use a NoTitle theme.

 1. Create a new project by going to File ⇒ New Android Project. Fill all the details and name your activity as AndroidDashboardActivity.
2. In this project i am separating dashboard screen into Action Bar(Header) and Dashboard. Finally i will merge both layouts together.
3. Under res/values create a new xml file and name it as styles.xml
( Right Click on res/values ⇒ New ⇒ Android XML File) and fill it with following code.

styles.xml
<resources>
    <style name = "ActionBarCompat">
        <item name = "android:layout_width">fill_parent</item >
        <item name = "android:layout_height">50dp</item >
        <item name = "android:orientation">horizontal</item >
        <item name = "android:background">@drawable/actionbar_background</item >
    </style>
        
    <style name = "DashboardButton">
        <item name = "android:textSize">14dp</item >
        <item name = "android:textStyle">bold</item >
        <item name = "android:textColor">@drawable/login_colorstate</item >
        <item name = "android:gravity">center_horizontal</item >
        <item name = "android:layout_gravity">center_vertical</item >
        <item name = "android:background">@null</item >
        <item name = "android:layout_width">wrap_content</item >
        <item name = "android:layout_height">wrap_content</item >
    </style>    

    <style name = "noTitleTheme">
        <item name = "android:windowNoTitle">true</
item>
    </
style>
</resources>
  

⇒ Designing Actionbar (Header)

 Design four images for 
  1. Gradient background for action-bar background,
  2. Logo of the application,
  3. Logout Button default,
  4. Logout Button pressed.












4.1 Create new xml file under res/layouts and name it as 
   actionbar_layout.xml
( Right Click on res/layout ⇒ New ⇒ Android XML File) and type the following code.


actionbar_layout.xml
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    style = "@style/ActionBarCompat" >
     <ImageView
        android:id = "@+id/logo"
        android:layout_width = "wrap_content"
        android:layout_height = "fill_parent"
        android:layout_alignParentLeft = "true"
        android:background = "@android:color/transparent"
        android:clickable = "false"
        android:paddingLeft = "10dp"
        android:scaleType = "center"
        android:src = "@drawable/actionbar_logo" />
     <ImageView
        android:id = "@+id/logout"
        android:layout_width = "wrap_content"
        android:layout_height = "fill_parent"
        android:layout_alignParentRight = "true"
        android:layout_centerVertical =
"true"
        android:layout_marginRight =
"5dp"
        android:clickable = "true"
        android:scaleType = "center"
        android:src = "@drawable/logout_selector" 
        android:visibility = "gone"  />
  
     <TextView
        android:id = "@+id/loggedUser"
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:layout_toLeftOf = "@+id/logout"
        android:layout_toRightOf = "@+id/logo"
        android:gravity = "center|right"
        android:paddingRight = "10dp"
        android:shadowColor = "@color/grey_shadow"
        android:shadowDx = "1"
        android:shadowDy = "1"
        android:shadowRadius = "0.6"
        android:textAppearance = "?android:attr/textAppearanceMedium"
        android:textColor = "@color/dark_grey"
        android:visibility = "gone" />

</RelativeLayout>

⇒ Designing Logout Button

For Logout we are going to design a custom button with the size of 50X39 using any of your desired image editing software. And name icon as logout_pressed and logout_default.

We got the button ready now we are going to write the selector for the logout button.


 4.2 Create a new xml file under res/drawable. Right Click on res/drawable folder ⇒ New ⇒ Android XML File and name it as logout_selector.xml and fill it with following code.


logout_selector.xml

<selector xmlns:android = "http://schemas.android.com/apk/res/android">

    <item android:drawable = "@drawable/logout_pressed" android:state_pressed = "true"/>
    <item android:drawable = "@drawable/logout_pressed" android:state_focused = "true"/>
    <item android:drawable = "@drawable/logout_default"/>

</
selector>

⇒ Designing Dashboard

For designing dashboard icons you have to use some image editing software (i used Photoshop and Illustrator to create icons). Design each icon for three stages Default State, Hover state and Selected state. You can design for different device densities like hdpi, ldpi, mdpi, xhdpi. For time being i am designing only for hdpi all icons with 160px height.












  
5. Create a new class under res/package. Right Click on src/package folder ⇒ New ⇒ Class and name it as DashboardLayout.java and fill it with following code.

DashboardLayout.java
package com.androiddelight.dashboard;
/*
 * Copyright 2011 Google Inc.
 *
 * 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.
 */
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
 * 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;
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) {
     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 col 
// 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;
        }
    }
}
6. Now we need to create a layout file dashboard screen. Create a new xml file under src/layouts and name it as fragment_layout.xml
( Right Click on res/layout ⇒ New ⇒ Android XML File)

fragment_layout.xml
<!-- Your package folder -->
<com.androiddelight.dashboard.DashboardLayout
    xmlns:android = "http://schemas.android.com/apk/res/android"
    android:layout_width = "fill_parent"
    android:layout_height = "fill_parent"
    android:layout_weight = "1" 
    android:background = "@drawable/dashboard_bg" >
    
    <!--  Make order Button -->
    <Button
        android:id = "@id/btn_make_order"
        style = "@style/DashboardButton"
        android:drawableTop = "@drawable/btn_makeorder"
        android:text = "Make Order" />
     
    <!--  Check update Button -->
    <Button
        android:id = "@id/btn_check_updates"
        style = "@style/DashboardButton"
        android:drawableTop = "@drawable/btn_check_updates"
        android:text = "Check Updates" />




    <!--  Export data Button -->   
    <Button
        android:id = "@id/btn_export_data"
        style = "@style/DashboardButton"
        android:drawableTop = "@drawable/btn_export_data"
        android:text = "Export Data" />
    <!--  Make payment Button -->
    <Button
        android:id = "@id/btn_payment"
        style = "@style/DashboardButton"
        android:drawableTop = "@drawable/btn_make_payment"
        android:text = "Payment" />
</com.androiddelight.dashboard.DashboardLayout>

 The output of above code will be




  ⇒ Merging both layout together

 So far we designed Action bar and Dashboard. Finally we need to merge both layouts in one xml file.

8. Create a new xml file under src/layouts and name it as dashboard_layout.xml and type following code.
( Right Click on res/layout ⇒ New ⇒ Android XML File)

dashboard_layout.xml
<LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android"
    android:id = "@+id/home_root"
    android:orientation = "vertical"
    android:layout_width = "fill_parent"
    android:layout_height = "fill_parent">
        <!-- Include Action Bar -->
        <include layout = "@layout/actionbar_layout"/>
         
        <!--  Include Fragmented dashboard -->   
        <include layout = "@layout/fragment_layout"/>  
             
</LinearLayout >

The final output will be


Check out this live demo :



That's it! This tutorial is to demonstrate a neat and standard looking dashboard. While following this tutorial if you find any of the resources are missing you can click here to get the source code. Share this tutorial if it is useful.

1 comment: