Android Spinner: уникайте дзвінків onItemSelected під час ініціалізації


156

Я створив додаток для Android з а Spinnerта a TextView. Я хочу відобразити вибраний елемент із випадаючого списку Spinner у TextView. Я реалізував Spinner у onCreateметоді, тож коли я запускаю програму, вона показує значення TextView(перед вибором елемента зі спадного списку).

Я хочу показати значення в TextView лише після вибору елемента зі спадного списку. Як це зробити?

Ось мій код:

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class GPACal01Activity extends Activity implements OnItemSelectedListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

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

        // Create an ArrayAdapter using the string array and a default spinner layout
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.noofsubjects_array, android.R.layout.simple_spinner_item);
        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        // Apply the adapter to the spinner
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(this);
    }

    public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
        TextView textView = (TextView) findViewById(R.id.textView1);
        String str = (String) parent.getItemAtPosition(pos);
        textView.setText(str);
    }

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

    }
}

у спінерів завжди є вибране значення за замовчуванням, якщо ви хочете мати спінер без вибраного значення за замовчуванням, вам слід створити власний спінер або створити спеціальний спінер із порожнім записом, а в методі getView () змінити видимість сирий макет GONE
Houcine

4
Звідки така набридлива помилка все-таки існує наприкінці 2018 року ???
користувач-123

Відповіді:


174
spinner.setOnItemSelectedListener(this); // Will call onItemSelected() Listener.

Тому вперше обробіть це будь-яким значенням Integer

Приклад: Спочатку візьміть int check = 0;

public void onItemSelected(AdapterView<?> parent, View arg1, int pos,long id) {
   if(++check > 1) {
      TextView textView = (TextView) findViewById(R.id.textView1);
      String str = (String) parent.getItemAtPosition(pos);
      textView.setText(str);
   }
}

Ви можете зробити це з булевим значенням, а також перевіривши поточні та попередні позиції. Дивіться тут


2
Дивовижний чоловік ... коли я вперше зіткнувся з цією проблемою, я спробував реалізувати користувальницький спінер .. доки це не спрацювало ... але ваше рішення працювало як шарм. Дякую.
Sash_KP

1
Де ви декларуєте чек? Поза getView ()? Всередині видуHolder? Де? спробував ваше рішення, але не працює для мене.
користувач3718908

9
його патч не рішення, я здогадуюсь.
saksham

1
Я зіткнувся з тією ж проблемою. Це якась помилка? також якщо я вибираю один і той самий елемент неодноразово, слухач не працює після першого разу. Він працює лише в тому випадку, якщо елемент вибору змінюється. Будь-який коментар, допомога?
амітава

1
Я спробував це рішення зі своєю тією ж проблемою, але це будить лише одну, наприклад: коли у мене є 2 пункти при спаді, він прокидається, коли я вибираю будь-який спінерний предмет 1-й раз, коли я спробую 2-й раз його не працює належним чином
Waseem

116

Просто поставте цей рядок перед встановленням OnItemSelectedListener

spinner.setSelection(0,false)

7
Було б кращою відповіддю, якби ви написали, як це допомагає і чому.
користувач3533716

1
Це допомагає, але хочете як?
AEMLoviji

14
Це працює, тому що ви спочатку встановлюєте вибір, а потім додаєте слухача, але слухач не буде викликаний, тому що ви вже вибрали цей вибір. Лише нові виділення зателефонують слухачеві.
андроїд розробник

4
Це працює тому, що setSelection(int, boolean)дзвонить setSelectionInt()внутрішньо, і вам потрібно встановити слухача після (замість того, як раніше) викликати це. Будьте обережні, що setSelection(int)це не спрацює, бо воно дзвонить setNextSelectedPositionInt()всередину, і саме це мене привело сюди.
Хай Чжан

3
Це не працює, якщо це оголошено під час або раніше onCreateView (). onItemSelected буде викликаний.
Араш

62

Починаючи з рівня 3 API, ви можете використовувати onUserInteraction () для діяльності з булевим елементом, щоб визначити, чи користувач взаємодіє з пристроєм.

http://developer.android.com/reference/android/app/Activity.html#onUserInteraction ()

@Override
public void onUserInteraction() {
     super.onUserInteraction();
     userIsInteracting = true;
}

Як поле у ​​розділі "Діяльність" я маю:

 private boolean userIsInteracting;

Нарешті, мій спінер:

      mSpinnerView.setOnItemSelectedListener(new OnItemSelectedListener() {

           @Override
           public void onItemSelected(AdapterView<?> arg0, View view, int position, long arg3) {
                spinnerAdapter.setmPreviousSelectedIndex(position);
                if (userIsInteracting) {
                     updateGUI();
                }
           }

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

           }
      });

По мірі того, як ви заходите і проходите діяльність, булева функція скидається на хибну. Працює як шарм.


3
Гарний законопроект. Я думаю, що це краще рішення, ніж прийняте як відповідь
Ritesh Gune

звідки ви берете setmPreviousSelectedIndex?!?!
TheQ

