Как сохранить onItemSelected от стрельбы по вновь созданной Spinner?

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

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

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

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

public class CMSHome extends Activity { 

private Spinner spinner;

@Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // Heres my spinner ///////////////////////////////////////////
    spinner = (Spinner) findViewById(R.id.spinner);
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);
    };

public void onResume() {
    super.onResume();
    spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}

    public class MyOnItemSelectedListener implements OnItemSelectedListener {

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

     Intent i = new Intent(CMSHome.this, ListProjects.class);
     i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
        startActivity(i);

        Toast.makeText(parent.getContext(), "The pm is " +
          parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
    }

    public void onNothingSelected(AdapterView parent) {
      // Do nothing.
    }
}
}

Ответ 1

Я бы ожидал, что ваше решение будет работать - хотя событие выбора не срабатывало, если вы настроили адаптер перед настройкой слушателя.

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

Ответ 2

Использование Runnables полностью неверно.

Используйте setSelection(position, false); в первоначальном выборе до setOnItemSelectedListener(listener)

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

Итак, следуйте этой точной последовательности:

Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);

Ответ 3

Ссылаясь на ответ Дэна Дайера, попробуйте зарегистрировать метод OnSelectListener в post(Runnable):

spinner.post(new Runnable() {
    public void run() {
        spinner.setOnItemSelectedListener(listener);
    }
});

Сделав это для меня, наконец, произошло желаемое поведение.

В этом случае это также означает, что прослушиватель запускается только на измененном элементе.

Ответ 4

Я создал небольшой метод утилиты для изменения выбора Spinner без уведомления пользователя:

private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
    final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
    spinner.setOnItemSelectedListener(null);
    spinner.post(new Runnable() {
        @Override
        public void run() {
            spinner.setSelection(selection);
            spinner.post(new Runnable() {
                @Override
                public void run() {
                    spinner.setOnItemSelectedListener(l);
                }
            });
        }
    });
}

Отключает прослушиватель, изменяет выбор и снова включает слушателя.

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

Ответ 5

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

import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;

/**
 * Spinner Helper class that works around some common issues 
 * with the stock Android Spinner
 * 
 * A Spinner will normally call it OnItemSelectedListener
 * when you use setSelection(...) in your initialization code. 
 * This is usually unwanted behavior, and a common work-around 
 * is to use spinner.post(...) with a Runnable to assign the 
 * OnItemSelectedListener after layout.
 * 
 * If you do not call setSelection(...) manually, the callback
 * may be called with the first item in the adapter you have 
 * set. The common work-around for that is to count callbacks.
 * 
 * While these workarounds usually *seem* to work, the callback
 * may still be called repeatedly for other reasons while the 
 * selection hasn't actually changed. This will happen for 
 * example, if the user has accessibility options enabled - 
 * which is more common than you might think as several apps 
 * use this for different purposes, like detecting which 
 * notifications are active.
 * 
 * Ideally, your OnItemSelectedListener callback should be
 * coded defensively so that no problem would occur even
 * if the callback was called repeatedly with the same values
 * without any user interaction, so no workarounds are needed.
 * 
 * This class does that for you. It keeps track of the values
 * you have set with the setSelection(...) methods, and 
 * proxies the OnItemSelectedListener callback so your callback
 * only gets called if the selected item position differs 
 * from the one you have set by code, or the first item if you
 * did not set it.
 * 
 * This also means that if the user actually clicks the item
 * that was previously selected by code (or the first item
 * if you didn't set a selection by code), the callback will 
 * not fire.
 * 
 * To implement, replace current occurrences of:
 * 
 *     Spinner spinner = 
 *         (Spinner)findViewById(R.id.xxx);
 *     
 * with:
 * 
 *     SpinnerHelper spinner = 
 *         new SpinnerHelper(findViewById(R.id.xxx))
 *         
 * SpinnerHelper proxies the (my) most used calls to Spinner
 * but not all of them. Should a method not be available, use: 
 * 
 *      spinner.getSpinner().someMethod(...)
 *
 * Or just add the proxy method yourself :)
 * 
 * (Quickly) Tested on devices from 2.3.6 through 4.2.2
 * 
 * @author Jorrit "Chainfire" Jongma
 * @license WTFPL (do whatever you want with this, nobody cares)
 */
public class SpinnerHelper implements OnItemSelectedListener {
    private final Spinner spinner;

