ListView项和状态选择器背景drawable的奇怪行为

时间:2011-07-08 21:37:33

标签: android listview drawable

当使用StateListDrawable作为背景时,我有一些非常奇怪的ListView行为。我试着按照this帖子的回答,因为我没有让state_checked状态工作,但现在我的ListView变得疯狂了。

当我点击某个项目时,它不会立即将颜色更改为选择器中的state_checked项目。稍微点击一下后,许多视图将突然切换到state_checked背景。它似乎是随机的。

这是我的状态选择器xml代码:

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true" >
        <shape>
            <gradient
                android:startColor="@color/grey"
                android:endColor="@color/darkgrey"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_focused="true" >
        <shape>
            <gradient
                android:endColor="@color/orange4"
                android:startColor="@color/orange5"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_checked="true">
        <shape>
            <gradient
                android:endColor="@color/brown2"
                android:startColor="@color/brown1"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item android:state_selected="true">
        <shape>
            <gradient
                android:endColor="@color/brown2"
                android:startColor="@color/brown1"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

    <item>        
        <shape>
            <gradient
                android:startColor="@color/white"
                android:endColor="@color/white2"
                android:angle="270" />
            <stroke
                android:width="0dp"
                android:color="@color/grey05" />
            <corners
                android:radius="0dp" />
            <padding
                android:left="10sp"
                android:top="10sp"
                android:right="10sp"
                android:bottom="10sp" />
        </shape>
    </item>

</selector>

这是我的自定义视图实现可检查的.java类:

public class Entry extends LinearLayout implements Checkable {

    public Entry(Context context) {
        super(context, null);

        // Inflate this view
        LayoutInflater temp = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        temp.inflate(R.layout.entry, this, true);

        initViews();
    }

    private static final int[] CheckedStateSet = {
        android.R.attr.state_checked
    };

    private void initViews() {
        this.setBackgroundResource(R.drawable.listview_row);
    }

    public boolean isChecked() {
        return _checked;
    }

    public void toggle() {
        _checked = !_checked;
    }

    public void setChecked(boolean checked) {
        _checked = checked;
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CheckedStateSet);
        }
        return drawableState;
    }

    @Override
    public boolean performClick() {
        toggle();
        return super.performClick();
    }
}

我已经开了几个小时试图解决这个问题,但不幸的是必须让步以寻求帮助。任何人都可以看到上面的代码有问题会导致ListView在项目上表现奇怪吗?如果需要,我也可以发布更多代码。

3 个答案:

答案 0 :(得分:21)

使用ListView时,始终牢记视图是演示文稿,适配器是数据模型非常重要。

这意味着你的所有状态都应该在视图中的适配器(数据模型)中,不是

从我所知的代码中,您有一个显示检查状态的视图,该状态位于视图而不是适配器中。也就是说,当用户点击列表中的该项时,用于显示其项目的视图的内部检查状态已更改为切换显示给用户的内容。

但由于视图不是数据模型,因此您在此处使用的状态是瞬态的,并且实际上与所单击的适配器项目无关。

这个问题最明显的问题在于回收视图。当您滚动ListView时,当项目滚动到最后并且新项目显示在底部时,用于显示旧项目的视图将重新用于显示新项目。这比每次显示新项目时必须为新的项目视图层次结构充气更有效。

因为您在视图中拥有自己的状态,所以当此回收发生时,视图中的状态会随机重新关联到某个新项目。在许多情况下都会发生这种情况,而不仅仅是滚动。

解决方案是将您的检查状态放在适配器中,并实现Adapter.getView()以根据您在适配器中的状态设置视图的已检查状态。这样一旦视图被回收(并调用getView()以将新数据行绑定到它),您将更新其检查状态以正确地跟随它正在显示的新数据。

答案 1 :(得分:3)

我认为问题不是来自上面的代码。我之前遇到过这个问题,它与listview中的视图回收有关。如果您的列表在屏幕上继续显示,则可能就是这种情况。如果是这种情况,修复它的一个好方法是将项目的状态存储在列表中,以便您可以跟踪它们并将其状态基于您创建的列表。有关回收视图的详细信息,请查看thisthis

答案 2 :(得分:0)

当使用相当复杂的ListViews做类似的事情时,我向适配器添加了一个额外的列表,并将点击的项目的位置添加到它。 getView(...)然后膨胀/回收视图,在它完成之前检查项目的状态,以及内部适配器状态以决定应用哪个背景。

我还设置了状态列表xml文件,以便在当前按下时使背景透明,这样选择器可见,它可以处理。