长话短说
- 列表视图选择器 (
android:listSelector
) 旨在指示点击事件,但是未选择的项目.
- 如果绘制了 ListView 选择器(第一次单击后),如果 ListView 没有发生剧烈变化,它不会消失
- 因此,如果没有状态作为 ListView 选择器应用于它,则仅使用具有透明背景的可绘制对象。不要使用纯色资源,不要让自己感到困惑.
- 使用ListView选择模式(
android:choiceMode
) 来指示选定的项目。
- ListView 通过设置告诉选择哪一行
android:state_activated
在行的根视图上。为您的适配器提供相应的布局/视图,以正确表示所选项目。
TL/DR 解决方案
- You can hide/remove selector with one of the following:
- 使选择器透明
getSelector().setAlpha(0)
- 重置当前适配器
setAdapter(myAdapter)
(适配器可能是相同的)
- Solutions that might or might not work, depending on the OS version:
- 使列表视图完全刷新布局通过
requestLayout()
, invalidate()
or forceLayout()
方法;
- 使列表视图刷新布局通过
notifyDataSetChanged()
Theory
嗯,内置选择ListView
乍一看非常棘手。然而,您应该记住两个主要区别,以避免像这样的混淆 - 列表视图selector and 选择模式.
列表视图选择器
ListView 选择器是一个可绘制资源,假设表示单击列表项的事件。您可以通过 XML 属性指定它android:listSelector https://developer.android.com/reference/android/widget/AbsListView.html#attr_android:listSelector或使用方法setSelector() https://developer.android.com/reference/android/widget/AbsListView.html#setSelector(int)。我在文档中找不到它,但我的理解是这个资源不应该是纯色的,因为在绘制它之后,如果视图没有发生巨大的变化(比如设置适配器,这反过来可能会导致它消失),它就不会消失。导致出现一些故障),因此此类可绘制对象应该仅在特定状态(例如android:state_pressed https://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html#attr_android:state_pressed) 被申请;被应用。这是可用作列表视图选择器的可绘制对象的简单示例
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@android:color/darker_gray" />
<item
android:drawable="@android:color/transparent" />
</selector>
无论出于何种原因,您都不能使用颜色状态列表 https://developer.android.com/guide/topics/resources/color-list-resource.html作为列表视图选择器,但仍然可以使用纯色(大多数情况下不合适)和州列表 https://developer.android.com/guide/topics/resources/drawable-resource.html#StateList绘图。这让事情变得有些混乱。
第一次单击列表视图后,您将无法轻松地从列表视图中删除列表视图选择器。
这里的主要思想是列表视图选择器并非旨在指示所选项目.
ListView选择模式
ListView 选择模式假设为表示选定的项目。您可能知道,我们在 ListView 中主要可以使用两种选择模式 - 单选和多选。它们允许跟踪分别选择的单行或多行。您可以通过设置它们android:choiceMode https://developer.android.com/reference/android/widget/AbsListView.html#attr_android:choiceModeXML 属性或setChoiceMode() https://developer.android.com/reference/android/widget/AbsListView.html#setChoiceMode(int)方法。
ListView 本身将选定的行保留在其中,并通过设置让他们知道在任何给定时刻选择了哪一行android:state_activated https://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html#attr_android:state_activated行根视图的属性。为了使您的行反映这种状态,它们的根视图必须有相应的可绘制集,例如作为背景。这是此类可绘制的示例:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
android:drawable="@android:color/holo_green_light" />
<item
android:drawable="@android:color/transparent" />
</selector>
您可以使用以下命令以编程方式选择/取消选择行setItemChecked() https://developer.android.com/reference/android/widget/AbsListView.html#setItemChecked(int,%20boolean)方法。如果您希望 ListView 清除所有选定的项目,您可以使用clearChoices() https://developer.android.com/reference/android/widget/AbsListView.html#clearChoices()方法。您还可以使用以下方法系列检查选定的项目:getCheckedItemCount() https://developer.android.com/reference/android/widget/AbsListView.html#getCheckedItemCount(), getCheckedItemIds() https://developer.android.com/reference/android/widget/AbsListView.html#getCheckedItemIds(), getCheckedItemPosition() https://developer.android.com/reference/android/widget/AbsListView.html#getCheckedItemPosition()(对于单选模式),getCheckedItemPositions() https://developer.android.com/reference/android/widget/AbsListView.html#getCheckedItemPositions()(对于多项选择模式)
结论
如果你想让事情变得简单,不要使用列表视图选择器来指示所选项目.
解决问题
选项 1. 脏修复 - 隐藏选择器
我们可以在需要时隐藏选择器可绘制对象,然后在单击 ListView 项时显示它,而不是实际删除选择器、更改布局和实现稳健的方法:
public void hideListViewSelector() {
mListView.getSelector().setAlpha(0);
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mListView.getSelector().getAlpha() == 0) {
mListView.getSelector().setAlpha(255);
}
}
选项2.深思熟虑的方式
让我们检查一下您的代码并使其符合我逐步描述的规则。
修复 ListView 布局
在 ListView 布局中,选择器设置为纯色,因此当单击项目时,它们会随之着色。您用作 ListView 背景的可绘制对象没有影响,因为单击其行时 ListView 状态不会更改,因此您的 ListView 始终只有@color/windowBackground
背景。
要解决您的问题,您需要首先从 ListView 布局中删除选择器:
<ListView
android:id="@+id/listview"
android:layout_width="wrap_content"
android:layout_height="250dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/toolbar"
android:listSelector="@color/colorPrimary"
android:background="@color/windowBackground"
android:choiceMode="singleChoice"/>
让您的行反映激活状态
在评论中,您给出的适配器如下:
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, text1, listOfThings);
您还问我是否可以继续使用标准适配器来实现所需的行为。我们当然可以,但无论如何都需要进行一些更改。对于这种情况,我可以看到 3 个选项:
1.使用标准的android检查布局
您只需指定相应的标准布局 - 使用的任何布局CheckedTextView https://developer.android.com/reference/android/widget/CheckedTextView.html 没有改变背景可绘制作为根组件或使用的根组件activatedBackgroundIndicator https://developer.android.com/reference/android/R.attr.html#activatedBackgroundIndicator作为他们的背景可绘制。对于您的情况,最合适的选项应该是simple_list_item_activated_1 https://android.googlesource.com/platform/frameworks/base/+/master/core/res/res/layout/simple_list_item_activated_1.xml。只需将其设置为您的ArrayAdapter https://developer.android.com/reference/android/widget/ArrayAdapter.html像这样的构造函数:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_activated_1, android.R.id.text1, listOfThings);
这个选项最接近我所理解的“标准”适配器。
2. 定制您的适配器
您可以使用标准布局和大多数标准适配器,但获取项目视图有一个小例外。只需引入一个匿名类并重写方法即可getView() https://developer.android.com/reference/android/widget/ArrayAdapter.html#getView(int,%20android.view.View,%20android.view.ViewGroup),提供具有相应背景可绘制的行视图:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, listOfThings) {
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
final View view = super.getView(position, convertView, parent);
if (convertView == null) {
view.setBackgroundResource(R.drawable.list_item_bg);
}
return view;
}
};
3. 自定义布局
解决此问题的最常见方法当然是引入您自己的项目视图布局。这是我的简单例子:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/list_item_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:padding="16dp">
<TextView
android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
我把它保存在一个文件中/res/layout/list_view_item.xml
不要忘记在适配器中设置此布局:
final ArrayAdapter<String> adapter = new ArrayAdapter(this, R.layout.list_view_item, android.R.id.text1, listOfThings);
清算选择
之后,您的行将在单击时反映选定状态,并且您可以轻松清除行的选定状态ListView
通过致电clearChoices()
和后果requestLayout() https://developer.android.com/reference/android/view/View.html#requestLayout()要求 ListView 重绘自身。
这里有一点评论,如果您想在用户开始输入时取消选择该项目,而不是在他实际单击返回(完成)按钮时取消选择该项目,则需要使用TextWatcher https://developer.android.com/reference/android/text/TextWatcher.html回调改为:
mEditText.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mListView.getCheckedItemCount() > 0) {
mListView.clearChoices();
mListView.requestLayout();
}
}
@Override
public void afterTextChanged(Editable s) {}
});
希望它有所帮助。