上一篇文章 Spinner类setSelection执行流程源码解析 我们分析了setSelection方法执行的整个流程,那么Spinner(准确来讲是AdapterView)是如何通知我们selection状态已经改变了?
这边文章我们就来趁热打铁来聊聊这个话题。
其实我们都知道,要想知道selection状态是否改变我们只需要调用setOnItemSelectedListener方法注册一个回调方法就可以了。那么这个回调方法究竟在什么时候被回调呢?
还记得 Spinner类setSelection执行流程源码解析 一文layout方法末尾的checkSelectionChanged()方法么,这就是入口点。好了,上源码:
void checkSelectionChanged() {
if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
// 如果positon和rowid有一个不一致,那么执行回调
selectionChanged();
mOldSelectedPosition = mSelectedPosition;
mOldSelectedRowId = mSelectedRowId;
}
// mPendingSelectionNotifier是一个SelectionNotifier对象。SelectionNotifier实现了Runnable接口,在run方法中实现了回调策略。mPendingSelectionNotifier.run和selectionChanged方法只会执行一个。原因是selectionChanged方法刚进去就会将mPendingSelectionNotifier设置为null
if (mPendingSelectionNotifier != null) {
mPendingSelectionNotifier.run();
}
}
看看selectionChanged方法做了什么
void selectionChanged() {
// 就是在这里置null的。。
mPendingSelectionNotifier = null;
// 判断回调接口是否为null以及AccessibilityManager是否使能。AccessibilityManager管理着各种回调事件,如点击事件、焦点改变事件等等,在这里我们默认为true就好。
if (mOnItemSelectedListener != null
|| AccessibilityManager.getInstance(mContext).isEnabled()) {
// 在之前的文章中提过这两个变量
// mInLayout会在Spinner.onLayout中被使能,在layout的过程中都是使能状态
// mBlockLayoutRequests在两个地方会使能
// 1.AbsSpinner.onMeasure方法中view设置LayoutParam的时候
// 2.setSelectionInt方法中
// 也就是说在以上三种情况使能状态下是不会进行直接回调,而是通过消息机制进行回调。
if (mInLayout || mBlockLayoutRequests) {
if (mSelectionNotifier == null) {
// 如果mSelectionNotifier为null那么创建一个
mSelectionNotifier = new SelectionNotifier();
} else {
// 如果mSelectionNotifier不为null,那么先从消息队列中移除该mSelectionNotifier
removeCallbacks(mSelectionNotifier);
}
// 将mSelectionNotifier放入到消息队列中
post(mSelectionNotifier);
} else {
// 执行回调
dispatchOnItemSelected();
}
}
}
再来看看SelectionNotifier中是如何处理回调的
private class SelectionNotifier implements Runnable {
public void run() {
mPendingSelectionNotifier = null;
// 如果此刻与发送该消息的时刻之间数据源发生了改变,也就是调用了adapter的notifyDataSetChanged或者notifyDataSetInvalidated方法,那么将此消息保存为mPendingSelectionNotifier。而mPendingSelectionNotifier会在下次layout结束的时候被调用。
if (mDataChanged && getViewRootImpl() != null
&& getViewRootImpl().isLayoutRequested()) {
if (getAdapter() != null) {
mPendingSelectionNotifier = this;
}
} else {
// 数据源未发生改变,那么直接进行回调。
dispatchOnItemSelected();
}
}
}
dispatchOnItemSelected()方法中还有一些东西,不过也比较简单,所以就不进行分析了。
好了Spinner的OnItemSelected事件的回调分析大致就是这样了。
以上。