    private int lastPosition = -1;
    private OnItemSelectedListener proxiedItemSelectedListener = null;  

    public SpinnerHelper(Object spinner) {
         this.spinner = (spinner != null) ? (Spinner)spinner : null;        
    }

    public Spinner getSpinner() {
        return spinner;
    }

    public void setSelection(int position) { 
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position);     
    }

    public void setSelection(int position, boolean animate) {
        lastPosition = Math.max(-1, position);
        spinner.setSelection(position, animate);        
    }

    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        proxiedItemSelectedListener = listener;
        spinner.setOnItemSelectedListener(listener == null ? null : this);
    }   

    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (position != lastPosition) {
            lastPosition = position;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onItemSelected(
                        parent, view, position, id
                );
            }
        }
    }

    public void onNothingSelected(AdapterView<?> parent) {
        if (-1 != lastPosition) {
            lastPosition = -1;
            if (proxiedItemSelectedListener != null) {
                proxiedItemSelectedListener.onNothingSelected(
                        parent
                );
            }
        }
    }

    public void setAdapter(SpinnerAdapter adapter) {
        if (adapter.getCount() > 0) {
            lastPosition = 0;
        }
        spinner.setAdapter(adapter);
    }

    public SpinnerAdapter getAdapter() { return spinner.getAdapter(); } 
    public int getCount() { return spinner.getCount(); }    
    public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }   
    public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
    public Object getSelectedItem() { return spinner.getSelectedItem(); }
    public long getSelectedItemId() { return spinner.getSelectedItemId(); }
    public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
    public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
    public boolean isEnabled() { return spinner.isEnabled(); }
}

Ответ 6

У меня было много проблем с запуском spinner, когда я этого не хотел, и все ответы здесь ненадежны. Они работают, но только иногда. Вы в конечном итоге столкнетесь с сценариями, в которых они потерпят неудачу, и вносят ошибки в ваш код.

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

//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;

//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {

  if(mLastSpinnerPosition == i){
        return; //do nothing
  }

  mLastSpinnerPosition = i;
  //do the rest of your code now

}

Поверьте мне, когда я скажу это, это, безусловно, самое надежное решение. Взлом, но он работает!

Ответ 7

Я был в похожей ситуации, и у меня есть простое решение, работающее для меня.

Похоже, что методы setSelection(int position) и setSelected(int position, boolean animate) имеют разную внутреннюю реализацию.

Когда вы используете второй метод setSelected(int position, boolean animate) с флагом флага animate, вы получаете выделение без запуска onItemSelected прослушивателя.

Ответ 8

Просто для определения подсказок при использовании onTouchListener для различения автоматических вызовов с помощью setOnItemSelectedListener (которые являются частью инициализации Activity и т.д.) против вызовов, вызванных фактическим взаимодействием с пользователем, я сделал следующее, попробовав другое предложения здесь и обнаружили, что он хорошо работает с наименьшими строками кода.

Просто установите логическое поле для вашего Activity/Fragment, например:

private Boolean spinnerTouched = false;

Затем перед установкой spinner setOnItemSelectedListener установите onTouchListener:

    spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            spinnerTouched = true;
            return false;
        }
    });

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    ...
         if (spinnerTouched){
         //Do the stuff you only want triggered by real user interaction.
        }
        spinnerTouched = false;

Ответ 9

spinner.setSelection(Adapter.NO_SELECTION, false);

Ответ 10

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

public class SaneSpinner extends Spinner {
    public SaneSpinner(Context context) {
        super(context);
    }

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

    public SaneSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    // set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
    public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
        OnItemSelectedListener l = getOnItemSelectedListener();
        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(null);
        }

        super.setSelection(position, animate);

        if (ceaseFireOnItemClickEvent) {
            setOnItemSelectedListener(l);
        }
    }
}

Используйте его в своем XML следующим образом:

<my.package.name.SaneSpinner
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/mySaneSpinner"
    android:entries="@array/supportedCurrenciesFullName"
    android:layout_weight="2" />

Все, что вам нужно сделать, - это получить экземпляр SaneSpinner после выбора уровня инфляции и набора вызовов следующим образом:

mMySaneSpinner.setSelection(1, true, true);

При этом ни одно событие не запускается, и взаимодействие пользователя не прерывается. Это значительно сократило мою сложность кода. Это должно быть включено в акции Android, так как это действительно PITA.

Ответ 11

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

