Android Spinner: Hindari panggilan yang dipilih pada saat inisialisasi

156

Saya membuat aplikasi Android dengan a Spinnerdan a TextView. Saya ingin menampilkan item yang dipilih dari daftar tarik Spinner di TextView. Saya menerapkan Spinner dalam onCreatemetode sehingga ketika saya menjalankan program, itu menunjukkan nilai dalam TextView(sebelum memilih item dari daftar drop-down).

Saya ingin menunjukkan nilai di TextView hanya setelah memilih item dari daftar turun bawah. Bagaimana saya melakukan ini?

Ini kode saya:

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

    }
}
Hibah
sumber
pemintal selalu memiliki nilai yang dipilih default, jika Anda ingin memiliki pemintal tanpa nilai yang dipilih default, Anda harus membuat pemintal khusus Anda, atau membuat pemintal khusus dengan entri kosong, dan dalam metode getView (), ubah visibilitas dari tata letak mentah ke GONE
Houcine
4
Kenapa bug yang menjengkelkan itu masih ada pada akhir 2018 ???
user-123

Jawaban:

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

Jadi pertama kali menangani ini dengan nilai Integer

Contoh: Awalnya Ambil 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);
   }
}

Anda dapat melakukannya dengan nilai boolean dan juga dengan memeriksa posisi saat ini dan sebelumnya. Lihat disini

Abhi
sumber
2
Pria yang luar biasa..ketika saya pertama kali menghadapi masalah ini saya mencoba menerapkan pemintal kustom..masih tidak berhasil..tapi solusi Anda bekerja seperti pesona .. Terima kasih.
Sash_KP
1
Di mana Anda menyatakan cek? Di luar getView ()? Di dalam viewHolder? Dimana? mencoba solusi Anda tetapi tidak berhasil untuk saya.
user3718908
9
itu tambalan bukan solusi kurasa.
saksham
1
Saya menghadapi masalah yang sama. Apakah ini semacam bug? juga jika saya memilih item yang sama berulang kali, pendengar tidak berfungsi setelah pertama kali. Ini hanya berfungsi jika item pilihan berubah. Ada komentar, tolong?
amitava
1
Saya mencoba solusi ini dengan masalah saya yang sama, tetapi ini hanya wajan satu, misalnya: ketika saya memiliki 2 item pada dropdown, itu woks ketika saya memilih item pemintal 1 kali, ketika saya mencoba 2 kali itu tidak berfungsi dengan baik
Waseem
116

Cukup letakkan baris ini sebelum mengatur OnItemSelectedListener

spinner.setSelection(0,false)
Dayanand Waghmare
sumber
7
Ini akan menjadi jawaban yang lebih baik jika Anda menulis bagaimana ini membantu dan mengapa.
user3533716
1
Ini membantu, tetapi ingin bagaimana?
AEMLoviji
14
Ini berfungsi karena Anda pertama-tama mengatur pemilihan, dan kemudian menambahkan pendengar, tetapi pendengar tidak akan dipanggil karena Anda sudah memilih pilihan ini sebelumnya. Hanya pilihan baru yang akan memanggil pendengar.
pengembang android
4
Ini berfungsi karena setSelection(int, boolean)panggilan setSelectionInt()internal, dan Anda perlu mengatur pendengar setelah (daripada sebelumnya) memanggil ini. Waspadalah itu setSelection(int)tidak akan berhasil, karena itu panggilan setNextSelectedPositionInt()internal, dan inilah yang membawa saya ke sini.
Hai Zhang
3
Ini tidak berfungsi jika dinyatakan selama atau sebelum onCreateView (). onItemSelected akan dipanggil.
Arash
62

Dimulai dengan API level 3 Anda dapat menggunakan onUserInteraction () pada Aktivitas dengan boolean untuk menentukan apakah pengguna berinteraksi dengan perangkat.

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

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

Sebagai bidang pada Kegiatan yang saya miliki:

 private boolean userIsInteracting;

Akhirnya, pemintal saya:

      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) {

           }
      });

Saat Anda datang dan pergi melalui aktivitas, boolean diatur ulang ke false. Bekerja seperti pesona.

Bill Mote
sumber
3
Bagus satu Bill..Saya pikir ini solusi yang lebih baik daripada yang diterima sebagai jawaban
Ritesh Gune
di mana Anda mendapatkan setmPreviousSelectedIndex?!?!
TheQ
Salah ketik? :) setPreviousSelectedIndex ()
Bill Mote
4
Ini tidak akan berfungsi jika ada fragmen sarang karena onUserInteraction adalah metode Aktivitas. Ada solusi lain?
Chitrang
1
@ ErikB nvm, menemukan jawabannya. Menetapkannya salah di dalam pendengar bekerja dengan baik.
Sikander
20

Ini berhasil untuk saya

Inisialisasi Spinner di Android bermasalah kadang-kadang masalah di atas diselesaikan dengan pola ini.

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

Pengaturan adaptor harus menjadi bagian pertama dan onItemSelectedListener (ini) akan menjadi yang terakhir saat menginisialisasi pemintal. Dengan pola di atas OnItemSelected () saya tidak dipanggil selama inisialisasi spinner

saksham
sumber
11

haha ... Saya punya pertanyaan yang sama. Ketika initViews () lakukan saja seperti ini. Urutan adalah kuncinya, pendengar adalah yang terakhir. Semoga berhasil !

spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(listener);
Treesouth
sumber
Bagus Tidak ada yang berhasil untuk saya apa pun yang telah saya terapkan sebelumnya tetapi yang ini telah bekerja untuk saya seperti pesona. Terima kasih @TreeSouth
Deepika Lalra
11
Bagi saya bekerja spinner.setSelection (posisi, false) digunakan dengan cara yang sama. Dengan metode setSelection (position), pendengar dipanggil saat inisialisasi.
Mario Kutlev
2
@HugoGresse Coba panggil spinner.setSelection (0, false); . Masalahnya, sekarang akan mengabaikan pemilihan posisi ini, karena sudah dipilih
pengembang android
8

Solusi saya:

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

        }
codareee
sumber
7

Untuk menghindari memanggil spinner.setOnItemSelectedListener () selama inisialisasi

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) {

    }
});
Ketan Ramani
sumber
6

Anda dapat melakukan ini dengan cara ini:

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) {

        }
    });

Pada awalnya saya membuat pendengar dan dikaitkan dengan panggilan balik variabel; maka saya membuat pendengar kedua anonim dan ketika ini dipanggil pertama kali, ini mengubah pendengar =]

Charleston
sumber
3

Bendera interaksi pengguna kemudian dapat disetel ke true dalam metode onTouch dan mengatur ulang onItemSelected()begitu perubahan pemilihan telah ditangani. Saya lebih suka solusi ini karena flag interaksi pengguna ditangani secara eksklusif untuk pemintal, dan bukan untuk tampilan lain dalam aktivitas yang dapat memengaruhi perilaku yang diinginkan.

Dalam kode:

Buat pendengar Anda untuk pemintal:

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
        }
    }

}

Menambahkan pendengar untuk pemintal baik sebagai OnItemSelectedListenerdan OnTouchListener:

SpinnerInteractionListener listener = new SpinnerInteractionListener();
mSpinnerView.setOnTouchListener(listener);
mSpinnerView.setOnItemSelectedListener(listener);
Ranjith Kumar
sumber
2

Solusi sederhana serupa yang memungkinkan banyak pemintal adalah dengan menempatkan AdapterView dalam koleksi - di dalam superclass Kegiatan - pada eksekusi pertama padaItItSelected (...) Kemudian periksa untuk melihat apakah AdapterView ada di koleksi sebelum menjalankannya. Ini memungkinkan satu set metode dalam superclass dan mendukung beberapa AdapterViews dan beberapa pemintal.

Superclass ...

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

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

Subkelas ...

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.

}

Jonathan Cole
sumber
2

buat bidang boolean

private boolean inispinner;

di dalam membuat aktivitas

    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) {

        }
    });
Afjalur Rahman Rana
sumber
2

Coba ini

spinner.postDelayed(new Runnable() {
        @Override
        public void run() {
            addListeners();
        }
    }, 1000);.o
Ubirajara Erthal
sumber
1

Anda bisa mencapainya dengan setOnTouchListener terlebih dahulu kemudian setOnItemSelectedListener di onTouch

@Override
public boolean onTouch(final View view, final MotionEvent event) {
 view.setOnItemSelectedListener(this)
 return false;
}
vviieett
sumber
Aku suka ini. Meskipun itu menciptakan pendengar baru setiap kali pengguna menyentuh tampilan. Jadi saya lebih suka untuk cache pendengar yang pertama dibuat dan menggunakannya kembali.
Vlad
1

Ini bekerja untuk saya:

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

Berdasarkan jawaban Abhi saya membuat pendengar sederhana ini

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)
        }
    }
}
AdrianoCelentano
sumber
0

Punya masalah yang sama dan ini bekerja untuk saya:

Saya memiliki 2 pemintal dan saya memperbaruinya selama init dan selama interaksi dengan kontrol lain atau setelah mendapatkan data dari server.

Ini templat saya:

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) {

    }
}

Ketika kelas memulai gunakan setSpinnersListeners () alih-alih langsung mengatur pendengar.

Runnable akan mencegah spinner menembakkan onItemSelected tepat setelah Anda menetapkan nilainya.

Jika Anda perlu memperbarui pemintal (setelah panggilan server dll.) Gunakan removeSpinnersListeners () tepat sebelum jalur pembaruan Anda, dan setSpinnersListeners () tepat setelah garis pembaruan. Ini akan mencegah onItemSelected dari menembak setelah pembaruan.

RoyBS
sumber
0

Kode

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) {

            }
        });
Messi
sumber
0

Bagi saya, solusi Abhi bekerja sangat baik hingga Api level 27.

Tetapi tampaknya dari Api level 28 ke atas, onItemSelected () tidak dipanggil ketika listener diatur, yang berarti onItemSelected () tidak pernah dipanggil.

Oleh karena itu, saya menambahkan pernyataan if pendek untuk memeriksa level 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
            }
        }

Saya pikir itu cukup aneh dan saya tidak yakin apakah orang lain juga memiliki masalah ini, tetapi dalam kasus saya ini bekerja dengan baik.

Jannik B
sumber
0

Saya menempatkan TextView di atas Spinner, dengan ukuran dan latar belakang yang sama dengan Spinner, sehingga saya akan memiliki kontrol lebih besar atas seperti apa itu sebelum pengguna mengkliknya. Dengan TextView di sana, saya juga bisa menggunakan TextView untuk menandai ketika pengguna sudah mulai berinteraksi.

Kode Kotlin saya terlihat seperti ini:

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
        }
    }
}

File tata letak terlihat seperti ini, dengan bagian penting adalah Spinner dan TextView berbagi lebar, tinggi, dan margin yang sama:

        <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>

Saya yakin solusi lain bekerja di mana Anda mengabaikan panggilan onItemSelected pertama, tapi saya benar-benar tidak menyukai gagasan untuk menganggapnya akan selalu dipanggil.

iOS_Mouse
sumber