Smartphones

Dressing up Android buttons

Check out this Android button class tutorial from developer William Francis, who uses minimalistic code to produce the desired effect.

Step six: Now let's move on to our /layout folder and define the main UI for our app. In this case, we are using a text view to display a title and a grid view to hold our buttons.
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:layout_gravity="center"
android:layout_margin="12dip"
android:text="Fancy Button Demo"/>
<GridView
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:columnWidth="90dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:layout_gravity="center"/>
</LinearLayout>
Note: Later, you will see we are actually only using the grid view for its handy auto-layout features. A table view would work here as well, but a table view would require a lot more code in our Main.java file to handle adding buttons to the view on the fly and arranging them in an aesthetically pleasing manner. Step seven: Proceed to our /src folder and the associated Java classes. Any time you use a view that depends on a base adapter (like our GridView class), you'll likely need to implement a custom adapter. Custom adapters often scare off new Android developers. If you're a green Android coder, seize this opportunity to overcome your fear. Since our grid view will hold nothing more than a single button, the code should be almost self-explanatory. The only method that might require a second look is getView(). See the additional note following the code listing for an explanation.
CustomAdapter.java
package com.authorwjf;
import java.util.ArrayList;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
public class CustomAdapter extends BaseAdapter {
private ArrayList<Button> mButtons = null;
public CustomAdapter(ArrayList<Button> b)
{
mButtons = b;
}
@Override
public int getCount() {
return mButtons.size();
}
@Override
public Object getItem(int position) {
return (Object) mButtons.get(position);
}
@Override
public long getItemId(int position) {
//in our case position and id are synonymous
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Button button;
if (convertView == null) {
button = mButtons.get(position);
} else {
button = (Button) convertView;
}
return button;
}
}
Note: Android adapters conserve resources in the getView() method by utilizing a parameter called convertView. The idea is that the adapter checks to see if the converted view parameter is null when the function is invoked, and if it is, assigns a view to it. Otherwise, the converted view needs only be coerced (cast) to the correct data type. If we took out the entire IF-STATEMENT and replaced it with the line "return mButtons.get(position);" we would find that our code still operated with no noticeable side effect. However, when you get into big lists that contain multiple views, failing to use the converted view can result in a laggy/jerky UI. Step eight: Our Main.java file extends the Activity class and implements the OnClickListener. If we were using the grid view to its full potential, we'd implement the OnItemClickListener, but since we are just taking advantage of the dynamic layout capability for our rows of buttons, there is no need. While we don't define any buttons in our main.xml layout file, you can see here that in the onCreate, I have a simple FOR-LOOP that generates the buttons dynamically. We add the generated buttons to a list array, and then use that list array to populate our custom adapter.
Main.java
package com.authorwjf;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.GridView;
import android.widget.Toast;
public class Main extends Activity implements OnClickListener {
private ArrayList<Button> mButtons = new ArrayList<Button>();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button cb = null;
for (int i =0; i<12; i++) {
cb = new Button(this);
cb.setText("Button #"+Integer.toString(i));
cb.setBackgroundResource(R.drawable.fancy_button_selector);
cb.setOnClickListener(this);
cb.setId(i);
registerForContextMenu(cb);
mButtons.add(cb);
}
GridView gridView = (GridView) findViewById(R.id.gridview);
gridView.setAdapter(new CustomAdapter(mButtons));
}
}
Step nine: If you noticed in our onCreate, besides generating an array of buttons, we also wired the on click listener of each button to our Main.java implementation. Now we need to tool the handler function in the class.
@Override
public void onClick(View v) {
Button selection = (Button)v;
Toast.makeText(getBaseContext(),  selection.getText()+ " was pressed!", Toast.LENGTH_SHORT).show();
}
Step 10: Similarly, in the onCreate override we called the registerForContextMenu()on each button we created in our FOR-LOOP. That requires us to override both onCreateContextMenu() and onContextItemSelected().
@Override
public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
Button b = (Button)v;
menu.setHeaderTitle(b.getText());
menu.add(0, v.getId(), 0, "Action 1");
menu.add(0, v.getId(), 0, "Action 2");
}
@Override
public boolean onContextItemSelected(MenuItem item) {
Button selectedButton = mButtons.get(item.getItemId());
Toast.makeText(getBaseContext(),  item.getTitle()+" of "+selectedButton.getText()+ " was pressed!", Toast.LENGTH_SHORT).show();
return true;
}

Executing the app on your device displays a grid of buttons that will respond to both a click and a long click with a simple toast message. As you play around with both the number of buttons you add to the grid as well as the XML drawables for those buttons, you should be able to create some interesting results that hopefully prove useful in your future Android coding endeavors.

Figure B

Fancy buttons in action
Figure C

Fancy buttons context menu

Again, for those of you who prefer to download and import the entire project into Eclipse, you can grab it here. I hope you found this Android button tutorial straight-forward and informative.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

0 comments

Editor's Picks