spinner.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Ensure you call it only once works for JELLY_BEAN and later
            spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            // add the listener
            spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                    // check if pos has changed
                    // then do your work
                }

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

            });

        }
    });

Ответ 12

Я получил очень простой ответ, 100% уверен, что он работает:

boolean Touched=false; // this a a global variable

public void changetouchvalue()
{
   Touched=true;
}

// this code is written just before onItemSelectedListener

 spinner.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            System.out.println("Real touch felt.");
            changetouchvalue();
            return false;
        }
    });

//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code

if(Touched)
{
 // the code u want to do in touch event
}

Ответ 13

Я нашел гораздо более элегантное решение. Это связано с подсчетом того, сколько раз вызывается ArrayAdapter (в вашем случае "адаптер" ). Скажем, у вас есть 1 прядильщик, и вы звоните:

int iCountAdapterCalls = 0;

ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
            this, R.array.pm_list, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    spinner.setAdapter(adapter);

Объявите счетчик int после метода onCreate, а затем внутри метода onItemSelected() установите условие "если", чтобы проверить, сколько раз был вызван адаптер. В вашем случае вы вызываете его только один раз:

if(iCountAdapterCalls < 1)
{
  iCountAdapterCalls++;
  //This section executes in onCreate, during the initialization
}
else
{
  //This section corresponds to user clicks, after the initialization
}

Ответ 14

Мой небольшой вклад - это вариация на некоторые из вышеперечисленных, которые мне несколько раз подходили.

Объявить целочисленную переменную как значение по умолчанию (или последнее использованное значение, сохраненное в настройках). Используйте spinner.setSelection(myDefault), чтобы установить это значение до регистрации слушателя. В onItemSelected проверьте, соответствует ли новое значение spinner значению, которое вы назначили, перед запуском какого-либо дополнительного кода.

У этого есть дополнительное преимущество не запускать код, если пользователь снова выбирает одно и то же значение.

Ответ 15

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

Ниже приведен мой новый класс "spinner proxy":

package com.samplepackage;

import com.samplepackage.R;
import android.widget.Spinner;

public class SpinnerFixed {

    private Spinner mSpinner;

    public SpinnerFixed(View spinner) {
         mSpinner = (Spinner)spinner;
         mSpinner.setTag(R.id.spinner_pos, -2);
    }

    public boolean isUiTriggered() {
         int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
         int pos = mSpinner.getSelectedItemPosition();
         mSpinner.setTag(R.id.spinner_pos, pos);
         return (tag != -2 && tag != pos);
    }

    public void setSelection(int position) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position);
    }

    public void setSelection(int position, boolean animate) {
        mSpinner.setTag(R.id.spinner_pos, position);
        mSpinner.setSelection(position, animate);
    }

    // If you need to proxy more methods, use "Generate Delegate Methods"
    // from the context menu in Eclipse.
}

Вам также понадобится XML файл с настройкой тега в вашем каталоге Values. Я назвал свой файл spinner_tag.xml, но это до вас. Это выглядит так:

<resources xmlns:android="http://schemas.android.com/apk/res/android">
  <item name="spinner_pos" type="id" />
</resources>

Теперь замените

Spinner myspinner;
...
myspinner = (Spinner)findViewById(R.id.myspinner);

в вашем коде с помощью

SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));

И сделайте ваш обработчик несколько похожим на это:

myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (myspinner.isUiTriggered()) {
            // Code you want to execute only on UI selects of the spinner
        }
    }

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

Функция isUiTriggered() вернет значение true, если и только если счетчик был изменен пользователем. Обратите внимание, что эта функция имеет побочный эффект - она ​​установит тег, поэтому второй вызов в том же вызове прослушивателя всегда будет возвращать false.

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

Удачи, Jens.

Ответ 16

Так как для меня ничего не работало, и у меня на моем счету более 1 счетчик (и IMHO, содержащий карту bool, является излишним), я использую тег для подсчета кликов:

spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            Integer selections = (Integer) parent.getTag();
            if (selections > 0) {
                // real selection
            }
            parent.setTag(++selections); // (or even just '1')
        }

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

Ответ 17

Это произойдет, если вы делаете выбор в коде как:

   mSpinner.setSelection(0);

Вместо указанного выше выражения используйте

   mSpinner.setSelection(0,false);//just simply do not animate it.

Изменить: этот метод не работает для Mi Android Version Mi UI.

Ответ 18

