Выбор Android Spinner - PullRequest
       42

Выбор Android Spinner

23 голосов
/ 14 апреля 2010

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

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

Ответы [ 8 ]

49 голосов
/ 16 апреля 2010

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

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

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

20 голосов
/ 07 октября 2011

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

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

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

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

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

2 голосов
/ 28 марта 2013

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

Сценарий моего приложения:

X счетчиков (динамически, 2 на каждый процессор, мин. И макс.) Для установки и просмотра частоты процессора. Они заполняются при запуске приложения и также получают текущую максимальную / минимальную частоту набора процессоров. Поток работает в фоновом режиме и каждую секунду проверяет наличие изменений и соответственно обновляет счетчики. Если пользователь устанавливает новую частоту внутри счетчика, устанавливается новая частота.

Проблема заключалась в том, что поток обращался к 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 имеет значение true или false. True вызовет прослушиватель и используется по умолчанию, поэтому пользовательские взаимодействия будут идентифицированы, false также вызовет прослушиватель, но использует код, который вы хотите для этого особого случая, в качестве примера gratia в моем случае: ничего.

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

2 голосов
/ 14 апреля 2010

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

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

1 голос
/ 25 июня 2012

Просто чтобы расширить пост 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.

1 голос
/ 03 января 2012

Я также искал хорошее решение в интернете, но не нашел ни одного, который бы удовлетворял мои потребности. Поэтому я написал это расширение для класса 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());
            }
        }
    }
0 голосов
/ 16 марта 2014

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

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

0 голосов
/ 21 июня 2012

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

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

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

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

...