Нежелательные вызовы onItemSelected
У меня есть 36 прядильщиков, которые я инициализировал некоторыми значениями. Я использовал onItemSelectedListener с ними. Как обычно, пользователь может взаимодействовать с этими прядильщиками, активируя функцию onItemSeected.
Одна из проблем заключается в том, что вызов выполняется во время init, но я нашел для него решения здесь и избегал использования глобальной переменной "count" и проверки, если count > 36, прежде чем выполнять код внутри onItemSelected.
Моя проблема заключается в следующем:
У пользователя есть возможность щелкнуть по кнопке "Предыдущая", после которой я должен reset НЕИСПРАВИТЬ значения счетчика.
Я попробовал изменить значение count на 0 перед сбросом прядильщиков, а затем сменил его на 37 после сброса, но я понял, что onItemSelected вызывается только после выполнения любой другой функции, поэтому вызванное AFTER count возвращается к 37, даже если значения счетчика установлены, как только они будут выбраны пользователем.
Мне нужно многократно обновлять некоторые прядильщики БЕЗ стрельбы из функции onItemSelected. Может ли кто-нибудь помочь мне найти решение? Спасибо.
Ответы
Ответ 1
Я нашел простое и, я думаю, элегантное решение.
Использование тегов.
Сначала я создал новый XML файл под названием "теги" и ввел следующий код:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item name="pos" type="id" />
</resources>
Когда я сам использую spin.setSelection(pos)
, я также делаю spin.setTag(R.id.pos, pos)
, поэтому я устанавливаю текущую позицию как тег.
Затем в onItemSelected я выполняю только код if(spin.getTag(R.id.pos) != position)
, где position - это переменная положения, предоставляемая функцией.
Таким образом, мой код выполняется только тогда, когда пользователь делает выбор.
Поскольку пользователь сделал выбор, тег не был обновлен, поэтому после завершения обработки я обновляю тег как spin.setTag(R.id.pos, position)
.
ПРИМЕЧАНИЕ. Важно использовать один и тот же адаптер повсюду, или переменная "position" может указывать на разные элементы.
EDIT:. Как отметила kaciula, если вы не используете несколько тегов, вы можете использовать более простую версию, то есть spin.setTag(pos)
и spin.getTag()
БЕЗ необходимости в XML файле.
Ответ 2
Когда используется Spinner.setSelection(position), он всегда активирует setOnItemSelectedListener()
Чтобы не запускать код дважды, я использую это решение:
private Boolean mIsSpinnerFirstCall = true;
...
Spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
//If a new value is selected (avoid activating on setSelection())
if(!mIsSpinnerFirstCall) {
// Your code goes gere
}
mIsSpinnerFirstCall = false;
}
public void onNothingSelected(AdapterView<?> arg0) {
}
});
Ответ 3
Как я решил, это сначала сохранить OnItemSelectedListener. Затем установите для параметра OnItemSelectedListener Spinner значение null. После установки элемента в Spinner по коду снова восстановите OnItemSelectedListener. Это сработало для меня.
Смотрите код ниже:
// disable the onItemClickListener before changing the selection by code. Set it back again afterwards
AdapterView.OnItemSelectedListener onItemSelectedListener = historyPeriodSpinner.getOnItemSelectedListener();
historyPeriodSpinner.setOnItemSelectedListener(null);
historyPeriodSpinner.setSelection(0);
historyPeriodSpinner.setOnItemSelectedListener(onItemSelectedListener);
Ответ 4
Вот мое решение этой проблемы. Я расширяю AppCompatSpinner
и добавляю метод pgmSetSelection(int pos)
, который позволяет устанавливать программный выбор без запуска обратного вызова выбора. Я закодировал это с помощью RxJava, чтобы события выбора доставлялись через Observable
.
package com.controlj.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import io.reactivex.Observable;
/**
* Created by clyde on 22/11/17.
*/
public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
private int lastSelection = INVALID_POSITION;
public void pgmSetSelection(int i) {
lastSelection = i;
setSelection(i);
}
/**
* Observe item selections within this spinner. Events will not be delivered if they were triggered
* by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
*
* @return an Observable delivering selection events
*/
public Observable<Integer> observeSelections() {
return Observable.create(emitter -> {
setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(i != lastSelection) {
lastSelection = i;
emitter.onNext(i);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
onItemSelected(adapterView, null, INVALID_POSITION, 0);
}
});
});
}
public FilteredSpinner(Context context) {
super(context);
}
public FilteredSpinner(Context context, int mode) {
super(context, mode);
}
public FilteredSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
}
Пример его использования, называемый onCreateView()
в Fragment
, например:
mySpinner = view.findViewById(R.id.history);
mySpinner.observeSelections()
.subscribe(this::setSelection);
где setSelection()
- это метод в представлении, который выглядит так, и который вызван как из событий выбора пользователя через Observable
, так и в другом месте программно, поэтому логика обработки выборки является общей для обоих методов выбора.
private void setSelection(int position) {
if(adapter.isEmpty())
position = INVALID_POSITION;
else if(position >= adapter.getCount())
position = adapter.getCount() - 1;
MyData result = null;
mySpinner.pgmSetSelection(position);
if(position != INVALID_POSITION) {
result = adapter.getItem(position);
}
display(result); // show the selected item somewhere
}
Ответ 5
Я не знаю, является ли это решение таким же надежным, как выбранное здесь, но оно работает хорошо для меня и кажется еще проще:
boolean executeOnItemSelected = false;
spinner.setSelection(pos)
И затем в OnItemSelectedListener
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if(executeOnItemSelected){
//Perform desired action
} else {
executeOnItemSelected = true;
}
}