Android ListView State List not showing default item background
Have read over a number of related questions here at SO, as well as looked through the Android docs and source to try to figure this out, but I am stumped, although given that the listSelector seems to only apply styles on selected items, I'm not shocked...
I have a Listview defined in main.xml here:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fadingEdgeLength="5dp"
android:divider="#000000"
android:dividerHeight="1dp"
android:listSelector="@drawable/list_selector" />
<TextView
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:singleLine="false"
android:text="@string/message_list_empty" />
</FrameLayout>
The listSelector referenced is here (borrowed largely from the default Android State List found in the SDK: /android/platforms/android-8/data/res/drawable/list_selector_background.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- the window has lost focus, disable the items -->
<item
android:state_window_focused="false"
android:drawable="@drawable/shape_row_disabled" />
<!-- the list items are disabled -->
<item
android:state_enabled="false"
android:state_focused="true"
android:state_pressed="true"
android:drawable="@drawable/shape_row_disabled" />
<item
android:state_enabled="false"
android:state_focused="true"
android:drawable="@drawable/shape_row_disabled" />
<item
android:state_enabled="false"
android:drawable="@drawable/shape_row_disabled" />
<!-- the list items are enabled and being pressed -->
<item
android:state_focused="true"
android:state_pressed="true"
android:drawable="@drawable/shape_row_transition" />
<item
android:state_focused="false"
android:state_pressed="true"
android:drawable="@drawable/shape_row_transition" />
<!-- the list items are enabled and being used -->
<item
android:state_focused="true"
android:drawable="@drawable/shape_row_selected" />
<!-- the default item -->
<item
android:drawable="@drawable/shape_row" />
</selector>
The drawables referenced are shapes, rounded rectangles, like so:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#EF6100"
android:centerColor="#FF8E00"
android:endColor="#EF6100"
android:angle="270" />
<corners
android:bottomRightRadius="7dp"
android:bottomLeftRadius="7dp"
android:topLeftRadius="7dp"
android:topRightRadius="7dp" />
</shape>
So, the end result should be a ListView with items with a @drawable/shape_row background by default, and then different colored backgrounds depending on the state. When I fire up my list, everything works fine in terms of when states are active, such as an item gets focus, is pressed, etc, but the items themselves have a transparent background when none are selected (i.e. the default view state). I had hoped that the last rule of the State List,
<item android:drawable="@drawable/shape_row" />
would capture that state, but for some reason it is not working. I have moved things around and tried different settings, and nothing. If I edit the row.xml file which I use to define the list items in the ListView and try to add a specific android:background that points to @drawable/shape_row, every row gets the correct background, BUT on pressed, on focus, etc states fail to process (or at least, can't be seen as the background never changes).
Can anyone point me in the right direction here? I am SO close to putting this one to bed, but after trying nearly everything, just can't get the ListView items to take a default background AND respond to the implemented State List via android:listSelector.
Thanks,
Paul
EDIT:
Also just tried adding android:itemBackground="@drawable/shape_row" to the ListView in main.xml, and unsurprisingly no luck (the docs state it should be used to specify menu items' backgrounds, but thought it was worth a shot with list items).
Another thing tried, adding android:visible="true" to each of the selector and item definitions. The StateListDrawable docs seem to indicate that the "Provides initial visibility state of the drawable; the default value is false", but adding this changed nothing.
EDIT2: So, based on the research done below by Qberticus, it seems that there can be only one set of list selectors per view, which confirms the list selector behavior. I can also confirm that setting the android:drawSelectorOnTop does move the selector IN FRONT of the selected item, but this of course obscures the item itself such that I only see the selector itself (which in my case is a colored shape).
Here is what the list looks like with backgrounds set:
Due to the set background, there is no change in appearance when items are selected. If I change the android:drawSelectorOnTop directive to true and select an item, I get:
And finally, if I set no background image for the list items, the selector works as expected, but of course, there are no background images as the last rule of the StateSelector does not appear to be followed (shouldn't it act as a catchall?) for non-selected items meaning no nice custom rounded shapes:
So, my problem remains; if I don't set a background for eac开发者_JAVA技巧h list item, I can see the selector working BUT the items have no background when not selected. If I set a background for the item, they look great when not selected, but the background obscures the selector. Is it simply not possible to have a uniquely shaped list item with working selectors?
Thanks for anyone else who can weight in.
Figured this out, the issue was that while I was setting the listSelector for the ListView to the State List correctly via android:listSelector="@drawable/list_selector"
, I also needed to set the background for the list item to another State List via android:background="@drawable/list_background"
In the list_background.xml state list, I have:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:visible="true">
<!-- the list items are disabled -->
<item
android:state_window_focused="false"
android:drawable="@drawable/shape_row_disabled" />
<!-- the list items are enabled, in focus but not being touched/pressed (idle) -->
<item
android:state_window_focused="true"
android:state_pressed="false"
android:state_focused="false"
android:state_selected="false"
android:drawable="@drawable/shape_row" />
<!-- the catch-all -->
<item
android:drawable="@android:color/transparent" />
</selector>
Found the answer here:
Changing background color of ListView items on Android
In conjunction with my list selector above, it works perfectly.
The listSelector
is only used to specify a View
that will be drawn in the same place as the selected item View
. I.e., there is only one instance of the listSelector
per ListView
. You can specify if it can draw on top or not using drawSelectorOnTop
.
If you want all your View
s to use a state list then you should specify that where the child View
s are defined.
I'm using the source for AbsListView
as reference. Specifically AbsListView#setSelector(Drawable)
, AbsListView#positionSelector
, AbsListView#drawSelector
, and AbsListView#dispatchDraw
AbsListView#drawSelector
:
private void drawSelector(Canvas canvas) {
if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {
final Drawable selector = mSelector;
selector.setBounds(mSelectorRect);
selector.draw(canvas);
}
}
AbsListView#positionSelector
:
void positionSelector(View sel) {
final Rect selectorRect = mSelectorRect;
selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
selectorRect.bottom);
final boolean isChildViewEnabled = mIsChildViewEnabled;
if (sel.isEnabled() != isChildViewEnabled) {
mIsChildViewEnabled = !isChildViewEnabled;
refreshDrawableState();
}
}
The source indicates that there is only one mSelector
created/used and that it is positioned in the same bounding rect as the selected item.
You can assign default background for root layout and selector_background for child, in this case if item not selected then default background not obscures the selector. Its work for me.
精彩评论