Подтвердить что ты не робот

Выбор Android Spinner

Обработчик события OnItemSelectedListener вызывается как при использовании счетчика выбор изменяется программно, а когда пользователь физически щелкает элемент управления прядильщика. Можно ли определить, было ли событие инициировано пользователем выбор как-то?

Или есть ли другой способ обработки пользовательских настроек spinner?

4b9b3361

Ответ 1

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

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

Да, Spinner в Android больно. Я бы даже сказал, что боль начинается с ее имени - "Spinner". Разве это не вводит в заблуждение?:) Насколько мы говорим об этом, вы также должны знать об ошибке: Spinner не может восстановить (не всегда) его состояние (при вращении устройства), поэтому убедитесь, что вы вручную обрабатываете состояние Spinner.

Ответ 2

Трудно поверить, что через полтора года проблема все еще существует и продолжает пугать людей...

Думал, что поделился бы обходным решением, которое я придумал после прочтения наиболее полезного сообщения Arhimed (спасибо, и я согласен с тем, что прядильщики мучительны!). То, что я делал, чтобы избежать этих ложных срабатываний, - использовать простой класс-оболочку:

import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;

public class OnItemSelectedListenerWrapper implements OnItemSelectedListener {

    private int lastPosition;
    private OnItemSelectedListener listener;

    public OnItemSelectedListenerWrapper(OnItemSelectedListener aListener) {
        lastPosition = 0;
        listener = aListener;
    }

    @Override
    public void onItemSelected(AdapterView<?> aParentView, View aView, int aPosition, long anId) {
        if (lastPosition == aPosition) {
            Log.d(getClass().getName(), "Ignoring onItemSelected for same position: " + aPosition);
        } else {
            Log.d(getClass().getName(), "Passing on onItemSelected for different position: " + aPosition);
            listener.onItemSelected(aParentView, aView, aPosition, anId);
        }
        lastPosition = aPosition;
    }

    @Override
    public void onNothingSelected(AdapterView<?> aParentView) {
        listener.onNothingSelected(aParentView);
    }
}

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

mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
    ...
});

у вас будет следующее:

mySpinner.setOnItemSelectedListener(new OnItemSelectedListenerWrapper(new OnItemSelectedListener() {
    ...
}));

Очевидно, что после того, как вы протестировали его, вы можете избавиться от вызовов журнала, и вы могли бы добавить возможность reset последней позиции, если это необходимо (вам нужно было бы сохранить ссылку на экземпляр, конечно, а не объявлять "на лету", как сказал Армимед.

Надеюсь, что это может помочь кому-то с ума сходить от этого странного поведения; -)

Ответ 3

В прошлом я делал такие вещи, чтобы отличить

internal++; // 'internal' is an integer field initialized to 0
textBox.setValue("...."); // listener should not act on this internal setting
internal--;

Затем в прослушивателе textBox

if (internal == 0) {
  // ... Act on user change action
}

Я использую ++ и - вместо того, чтобы устанавливать логическое значение в значение "true", поэтому нет никаких проблем, когда методы встраивают другие методы, которые также могут устанавливать индикатор внутреннего изменения.

Ответ 4

У меня была эта ситуация в последнее время при использовании прядильщиков, и в Интернете не было подходящего решения.

Мой сценарий приложения:

X spinners (динамически, 2 для каждого процессора, мин и макс) для настройки и просмотра CPU-Frequency. Они заполняются, когда приложение запускается, и они также получают текущую частоту max/min для процессора. Нить работает в фоновом режиме и проверяет изменения каждую секунду и соответственно обновляет прядильщики. Если новая частота внутри счетчика установлена ​​пользователем, устанавливается новая частота.

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

Я придумал решение, которое идеально подходит для моих потребностей и работает вокруг слушателя на вашем телефоне:) (и я думаю, что это решение дает максимальный контроль)

Я расширил Spinner:

import android.content.Context;
import android.widget.Spinner;

public class MySpinner extends Spinner {
    private boolean call_listener = true;