Друкарська помилка? :) setPreviousSelectedIndex ()
Білл Мот

4
Це не буде працювати у випадку фрагментів гнізд, оскільки onUserInteraction - це метод діяльності. Будь-яке інше рішення?
Читранг

1
@ErikB nvm, зрозумів це. Встановлення фальшивого вмісту у слухача працює чудово.
Сікандер

20

Це працювало для мене

Ініціалізація Spinner в Android є проблематичною, іноді вищезазначена проблема була вирішена за цією схемою.

Spinner.setAdapter();
Spinner.setSelected(false);  // must
Spinner.setSelection(0,true);  //must
Spinner.setonItemSelectedListener(this);

Налаштування адаптера має бути першою частиною, а onItemSelectedListener (це) буде останнім при ініціалізації спінера. За вищезазначеним шаблоном мій OnItemSelected () не викликається під час ініціалізації спінера


11

ха-ха ... у мене те саме питання. Коли initViews () просто так роблять. Послідовність є ключовою, слухач - останнім. Щасти !

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);

Хороший! Ніщо не працювало для мене, що б я раніше не застосовував, але цей працював на мене як на принадність. Дякуємо @TreeSouth
Deepika Lalra

11
Для мене працював spinner.setSelection (позиція, false), що використовується таким же чином. За допомогою методу setSelection (position) слухача викликали під час ініціалізації.
Маріо Кутлєв

2
@HugoGresse Спробуйте зателефонувати на spinner.setSelection (0, false); . Річ у тому, що тепер вона ігнорує вибір цієї позиції, оскільки вона вже вибрана
андроїд розробник

8

Моє рішення:

protected boolean inhibit_spinner = true;


@Override
        public void onItemSelected(AdapterView<?> arg0, View arg1,
                int pos, long arg3) {

            if (inhibit_spinner) {
                inhibit_spinner = false;
            }else {

            if (getDataTask != null) getDataTask.cancel(true);
            updateData();
            }

        }

7

Щоб уникнути виклику spinner.setOnItemSelectedListener () під час ініціалізації

spinner.setSelection(Adapter.NO_SELECTION, true); //Add this line before setting listener
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

    }

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

    }
});

6

Це можна зробити таким чином:

AdapterView.OnItemSelectedListener listener = new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            //set the text of TextView
        }

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

        }
    });

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

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

        }
    });

Спочатку я створюю слухача і відношую змінний зворотний виклик; тоді я створюю другого слухача анонімним, і коли це викликається в перший раз, це змінює слухача =]


3

Потім прапор взаємодії з користувачем може бути встановлений на істинне в методі onTouch та скинутись onItemSelected()після того, як зміна вибору буде оброблена. Я вважаю за краще це рішення, оскільки прапор взаємодії з користувачем обробляється виключно для спінера, а не для інших поглядів на діяльність, які можуть вплинути на бажану поведінку.

У коді:

Створіть свого слухача для спінера:

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) { 
            userSelect = false;
            // Your selection handling code here
        }
    }

}

Додайте слухача до спінера як як OnItemSelectedListenerі як OnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);

2

Подібне просте рішення, яке дозволяє кілька спінерів, полягає в тому, щоб помістити AdapterView в колекцію - в надклас "Діяльність" - при першому виконанні onItemSelected (...). Це дозволяє отримати один набір методів у надкласі та підтримує декілька AdapterViews і для цього кілька спінерів.

Суперклас ...

private Collection<AdapterView> AdapterViewCollection = new ArrayList<AdapterView>();

   protected boolean firstTimeThrough(AdapterView parent) {
    boolean firstTimeThrough = ! AdapterViewCollection.contains(parent);
    if (firstTimeThrough) {
       AdapterViewCollection.add(parent);
     }
    return firstTimeThrough;
   }

Підклас ...

public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
      if (! firstTimeThrough(parent)) {
        String value = safeString(parent.getItemAtPosition(pos).toString());
        String extraMessage = EXTRA_MESSAGE;
        Intent sharedPreferencesDisplayIntent = new         Intent(SharedPreferencesSelectionActivity.this,SharedPreferencesDisplayActivity.class);
    sharedPreferencesDisplayIntent.putExtra(extraMessage,value);
    startActivity(sharedPreferencesDisplayIntent);
  }
  // don't execute the above code if its the first time through
  // do to onItemSelected being called during view initialization.

}


2

створити булеве поле

private boolean inispinner;

всередині творення діяльності

    spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            if (!inispinner) {
                inispinner = true;
                return;
            }
            //do your work here
        }

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

        }
    });


1

Ви можете досягти цього, встановивши спершу setOnTouchListener, а потім встановітьOnItemSelectedListener в onTouch

@Override
public boolean onTouch(final View view, final MotionEvent event) {
 view.setOnItemSelectedListener(this)
 return false;
}

Я обожнюю це. Хоча це створює нового слухача кожного разу, коли користувач торкається перегляду. Тому я вважаю за краще кешувати першого створеного слухача та використовувати його повторно.
Влад

