Spinner: как узнать, был ли изменен выбор предмета программным путем или действием пользователя через пользовательский интерфейс

У меня есть код, который запускает событие OnItemSelectedListener spinner. Поэтому, когда я нахожусь в методе:

 public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { // I want to do something here if it's a user who changed the the selected item } 

… как я могу узнать, был ли выбор элемента выполнен программным путем или действием пользователя через пользовательский интерфейс?

Solutions Collecting From Web of "Spinner: как узнать, был ли изменен выбор предмета программным путем или действием пользователя через пользовательский интерфейс"

Я не знаю, что это можно отличить от метода. Действительно, это проблема, с которой сталкиваются многие люди, что onItemSelected запускается при onItemSelected счетчика. Кажется, что в настоящее время единственным обходным путем является использование для этого внешней переменной.

 private Boolean isUserAction = false; ... public void onItemSelected( ... ) { if( isUserAction ) { // code for user initiated selection } else { // code for programmatic selection // also triggers on init (hence the default false) } // reset variable, so that it will always be true unless tampered with isUserAction = true; } public void myButtonClick( ... ) { isUserAction = false; mySpinner.setSelectedItem ( ... ); } 

Я создал новый класс Spinner, инкапсулирующий вышеупомянутые принципы. Но даже тогда вам нужно обязательно вызвать правильный метод, а не setSelection

То же самое в сущности

 import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.AdapterView; /** * Used this to differentiate between user selected and prorammatically selected * Call {@link Spinner#programmaticallySetPosition} to use this feature. * Created by vedant on 6/1/15. */ public class Spinner extends android.widget.Spinner implements AdapterView.OnItemSelectedListener { OnItemSelectedListener mListener; /** * used to ascertain whether the user selected an item on spinner (and not programmatically) */ private boolean mUserActionOnSpinner = true; @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (mListener != null) { mListener.onItemSelected(parent, view, position, id, mUserActionOnSpinner); } // reset variable, so that it will always be true unless tampered with mUserActionOnSpinner = true; } @Override public void onNothingSelected(AdapterView<?> parent) { if (mListener != null) mListener.onNothingSelected(parent); } public interface OnItemSelectedListener { /** * <p>Callback method to be invoked when an item in this view has been * selected. This callback is invoked only when the newly selected * position is different from the previously selected position or if * there was no selected item.</p> * * Impelmenters can call getItemAtPosition(position) if they need to access the * data associated with the selected item. * * @param parent The AdapterView where the selection happened * @param view The view within the AdapterView that was clicked * @param position The position of the view in the adapter * @param id The row id of the item that is selected */ void onItemSelected(AdapterView<?> parent, View view, int position, long id, boolean userSelected); /** * Callback method to be invoked when the selection disappears from this * view. The selection can disappear for instance when touch is activated * or when the adapter becomes empty. * * @param parent The AdapterView that now contains no selected item. */ void onNothingSelected(AdapterView<?> parent); } public void programmaticallySetPosition(int pos, boolean animate) { mUserActionOnSpinner = false; setSelection(pos, animate); } public void setOnItemSelectedListener (OnItemSelectedListener listener) { mListener = listener; } public Spinner(Context context) { super(context); super.setOnItemSelectedListener(this); } public Spinner(Context context, int mode) { super(context, mode); super.setOnItemSelectedListener(this); } public Spinner(Context context, AttributeSet attrs) { super(context, attrs); super.setOnItemSelectedListener(this); } public Spinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); super.setOnItemSelectedListener(this); } public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) { super(context, attrs, defStyle, mode); super.setOnItemSelectedListener(this); } } 

Я придумал обходное решение, которое является простым и универсальным. См. Принятый ответ на этот вопрос:

Нежелательные вызовы onItemSelected

Итак, если position не равна spin.getTag(R.id.pos) , вы знаете, что обратный вызов был вызван тем, что пользователь внес изменения, потому что всякий раз, когда вы сами делаете изменение, вы устанавливаете тег как spin.setTag(R.id.pos, pos) где pos – это значение, которое вы установили. Если вы используете этот подход, не забудьте установить тег в onItemSelected после завершения вашей работы!

В отличие от SeekBar Spinner не имеет встроенной поддержки для определения того, является ли изменение программным или пользователем, поэтому я предлагаю, чтобы никогда не использовать счетчик для каких-либо рекурсивных программных задач. У меня был очень плохой опыт, когда я попытался реализовать MediaPlayer с рекурсивным подключением к SeekBar и Spinner . Результат был полон разочарования. Таким образом, вы можете попробовать, только если вам нравится несчастье и разочарование.

Примечание. Я решил проблему, добавив Button Применить» к моему выбору счетчика. Не тратьте время на решение ненужных вещей. Я имею в виду, что работа над работой – это не лучшая практика, а повторная реализация Spinner для нашего собственного ожидаемого поведения.

Мне очень жаль, если вышеуказанные утверждения ошибочны. Я поделился этим, потому что я люблю кодировщики и кодирование.

Вы можете добиться желаемого результата достаточно просто, используя метод setOnTouchListener() Spinner :

 // Instance variables boolean spinnerTouched = false; Spinner spinner; // onCreate() / onCreateView() / etc. method.. spinner = ...; spinner.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { spinnerTouched = true; // User DID touched the spinner! } return false; } }); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) { if (spinnerTouched) { // Do something } else { // Do something else } } @Override public void onNothingSelected(AdapterView<?> parentView) { } }); // Your method that you use the change the spinner selection programmatically... private void changeSpinnerSelectionProgrammatically(int pos) { stateSpinnerTouched = false; // User DIDN'T touch the spinner boolean useAnimation = false; spinner.setSelection(pos, useAnimation); // Calls onItemSelected() } 

Я знаю, что это поздно, но я только начал работать на Android и столкнулся с этой проблемой, и я нашел подходящую работу для этого.

Я использовал обходное решение, основанное на сценарии сфокусированного в касании.

  1. Установите режим поворота в режиме фокусировки в режиме касания.

  2. Настройте прослушиватель смены фокуса счетчика, чтобы вызвать spinner.performClick () при фокусировке.

  3. В onItemSelected прослушивании счетчика, верните фокус на родительский вид макета (или который когда-либо просматривается, когда вы найдете подходящий)

  4. Ввод пользователя может быть идентифицирован, проверяя, имеет ли счетчик фокус, поскольку программные изменения не потребуют фокуса.

PS: когда вы устанавливаете focusableintouchmode для счетчика в onCreate, убедитесь, что вы сразу же возвращаете фокус в родительское представление, если вам не хватает других фокусных представлений.