對于Spinner,也是最近項目需求用的,才去了解這個控件磕潮。項目里面的出生年月日,一開始使用OS標準的DatePicker容贝,但是后來客戶想要這樣的自脯。
立馬就想到了Spinner,于是就開始著手斤富,具體的代碼就不貼了膏潮。
想要解決的是選擇同一項的時候,也能回調
void onItemSelected(AdapterView<?> parent, View view, int position, long id)
方法满力。先看下為什么不能回調戏罢?
AdapterView.class源碼
/**
* Register a callback to be invoked when an item in this AdapterView has
* been selected.
*
* @param listener The callback that will run
*/
public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
mOnItemSelectedListener = listener;
}
// 然后看下 mOnItemSelectedListener 這個監(jiān)聽對象在哪兒調用onItemSelected方法
// 也就觸發(fā)了onItemSelected事件屋谭。
private void fireOnSelected() {
if (mOnItemSelectedListener == null) {
return;
}
final int selection = getSelectedItemPosition();
if (selection >= 0) {
View v = getSelectedView();
mOnItemSelectedListener.onItemSelected(this, v, selection,
getAdapter().getItemId(selection));
} else {
mOnItemSelectedListener.onNothingSelected(this);
}
}
// 那在看看是在哪里調用fireOnSelected()方法
private void dispatchOnItemSelected() {
fireOnSelected();
performAccessibilityActionsOnSelected();
}
void selectionChanged() {
// We're about to post or run the selection notifier, so we don't need
// a pending notifier.
mPendingSelectionNotifier = null;
if (mOnItemSelectedListener != null
|| AccessibilityManager.getInstance(mContext).isEnabled()) {
if (mInLayout || mBlockLayoutRequests) {
// If we are in a layout traversal, defer notification
// by posting. This ensures that the view tree is
// in a consistent state and is able to accommodate
// new layout or invalidate requests.
if (mSelectionNotifier == null) {
mSelectionNotifier = new SelectionNotifier();
} else {
removeCallbacks(mSelectionNotifier);
}
post(mSelectionNotifier);
} else {
dispatchOnItemSelected();
}
}
// Always notify AutoFillManager - it will return right away if autofill is disabled.
final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
if (afm != null) {
afm.notifyValueChanged(this);
}
}
/**
* Called after layout to determine whether the selection position needs to
* be updated. Also used to fire any pending selection events.
*/
void checkSelectionChanged() {
if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {
selectionChanged();
mOldSelectedPosition = mSelectedPosition;
mOldSelectedRowId = mSelectedRowId;
}
// If we have a pending selection notification -- and we won't if we
// just fired one in selectionChanged() -- run it now.
if (mPendingSelectionNotifier != null) {
mPendingSelectionNotifier.run();
}
}
我們看這個判斷條件
if ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId))
,比較當前選項的位置與上一次選擇的位置是否不相同龟糕,如果不相同就可以調用selectionChanged()執(zhí)行onItemSelected事件。這就是選擇一項時悔耘,必須與上一次選項的位置不相同才可以觸發(fā)選擇事件讲岁。
那該如何解決?
方法1:
利用反射的方式修改上一次位置的值衬以,只要與當前選擇的位置不同就可以出發(fā)事件了缓艳。因為mOldSelectedPosition是私有屬性,所以我們需要使用反射來修改mOldSelectedPosition的值看峻。代碼如下:
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
try {
Field field = AdapterView.class.getDeclaredField("mOldSelectedPosition");
field.setAccessible(true); //設置mOldSelectedPosition可訪問
field.setInt(spinner, AdapterView.INVALID_POSITION); //設置mOldSelectedPosition的值
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
如果頁面只有一個Spinner是沒有問題的阶淘,但是當頁面包含了兩個及以上的Spinner的時候就會出現(xiàn)一個問題:當點擊
第一個
spinner的item時候,會觸發(fā)其他Spinner的listener的onItemSelected
方法互妓,這就比較難受了溪窒,明明是只操作一個,但是其他也有響應冯勉。
方法2
在原有Spinner基礎上自定義澈蚌,代碼不多
public class CKDateSpinner extends AppCompatSpinner {
public CKDateSpinner(Context context) {
super(context);
}
public CKDateSpinner(Context context, int mode) {
super(context, mode);
}
public CKDateSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setSelection(int position, boolean animate) {
boolean sameSelected = position == getSelectedItemPosition();
super.setSelection(position, animate);
if (sameSelected) {
// Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
}
}
@Override
public void setSelection(int position) {
boolean sameSelected = position == getSelectedItemPosition();
super.setSelection(position);
if (sameSelected) {
// Spinner does not call the OnItemSelectedListener if the same item is selected, so do it manually now
getOnItemSelectedListener().onItemSelected(this, getSelectedView(), position, getSelectedItemId());
}
}
}
以上就是兩種解決方法。