【问题标题】:How do I clear ListView selection?如何清除 ListView 选择?
【发布时间】:2018-01-14 20:14:31
【问题描述】:

TL;DR:您从 (a) 我的列表视图中选择一个选项。然后,您改变主意并在 (b) 我的编辑文本中输入一些内容。如何清除您的列表视图选择并仅显示您的编辑文本? (反之亦然)

我有一个带有选项列表视图的应用程序以及用于创建自己的选项的编辑文本。我需要用户选择或创建一个选项,但不能同时选择两者。这是我的布局图:

每当用户从列表视图中选择一个选项时,我将其设置为“已选择”,将其设为绿色,如下所示:

<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_selected="true"
        android:drawable="@color/colorPrimary"/>
    <item
        android:state_selected="false"
        android:drawable="@color/windowBackground" />
</selector>

(这被设置为我的列表视图的背景)

问题:如果用户决定输入自己的选项,我想取消选择列表视图选项,因为他们只能有一个选项。

  1. 用户从列表视图中选择一个选项
  2. 用户决定要使用编辑文本创建自己的选项
  3. 当他们开始输入自己的内容时,未选择列表视图选项

我尝试过following,但没有任何取消选择。

e.setOnEditorActionListener(new TextView.OnEditorActionListener()
        {
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

                for(int i=0; i<=5; i++){
                                listView.setItemChecked(i, false);
                }
                listView.clearChoices();
                listView.requestLayout()
                adapter.notifyDataSetChanged()
             }
        }

一个非常令人费解的困境,感谢任何帮助!

编辑:这里是编辑文本的布局:

<EditText
                android:id="@+id/editText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignBaseline="@+id/textView4"
                android:layout_alignBottom="@+id/textView4"
                android:layout_toEndOf="@+id/textView4"
                android:layout_toRightOf="@+id/textView4"
                android:color="@color/colorPrimary"
                android:imeOptions="actionDone"
                android:inputType="text"
                android:textColor="@color/textColorPrimary"
                android:textColorHint="@color/colorPrimary" />

编辑:这是列表视图的布局:

    <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:background="@drawable/bg_key"
        android:choiceMode="singleChoice"
        android:listSelector="@color/colorPrimary">

    </ListView>

【问题讨论】:

  • eTextView 吗?你会怎么输入呢?
  • 不,e 是编辑文本@Napster
  • 您能展示一下您的EditText 布局吗?你也调试看看这个监听器是否被调用?
  • @Napster 监听器肯定被调用了,我已经编辑了我的问题以添加 EditText 布局
  • 您使用的是自定义布局还是标准ArrayAdapter

标签: java android android-listview android-xml


【解决方案1】:

长话短说

  • ListView 选择器 (android:listSelector) 旨在指示点击事件,但未选择项目
  • 如果 ListView 选择器被绘制(在第一次单击后),它不会消失而不会在 ListView 中发生剧烈变化
  • 因此,如果没有将任何状态作为 ListView 选择器应用于它,则仅使用具有透明背景的可绘制对象。 不要使用纯色资源,不要混淆自己
  • 使用 ListView 选择模式 (android:choiceMode) 来指示所选项目。
  • ListView 通过在行的根视图上设置android:state_activated 来告知选择了哪一行。为您的适配器提供相应的布局/视图以正确表示所选项目。

理论

嗯,ListView 中的内置选择乍一看非常棘手。但是,您应该记住两个主要区别以避免像这样混淆 - 列表视图选择器选择模式

列表视图选择器

ListView 选择器是一个可绘制资源,被假定为指示单击列表项的事件。您可以通过 XML 属性 android:listSelector 或使用方法 setSelector() 指定它。我在文档中找不到它,但我的理解是这个资源不应该是纯色,因为在它被绘制之后,它不会在视图中没有发生巨大变化的情况下消失(比如设置一个适配器,这反过来可能导致出现一些故障),因此只有在应用特定状态(例如android:state_pressed)时才应该看到这样的drawable。这是一个可以用作列表视图选择器的drawable的简单示例

<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>

无论出于何种原因,您都不能使用Color State List 作为列表视图选择器,但仍然可以使用纯色(大多数情况下不合适)和State List 可绘制对象。这让事情有些混乱。 在第一次单击列表视图后,您将无法轻松地从列表视图中删除列表视图选择器。

这里的主要思想是列表视图选择器并非旨在指示所选项目

ListView选择模式

ListView 选择模式被假定为指示所选项目。您可能知道,我们可以在 ListView 中使用主要有两种选择模式 - 单选和多选。它们允许跟踪分别选择的单个或多个行。您可以通过android:choiceMode XML-property 或setChoiceMode() 方法设置它们。 ListView 本身将选定的行保存在其中,并通过设置行根视图的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() 方法以编程方式选择/取消选择行。如果希望 ListView 清除所有选定项,可以使用 clearChoices() 方法。您还可以使用方法系列检查所选项目:getCheckedItemCount()getCheckedItemIds()getCheckedItemPosition()(用于单选模式)、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"/> 

让你的行反映激活状态

