setSelection不起作用的原因和解决办法

本文探讨了在Android中ListView使用setSelection(int position)时出现的失效情况。当设备处于touch mode时,setSelection不会改变选中状态,原因是Android在触摸模式下不显示焦点。同时,介绍了Android的Touch Mode概念,以及如何通过isInTouchMode判断当前模式。特殊情况如EditText即使在touch mode也能获取焦点,可通过setFocusableInTouchMode进行处理。
摘要由CSDN通过智能技术生成

打开一个listview的时候能够自动设置显示的位置, setSelection(int pos)可以设置显示的位置。

但是使用的时候大家会发现有时候起作用,有时候不起。

原因:

public void setSelection (int position)
Added in API level 1

Sets the currently selected item. If in touch mode, the item will not be selected but it will still be positioned appropriately. If the specified selection position is less than 0, then the item at position 0 will be selected.

touch mode的时候不起作用
touch mode指啥:从英文单词上来说,就是在触摸状态,下面一段具体的解释

大多数Android设备都是触摸屏的,但是实际上Android设备也支持键盘操作,允许通过键盘来完成导航,点击,输入等。

      当用户通过键盘(或者轨迹球)操作的时候,有必要聚焦当前接受输入的UI元素,例如,高亮(聚焦)某个按钮,让用户知道当前正在操作的UI元素是哪个。

      但是,当用户使用触摸屏与设备交互的时候,始终聚焦当前UI元素就没有必要了,而且很丑陋;用户点击哪个元素,哪个元素就是当前元素,无需高亮标识。并且,通过触摸屏与设备交互的时候,点击某个UI元素也不会导致该元素聚焦,此时的高亮效果是由Pressed状态来完成的。也就是说,在Touch Mode模式之下,UI元素是不会进入聚焦状态的,即使调用requestFocus也不会。

      那个,Android是如何区分这两种情况的呢?

      答案就是Touch Mode。当用户开始通过键盘与设备交互的时候,设备就退出Touch Mode模式;当用户开始通过触摸屏与设备交互的时候,设备就进入Touch Mode模式。可以通过调用View的isInTouchMode来判断设备当前是否处于Touch Mode模式。

      但是,也有例外情况。有些UI元素,即使是在Touch Mode的状态之下,也需要获得焦点,典型的就是Edittext。那么,这种情况该如何处理呢?

      答案就是做特殊处理。Android规定,某些元素,即使是在Touch Mode模式下,也可以获得焦点。调用View的setFocusableInTouchMode(true)可以使View在Touch Mode模式之下仍然可获得焦点(像Edittext就是在内部设置了这个属性),调用isFocusableInTouchMode可以判断View是否可在Touch Mode模式下聚焦。

所以有时候不起作用,那么让其起作用,就很简单了,采用
mListView.clearFocus();
mListView.post(new Runnable() { 
	@Override 
	public void run() {
	 mListView.setSelection(index); 
	}
});
post方法是发消息,那么就是等Touch Mode完成后,在去处理setSelection事件

当然还有一种简单粗暴的方法
list.setAdapter(adapter);  // or  list.setAdapter(list.getAdapter())
list.setPosition(...);
@Override
public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    resetList();
    mRecycler.clear();

    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }

    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;

    // AbsListView#setAdapter will update choice mode states.
    super.setAdapter(adapter);

    if (mAdapter != null) {
        mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
        mOldItemCount = mItemCount;
        mItemCount = mAdapter.getCount();
        checkFocus();

        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);

        mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

        int position;
        if (mStackFromBottom) {
            position = lookForSelectablePosition(mItemCount - 1, false);
        } else {
            position = lookForSelectablePosition(0, true);
        }
        setSelectedPositionInt(position);
        setNextSelectedPositionInt(position);

        if (mItemCount == 0) {
            // Nothing selected
            checkSelectionChanged();
        }
    } else {
        mAreAllItemsSelectable = true;
        checkFocus();
        // Nothing selected
        checkSelectionChanged();
    }

    requestLayout();
}

直接设置setAdapter,从代码上看,相当于列表的reset,自然就都不存在阻碍了,此时设置setSelection必然成功。

那么简单粗暴的方式和post的方法的区别在哪里呢
区别在于,是否继续响应事件,post,相当于把回到position的事件发送出去,它不会阻碍其他事件的进行。举例说明,当你正在很快的滑动时,点击了回到顶部按钮,那么采用post,就会回到顶部,但是滑动事件还会继续发生。结果是回到顶部,继续滑动。
而粗暴方式相当于reset,滑动事件会被丢弃。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值