Smartphones

Dynamic styling of views on Android

William J. Francis shows a type of dynamic styling in Android that has powerful potential, particularly in apps where you allow users to customize your views on the fly.

One thing I like about software engineering is that there is often more than one right answer to a question. If you lock 10 developers in 10 separate rooms and task them all with implementing a program to catalog your personal video collection, you'll likely get 10 very different programs. However, providing that the developers know what they're doing, each of the resulting programs will be perfectly capable of cataloging your videos.

Many times as a developer my duty is not just figuring out how to make something work, but weighing a number of viable options and determining which of those options I believe makes the most sense in the context of the job at hand. This part of being a software engineer holds a lot of appeal for me. It also probably has a lot to do with why I love the Android operating system. Android is all about options.

In my TechRepublic post about ways to dress up buttons in your Android applications, I created the state list and drawables using Android's XML layout language. In fact, that's how 99% of the tutorials you'll find on the web and in the Google SDK handle applying styles to views. It's quick, and it works. But from time to time, you may run across a scenario where you need to do it all from within the source code. That's where Android's flexibility comes in handy.

In this tutorial, I will demonstrate another technique for styling buttons. Feel free to follow along, or, you can download the source project and import it directly into Eclipse.

1. Create a new Android project in Eclipse. Target Android 1.6 or higher. Be sure to rename the startup activity to Main.java.

2. In the /res/layout folder, add the XML that describes our screen. Notice that we assigned an id not just to the button but also to our parent view.

main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

android:id="@+id/main_layout">

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center"

android:text="Dynamic Styling Demo"/>

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center"

android:text="One Big Button!" android:layout_margin="30dip"

android:id="@+id/one_big_button"/> </LinearLayout>
3. Because the demo is so short, we are going to implement it entirely in the on create override. Let's start by defining a view tree observer. If you haven't stumbled across this class before, today is your lucky day. View tree observer provides a handy way to get at the views after the layout is finished. If you've ever tried to look at the width of a widget in your on create, you know exactly what I'm getting at here. Generally speaking, on create occurs before layout and as such, while you can set properties of controls, you normally can't get at attributes like width and height. View tree observer solves this problem nicely.
Main.java
package com.authorwjf.dynamic_ui_styling;
import android.app.Activity;
import android.graphics.LinearGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.os.Bundle;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.LinearLayout;
public class Main extends Activity {
 private StateListDrawable mStateListDrawable = new StateListDrawable();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.main);

final LinearLayout main = (LinearLayout) findViewById(R.id.main_layout);

ViewTreeObserver vto = main.getViewTreeObserver();

vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

//overrides go here!

});

}

}

4. The last thing you need to do is replace the comment inside of the global layout listener with the drawing code that will create and assign our button backgrounds.

Main.java

View v = findViewById(R.id.one_big_button);

float[ ] rounded = new float[] {12,12,12,12,12,12,12,12}; Shader shader = new LinearGradient(0,0,v.getWidth(),v.getHeight(), new int[ ] {0xFF006600, 0xFF00ff00, 0xFF00ff00, 0 }, null, Shader.TileMode.CLAMP); ShapeDrawable defaultDrawable = new ShapeDrawable(new RoundRectShape(rounded, null, null));

defaultDrawable.getPaint().setColor(0xff00ff00);

ShapeDrawable pressedDrawable = new ShapeDrawable(new RoundRectShape(rounded, null, null));

pressedDrawable.getPaint().setShader(shader);

mStateListDrawable.addState(new int[ ] {android.R.attr.state_pressed}, pressedDrawable); mStateListDrawable.addState(new int[0], defaultDrawable); v.setBackgroundDrawable(mStateListDrawable);

Running the program will show a single button that has a nice gradient applied to it when it gets pressed. This type of dynamic styling has some powerful potential, particularly in applications where you allow users to customize your views on the fly. It's not the right solution for every scenario, but it's nice to know that when it makes sense, Android is up to the task.

Figure A

Figure B

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