【问题标题】:Weird behavior of ListView item and state selector background drawableListView 项目和状态选择器背景可绘制的奇怪行为
【发布时间】:2023-03-23 08:57:01
【问题描述】:

当使用 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 在项目上出现奇怪的行为吗?如果需要,我也可以发布更多代码。

【问题讨论】:

  • 我认为你应该使用 View Wrapper 类

标签: android listview drawable


【解决方案1】:

在使用ListView 时,始终牢记视图是表示而适配器是数据模型,这一点非常重要。

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

根据我对您的代码的了解,您有一个显示检查状态的视图,并且该状态在 view 中,而不是在 适配器 中。也就是说,当用户单击列表中的该项目时,用于显示其项目的视图将其内部选中状态更改为切换显示给用户的内容。

但由于视图不是数据模型,因此您在此处使用的这种状态是暂时的,实际上与被单击的适配器项无关。

这导致的最明显问题在于视图回收。当您滚动浏览ListView 时,当项目从末尾滚动并且新项目出现在底部时,用于显示旧项目的视图将重新用于显示新项目。这比每次显示新项目时都必须膨胀新的项目视图层次结构要有效得多。

因为你在视图中有你的状态,当这种回收发生时,视图中的状态会随机地与一些新项目重新关联。这在很多情况下都可能发生,而不仅仅是滚动。

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

【讨论】:

  • 哦,是的,我现在看到了。所以现在,我的适配器从数据库中检索它的数据(它没有“点击”列,我也不应该这么想)。将当前检查项目的状态保留在适配器中的某种集合中是最佳实践吗?另外,当我使用ListView.setItemChecked() 方法时,是否也会检查适配器中的项目?
  • 如果 ListView 执行您想要的操作,请考虑使用它的检查状态——它可以进行单选或多选。它负责您需要做的事情——跟踪与每个行 ID 关联的状态(不是行索引,因为如果数据库中的数据发生变化,这些状态可能会发生变化)。您需要通过在列表项的顶级视图上实现 Checkable 使您的 UI 与列表视图正确交互以显示此状态。 ApiDemos 中有这样的示例。
  • 感谢 hackbod,但我认为这实际上让我更加困惑,主要是因为这正是我正在做的事情。我的 ListView 顶级列表项视图是我在上面发布代码的可检查条目类。过去我避免覆盖 performclick() 方法,并且过去依赖于我的 listview 实例上的 ListView.setItemChecked() 方法。但是,这不起作用,并且视图不会将其背景更改为我的 state_checked 项目的背景(即使其他状态显示正确)。这是一个单独的问题,还是相关的?
  • 知道了!结果我需要代码来在我的视图中添加额外的可检查状态以及所有适配器更改。我的视图知道它们处于state_checked 状态,它们只是无法从可绘制的状态列表中提取背景。
  • 嘿,约翰,我遇到了与您完全相同的问题(抱歉挖掘了一个老问题)。您能否详细说明使其正常工作所需的条件?
【解决方案2】:

我认为问题不在于上面的代码。我以前遇到过这个问题,它与列表视图中视图的回收有关。如果您的列表继续离开屏幕,您可能就是这种情况。如果是这种情况,解决它的一个好方法是将项目的状态存储在列表中,以便您可以跟踪它们并将它们的状态基于您创建的列表。请查看thisthis,了解有关视图回收的更多信息。

【讨论】:

    【解决方案3】:

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

    我还设置了状态列表 xml 文件,以在当前按下时使背景透明,以便选择器可见,这是一种享受。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-02
      • 2010-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多