News

Welcome to End Point’s blog

Ongoing observations by End Point people

Dynamically adding custom radio buttons in Android

I've been writing a timesheet tracking app for End Point. In working on various features of this app, I've had more than a few problems to work through, since this project is one of my first on Android and I've never used many of the Android features that I'm now using. One particularly fun bit was setting up a scrollable list of radio buttons with numbers from 0 - 23 (no, we don't often have people working 23 hours on a project in a day, but just in case!) when the user is creating an entry, as a prettier and more backward-compatible alternative to using a number picker to indicate how many hours were spent on this particular job.

In Android, view layouts are usually defined in XML like this:

layout/activity_hour_picker.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
   android:layout_width="match_parent"  
   android:layout_height="match_parent" >  
   <Button android:id="@+id/button"  
     android:layout_height="wrap_content"  
     android:layout_width="wrap_content"  
     android:onClick="doStuff"  
     android:text="Hello World!" />  
 </RelativeLayout>  

Simple. But, as you can imagine, not so fun when you're adding a list of 24 buttons - so, I decided to add them dynamically in the code. First, though, a ScrollView and RadioGroup (ScrollView only allows one child) need to be defined in the XML, no point in doing that programmatically. Let's add those:

layout/activity_hour_picker.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent" >  
    <HorizontalScrollView   
         android:id="@+id/hour_scroll_view"  
         android:layout_width="match_parent"  
         android:layout_height="wrap_content"  
         android:fillViewport="true"  
         android:scrollbars="none" >  
         <RadioGroup  
             android:id="@+id/hour_radio_group"  
             android:layout_width="wrap_content"  
             android:layout_height="match_parent"  
             android:orientation="horizontal">  
             // This is where our buttons will be  
         </RadioGroup>  
     </HorizontalScrollView>  
 </RelativeLayout>  

Okay. So, now, in our Activity, we need to override onCreate if we haven't already and add the following code:

src/com/example/HourPickerActivity.java

 @Override  
 public void onCreate(Bundle icicle) {  
   super.onCreate(icicle);  
   setContentView(R.layout.activity_hour_picker);  // This adds the views from the XML we wrote earlier
   ViewGroup hourButtonLayout = (ViewGroup) findViewById(R.id.hour_radio_group);  // This is the id of the RadioGroup we defined
   for (int i = 0; i &lt; RANGE_HOURS; i++) {  
     RadioButton button = new RadioButton(this);  
     button.setId(i);  
     button.setText(Integer.toString(i));  
     button.setChecked(i == currentHours); // Only select button with same index as currently selected number of hours  
     button.setBackgroundResource(R.drawable.item_selector); // This is a custom button drawable, defined in XML   
     hourButtonLayout.addView(button);  
   }    
 }  

And this is what we get:

It scrolls horizontally like we want, but there's a problem - we don't want the default radio button selector showing up, since we've already got custom button graphics showing. And we can't forget that we're still missing the code to make everything within the RadioGroup work properly - In other words, the buttons won't do anything when a user clicks them. So, let's add a listener to each button as it's created:

src/com/example/HourPickerActivity.java

 button.setId(i);  
 button.setBackgroundResource(R.drawable.item_selector); // This is a custom button drawable, defined in XML
 button.setOnClickListener(new OnClickListener() {  
         @Override  
         public void onClick(View view) {  
             ((RadioGroup) view.getParent()).check(view.getId());  
             currentHours = view.getId();  
         }  
     });  
 button.setText(Integer.toString(i));

So now the currently selected value is ready in our static variable currentHours for whenever the user is finished. Now we need to get rid of the standard radio button graphics. The solution I found is to use selector XML, with just one item that points to a transparent drawable:

drawable/null_selector.xml

 <?xml version="1.0" encoding="utf-8"?>  
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >  
   <item android:drawable="@android:color/transparent" />  
 </selector>  

Set each button to use it (and to center the text) like this (R.drawable.null_selector is our selector XML):

src/com/example/HourPickerActivity.java

 button.setText(Integer.toString(i));
 button.setGravity(Gravity.CENTER);  
 button.setButtonDrawable(R.drawable.null_selector);  
 button.setChecked(i == currentHours); // Only select button with same index as currently selected number of hours  

Now, let's see how this all has pulled together.

There - that looks much better! And it works great.

2 comments:

PRAGNESH said...

where is the item_selector.xml file

jyo 243 said...

I am having one radio button. i need to check need uncheck dyamically.will you please give me any solution.Thanks in advance...