Я думал о некоторых не очень элегантных способах решения этой проблемы, но я знаю, что, должно быть, чего-то не хватает.
Мой onItemSelected
немедленно отключается без какого-либо взаимодействия с пользователем, и это нежелательное поведение. Я хочу, чтобы пользовательский интерфейс ждал, пока пользователь не выберет что-то, прежде чем он что-то сделает.
Я даже попытался настроить слушателя в onResume()
надежде, что это поможет, но это не так.
Как я могу остановить это, прежде чем пользователь сможет коснуться элемента управления?
public class CMSHome extends Activity {
private Spinner spinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Heres my spinner ///////////////////////////////////////////
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
};
public void onResume() {
super.onResume();
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Intent i = new Intent(CMSHome.this, ListProjects.class);
i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
startActivity(i);
Toast.makeText(parent.getContext(), "The pm is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
}
android
spinner
android-spinner
FauxReal
источник
источник
Spinner
пустым, а внутриonItemSelected
вы можете обнаружить, не является ли строка тогда пустойstartActivity
!Ответы:
Я ожидал, что ваше решение сработает - хотя событие выбора не сработает, если вы установите адаптер до настройки прослушивателя.
При этом простой логический флаг позволит вам обнаружить мошенническое событие первого выбора и проигнорировать его.
источник
onResume()
иonPostResume()
, таким образом, все нормальные перехваты завершены к моменту раскладки.Использование Runnables совершенно некорректно.
Используйте
setSelection(position, false);
в первоначальном выборе передsetOnItemSelectedListener(listener)
Таким образом, вы устанавливаете свой выбор без анимации, что вызывает вызов выбранного слушателя. Но слушатель нулевой, поэтому ничего не запускается. Тогда ваш слушатель назначен.
Так что следуйте этой точной последовательности:
источник
Ссылаясь на ответ Дэна Дайера, попробуйте зарегистрировать
OnSelectListener
свойpost(Runnable)
метод:Сделав это для меня, наконец-то произошло желаемое поведение.
В этом случае это также означает, что слушатель запускает только измененный предмет.
источник
onCreate()
,onResume()
и т. д. В этом случае это фантастический трюк, без опасности состояния гонки. Я обычно использую этот трюкonCreate()
сразу после кода макета.Я создал небольшую утилиту для изменения
Spinner
выбора без уведомления пользователя:Это отключает слушателя, изменяет выбор и повторно включает слушателя после этого.
Хитрость в том, что вызовы асинхронны с потоком пользовательского интерфейса, поэтому вы должны делать это в последовательных сообщениях обработчиков.
источник
setSpinnerSelectionWithoutCallingListener
дважды быстро позвоните , чтобы сделать второй вызов, когда первый уже установил слушателяnull
, ваш счетчик будет постоянно зависать соnull
слушателем. Я предлагаю следующее исправление: добавитьif (listener == null) return;
послеspinner.setSelection(selection)
.К сожалению, кажется, что два наиболее часто предлагаемых решения этой проблемы, а именно подсчет повторных вызовов и публикация Runnable для установки обратного вызова позднее, могут оба не дать результатов, если, например, включены опции доступности. Вот вспомогательный класс, который работает вокруг этих проблем. Дальнейшее объяснение в блоке комментариев.
источник
У меня было много проблем с вращением вертушки, когда я не хотел, и все ответы здесь ненадежны. Они работают - но только иногда. В конечном итоге вы столкнетесь со сценариями, в которых они потерпят неудачу, и внесете ошибки в ваш код.
Для меня работало хранить последний выбранный индекс в переменной и оценивать его в слушателе. Если это то же самое, что новый выбранный индекс, ничего не делать и возвращать, иначе продолжите со слушателем. Сделай это:
Поверьте мне, когда я говорю это, это, безусловно, самое надежное решение. Взломать, но это работает!
источник
Я был в подобной ситуации, и у меня есть простое решение для меня.
Вроде бы методы
setSelection(int position)
иsetSelected(int position, boolean animate)
имеют разную внутреннюю реализацию.Когда вы используете второй метод
setSelected(int position, boolean animate)
с ложным флагом анимации, вы получаете выбор без запускаonItemSelected
слушателя.источник
setSelection(int position, boolean animate);
onItemSelected
API23Просто чтобы прояснить намеки на использование onTouchListener, чтобы различать автоматические вызовы setOnItemSelectedListener (которые являются частью инициализации Activity и т. Д.) И вызовы к нему, инициированные фактическим взаимодействием с пользователем, я сделал следующее, попробовав некоторые другие предложения здесь и обнаружил, что он хорошо работает с наименьшим количеством строк кода.
Просто установите логическое поле для вашей активности / фрагмента, например:
Затем, перед тем, как установить setOnItemSelectedListener вашего счетчика, установите onTouchListener:
источник
источник
После того, как я долго выдергивал свои волосы, я создал свой собственный класс Spinner. Я добавил метод, который отключает и подключает слушателя соответствующим образом.
Используйте это в своем XML, как это:
Все, что вам нужно сделать, это получить экземпляр SaneSpinner после инфляции и выбрать набор вызовов следующим образом:
При этом событие не запускается и взаимодействие с пользователем не прерывается. Это значительно уменьшило сложность моего кода. Это должно быть включено в стандартный Android, поскольку это действительно PITA.
источник
Никаких нежелательных событий на этапе макета, если вы отложите добавление слушателя до завершения макета:
источник
ViewTreeObserver.OnGlobalLayoutListener
версии on ниже J, вызвавViewTreeObserver.removeGlobalOnLayoutListener
этот метод, который устарел и имеет имя, аналогичное методу, используемому в этом ответе.Это произойдет, если вы делаете выбор в коде как;
Вместо приведенного выше утверждения используйте
Изменить: Этот метод не работает для Mi Android версии Mi UI.
источник
Я получил очень простой ответ, уверен на 100%:
источник
Я нашел гораздо более элегантное решение для этого. Он включает в себя подсчет того, сколько раз был вызван ArrayAdapter (в вашем случае «адаптер»). Допустим, у вас есть 1 счетчик, и вы звоните:
Объявите счетчик int после onCreate, а затем внутри метода onItemSelected () поместите условие «if», чтобы проверить, сколько раз был вызван atapter. В вашем случае это называется только один раз так:
источник
Мой небольшой вклад - это вариация некоторых из вышеперечисленных, которая меня устраивала несколько раз.
Объявите целочисленную переменную как значение по умолчанию (или последнее использованное значение, сохраненное в настройках). Используйте spinner.setSelection (myDefault), чтобы установить это значение до регистрации слушателя. В onItemSelected проверьте, соответствует ли новое значение счетчика значению, которое вы присвоили, прежде чем запускать какой-либо дополнительный код.
Это дает дополнительное преимущество: не выполняется код, если пользователь снова выбирает то же значение.
источник
После того, как у меня возникла та же проблема, я пришел к этому решению, используя теги Идея этого проста: всякий раз, когда прядильщик изменяется программно, убедитесь, что тег отражает выбранную позицию. Затем в слушателе вы проверяете, соответствует ли выбранная позиция метке. Если это так, выбор счетчика был изменен программно.
Ниже мой новый класс "Spinner Proxy":
Вам также понадобится файл XML с настройкой тега в вашем
Values
каталоге. Я назвал свой файлspinner_tag.xml
, но это зависит от вас. Это выглядит так:Теперь замени
в вашем коде с
И сделайте так, чтобы ваш обработчик выглядел примерно так:
Функция
isUiTriggered()
вернет true, если и только если пользователь изменил счетчик. Обратите внимание, что у этой функции есть побочный эффект - она устанавливает тег, поэтому второй вызов в том же вызове слушателя всегда будет возвращатьfalse
.Эта обертка также решит проблему с вызывающим слушателем во время создания макета.
Веселись, Дженс.
источник
Поскольку у меня ничего не получалось, и у меня больше 1 блесны (и IMHO держать карту bool - это перебор), я использую тег для подсчета кликов:
источник
Уже много ответов, вот мой.
Я расширяю
AppCompatSpinner
и добавляю метод,pgmSetSelection(int pos)
который позволяет устанавливать программный выбор, не вызывая обратный вызов выбора. Я кодировал это с помощью RxJava, чтобы события выбора доставлялись черезObservable
.Пример его использования, названное в
onCreateView()
вFragment
, например:где
setSelection()
- это метод во включающем представлении, который выглядит следующим образом и который вызывается как из событий выбора пользователя через, такObservable
и из других мест программно, поэтому логика для обработки выбора является общей для обоих методов выбора.источник
Я бы попробовал позвонить
после вызова setAdapter (). Также попробуйте позвонить перед адаптером.
У вас всегда есть решение использовать подклассы, где вы можете обернуть логический флаг в свой переопределенный метод setAdapter, чтобы пропустить событие.
источник
Решение с булевым флагом или счетчиком мне не помогло, потому что при изменении ориентации onItemSelected () вызывает «перелет» флага или счетчика.
Я подкласс
android.widget.Spinner
и сделал крошечные дополнения. Соответствующие части ниже. Это решение сработало для меня.источник
Это тоже не элегантное решение. На самом деле это скорее Рубе-Гольдберг, но, похоже, работает. Я уверен, что спиннер использовался хотя бы один раз, расширяя адаптер массива и переопределяя его getDropDownView. В новом методе getDropDownView у меня есть логический флаг, который показывает, что выпадающее меню использовалось хотя бы один раз. Я игнорирую вызовы слушателя, пока флаг не установлен.
MainActivity.onCreate ():
переопределить адаптер массива:
модифицированный слушатель:
источник
если вам нужно воссоздать активность на лету, например: смена тем, простой флаг / счетчик не сработает
использовать функцию onUserInteraction () для определения активности пользователя,
ссылка: https://stackoverflow.com/a/25070696/4772917
источник
Я сделал с самым простым способом:
OnCreate ();
Выполнено
источник
источник
Это мое последнее и простое в использовании решение:
Используйте значение
setSelection(...)
по умолчанию для поведения по умолчанию или используйтеsetSelectionWithoutInformListener(...)
для выбора элемента в счетчике без запуска обратного вызова OnItemSelectedListener.источник
Мне нужно использовать
mSpinner
в ViewHolder, поэтому флагmOldPosition
устанавливается в анонимном внутреннем классе.источник
Я бы сохранил начальный индекс при создании объекта onClickListener.
источник
Мое решение использует,
onTouchListener
но не ограничивает его использование. Он создает обертку дляonTouchListener
при необходимости, где установкаonItemSelectedListener
.источник
Возможно, я слишком поздно отвечаю на этот пост, однако мне удалось добиться этого с помощью библиотеки привязки данных Android Android Databinding . Я создал пользовательскую привязку, чтобы убедиться, что слушатель не вызывается до тех пор, пока не будет изменен выбранный элемент, поэтому даже если пользователь выбирает одну и ту же позицию снова и снова, событие не запускается.
Макет XML-файла
app:position
где вы проходите позицию для выбора.Пользовательская привязка
Вы можете узнать больше о привязке пользовательских данных здесь Android Custom Setter
НОТА
Не забудьте включить привязку данных в вашем файле Gradle
Включите файлы макета в
<layout>
тегиисточник
источник