Android Health App Design : User Interface Made Beautiful

android_imirire_add_foodsThe previous tutorial of our series left imirire list view showing up nice and clean but it is no secret it looks awful .In this article we are going to fix that.In that regard ,we modify the text display to resemble a calculator display screen ,change the lists items so they fill up the width of the screen and fill our model with more realistic data the user can work with.Lets just jump right in.

The goal.

In the figure below on the left we see what we had in the last tutorial ,at the end of this one ,we will have what we see to the right.

android_imirire_beautiful_goal

Changing the look.

Lets first change the calorie count display screen.The xml code for that text view lives in activity_main.xml.Open up that file and change the text view’s code as shown in the code snippet below ,the changed code is highlighted in bold red.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.blikoon.imirire.MainActivity">

    <TextView
        android:id="@+id/calorie_display"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="777.5"
        android:gravity="center"
        android:numeric="integer|decimal"
        android:textColor="#c35757"
        android:textSize="70dp"
        android:background="#726666"
        />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/food_list_recycler_view"
        android:layout_below="@id/calorie_display"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</RelativeLayout>

android::text controls the text that is shown in the textView ,we sure will override this in java code but this is just something to look at as we design. android::gravity controls where the text is shown relatively to the box of the TextView.Please note the difference with layout::gravity which controls where a view is shown relatively to a LinearLayout.android::numeric|decimal specifies that our text view can show decimals or integers.Finaly we set a textColor ,textSize and the backgroundColor to our text View.Switch to the design view of your layout file and your design should look as follows.

android_imirire_calorie_display_preview

You should know that it is also possible to edit the properties of your views in the design view.All you have to do is select your view on the left,you can see calorie_display(TextView) selected in the figure ,and scroll to your property of interest in the properties window at the bottom.You can see my currently selected background color in the figure.Our TextView looks nice now we move to the list items.The code that controls how list items look lives inside our list_item_food.xml file.Open it up and apply the following changes.

list_item_food.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="5dp"
    android:background="#aaaaaf"
    >


    <CheckBox
        android:id="@+id/list_item_food_checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"/>

    <TextView
        android:id="@+id/list_item_food_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="foodName"
        android:layout_marginLeft="70dp"
        android:textSize="30dp"
       />

    <EditText
        android:id="@+id/list_item_gram_input_field"
        android:layout_width="90dp"
        android:layout_height="wrap_content"
        android:hint="0.0"
        android:layout_gravity="right"
        android:inputType="number|numberDecimal"

        />
</LinearLayout>

We give a nice background to the list item using the android::background property on the LinearLayout tag.We give list_item_food_name an android::layout_weight of 1 so it fills up the remaining space.We add a left margin to it doesn’t bump in the checkbox and we force list_item_gram_input_field to be on the right of the layout with android:layout_gravity=”right”.The design preview of the list item layout file looks as shown in the figure below.

android_imirire_list_item_preview

If you run your app ,you’ll be amazed at how great it looks now.

android_imirire_app_preview

Adding Separators between items.

The calorie count display TextView looks better, and the list items are laid out nicely.But it would be nice if we could add simple separators between the list items so it becomes more clear to the user which item is which.There probably are a several ways to achieve this but the easiest I have found is to use ItemDecorations we can set directly to the recyclerView in our app.I got the one I used from this gist .Add a new java file to your package and name it DividerItemDecoration.java .Copy and paste in the code from the gist link as shown below.

package com.blikoon.imirire;


/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;

/**
 * This class is from the v7 samples of the Android SDK. It's not by me!
 * <p/>
 * See the license above for details.
 */
public class DividerItemDecoration extends RecyclerView.ItemDecoration
{

    private static final int[] ATTRS = new int[] { android.R.attr.listDivider };

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;

    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;



    private Drawable mDivider;

    private int mOrientation;

