ListView保持选中状态?

10

我有一个列表视图,其中包含许多项目。当用户选择一个项目后,它会变亮,然后恢复正常。是否有办法让用户在我的ListView中选择一个项目后保持选中和高亮?


这是一段有用的代码:https://dev59.com/7G025IYBdhLWcg3wkW4F - Brane
7个回答

15

显然,“选择消失”是设计上的问题;这被称为“触摸模式”。我已经阅读了该文档,但仍然不明白他们为什么认为这是一个好主意。我的猜测是,由于Android最初是为小屏幕设备设计的,他们预计您会用列表填满屏幕,然后当用户单击项目时,在不同的屏幕上移动到新的列表。因此,用户不会意识到Android丢失了所选项目。

但是,如果例如您希望用户选择一个项目,然后在同一屏幕上显示有关该项目的信息,则此行为非常令人讨厌。如果选择消失了,用户怎么知道他们点击了什么(假设用户的注意力持续时间与金鱼相同)?

一个可能的解决方案是将所有列表项更改为单选按钮。我并不真的喜欢那种解决方案,因为它浪费屏幕实际空间。我宁愿使用背景颜色来显示选定的项。到目前为止,我已经看到了一个解决方案,但它还不太完整或通用。因此,这是我的解决方案:

1. 在您的XML布局文件中

转到您的ListView元素并添加以下属性:android:choiceMode="singleChoice"。我不完全确定这是什么意思(单独使用它不允许用户选择任何内容),但没有此属性,下面的代码将无法工作。

2. 定义以下类

它用于跟踪所选项目,并允许您在Java中模拟按引用传递:

public class IntHolder {
    public int value;
    public IntHolder() {}
    public IntHolder(int v) { value = v; } 
}

3. 把以下代码放到某个地方

我假设你把它放在了你的Activity中,但实际上它可以放在任何一个类中:

static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition)
{
    setListItems(context, listView, listItems, selectedPosition, 
                 android.R.layout.simple_list_item_1, 
                 android.R.layout.simple_spinner_dropdown_item);
}
static void setListItems(Context context, AdapterView listView, List listItems, final IntHolder selectedPosition, 
                         int list_item_id, int dropdown_id)
{
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> list, View lv, int position, long id) {
            selectedPosition.value = position;
        }
    });
    ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(context, list_item_id, listItems) { 
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View itemView = super.getView(position, convertView, parent);
            if (selectedPosition.value == position)
                itemView.setBackgroundColor(0xA0FF8000); // orange
            else
                itemView.setBackgroundColor(Color.TRANSPARENT);
            return itemView;
        }
    };
    adapter.setDropDownViewResource(dropdown_id);
    listView.setAdapter(adapter);
}

这段代码有两个作用:它将您的列表项(例如List<String>)附加到您的ListView上,并使用一些代码覆盖了ArrayAdapter.getView(),以更改所选项目的背景。

4. 使用该代码设置您的列表

例如:

ListView _list;
IntHolder _selectedItem = new IntHolder(-1); // nothing selected at first

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    _list = (ListView)findViewById(R.id.list);
    List<String> items = Arrays.asList("Item 1", "Item 2", "Item 3");
    setListItems(this, _list, items, _selectedItem);
}

就这些!以上假设您想要单选。如果您对 getView() 进行一些小的修改,可能还可以支持多选,但最好使用复选框。

警告:这个解决方案需要进一步开发。如果用户使用箭头键或按钮选择一个项目,则该项目将从 IntHolder 的角度不会被选中。如果用户按下未标记的按钮(那个按钮叫什么?“Enter”?),则该项目将变为“正式”选定,但现在您有另一个问题,因为如果用户再次使用箭头键,它看起来有点像选择了两个项目。如果您找出如何使“内部选择”与“键盘选择”或其他名称同步,请留言告诉我。那到底叫什么呢?


这对于当一个屏幕同时包含列表(左半部分)和所选行的详细信息(右半部分)时,也非常有帮助。因此,这是告诉/展示用户他之前选择的唯一方式! - Stan
我已经卡在这里6个小时了,我真的需要帮助。http://stackoverflow.com/questions/35108940/why-cant-i-remove-an-item/35109304#35109304 - Ruchir Baronia

