Cyril Mottier

β€œIt’s the little details that are vital. Little things make big things happen.” – John Wooden

Split Information Using a SegmentedBar

In a previous post, I’ve been introducing you with a library I’m developing. I strongly encourage you to read the long post I have written because it explains almost everything about the GreenDroid library: its purpose, the purpose I’m looking for with it, etc. To sum up, GreenDroid is an approach to unify Android developments and UI/UXs by making application development easier and straightforward.

The first release of GreenDroid focused on creating and populating ListView in a very simple manner. This time, I’ve included a new widget that can be very helpful when designing applications: SegmentedBar. Used with its associated SegmentedHost and SegmentedAdapter classes, a SegmentedBar is really similar to a TabWidget. The main difference relie in the fact a SegmentedBar only uses views and look different:

As you may have noticed, a SegmentedBar looks like the widget that were previously (prior to Froyo) used in the Android Market application to switch between applications types. A SegmentedBar is a bunch of segments. Each segment has an indicator that can be on or off depending on its current state (checked or not). Once you’re tapping a segment, the associated view is displayed.

Note: The snippets of code below are all extracted from the GDCatolog and GreenDroid projects. Those projects are all available on GitHub at the following address: http://github.com/cyrilmottier/GreenDroid

As a developer, you probably prefer reading code than explainations … I’ve implementing a really tiny application available in the GDCatalog application that uses the 3 classes given above. The first thing to do is to created our layout:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<greendroid.widget.SegmentedHost xmlns:greendroid="http://schemas.android.com/apk/res/com.cyrilmottier.android.gdcatalog"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/segmentedHost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    greendroid:segmentedBar="@+id/segmentedBar"
    greendroid:segmentedContentView="@+id/segmentedContentView" >

    <greendroid.widget.SegmentedBar
        android:id="@+id/segmentedBar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <FrameLayout
        android:id="@+id/segmentedContentView"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:foreground="@drawable/gd_shadow_top" />

</greendroid.widget.SegmentedHost>

This layout is composed of a root SegmentedHost element that fills (or match according to the brand new match_parent value) the size of the screen. A SegmentedHost is a wrapper that contains a SegmentedBar and a content view (FrameLayout). This two elements will be filled with the data provided by a SegmentedAdapter. The XML attributes greendroid:segmentedBar and greendroid:segmentedContentView are mandatory and must be used to inform the SegmentedHost which subview must be used. I assume the rest is pretty clear so I won’t explain anything else but the android:foreground attribute. Android often uses shadows to separate two main areas on screen. The GreenDroid library includes shadows (named gd_shadow_top and gd_shadow_bottom) that can be reused easily to enhance your UIs.

Now our layout is ready, the only thing we have to do is to fill the SegmentedHost with the appropriate data. Data are provided through a SegmentedAdapter as shown below:

SegmentedActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package com.cyrilmottier.android.gdcatalog;

import com.cyrilmottier.android.gdcatalog.util.ColorUtils;

import greendroid.widget.SegmentedAdapter;
import greendroid.widget.SegmentedHost;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.TextView;

public class SegmentedActivity extends Activity {

    private final Handler mHandler = new Handler();
    private PeopleSegmentedAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.segmented_controls);

        SegmentedHost segmentedHost = (SegmentedHost) findViewById(R.id.segmentedHost);

        mAdapter = new PeopleSegmentedAdapter();
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mAdapter.mReverse = true;
                mAdapter.notifyDataSetChanged();
            }
        }, 4000);

        segmentedHost.setAdapter(mAdapter);
    }

    private class PeopleSegmentedAdapter extends SegmentedAdapter {

        public boolean mReverse = false;

        @Override
        public View getView(int position, ViewGroup parent) {

            TextView textView = new TextView(SegmentedActivity.this);
            textView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
            textView.setGravity(Gravity.CENTER);

            final int color = getColor(mReverse ? ((getCount() - 1) - position) : position);
            textView.setBackgroundColor(color);
            textView.setTextColor(ColorUtils.negativeColor(color));
            // It's not necessary to compute the "reversed" position as the
            // getSegmentTitle will do it automatically
            textView.setText(getSegmentTitle(position));

            return textView;
        }

        @Override
        public int getCount() {
            return 4;
        }

        @Override
        public String getSegmentTitle(int position) {

            switch (mReverse ? ((getCount() - 1) - position) : position) {
                case 0:
                    return getString(R.string.segment_1);
                case 1:
                    return getString(R.string.segment_2);
                case 2:
                    return getString(R.string.segment_3);
                case 3:
                    return getString(R.string.segment_4);
            }

            return null;
        }

        private int getColor(int position) {
            switch (position) {
                case 0:
                    return Color.RED;
                case 1:
                    return Color.GREEN;
                case 2:
                    return Color.BLUE;
                case 3:
                    return Color.CYAN;
            }
            return Color.TRANSPARENT;
        }
    }

}

In order to create your own SegmentedAdapter, you simply need to extend the SegmentedAdapter and override the three following methods:

  • int getCount(): This method must return the number of segments your SegmentedBar will contain.

  • String getSegmentTitle(int position): Returns the title for the segment at the given position

  • View getView(int position, ViewGroup parent): This method is very similar to the traditionnal getView method. The only difference is there is no convertView parameter. Actually this method will be called only once in the lifetime of your SegmentedHost. Once created, The SegmentedHost will automatically keep a reference on the generated view. Hence, there is no need to recycle views. If you want to reorganize your segments, simply call notifyDataSetChanged() as shown in the exemple above

Using a SegmentedBar is very useful when your screen have a lot of information that can be split in several pieces. A great sample would be an “About” screen : you can now split the developer information from the legal or partners information. I hope you’ll use this new widget as well as all other cool features integrated to the GreenDroid library. Happy coding and see you soon for new amazing GreenDroid features!