3

Restyling Android ImageButtons

Discovered an interesting thing about Android today: the fairly ugly default ImageButton can be replaced with a custom drawable while retaining the very user-friendly highlighting behavior.

I was trying to create three buttons for an application I am working on: zoom-in, zoom-out, and next. For development purposes, I had just been using regular (text-only) Buttons, but for the final product I wanted something a little snazzier. While it is certainly possible to create your own button functionality, I’m guessing it is wise to stick to the defaults when possible – enter the ImageButton class. It works the same as a Button, but shows an image instead of text. The problem is, the default look was gonna be kinda ugly in my application.

My first thought was – well, maybe I can just set the background to black. That works, but you lose the highlighting that makes the ImageButton nice in the first place. So, a few searches later, here is what you do – this example is for a two-state (“normal” and “pressed”) button called “Next”:

1. Create two image files (one for the “normal” state and one for the “highlighted” state

2. Create an XML Drawable (that is, a .xml file in your /resources/drawable/ directory

3. Paste the following XML:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:state_enabled="true"
android:drawable="@drawable/btn_next_normal" />
<item android:state_window_focused="false" android:state_enabled="false"
android:drawable="@drawable/btn_next_normal" />
<item android:state_pressed="true"
android:drawable="@drawable/btn_next_pressed" />
<item android:state_focused="true" android:state_enabled="true"
android:drawable="@drawable/btn_next_pressed" />
<item android:state_enabled="true"
android:drawable="@drawable/btn_next_normal" />
<item android:state_focused="true"
android:drawable="@drawable/btn_next_normal" />
<item android:drawable="@drawable/btn_next_normal" />
</selector>

4. In your Java code (should also work in XML), use it just like any other Drawable:

ImageButton next = new ImageButton(this);
  next.setImageResource(R.drawable.btn_next);
  next.setScaleType(ScaleType.CENTER_INSIDE);
  next.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    //do stuff here
  }
});

Note that this button only shows two images “normal” and “pressed” because that seems sufficient for my situation, but the XML allows 7 views depending on the state of the button.

For more information, see:

http://stackoverflow.com/questions/606694/android-different-image-for-rollover-on-imagebutton

http://groups.google.com/group/android-developers/msg/ef6014f46c35a824

0

The Amazing World of COM

COM is one of those things that I’d heard about but never really needed. Well yesterday I stumbled on a script for converting Word documents to PDF using COM and that got me thinking — “Can I save myself TONS of time by writing a few scripts to do the really boring and repetitive parts of my job?”. Indeed, the answer is yes.

Since Python is my first choice when there is a choice, I was pleased to discover the win32com package. Following is a very simple script using COM to convert Powerpoint slideshows from PPT to PPS (powerpoint show):

import os, win32com.client
doc_template_name = os.path.abspath('test.ppt')
doc_final_name = os.path.abspath('test.pps')
app = win32com.client.Dispatch("PowerPoint.Application")
app.Visible = True
doc_template = app.Presentations.Open(doc_template_name)
doc_final = app.Presentations[0]
doc_final.SaveAs(doc_final_name)
doc_template.Close()
app.Quit()

This script is heavily influenced by the many wonderful examples at: http://win32com.goermezer.de/

(This was supposed to be published more than a month ago.. I just noticed it was marked as a Draft, danggit)

0

Bridging Android’s ListPreference and Database

Long time, no post.. I’ve been waay too busy programming Android for the last few weeks to get anything useful done. As mentioned a few days ago, I’m getting deluged by weird comment spam, so comments are turned off for the moment – sorry.

Today I was trying to mix Android’s two storage mechanisms. The application needs to store a small number of settings which a user can create/update/delete -  a database. Additionally, one of these settings is considered to be “selected” – probably SharedPreferences, though one could argue for the database here too. My first intuition was to specialize a subclass of ListPreference to override the getEntries() and getEntryValues() methods – nope. The next idea was to call setEntries(CharSequence[]) and setEntryValues(CharSequence[]) in the constuctor – that seems to work fine. However, since I was reading from a database, I wanted the Cursor to be managed by an activity. Since I was subclassing ListPreference, that wasn’t gonna work. Ultimately, I settled on subclassing PreferenceActivity and building the menus in code instead of XML. Adapting the sample database code from Notepadv3, here is a custom Preferences menu:

public class EditPreferences extends PreferenceActivity {
 
public static final String SELECTED_TARGET_KEY = "SelectedTargetKey";
public static final String NO_SELECTION  = "0";
 
private TargetDbAdapter mDbHelper;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setPreferenceScreen(createPreferenceHierarchy());
}
 
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
PreferenceCategory dialogBasedPrefCat = new PreferenceCategory(this);
dialogBasedPrefCat.setTitle(R.string.pref_cat_title);
root.addPreference(dialogBasedPrefCat);
 
//builds list from DB
mDbHelper = new TargetDbAdapter(this);
mDbHelper.open();
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
 
int count = c.getCount();
CharSequence[] entries = new CharSequence[count];
CharSequence[] entryValues = new CharSequence[count];
 
c.moveToFirst();
for(int i=0; i&lt;count; i++) {
entries[i] = c.getString(c.getColumnIndexOrThrow(TargetDbAdapter.KEY_TITLE));
entryValues[i] = c.getString(c.getColumnIndexOrThrow(TargetDbAdapter.KEY_ROWID));
c.moveToNext();
}
 
ListPreference targets = new ListPreference(this);
targets.setEntries(entries);
targets.setEntryValues(entryValues);
targets.setDefaultValue(NO_SELECTION);
targets.setDialogTitle(R.string.pref_dialog_title);
targets.setKey(SELECTED_TARGET_KEY);
targets.setTitle(R.string.pref_title);
targets.setSummary(R.string.pref_summary);
dialogBasedPrefCat.addPreference(targets);
 
//add other preference screens
 
return root;
}
 
}