在 cmets 中你给你的适配器如下:

final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, text1, listOfThings);

您还问我是否可以继续使用标准适配器来实现所需的行为。我们可以肯定,但无论如何都需要进行一些更改。对于这种情况,我可以看到 3 个选项:

1.使用标准的 android 检查布局

您可以只指定相应的标准布局 - 使用 CheckedTextView 不更改背景可绘制对象 作为根组件的任何布局,或者使用 activatedBackgroundIndicator 作为其背景可绘制对象的任何布局。对于您的情况,最合适的选项应该是simple_list_item_activated_1。只需将其设置为您的 ArrayAdapter 构造函数,如下所示:

final ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter(this, <b>android.R.layout.simple_list_item_activated_1</b>, android.R.id.text1, listOfThings);

这个选项最接近我对“标准”适配器的理解。

2。自定义您的适配器

您可以使用标准布局和大多数标准适配器,但获取项目视图的小例外。只需引入一个匿名类并重写方法getView(),即可提供具有相应背景drawable的行视图:

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&lt;String&gt; adapter = new ArrayAdapter(this, <b>R.layout.list_view_item</b>, android.R.id.text1, listOfThings);

清除选择

之后,您的行将在单击时反映选定状态,并且您可以通过调用 clearChoices() 和结果 requestLayout() 来要求 ListView 重绘自身,轻松清除 ListView 的选定状态。

这里有一点评论,如果您想在用户开始输入时取消选择项目,而不是在他实际单击返回(完成)按钮时,您需要使用 TextWatcher 回调代替:

    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) {}
    });

希望它有所帮助。

【讨论】:

  • 非常感谢您的回答!这种方法是有道理的,但是,我不知道如何将StateListDrawable 应用于我的适配器。我使用的是普通的ArrayAdapter,似乎不可能
  • final ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;&gt;(this, android.R.layout.simple_list_item_1, text1, keyPhrases); 这是我的适配器在当前状态下的样子
  • @RuchirBaronia 您可以在适配器构造函数中指定您想要的任何布局。我认为,如果您只想使用似乎有点不同的问题的标准布局。但是我更新了我的答案“让你的行反映 state_activated 状态”部分,请检查它
【解决方案2】:

我有一个很好的解决方案。将 EditText 添加到您的布局中,其中包含您的 ListView 作为此布局:

    <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical"
        android:choiceMode="singleChoice"
        />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Comment"
        android:layout_below="@id/list_view"
        android:id="@+id/editText"
        android:nextFocusUp="@id/editText"
        android:nextFocusLeft="@id/editText"/>
</RelativeLayout>

然后初始化布尔变量来检查editText是否有焦点,例如使用这个:boolean canBeSelected = true;

然后在设置适配器后使用此代码:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            if (canBeSelected) {
                listView.setSelector(R.drawable.background);
                listView.setSelected(true);
                listView.setSelection(i);
            } else {
                if (!editText.isFocused()){
                    canBeSelected = true;
                    listView.setSelector(R.drawable.background);
                    listView.setSelected(true);
                    listView.setSelection(i);
                }
            }
        }
    });
    editText.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            canBeSelected = false;
            Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
            listView.setSelector(transparentDrawable);
            listView.clearChoices();
            listView.setSelected(false);
            return false;
        }
    });



    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            if (editText.isFocused()){
                Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
                listView.setSelector(transparentDrawable);
                listView.clearChoices();
                listView.setSelected(false);
                canBeSelected = false;
            }
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
            listView.setSelector(transparentDrawable);
            listView.clearChoices();
            listView.setSelected(false);
            canBeSelected = false;
        }

        @Override
        public void afterTextChanged(Editable editable) {
            if (editText.isFocused()) {
                Drawable transparentDrawable = new ColorDrawable(Color.TRANSPARENT);
                listView.setSelector(transparentDrawable);
                listView.clearChoices();
                listView.setSelected(false);
                canBeSelected = false;
            }
        }
    });
}

希望它对你有用:)

【讨论】:

    【解决方案3】:

    在edittext监听器中重新设置适配器对我有用:

    editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    
                listview.clearChoices();
                listview.setAdapter(adapter);
                Toast.makeText(getApplicationContext(),"Typing" + listview.getSelectedItemPosition(), Toast.LENGTH_SHORT).show();
            }
            @Override
            public void afterTextChanged(Editable editable) {
    
            }
        });
    

    我将选定的索引放入 toast 以检查项目是否被正确取消选择。

    希望这行得通!

    【讨论】:

      【解决方案4】:

      当您请求第二个数据集时只需调用 clear:

          arrayAdapter!!.clear()
      

      您加载了您的第一个数据集
      用户选择一个元素,
      此操作突出显示您的项目
      无论出于何种原因,您都会重新加载数据集(因为 edittext 的值已更改),
      此时请在您的适配器上调用 clear()。
      然后您检索数据集,将其发送到 arrayAdapter 并 没有人被选中。
      这是因为当你清除时,它也会清除选定的标志

      【讨论】:

        猜你喜欢
        • 2015-10-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-27
        相关资源
        最近更新 更多