javaandroidandroid-layoutandroid-tabhostcolorfilter

StateListDrawable to switch colorfilters


I want to create custom buttons to use in a TabHost. I haven been trying to just use the same image resource (png), but have the colorfilter change depending on the state. So I made this bit to serve as the layout for the custom button:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="fill_parent">
    <ImageView android:id="@+id/tab_icon"
        android:layout_centerInParent="true" android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView android:id="@+id/tab_text" android:layout_below="@id/tab_icon"
        android:layout_centerHorizontal="true" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</RelativeLayout>

In my activity, I add the tabs like this:

tabHost.addTab(tabHost.newTabSpec(TAB_NAME_NEWS).setIndicator(buildTab(R.drawable.tab_icon_news, R.string.news))
          .setContent(newsIntent));

And this is the 'buildTab' method:

private final static int[] SELECTED = new int[] { android.R.attr.state_selected };
private final static int[] IDLE = new int[] { -android.R.attr.state_selected };

private View buildTab(int icon, int label) {
    LayoutInflater inflater = LayoutInflater.from(this);
    View view = inflater.inflate(R.layout.tab_button, null);
    StateListDrawable drawable = new StateListDrawable();

    Drawable selected = getResources().getDrawable(icon);
    selected.mutate();
    selected.setBounds(0, 0, selected.getIntrinsicWidth(), selected.getIntrinsicHeight());
    selected.setColorFilter(new LightingColorFilter(0xFFFFFFFF, 0x0000FF00));
    drawable.addState(SELECTED, selected);

    Drawable idle = getResources().getDrawable(icon);
    idle.mutate();
    idle.setColorFilter(new LightingColorFilter(0xFFFFFFFF, 0x000000FF));
    drawable.addState(IDLE, idle);

    ((ImageView) view.findViewById(R.id.tab_icon)).setImageDrawable(drawable);
    ((TextView) view.findViewById(R.id.tab_text)).setText(getString(label));
    return view;
}

In the selected state, the image should be completely green (0x0000FF00), and in the non-selected state, it should be blue (0x000000FF).

The problem is that the colorfilters appear to be be completely ignored. I can not see the colors change under any circumstances.

I've also tried to get the same result by setting the android:tint property on the <ImageView/>, but apparently you cannot use a reference to a <selector> there, since it throws a NumberFormatException.

I don't see what I'm doing wrong so any help would be appreciated.


Solution

  • OK, I never got the above code to work, so here's what I ended up doing.

    First, I subclassed LayerDrawable:

    public class StateDrawable extends LayerDrawable {
    
        public StateDrawable(Drawable[] layers) {
            super(layers);
        }
    
        @Override
        protected boolean onStateChange(int[] states) {
            for (int state : states) {
                if (state == android.R.attr.state_selected) {
                    super.setColorFilter(Color.argb(255, 255, 195, 0), PorterDuff.Mode.SRC_ATOP);
                } else {
                    super.setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP);
                }
            }
            return super.onStateChange(states);
        }
    
        @Override
        public boolean isStateful() {
            return true;
        }
    
    }
    

    I changed the buildTab() method to the following:

    private View buildTab(int icon, int label) {
        LayoutInflater inflater = LayoutInflater.from(this);
        View view = inflater.inflate(R.layout.tab_button, null);
        ((ImageView) view.findViewById(R.id.tab_icon)).setImageDrawable(new StateDrawable(new Drawable[] { getResources()
              .getDrawable(icon) }));
        ((TextView) view.findViewById(R.id.tab_text)).setText(getString(label));
        return view;
    }
    

    I still add the tabs like this:

    Intent fooIntent = new Intent().setClass(this, FooActivity.class);
    tabHost.addTab(tabHost.newTabSpec(TAB_NAME_INFO).setIndicator(buildTab(R.drawable.tab_icon_info, R.string.info)).setContent(infoIntent));
    

    This works for me, compatible with android 1.6.