1

Це працювало для мене:

    spinner.setSelection(0, false);
    new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spinner.setOnItemSelectedListener(listener);
            }, 500);

1

На основі відповіді Абхі я зробив цього простого слухача

class SpinnerListener constructor(private val onItemSelected: (position: Int) -> Unit) : AdapterView.OnItemSelectedListener {

    private var selectionCount = 0

    override fun onNothingSelected(parent: AdapterView<*>?) {
        //no op
    }

    override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
        if (selectionCount++ > 1) {
           onItemSelected(position)
        }
    }
}

0

У мене була така ж проблема, і це працює для мене:

У мене є 2 спінери і я оновлюю їх під час init та під час взаємодії з іншими елементами управління або після отримання даних із сервера.

Ось мій шаблон:

public class MyClass extends <Activity/Fragment/Whatever> implements Spinner.OnItemSelectedListener {

    private void removeSpinnersListeners() {
        spn1.setOnItemSelectedListener(null);
        spn2.setOnItemSelectedListener(null);
    }

    private void setSpinnersListeners() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                spn1.setOnItemSelectedListener(MyClass.this);
                spn2.setOnItemSelectedListener(MyClass.this);
            }
        }, 1);
    }

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

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

    }
}

Коли клас ініціює використання setSpinnersListeners () замість прямого встановлення слухача.

Функція Runnable не дозволить спінеру стріляти onItemSelected відразу після встановлення їх значень.

Якщо вам потрібно оновити спінер (після виклику сервера тощо), використовуйте deleteSpinnersListeners () безпосередньо перед лініями оновлення, а setSpinnersListeners () відразу після рядків оновлення. Це запобіжить запусканню OnItemSelected після оновлення.


0

Код

spinner.setOnTouchListener(new View.OnTouchListener() { 
@Override public boolean onTouch(View view, MotionEvent motionEvent) { isSpinnerTouch=true; return false; }});

holder.spinner_from.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int slot_position, long l) {
                if(isSpinnerTouch)
                {
                    Log.d("spinner_from", "spinner_from");
                    spinnerItemClickListener.onSpinnerItemClickListener(position, slot_position, AppConstant.FROM_SLOT_ONCLICK_CODE);
                }
                else {

                }
            }

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

            }
        });

0

Для мене рішення Абхі чудово підходить до рівня Апі 27.

Але здається, що від рівня Api 28 і вище, onItemSelected () не викликається, коли слухач встановлений, що означає, що OnItemSelected () ніколи не викликається.

Тому я додав коротку операцію if для перевірки рівня Api:

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

            if(Build.VERSION.SDK_INT >= 28){ //onItemSelected() doesn't seem to be called when listener is set on Api 28+
                check = 1;
            }

            if(++check > 1) {
                //Do your action here
            }
        }

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


0

Я розмістив TextView поверх Spinner, такого ж розміру та фону, як Spinner, щоб я мав більше контролю над тим, як він виглядав, перш ніж користувач натискає на нього. З TextView там я міг також використовувати TextView для позначення прапор, коли користувач почав взаємодіяти.

Мій код Котліна виглядає приблизно так:

private var mySpinnerHasBeenTapped = false

private fun initializeMySpinner() {

    my_hint_text_view.setOnClickListener {
        mySpinnerHasBeenTapped = true //turn flag to true
        my_spinner.performClick() //call spinner click
    }

    //Basic spinner setup stuff
    val myList = listOf("Leonardo", "Michelangelo", "Rafael", "Donatello")
    val dataAdapter: ArrayAdapter<String> = ArrayAdapter<String>(this, android.R.layout.simple_spinner_dropdown_item, myList)
    my_spinner.adapter = dataAdapter

    my_spinner.onItemSelectedListener = object : OnItemSelectedListener {

        override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) {

            if (mySpinnerHasBeenTapped) { //code below will only run after the user has clicked
                my_hint_text_view.visibility = View.GONE //once an item has been selected, hide the textView
                //Perform action here
            }
        }

        override fun onNothingSelected(parent: AdapterView<*>?) {
            //Do nothing
        }
    }
}

Файл макета виглядає приблизно так, важливою частиною є те, що Spinner та TextView мають однакову ширину, висоту та поля:

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Spinner
                android:id="@+id/my_spinner"
                android:layout_width="match_parent"
                android:layout_height="35dp"
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true" />

            <TextView
                android:id="@+id/my_hint_text_view"
                android:layout_width="match_parent"
                android:layout_height="35dp"                
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:background="@drawable/bg_for_spinners"

                android:paddingStart="8dp"
                android:paddingEnd="30dp"
                android:singleLine="true"
                android:gravity="center_vertical"
                android:text="*Select A Turtle"
                android:textColor="@color/green_ooze"
                android:textSize="16sp" />

        </FrameLayout>

Я впевнений, що інші рішення працюють там, де ви ігноруєте перший виклик onItemSelected, але мені дуже не подобається ідея припускати, що він завжди буде викликаний.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.