2
ListView中有一个属性叫做listSelector:
Drawable用于指示列表中当前选定的项目。

http://developer.android.com/reference/android/widget/AbsListView.html#attr_android:listSelector


编辑 Stan的评论之后

为了确保ListView保持选中状态,您应该:

① 通过xml或编程方式设置视图的属性choiceMode

② 使用一个使用实现了Checkable接口的视图的适配器,例如CheckedTextView(在simple_list_item_single_choice布局中)。

文件TestActivity.java

public class TestActivity extends Activity {

    private static final int SINGLE_CHOICE = android.R.layout.simple_list_item_single_choice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);

        String[] items = {"test 1", "test 2", "test 3"};
        ListAdapter adapter = new ArrayAdapter<String>(this, SINGLE_CHOICE, items);
        ListView list = (ListView) findViewById(R.id.testList);
        list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        list.setAdapter(adapter);
    }
}

这样做没有帮助,因为ListView的工作方式如此。使用选择器ListView只会在触摸单元格时显示高亮单元格,但是一旦用户结束触摸,单元格就会变为未选中/未聚焦等状态,所以只使用选择器无法显示它被选中的方式。 - Stan
@Stan 你是对的;我编辑了帖子,以便也使用了一个适配器。 - KitKat

2

这里有一个比Qwertie更简单的解决方案:

不要依赖于给定的选择机制。自己动手做。

View mSelectedItemView = null; //class member variable
View mTouchedItemView = null; //class member variable

ListView v = (ListView) getActivity().findViewById(R.id.listView);
// select on click
v.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> adapter,
            View clickedViewItem, int position, long id) {
        if (mSelectedItemView != null)
            selectedItemView.setBackgroundColor(Color.WHITE);
        clickedViewItem.setBackgroundColor(Color.YELLOW);
        mSelectedItemView = clickedViewItem;
    }
});
// highlight on touch
v.setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (v instanceof ListView) {
            ListView listView = (ListView) v;
            // Find the child view that was touched (perform a
            // hit test)
            Rect rect = new Rect();
            int childCount = listView.getChildCount();
            int[] listViewCoords = new int[2];
            v.getLocationOnScreen(listViewCoords);
            int x = (int) event.getRawX() - listViewCoords[0];
            int y = (int) event.getRawY() - listViewCoords[1];
            View child;
            for (int i = 0; i < childCount; i++) {
                child = listView.getChildAt(i);
                child.getHitRect(rect);
                if (rect.contains(x, y)) {
                    View touchedView = child;
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        touchedView
                                .setBackgroundColor(Color.RED);
                        mTouchedItemView = touchedView;
                    } else if (event.getAction() == MotionEvent.ACTION_UP) {
                        mTouchedItemView 
                                .setBackgroundColor(Color.WHITE);
                    }
                }
            }
        }
        return false;
    }
});

此方法仅处理点击事件,如果用户使用箭头键,则无法正常工作。
免责声明:触摸后取消高亮显示不可靠。
感谢ozik.dev提供的触摸部分: 仅使用OnTouchListener从ListView获取项目

是的,你说得对Jack Miller :),所以它完美地运行。 - Eldar Zeynalov

-1
使用一个 Selector.XML 文件和以下代码:
    //SetOnClickListner to catch Events
    listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         view.setSelected(true);
        }
    });

-1

只需将此代码添加到您的ListView布局中

 android:listSelector="@drawable/selector_expandable_listview" 
 android:drawSelectorOnTop="true"

-1

只需将此代码添加到您的 ListView 中:

android:listSelector="@color/my_color"

-3

这个答案是有效的,请尝试一下

@Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long arg3)
    {
        for(int a = 0; a < parent.getChildCount(); a++)
        {
            parent.getChildAt(a).setBackgroundColor(Color.TRANSPARENT);
        }
    view.setBackgroundColor(Color.GREEN);
}

1
现在想象一下,你的列表中有1000个项目,所以每次用户点击时,你的循环都会触发。这种解决方案完全不够用,而且会在大型列表上导致冻结/挂起,也会导致不可预测的视觉错误。 - Stan
列表视图重复使用其子项,因此仅会为适合屏幕的项目调用,这很好。 - Blackhex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接