I'm having some trouble converting some working XML files into code. I have a ListView, and I need to be able to switch its pressed/checked drawables at run-time, from resources not known in advanced (hence why not using XML);
The following configuration works great:
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" >
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:choiceMode="singleChoice" >
</ListView>
</LinearLayout>
selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/blue" android:state_pressed="true"/>
<item android:drawable="@drawable/green" android:state_checked="true"/>
<item android:drawable="@drawable/orange"/>
</selector>
list_row.xml:
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector"
android:padding="10dp" />
Main.java:
public class Main extends Activity {
StateListDrawable selector;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView listView1 = (ListView) findViewById(R.id.listView1);
StringAdapter adapter = new StringAdapter(this, R.layout.list_row);
adapter.add("one"); adapter.add("two"); adapter.add("three"); adapter.add("four");
adapter.add("five"); adapter.add("six"); adapter.add("seven"); adapter.add("eight");
adapter.add("nine"); adapter.add("ten"); adapter.add("eleven"); adapter.add("twelve");
listView1.setAdapter(adapter);
}
private class StringAdapter extends ArrayAdapter<String>{
public StringAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final CheckedTextView tv = (CheckedTextView ) inflater.inflate(R.layout.list_row, parent, false);
tv.setText(getItem(position));
return tv;
}
}
}
The result is like this, which is great (pressed:blue, checked:green, else:orange):
However, if I remove the
android:background="@drawable/selector
from list_row_xml, and apply it via code:
Drawable blue = getResources().getDrawable(R.drawable.blue);
Drawable green = getResources().getDrawable(R.drawable.green);
Drawable orange = getResources().getDrawable(R.drawable.orange);
selector = new StateListDrawable();
selector.addState(new int[] { android.R.attr.state_pressed }, blue);
selector.addState(new int[] { android.R.attr.state_checked }, green);
selector.addState(new int[] { }, orange);
tv.setBackgroundDrawable(selector);
I get the following (everything get the state_pressed drawable, blue):
What went wrong? I'm pretty sure I converted the selector to code appropriately.
I copied your code and it all works fine.
You should make sure to create a new instance of selector
in getView()
though. Otherwise if you use the same selector
for all your items, pressing one item will affect all your items.
Here's what your getView()
method should look like
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final CheckedTextView tv = (CheckedTextView) inflater.inflate(R.layout.list_row, parent, false);
tv.setText(getItem(position));
Drawable blue = getResources().getDrawable(R.drawable.blue);
Drawable green = getResources().getDrawable(R.drawable.green);
Drawable orange = getResources().getDrawable(R.drawable.orange);
selector = new StateListDrawable();
selector.addState(new int[] { android.R.attr.state_pressed }, blue);
selector.addState(new int[] { android.R.attr.state_checked }, green);
selector.addState(new int[] {}, orange);
tv.setBackgroundDrawable(selector);
return tv;
}
Of course there is some optimization you could do, but this will work.