Множество ответов уже, здесь мое.

Я расширяю AppCompatSpinner и добавляю метод pgmSetSelection(int pos), который позволяет установить программный выбор без запуска обратного вызова выбора. Я закодировал это с помощью RxJava, чтобы события выбора доставлялись через Observable.

package com.controlj.view;

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

import io.reactivex.Observable;

/**
 * Created by clyde on 22/11/17.
 */

public class FilteredSpinner extends android.support.v7.widget.AppCompatSpinner {
    private int lastSelection = INVALID_POSITION;


    public void pgmSetSelection(int i) {
        lastSelection = i;
        setSelection(i);
    }

    /**
     * Observe item selections within this spinner. Events will not be delivered if they were triggered
     * by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
     *
     * @return an Observable delivering selection events
     */
    public Observable<Integer> observeSelections() {
        return Observable.create(emitter -> {
            setOnItemSelectedListener(new OnItemSelectedListener() {
                @Override
                public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                    if(i != lastSelection) {
                        lastSelection = i;
                        emitter.onNext(i);
                    }
                }

                @Override
                public void onNothingSelected(AdapterView<?> adapterView) {
                    onItemSelected(adapterView, null, INVALID_POSITION, 0);
                }
            });
        });
    }

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

    public FilteredSpinner(Context context, int mode) {
        super(context, mode);
    }

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

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
        super(context, attrs, defStyleAttr, mode);
    }
}

Пример его использования, называемый onCreateView() в Fragment, например:

    mySpinner = view.findViewById(R.id.history);
    mySpinner.observeSelections()
        .subscribe(this::setSelection);

где setSelection() - это метод в представлении, который выглядит так, и который вызван как из событий выбора пользователя через Observable, так и в другом месте программно, поэтому логика обработки выборки является общей для обоих методов выбора.

private void setSelection(int position) {
    if(adapter.isEmpty())
        position = INVALID_POSITION;
    else if(position >= adapter.getCount())
        position = adapter.getCount() - 1;
    MyData result = null;
    mySpinner.pgmSetSelection(position);
    if(position != INVALID_POSITION) {
        result = adapter.getItem(position);
    }
    display(result);  // show the selected item somewhere
}

Ответ 19

Я бы попробовал позвонить

spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());

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

У вас всегда есть решение пойти с подклассом, где вы можете привязать булевский флаг к вашему методу override setAdapter, чтобы пропустить событие.

Ответ 20

Решение с булевым флагом или счетчиком не помогло мне, потому что во время изменения ориентации onItemSelected() вызывает "overllew" флаг или счетчик.

Я подклассифицировал android.widget.Spinner и сделал крошечные дополнения. Ниже приведены соответствующие части. Это решение сработало для меня.

private void setHandleOnItemSelected()
{
  final StackTraceElement [] elements = Thread.currentThread().getStackTrace();

  for (int index = 1; index < elements.length; index++)
  {
     handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$

     if (handleOnItemSelected)
     {
        break;
     }
  }
}

@Override
public void setSelection(int position, boolean animate)
{
  super.setSelection(position, animate);

  setHandleOnItemSelected();
}

@Override
public void setSelection(int position)
{
  super.setSelection(position);

  setHandleOnItemSelected();
}

public boolean shouldHandleOnItemSelected()
{
  return handleOnItemSelected;
}

Ответ 21

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

MainActivity.onCreate():

ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);

ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...

ArAd  abAdapt = new ArAd (this
   , android.R.layout.simple_list_item_1
   , android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);

перезагрузить адаптер массива:

private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
    private ArAd(Activity a
            , int layoutId, int resId, ArrayList<String> list) {
        super(a, layoutId, resId, list);
        viewed = false;
    }
    @Override
    public View getDropDownView(int position, View convertView,
            ViewGroup parent) {
        viewed = true;
        return super.getDropDownView(position, convertView, parent);
    }
}

измененный прослушиватель:

@Override
public boolean onNavigationItemSelected(
   int itemPosition, long itemId) {
   if (viewed) {
     ...
   }
   return false;
}

Ответ 22

если вам нужно воссоздать активность "на лету", например: изменение тем, простой флаг/счетчик не работает

использовать функцию onUserInteraction() для обнаружения активности пользователя,

ссылка: fooobar.com/questions/19093/...

Ответ 23

У меня сделано простейшим способом:

private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;

OnCreate();

