Showing posts with label widget. Show all posts
Showing posts with label widget. Show all posts

Tuesday, June 30, 2009

Custom adapter in color

I got a comment on this blog with relation to an old sin of mine, the custom adapter example program. That example was about custom views in a list adapter. The comment was innocent enough: is there a way to change the colors of that list? I thought it was a 5 minute task to answer that question, in the end I had to deal with a topic I never wanted to know about: themes.

Click here to download the example program.

It turned out that the color scheme used by ListView is eventually taken from the active theme. It is possible to change it programmatically but it is quite complicated to do so. Meanwhile, with themes, it is relatively easy. If you know the information source. The Android development guide is almost completely useless regarding themes, for example sample themes don't work. This blog entry from Brainflush is much more informative, at least its examples work. The source that helped me the most was the actual Android XML fileset that defines the themes. If you have access to the Android source tree, the Android system resources can be found under the platform\frameworks\base\core\res\res directory. The relevant files are styles.xml under the values subdirectory and the content of the drawable subdirectory.

If you check the styles.xml in the res/values directory in our example program, you will see that modifying the color scheme of the ListView is a quite complicated, multi-step process. First of all, in res/values/styles.xml, the ListView style is overridden with the android:listViewStyle property. The ListViewStyle (MyListView) in turn references a list selector (android:listSelector) that is stored in the res/drawable directory. That selector defines color schemes for all the possible state combination of the list row. To increase the fun, the resource compiler screwed up the image IDs in the R.java file if the weather icon image files were not at the beginning of the file name list, hence the bizarre names in the drawable subdirectory. Now what we need more is a transition that refers a 9-patch background image (block blue in our case) for the selected row and we are ready.

Don't say that it was not easy.

PS: I apologize for the hideous colors. :-)

Friday, April 18, 2008

Custom widget adapters

In an earlier post, I wrote about the SimpleAdapter and how SimpleAdapter allows significant flexibility when laying out list items. SimpleAdapter is great but then I became curious what it takes to write an adapter.

Adapters are simple devices. On one side of the adapter is a data structure like a Java object storing data. SimpleAdapter handles Java objects that can be meaningfully translated into Strings by invoking the objects' toString() method (every Java object supports that but for quite many of them, the toString() format is not meaningful for the end user). On the other side of the adapter, there is a View that the data structure was transformed into. That View is displayed to the user. As we use Adapters to supports list views, the Adapter handles lists of Java objects (that are eventually transformed into a list of Views).

Android's built-in adapters are sufficiently versatile but it is often handy to create a custom adapter. Let's look at the following example.



You can download the example code from here.

The example is a simple weather display. A weather entry consists of 3 data items: name of the city (String), temperature in the city in degrees (integer) and an icon showing whether the sky is sunny, overcast or it is raining. The first two entries could be handled using SimpleAdapter but the third cannot: SimpleAdapter does not handle icons. Therefore we create our own adapter, called WeatherAdapter that takes list of Weather objects storing the weather info and turns that list into Views that can be rendered as list rows.

The most important part of the trick happens in a private class in WeatherAdapter.java called WeatherAdapterView. WeatherAdapter does nothing more than manages a list of WeatherAdapterViews. WeatherAdapterView is the View the Weather data object is mapped to. It is itself a composite View, composed by a LinearLayout. The LinearLayout is set up programmatically (as opposed to an XML layout) and contains two TextViews and one ImageView. The ImageView encapsulates the icon. It is worth checking how the icon images are referenced in Weather.java: as the icons are in the res/drawable directory, R.java has integer IDs for them. The getSkyResource() method in the Weather class just returns this resource ID based on the sky member variable of the Weather class.

Sunday, March 16, 2008

My first meeting with the SimpleAdapter widget

Such a shame. Normally, Android developers have their SimpleAdapter adventures right at the beginning of their Android development career, meanwhile I met this beast just now. I wouldn't say that this UI feature is overdocumented and that there are truckloads of example programs out there (if there are, Google does not find them) so I decided to share my experiences with whoever cares to read this blog entry.

It all started with a very simple wish that I wanted to create a list like this:


Each list entry has two lines and the lines are styled differently. Fortunately, there is an example program at the ListActivity documentation page which happens not to work and takes the input behind the list from a database. So it took me some time to find my friend, the SimpleAdapter widget and to find out, how to use it.

You can download the example program from here.

The root of the problem is that each element of this list is a complex view, consisting of two TextViews (XML mangling because of blog engine limitations):

[?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="wrap_content"
android:orientation="vertical"]

[TextView android:id="@+id/text1"
android:textSize="16px"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/]

[TextView android:id="@+id/text2"
android:textSize="12px"
android:textStyle="italic"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/]
[/LinearLayout]

The corresponding SimpleAdapter maps an element from list of objects to this view.

SimpleAdapter notes = new SimpleAdapter(
this,
list,
R.layout.main_item_two_line_row,
new String[] { "line1","line2" },
new int[] { R.id.text1, R.id.text2 } );

Now this is not a trivial line of code. What it says is:
  • "list" is a reference to an object implementing the List interface. Each element in this list is an object implementing the Map interface. In our implementation example, the list is ArrayList and the elements in this list are HashMaps.
  • The layout describing one row in the main list is defined in layout/main_item_two_line_row.xml file.
  • In each individual HashMap, there are two key-value pairs. The keys are "line1" and "line2", the corresponding values are arbitrary objects whose toString() method yields the value displayed in the list row. In our case, the values are Strings.
  • Values stored with key "line1" will be displayed in the TextView whose id is "text1". Similarly, "line2" Map key is associated with "text2" TextView.
You can try the program following the instructions in the Start here entry. You can add items to the list using the menu.