Как отключить onItemSelectedListener, который будет вызываться при установке выбранного элемента по коду - PullRequest
32 голосов
/ 19 февраля 2010

Просто интересно, как вы справляетесь со следующей проблемой: результат вычисляется в зависимости от выбранных элементов двух счетчиков. Для управления пользовательским интерфейсом, то есть пользователь выбирает новый элемент в одном из счетчиков, я устанавливаю прослушиватель, используя setOnItemSelectedListener для счетчика в моем методе действия onCreate().

Теперь: это работает, конечно, хорошо. Работа слушателя состоит в том, чтобы запустить новый расчет результата.

Проблема: поскольку я перехватываю onPause() onResume(), чтобы сохранить / восстановить последнее состояние, я получил метод, который устанавливает выбранный элемент этих двух счетчиков программно, как здесь:

startSpinner.setSelection(pStart);
destSpinner.setSelection(pDest);

Эти два вызова также вызывают слушателей! Мой метод вычисления для результата плюс уведомление о новом наборе результатов вызывается здесь дважды!

Глупый прямой подход для этого состоит в том, чтобы иметь логическую переменную , отключающую независимо от того, что слушатель делает внутри, устанавливая его перед установкой выбранных элементов и сбрасывая впоследствии. Хорошо. Но есть ли лучший метод ??

Я не хочу, чтобы слушатели вызывались кодом - действиями, только действиями пользователя! : - (

Как ты это делаешь? Спасибо!

Ответы [ 11 ]

45 голосов
/ 12 февраля 2015

Более чистое решение, на мой взгляд, для дифференциации программных и инициируемых пользователем изменений заключается в следующем:

Создайте прослушиватель для счетчика как OnTouchListener и OnItemSelectedListener

public class SpinnerInteractionListener implements AdapterView.OnItemSelectedListener, View.OnTouchListener {

    boolean userSelect = false;

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        userSelect = true;
        return false;
    }

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
        if (userSelect) { 
            // Your selection handling code here
            userSelect = false;
        }
    }

}

Добавить слушателя в счетчик, регистрирующийся для обоих типов событий

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

Таким образом, любые неожиданные вызовы вашего метода-обработчика из-за инициализации или повторной инициализации будут игнорироваться.

11 голосов
/ 24 февраля 2010

Ладно, теперь у меня все работает так, как я хочу.

Здесь нужно понять (а я не сделал, когда писал этот вопрос ...), что все в Android работает в одном потоке - потоке пользовательского интерфейса.

Значение: даже если вы устанавливаете значения Spinner здесь и там: они обновляются только (визуально) и , их слушатели вызываются только после всех методов, которые вы используете в данный момент (например, onCreate, onResume или что-либо еще) закончены.

Это позволяет следующее:

  • сохранить выбранные позиции в переменных поля. (как currentPos1, currentPos2)
  • слушатели onItemSelectedListener() вызывают метод, подобный refreshMyResult() или как угодно.
  • при программной установке позиций, установите счетчики и , вызывая собственный метод обновления вручную сразу после этого.

Метод refreshMyResult() выглядит следующим образом:

int newPos1 = mySpinner1.getSelectedItemPosition();
int newPos2 = mySpinner2.getSelectedItemPosition();
// only do something if update is not done yet
if (newPos1 != currentPos1 || newPos2 != currentPos2) {
    currentPos1 = newPos1;
    currentPos2 = newPos2;

    // do whatever has to be done to update things!

}

Поскольку слушатели будут вызваны позже - и к тому времени запомненная позиция в currentPos уже обновится - ничего не произойдет и не будет происходить ненужное обновление чего-либо еще. Когда пользователь выбирает новое значение в одном из счетчиков, хорошо - обновление будет выполнено соответственно!

Вот и все! : -)

Ааа - еще одна вещь: ответ на мой вопрос: нет. Слушатели не могут быть отключены (легко) и будут вызываться при каждом изменении значения.

5 голосов
/ 13 февраля 2014

У меня есть более простое и, я думаю, лучшее решение.Поскольку мне приходилось обновлять счетчики даже после инициализации, это более общий подход.Пожалуйста, ознакомьтесь с принятым ответом:

Нежелательные на выбранные предметы

3 голосов
/ 30 августа 2017

Сначала добавьте логические значения для остановки вызова слушателя счетчика

  Boolean check = false;

Затем вы добавляете Touch Listener и нажимаете Item, слушаете как код ниже

 holder.filters.setOnTouchListener(new View.OnTouchListener() {
               @Override
               public boolean onTouch(View v, MotionEvent event) {

                   check = true;
                   return false;
               }
           });

           holder.filters.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
           {

               @Override
               public void onItemSelected(AdapterView<?> parent, View arg1, int position, long id)
               {
                   flag = filterids.get(position);

                   if(check)
                   {
                       check = false;
                       new Applyfilters().execute(flag,"3");
                   }else{

                   }

               }

               @Override
               public void onNothingSelected(AdapterView<?> arg0)
               {
                   // TODO Auto-generated method stub
               }
           });

Простая работа, позволяющая останавливать серверные вызовы несколько раз.

2 голосов
/ 11 октября 2015

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

spinner.setSelection(withAction,position);

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

Ссылка на Github: https://github.com/scijoker/spinner2

2 голосов
/ 05 сентября 2013

Spinner.setSelection (int position, boolean animate) действительно вызывает слушателя на 4.3

2 голосов
/ 12 июля 2013

Очень просто вызвать метод Spinner.setSelection(int position, boolean animate) с помощью false, чтобы слушатели не реагировали на изменение.

1 голос
/ 21 сентября 2018

Мое решение очень простое. Сначала инициализируйте глобальную логическую переменную.

boolean shouldWork = true;

Затем используйте приведенный ниже код в методе onCreate ().

Spinner spinner = findViewById(R.id.spinner);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView adapter, View v, int i, long lng) {
        if (shouldWork) {
               // Do your actions here
        }
        else
            shouldWork = true;
    }
    public void onNothingSelected(AdapterView<?> parentView)  {

    }
});

Теперь вы можете использовать метод setSelection везде, не вызывая метод onItemSelected () с помощью кода ниже.

shouldWork = false;
spinner.setSelection(0);
1 голос
/ 22 октября 2014

Когда используется Spinner.setSelection (position), он всегда активирует setOnItemSelectedListener ()

Чтобы избежать двойного запуска кода, я использую это решение:

private 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) {
    }
});

Это решение действительно, если вы уверены, что Spinner.setSelection (position) нами используется. Также важно каждый раз устанавливать mIsSpinnerFirstCall = true перед использованием Spinner.setSelection (position)

1 голос
/ 19 февраля 2010

Добавьте OnItemSelectedListener для каждого счетчика после , которое вы установили для любого предыдущего значения в onResume.

...