本文是基于作者遇到的一个bug而来,如果正常情况下,可以不考虑本文做法。
从官方的注释上看getSelectedView()
好像是得到当前选中的 item 的view
,getSelectedItem()
得到的当前选中的 item 所绑定的数据。这两个得到的数据类型都不相同怎么比较?
首先,看到getSelectedItem()
的源码 实现:
/**
* @return The data corresponding to the currently selected item, or
* null if there is nothing selected.
*/
public Object getSelectedItem() {
T adapter = getAdapter();
int selection = getSelectedItemPosition();
if (adapter != null && adapter.getCount() > 0 && selection >= 0) {
return adapter.getItem(selection);
} else {
return null;
}
}
可以看到 return 的是adapter.getItem(selection)
,也就是我们要重写的 Adapter 里面的getItem()
函数。如果重写的时候我们返回的不是data
数据,而是当前选中的itemView
(自定义的view)的话,就可以与getSelectedView()
相比较了(在重写getView()
里面,已经把自定义的itemView
设置为了convertView
的tag
了,也就是(getSelectedView().getTag() instanceof itemView)
)。
再接着看int selection = getSelectedItemPosition();
,点击看到getSelectedItemPosition()
的源码:
public int getSelectedItemPosition() {
return mNextSelectedPosition;
}
返回的是mNextSelectedPosition
!下一个位置!
再看到getSelectedView()
的源码
public View getSelectedView() {
if (mItemCount > 0 && mSelectedPosition >= 0) {
return getChildAt(mSelectedPosition - mFirstPosition);
} else {
return null;
}
}
由于一般mFirstPosition
的值为0,所以也可以看作是返回getChildAt(mSelectedPosition)
当前选中的位置!
根据源码可以看出这两个方法得到的数据是有一点区别的,可是我们平时用的时候好像完全感觉不到,直到遇到特定的情况的时候,也就是我遇到的bug。
项目有个需求,是在右边的Fragment
切换时,左边的菜单ListView
会刷新选中项,并且选中项改变时,做一些效果(选中和失去)。我给ListView
注册了一个监听器OnFocusChangeListener
,代码如下:
OnFocusChangeListener onFocusChangeListner = new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
Log.d(TAG, "hasFocus = " + hasFocus + ", pos = " + menuListView.getSelectedItemPosition());
Log.d(TAG, "getSelectedView = " + menuListView.getSelectedView().getTag());
Log.d(TAG, "getSelectedItem = " + menuListView.getSelectedItem());
if (hasFocus) {
//给菜单ListView选中项设置为“第一焦点”
setFirstFocus();
} else {
//给菜单ListView选中项设置为“第二焦点”
setSecondFocus();
}
}
};
menuListView.setOnFocusChangeListener(onFocusChangeListner);
为什么要这样做,就是因为menuListView
有两种状态的焦点,一种是选中时并且也得到焦点时(称为“第一焦点”),另一种是选中时但是焦点不在上面(称为“第二焦点”)。
第一焦点
第二焦点
接下来执行操作,当选中项为“媒体”且焦点不在ListView
上时,切换右侧Fragment
到“应用”,这个时候理想状况是“第二焦点”状态转移到“应用”。
我的做法是:在setFirstFocus()
中得到当前选中menuListView.getSelectedView().getTag()
设置为第一焦点文字颜色;在setSecondFocus()
中得到当前选中menuListView.getSelectedView().getTag()
设置为第二焦点文字颜色。(这里不考虑红色块背景以及右侧红色条,因为不属于ListView里面的部分,为了有滑动动画,是用ImageView
另外做的)。
当两个方法使用的都是getSelectedView()
时,效果变成了这个样子:
看打印发现onFocusChange
调用了两次,第一次打印hasFocus = true, pos = 2
;第二次打印hasFocus = false, pos = 2
。
而getSelectedView().getTag()
得到的itemView
是pos = 1
的view
,getSelectedItem()
得到的itemView
是pos = 2
的view
。(怎么知道的?通过打印getView()
里面所有position
的itemView
对比 id 号得出)
如前言所述,getSelectedItem()
得到的是getSelectedView()
的下一项!
可以根据现象证明,两次调用第一次把“媒体”设置为了第一焦点,第二次把“媒体”设置为了第二焦点。这显然不是我想要的结果。根据“应用”选项放大也可以看出现在选中项是“应用”。
如果这样调整:在setFirstFocus()
和setSecondFocus()
中把menuListView.getSelectedView().getTag()
替换为menuListView.getSelectedItem()
。
效果变成了:
很明显,应该改成setFirstFocus()
里面调用menuListView.getSelectedView().getTag()
,setSecondFocus()
里面调用menuListView.getSelectedItem()
。
完整效果图示:
1.作为第一焦点时
2.按右,作为第二焦点时
3.按下,第二焦点切换
/**
* ━━━━━━神兽出没━━━━━━
* ┏┓ ┏┓
* ┏┛┻━━━┛┻┓
* ┃ ┃
* ┃ ━ ┃
* ┃ ┳┛ ┗┳ ┃
* ┃ ┃
* ┃ ┻ ┃
* ┗━┓ ┏━┛ 看不懂,没关系..
* ┃ ┃
* ┃ ┗━━━┓
* ┃ ┣┓
* ┃ ┏┛
* ┗┓┓┏━┳┓┏┛
* ┃┫┫ ┃┫┫
* ┗┻┛ ┗┻┛
*
* ━━━━━━神兽保佑,代码无bug━━━━━━
*/