“What we’ve got here is… failure to theme. Some views you just can’t reach. So you get what we had here last project, which is the way Android wants it… well, it gets it. I don’t like it any more than you men.” – Captain, Road Prison 36
Some of you might recognize the previous paragraph as the introduction of Guns ‘N Roses’ Civil War or from the movie Cold Hand Luke starring Paul Newman. This is the feeling I get when I try to create a custom theme for an application on Android. The Android SDK does permit some level of theming, which is not really well documented to start with. Other things are hard-coded, “so you get what we had here last project”. Now, one of the things your application will most likely use is the Options menu, which is the menu you see when you press the hard menu key. It is kind of… orange. In our last project, we had to completely remove the orange in favor of our customer’s color scheme, which is on the blue side.
I couldn’t find a way to change the menu item background using the theme.xml and styles.xml files of our own theme. Furthermore, most blogs and articles I found told me that this was impossible. Inconceivable! So I went looking at Android’s source code. Thank [insert favorite deity or Darwin] for open source!
I started from the layout files for the platform I was using, in this case, Android 1.5, in /platforms/android-1.5/data/res/layout. There, I found the file icon_menu_item_layout.xml and in that file, I saw that it uses a custom item of class com.android.internal.view.menu.IconMenuItemView.
That class can be easily found in the Android source code (IconMenuItemView.java). Once I opened the file, I noticed that it doesn’t do much in regards to the background. So I went to the class that creates and controls it, IconMenuView.
Now, in that file, I did see that android resets the background of menu items every time it adds an item to the menu. That background is indeed hard-coded to an internal drawable file. So what can we do?
Enter the LayoutInflater.Factory class (again…).
The IconMenuItemView is just a derived class from the basic View class. And as such we can call setBackgroundResource method on it. So, in our LayoutInflater factory class, I simply detected the name “com.android.internal.view.menu.IconMenuItemView”, asked my inflater to create the view and kind of (more on this later) applied my own background.
public View onCreateView(String name, Context context, AttributeSet attrs)
{ // Do you own inflater stuff here // Check for menu items (Options menu AKA menu key)
if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) { try {
// Ask our inflater to create the view View view = inflater_.createView(name, null, attrs);
// Kind of apply our own background view.setBackgroundResource(myBackground); return view; }
catch (InflateException e) { } catch (ClassNotFoundException e) { } } return null; }
The only problem with that previous code is that Android will go on and set the default background right after this. So we simply need to post our background change as a Runnable to the UI thread, which will be executed after Android sets the default background. Just replace the bold line in the code above with the following:
new Handler().post(new Runnable() { public void run() { view.setBackgroundResource(myBackground); } });
Finally, every time your application is about to display the options menu, you need to re-apply the background. Android might re-use or re-create the menu item and push its default background. So in your factory, you just need to add the menu items in a list and create a function that sets your own background to the list items. Remember to post to the UI thread once again!
Comments
Post a Comment