Как отключить onItemSelectedListener для вызова при настройке выбранного элемента по коду

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

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

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

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

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

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

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

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

11 ответов


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

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


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

создайте своего слушателя для spinner как 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);

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


ладно, я получил это работает так, как я хочу сейчас.

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

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

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

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

The 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 уже обновлена-ничего не произойдет и не произойдет ненужного обновления чего-либо еще. Когда пользователь выбирает новое значение в одном из блесен, ну-обновление будет выполнено соответствующим образом!

вот именно! :-)

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


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


блесны.setSelection (int position, boolean animate) запускает прослушиватель на 4.3


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

spinner.setSelection(withAction,position);

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

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


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

  Boolean check = false;

затем вы добавляете на сенсорный прослушиватель и на пункт нажмите прослушиватель, как показано ниже code

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

его простая работа хороша для остановки вызова сервера несколько раз.


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


Когда Блесны.используется setSelection (позиция), он всегда активирует 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 (позиция) us используется. Кроме того, важно установить mIsSpinnerFirstCall=true каждый раз перед использованием Spinner.setSelection (позиция)


    This following method will help you to stop invoking automatically the selection listener


    yourspinnerobj.post(new Runnable() {
                @Override
                public void run() {
                    yourspinnerobj.setOnItemSelectedListener(yourspinnerlistener);
                }
            });

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

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 в everwhere без вызова метода onItemSelected () с помощью кода ниже.

shouldWork = false;
spinner.setSelection(0);