    public DividerItemDecoration(Context context, int orientation)
    {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    public void setOrientation(int orientation)
    {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST)
        {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent)
    {

        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }

    public void drawVertical(Canvas c, RecyclerView parent)
    {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();

        final int childCount = parent.getChildCount();

        for (int i = 0; i < childCount; i++)
        {
            final View child = parent.getChildAt(i);
            android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(
                    parent.getContext());
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    public void drawHorizontal(Canvas c, RecyclerView parent)
    {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++)
        {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition,
                               RecyclerView parent)
    {
        if (mOrientation == VERTICAL_LIST)
        {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else
        {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}

and inside MainActivity.java ,apply the decoration to the RecylerView as shown below:

mFoodListRecyclerView = (RecyclerView)
        findViewById(R.id.food_list_recycler_view);
mFoodListRecyclerView.addItemDecoration(
        new DividerItemDecoration(getBaseContext(), DividerItemDecoration.VERTICAL_LIST));
mFoodListRecyclerView.setLayoutManager(new LinearLayoutManager(getBaseContext()));

Run the application and it should look something like the figure below.

android_imirire_list_item_separators

This is better .A good lesson you should take out of this is that it is possible to use other people’s code to spice up your designs.You can go on and try and study how the Decoration code works but we will not do that today.Instead ,we want to add some more realistic data to our model so the users can start counting the calories.This is really nothing more than adding static food data to the populateWithInitialFoods() function inside our FoodModel class. The new code for the function is reproduced below for convenience.

private void populateWithInitialFoods()
{
    //Cereals
    Food food1 = new Food("Rice",0,"RiceIcon",351.0);
    mFoods.add(food1);

    Food food2 = new Food("Peanuts",0,"RiceIcon",567.0);
    mFoods.add(food2);

    Food food3 = new Food("Beans",0,"RiceIcon",328.9);
    mFoods.add(food3);

    Food food4 = new Food("Peas",0,"RiceIcon",81.0);
    mFoods.add(food4);

    Food food5 = new Food("Corn",0,"RiceIcon",365.0);
    mFoods.add(food5);

    Food food6 = new Food("Soy",0,"RiceIcon",54.0);
    mFoods.add(food6);

    //Vegetables

    Food food7 = new Food("Cabbages",0,"RiceIcon",26.0);
    mFoods.add(food7);

    Food food8 = new Food("Tomatoes",0,"RiceIcon",17.8);
    mFoods.add(food8);

    Food food9 = new Food("Onions",0,"RiceIcon",40.0);
    mFoods.add(food9);

    Food food10 = new Food("Celery",0,"RiceIcon",15.0);
    mFoods.add(food10);

    Food food11 = new Food("Cucumber",0,"RiceIcon",15.3);
    mFoods.add(food11);

    Food food12 = new Food("Carrot",0,"RiceIcon",41.0);
    mFoods.add(food12);

    Food food13 = new Food("Eggplant",0,"RiceIcon",25.0);
    mFoods.add(food13);

    Food food14 = new Food("Peppers",0,"RiceIcon",20.1);
    mFoods.add(food14);

    Food food15 = new Food("Cauliflower",0,"RiceIcon",25.0);
    mFoods.add(food15);

    Food food16 = new Food("Broccoli",0,"RiceIcon",34.0);
    mFoods.add(food16);

    Food food17 = new Food("Garlic",0,"RiceIcon",149.0);
    mFoods.add(food17);

    Food food18 = new Food("Ginger",0,"RiceIcon",80.0);
    mFoods.add(food18);

    Food food19 = new Food("Spinach",0,"RiceIcon",23.0);
    mFoods.add(food19);


    //Fruits
    Food food20 = new Food("Mango",0,"RiceIcon",60.0);
    mFoods.add(food20);

    Food food21 = new Food("Lemon",0,"RiceIcon",29.3);
    mFoods.add(food21);

    Food food22 = new Food("Banana",0,"RiceIcon",88.9);
    mFoods.add(food22);

    Food food23 = new Food("Apple",0,"RiceIcon",52.1);
    mFoods.add(food23);

    Food food24 = new Food("Pineapple",0,"RiceIcon",49.9);
    mFoods.add(food24);

    Food food25 = new Food("Avocado",0,"RiceIcon",160.0);
    mFoods.add(food25);

    Food food26 = new Food("Grapes",0,"RiceIcon",67.0);
    mFoods.add(food26);

    Food food27 = new Food("Tangerines",0,"RiceIcon",53.0);
    mFoods.add(food27);

    Food food28 = new Food("Orange",0,"RiceIcon",47.0);
    mFoods.add(food28);


    //Meat& Eggs& Mushrooms

    Food food29 = new Food("Chicken",0,"RiceIcon",239.0);
    mFoods.add(food29);

    Food food30 = new Food("Beef",0,"RiceIcon",250.5);
    mFoods.add(food30);

    Food food31 = new Food("Pork",0,"RiceIcon",242.3);
    mFoods.add(food31);

    Food food32 = new Food("Eggs",0,"RiceIcon",155.0);
    mFoods.add(food32);

    Food food33 = new Food("Mushrooms",0,"RiceIcon",38.0);
    mFoods.add(food33);

    Food food34 = new Food("Bass fish",0,"RiceIcon",124.0);
    mFoods.add(food34);

    //Others
    Food food35 = new Food("Potatoes",0,"RiceIcon",76.5);
    mFoods.add(food35);

    Food food36 = new Food("Milk",0,"RiceIcon",55.2);
    mFoods.add(food36);

    Food food37 = new Food("Wheat",0,"RiceIcon",342.4);
    mFoods.add(food37);

    Food food38 = new Food("Sugar",0,"RiceIcon",406.3);
    mFoods.add(food38);

}

A word about the Contructors of Food objects shown in the function.The second argument is the number of grams per food.This is the number we get from the gram input EditText in each list item.The fourth argument is the number of calories per 100 grams.We picked /100g because most foods are rated that way.A good run of the app shows the figure below.

android_imirire_add_foods

You can see that our foods are showing up correctly.The app looks great but there is one major thing we haven’t done yet.We need to handle user input.The design we have in mind is to constantly update the display screen as users input gram input in the EditTexts inside list items.Also the user has to check the checkbox on the item for its data to be taken into account.Input data handling together with menus adding will be subject of our very next tutorial.The completed source code for this tutorial is available at my git repository.For now ,I hope this has been informative to you and I would like to thank you for reading.

Posted in android, Tutorials and tagged , , .

Daniel Gakwaya loves computer Hardware/Software.He is a Software Engineer at BLIKOON and lead developer of bliboard-The whiteboard system currently marketed by the company.He is known to hack around on any piece of tech that happens to pick his interest. More on his tech endeavors here
Follow him on Twitter

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.