spinner = (Spinner) findViewById(R.id.spinner);

listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {

            Log.i("H - Spinner selected position", position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    };

 spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            spinner.setOnItemSelectedListener(listener);
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {

        }
    });

Готово

Ответ 24

if () {        
       spinner.setSelection(0);// No reaction to create spinner !!!
     } else {
        spinner.setSelection(intPosition);
     }


spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

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

         if (position > 0) {
           // real selection
         }

      }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

     }
});

Ответ 25

Это мое окончательное и простое в использовании решение:

public class ManualSelectedSpinner extends Spinner {
    //get a reference for the internal listener
    private OnItemSelectedListener mListener;

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

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

    public ManualSelectedSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
        mListener = listener;
        super.setOnItemSelectedListener(listener);
    }

    public void setSelectionWithoutInformListener(int position){
        super.setOnItemSelectedListener(null);
        super.setSelection(position);
        super.setOnItemSelectedListener(mListener);
    }

    public void setSelectionWithoutInformListener(int position, boolean animate){
        super.setOnItemSelectedListener(null);
        super.setSelection(position, animate);
        super.setOnItemSelectedListener(mListener);
    }
}

Используйте по умолчанию setSelection(...) для поведения по умолчанию или используйте setSelectionWithoutInformListener(...) для выбора элемента в счетчике без запуска обратного вызова OnItemSelectedListener.

Ответ 26

Мне нужно использовать mSpinner в ViewHolder, поэтому флаг mOldPosition установлен в анонимном внутреннем классе.

mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            int mOldPosition = mSpinner.getSelectedItemPosition();

            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
                if (mOldPosition != position) {
                    mOldPosition = position;
                    //Do something
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                //Do something
            }
        });

Ответ 27

Я бы сохранил начальный индекс во время создания объекта onClickListener.

   int thisInitialIndex = 0;//change as needed

   myspinner.setSelection(thisInitialIndex);

   myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

      int initIndex = thisInitialIndex;

      @Override
      public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (id != initIndex) { //if selectedIndex is the same as initial value
            // your real onselecteditemchange event
         }
      }

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

Ответ 28

Мое решение использует onTouchListener, но не ограничивает его использование. Он создает оболочку для onTouchListener, если необходимо, где setup onItemSelectedListener.

public class Spinner extends android.widget.Spinner {
    /* ...constructors... */

    private OnTouchListener onTouchListener;
    private OnItemSelectedListener onItemSelectedListener;

    @Override
    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
        onItemSelectedListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    @Override
    public void setOnTouchListener(OnTouchListener listener) {
        onTouchListener = listener;
        super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
    }

    private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
        return onItemSelectedListener != null ? new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
                return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
            }
        } : onTouchListener;
    }
}

Ответ 29

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

Макет xml файла

    <layout>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.android.com/apk/res-auto">


<Spinner
    android:id="@+id/spinner"
    android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:spinnerMode="dropdown"
    android:layout_below="@id/member_img"
    android:layout_marginTop="@dimen/activity_vertical_margin"
    android:background="@drawable/member_btn"
    android:padding="@dimen/activity_horizontal_margin"
    android:layout_marginStart="@dimen/activity_horizontal_margin"
    android:textColor="@color/colorAccent"
    app:position="@{0}"
    />
 </RelativeLayout>
 </layout>

app:position - это место, в котором вы выбираете позицию.

Пользовательская привязка

  @BindingAdapter(value={ "position"}, requireAll=false)
  public static void setSpinnerAdapter(Spinner spinner, int selected) 
  {

    final int [] selectedposition= new int[1];
    selectedposition[0]=selected;


    // custom adapter or you can set default adapter
        CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
        spinner.setAdapter(customSpinnerAdapter);
            spinner.setSelection(selected,false);


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

            String item = parent.getItemAtPosition(position).toString();
        if( position!=selectedposition[0]) {
                        selectedposition[0]=position;
            // do your stuff here
                    }
                }


        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    });
}

Подробнее о привязке пользовательских данных вы можете прочитать здесь Пользовательский сеттер Android

Примечание

  • Не забудьте включить привязку данных в файле Gradle

       android {
     ....
     dataBinding {
     enabled = true
    }
    }
    
  • Включите файлы макета в теги <layout>

Ответ 30

mYear.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View arg1, int item, long arg3) {
                if (mYearSpinnerAdapter.isEnabled(item)) {

                }
            }

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