Spinner: Как узнать, был ли выбор элемента изменен программно или действием пользователя через пользовательский интерфейс - PullRequest
11 голосов
/ 14 мая 2010

У меня есть код, который запускает OnItemSelectedListener событие спиннера. Поэтому, когда я в методе:

public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
    // I want to do something here if it's a user who changed the the selected item
}

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

Ответы [ 6 ]

10 голосов
/ 14 мая 2010

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

private Boolean isUserAction = false;

...

public void onItemSelected( ... ) {

    if( isUserAction ) {
       // code for user initiated selection
    } else {
       // code for programmatic selection
       // also triggers on init (hence the default false)
    }

    // reset variable, so that it will always be true unless tampered with
    isUserAction = true;
}

public void myButtonClick( ... ) {
    isUserAction = false;
    mySpinner.setSelectedItem ( ... );
}
5 голосов
/ 23 января 2017

Вы можете добиться желаемого результата довольно просто, используя Spinner setOnTouchListener() метод:

// Instance variables
boolean spinnerTouched = false;
Spinner spinner;

// onCreate() / onCreateView() / etc. method..
spinner = ...;
spinner.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            spinnerTouched = true; // User DID touched the spinner!
        }

        return false;
    }
});
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {

        if (spinnerTouched) {
            // Do something
        }
        else {
            // Do something else
        }

    }

    @Override
    public void onNothingSelected(AdapterView<?> parentView) {
    }
});

// Your method that you use the change the spinner selection programmatically...
private void changeSpinnerSelectionProgrammatically(int pos) {
    stateSpinnerTouched = false; // User DIDN'T touch the spinner
    boolean useAnimation = false;
    spinner.setSelection(pos, useAnimation); // Calls onItemSelected()
}
5 голосов
/ 06 января 2015

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

То же самое в гисте

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;

/**
 * Used this to differentiate between user selected and prorammatically selected
 * Call {@link Spinner#programmaticallySetPosition} to use this feature.
 * Created by vedant on 6/1/15.
 */
public class Spinner extends android.widget.Spinner implements AdapterView.OnItemSelectedListener {

    OnItemSelectedListener mListener;

    /**
     * used to ascertain whether the user selected an item on spinner (and not programmatically)
     */
    private boolean mUserActionOnSpinner = true;

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

        if (mListener != null) {

            mListener.onItemSelected(parent, view, position, id, mUserActionOnSpinner);
        }
        // reset variable, so that it will always be true unless tampered with
        mUserActionOnSpinner = true;
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        if (mListener != null)
            mListener.onNothingSelected(parent);
    }

    public interface OnItemSelectedListener {
        /**
         * <p>Callback method to be invoked when an item in this view has been
         * selected. This callback is invoked only when the newly selected
         * position is different from the previously selected position or if
         * there was no selected item.</p>
         *
         * Impelmenters can call getItemAtPosition(position) if they need to access the
         * data associated with the selected item.
         *
         * @param parent The AdapterView where the selection happened
         * @param view The view within the AdapterView that was clicked
         * @param position The position of the view in the adapter
         * @param id The row id of the item that is selected
         */
        void onItemSelected(AdapterView<?> parent, View view, int position, long id, boolean userSelected);

        /**
         * Callback method to be invoked when the selection disappears from this
         * view. The selection can disappear for instance when touch is activated
         * or when the adapter becomes empty.
         *
         * @param parent The AdapterView that now contains no selected item.
         */
        void onNothingSelected(AdapterView<?> parent);
    }

    public void programmaticallySetPosition(int pos, boolean animate) {
        mUserActionOnSpinner = false;
        setSelection(pos, animate);
    }

    public void setOnItemSelectedListener (OnItemSelectedListener listener) {
        mListener = listener;
    }

    public Spinner(Context context) {
        super(context);
        super.setOnItemSelectedListener(this);
    }

    public Spinner(Context context, int mode) {
        super(context, mode);
        super.setOnItemSelectedListener(this);
    }

    public Spinner(Context context, AttributeSet attrs) {
        super(context, attrs);
        super.setOnItemSelectedListener(this);
    }

    public Spinner(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        super.setOnItemSelectedListener(this);
    }

    public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
        super(context, attrs, defStyle, mode);
        super.setOnItemSelectedListener(this);
    }
}
1 голос
/ 14 августа 2014

В отличие от SeekBar a Spinner не имеет встроенной поддержки для определения того, было ли изменение программным или пользовательским, поэтому я предлагаю никогда не использовать счетчик для каких-либо рекурсивных программных задач. У меня был очень плохой опыт, когда я пытался реализовать MediaPlayer с рекурсивным подключением к SeekBar и Spinner. Результат был полон разочарований. Таким образом, вы можете попытаться, только если вы любите несчастье и разочарование.

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

Мне очень жаль, если вышеприведенные утверждения неверны. Я поделился этим, потому что люблю кодеры и кодирование.

1 голос
/ 16 марта 2014

Я нашел обходной путь, который прост и универсален. Примите принятый ответ на этот вопрос:

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

Итак, если position не равно spin.getTag(R.id.pos), вы знаете, что обратный вызов был вызван тем, что пользователь вносил изменение, потому что всякий раз, когда вы сами вносите изменение, вы устанавливаете тег как spin.setTag(R.id.pos, pos) pos это значение, которое вы установили. Если вы используете этот подход, обязательно установите тег в onItemSelected после завершения работы!

0 голосов
/ 08 марта 2014

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

Я использовал обходной путь, основанный на сценарии фокусировки в сенсорном режиме.

  1. Установите вид счетчика как фокусируемый в сенсорном режиме.

  2. Установить слушателя изменения фокуса счетчика, чтобы вызывать spinner.performClick (), когда фокус.

  3. в слушателе onItemSelected счетчика, верните фокус в родительское представление макета (или в любое удобное для вас представление)

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

PS: когда вы устанавливаете focusableintouchmode для счетчика в onCreate, убедитесь, что вы сразу же вернули фокус в родительский вид, если у вас нет других фокусируемых видов.

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