    public MySpinner(Context context) {
        super(context);
    }

    public boolean getCallListener() {
        return call_listener;
    }

    public void setCallListener(boolean b) {
        call_listener = b;
    }

    @Override
    public void setSelection(int position, boolean lswitch) {
        super.setSelection(position);
        call_listener = lswitch;
    }
}

и создал собственный OnItemSelectedListener:

import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;

public class SpinnerOnItemSelectedListener implements OnItemSelectedListener {
      public void onItemSelected(AdapterView<?> parent, View view, int pos,long id) {
          MySpinner spin = (MySpinner) parent.findViewById(parent.getId());
          if (!spin.getCallListener()) {
              Log.w("yourapptaghere", "Machine call!");
              spin.setCallListener(true);
          } else {
              Log.w("yourapptaghere", "UserCall!");
          }
      }

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

Если вы сейчас создаете MySpinner, вы можете использовать его для выбора:

setSelection(position, callListener);

Если callListener является либо истинным, либо ложным. Истина вызовет слушателя и будет по умолчанию, поэтому выясняется, что пользовательские взаимодействия идентифицируются, false также вызывает слушателя, но использует код, который вы хотите для этого особого случая, пример в gratia в моем случае: Nothing.

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

Ответ 5

Я также искал хорошее решение в Интернете, но не нашел того, что удовлетворяло мои потребности. Поэтому я написал это расширение в классе Spinner, чтобы вы могли установить простой OnItemClickListener, который имеет то же поведение, что и ListView.

Только когда элемент получает "выбранный", вызывается onItemClickListener.

Получайте удовольствие от этого!

 public class MySpinner extends Spinner
    {
        private OnItemClickListener onItemClickListener;


        public MySpinner(Context context)
        {
            super(context);
        }

        public MySpinner(Context context, AttributeSet attrs)
        {
            super(context, attrs);
        }

        public MySpinner(Context context, AttributeSet attrs, int defStyle)
        {
            super(context, attrs, defStyle);
        }

        @Override
        public void setOnItemClickListener(android.widget.AdapterView.OnItemClickListener inOnItemClickListener)
        {
            this.onItemClickListener = inOnItemClickListener;
        }

        @Override
        public void onClick(DialogInterface dialog, int which)
        {
            super.onClick(dialog, which);

            if (this.onItemClickListener != null)
            {
                this.onItemClickListener.onItemClick(this, this.getSelectedView(), which, this.getSelectedItemId());
            }
        }
    }

Ответ 6

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

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

@Override
public void setOnItemSelectedListener(final OnItemSelectedListener listener)
{
    if (listener != null)
        super.setOnItemSelectedListener(new OnItemSelectedListener()
        {
            private static final int NO_POSITION  = -1;

            private int lastPosition = NO_POSITION;


            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
            {
                if ((lastPosition != NO_POSITION) && (lastPosition != position))
                    listener.onItemSelected(parent, view, position, id);

                lastPosition = position;
            }


            @Override
            public void onNothingSelected(AdapterView<?> parent)
            {
                listener.onNothingSelected(parent);
            }
        });
    else
        super.setOnItemSelectedListener(null);
}

В основном, этот код будет игнорировать самый первый запуск onItemSelected(), а затем все последующие вызовы "одинаковой позиции".

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

Ответ 7

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

Я зарегистрировался при вызове метода onItemSelected, иначе он был вызван только один раз.

У меня возникла проблема, когда он создавал два из них и понял, что это потому, что я вызывал add() на свой пользовательский адаптер, который уже имел ссылку на список, который я ссылался, и добавил его вне адаптера. После того, как я понял это и удалил метод добавления, проблема исчезла.

Вы уверены, что вам нужен весь этот код?

Ответ 8

Я знаю, что это довольно поздно, но я придумал очень простое решение. Он основан на ответе Аргимеда, он точно такой же. Это очень легко реализовать. Обратите внимание на принятый ответ:

Нежелательные onItemВыбранные вызовы