Нежелательные вызовы 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;
    }
}