Saya telah memikirkan beberapa cara yang kurang elegan untuk menyelesaikan ini, tetapi saya tahu saya pasti kehilangan sesuatu.
onItemSelected
Fires saya langsung padam tanpa interaksi dengan pengguna, dan ini adalah perilaku yang tidak diinginkan. Saya berharap UI menunggu sampai pengguna memilih sesuatu sebelum melakukan apa pun.
Saya bahkan mencoba mengatur pendengar di onResume()
, berharap itu akan membantu, tetapi tidak.
Bagaimana saya bisa menghentikan ini agar tidak menyala sebelum pengguna dapat menyentuh kontrol?
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.
}
}
}
android
spinner
android-spinner
FauxReal
sumber
sumber
Spinner
kosong dan di dalamonItemSelected
Anda dapat mendeteksi jika String tidak kosong lalustartActivity
!Jawaban:
Saya akan mengharapkan solusi Anda bekerja - Saya pikir acara pemilihan tidak akan menyala jika Anda mengatur adaptor sebelum mengatur pendengar.
Yang sedang berkata, bendera boolean sederhana akan memungkinkan Anda untuk mendeteksi acara seleksi pertama yang nakal dan mengabaikannya.
sumber
onResume()
danonPostResume()
, jadi semua kait normal telah selesai pada saat tata letak terjadi.Penggunaan Runnables sepenuhnya salah.
Gunakan
setSelection(position, false);
dalam seleksi awal sebelumnyasetOnItemSelectedListener(listener)
Dengan cara ini Anda mengatur pilihan Anda tanpa animasi yang menyebabkan pendengar yang dipilih pada item dipilih. Tapi pendengarnya nol sehingga tidak ada yang berjalan. Kemudian pendengar Anda ditugaskan.
Jadi ikuti urutan yang tepat ini:
sumber
Mengacu pada jawaban Dan Dyer, cobalah mendaftar
OnSelectListener
dalampost(Runnable)
metode:Dengan melakukan itu untuk saya perilaku yang diinginkan akhirnya terjadi.
Dalam hal ini juga berarti bahwa pendengar hanya menembak pada item yang diubah.
sumber
onCreate()
,onResume()
dll. Dalam hal ini, ini adalah trik yang fantastis, tanpa bahaya kondisi balapan. Saya biasanya menggunakan trik inionCreate()
hanya setelah kode tata letak.Saya membuat metode utilitas kecil untuk mengubah
Spinner
pilihan tanpa memberi tahu pengguna:Ini menonaktifkan pendengar, mengubah pilihan, dan mengaktifkan kembali pendengar setelah itu.
Kuncinya adalah bahwa panggilan tidak sinkron dengan utas UI, jadi Anda harus melakukannya di posting handler berturut-turut.
sumber
setSpinnerSelectionWithoutCallingListener
dua kali dengan cepat, sehingga panggilan kedua dilakukan saat yang pertama telah mengatur pendengarnull
, pemintal Anda akan terjebak dengannull
pendengar selamanya. Saya mengusulkan perbaikan berikut: tambahkanif (listener == null) return;
setelahspinner.setSelection(selection)
.Sayangnya tampaknya dua solusi yang paling umum disarankan untuk masalah ini, yaitu menghitung kejadian panggilan balik dan memposting Runnable untuk mengatur panggilan balik di lain waktu dapat gagal ketika misalnya opsi aksesibilitas diaktifkan. Inilah kelas pembantu yang menangani masalah-masalah ini. Penjelasan lebih lanjut ada di blok komentar.
sumber
Saya telah memiliki BANYAK masalah dengan penembakan pemintal ketika saya tidak mau, dan semua jawaban di sini tidak dapat diandalkan. Mereka bekerja - tetapi hanya kadang-kadang. Anda akhirnya akan mengalami skenario di mana mereka akan gagal dan memperkenalkan bug ke dalam kode Anda.
Apa yang berhasil bagi saya adalah menyimpan indeks yang dipilih terakhir dalam sebuah variabel dan mengevaluasinya di pendengar. Jika sama dengan indeks baru yang dipilih tidak melakukan apa pun dan kembali, lanjutkan dengan pendengar. Melakukan hal ini:
Percayalah pada saya ketika saya mengatakan ini, ini adalah solusi yang paling dapat diandalkan. Peretasan, tapi berhasil!
sumber
Saya berada dalam situasi yang sama, dan saya punya solusi sederhana yang bekerja untuk saya.
Sepertinya metode
setSelection(int position)
dansetSelected(int position, boolean animate)
memiliki implementasi internal yang berbeda.Ketika Anda menggunakan metode kedua
setSelected(int position, boolean animate)
dengan bendera bernyawa palsu, Anda mendapatkan pilihan tanpa menembakkanonItemSelected
pendengar.sumber
setSelection(int position, boolean animate);
onItemSelected
di API23Hanya untuk menghilangkan petunjuk tentang penggunaan onTouchListener untuk membedakan antara panggilan otomatis ke setOnItemSelectedListener (yang merupakan bagian dari inisialisasi Kegiatan, dll.) Vs. panggilan yang dipicu oleh interaksi pengguna yang sebenarnya, saya melakukan yang berikut setelah mencoba beberapa saran lain di sini dan menemukan bahwa itu bekerja dengan baik dengan baris kode paling sedikit.
Cukup setel bidang Boolean untuk Aktivitas / Fragmen Anda seperti:
Kemudian tepat sebelum Anda mengatur setOnItemSelectedListener pemintal Anda, atur onTouchListener:
sumber
sumber
Setelah mencabut rambut saya untuk waktu yang lama sekarang saya telah membuat kelas Spinner saya sendiri. Saya telah menambahkan metode yang memutuskan dan menghubungkan pendengar dengan tepat.
Gunakan dalam XML Anda seperti ini:
Yang harus Anda lakukan adalah mengambil instance SaneSpinner setelah inflasi dan memanggil set pilihan seperti ini:
Dengan ini, tidak ada peristiwa yang dipecat dan interaksi pengguna tidak terganggu. Ini banyak mengurangi kompleksitas kode saya. Ini harus dimasukkan dalam stok Android karena itu benar-benar PITA.
sumber
Tidak ada kejadian yang tidak diinginkan dari fase tata letak jika Anda menunda menambahkan pendengar sampai tata letak selesai:
sumber
ViewTreeObserver.OnGlobalLayoutListener
versi di bawah J dengan memanggilViewTreeObserver.removeGlobalOnLayoutListener
, yang sudah usang dan memiliki nama yang mirip dengan metode yang digunakan jawaban ini.Ini akan terjadi jika Anda membuat seleksi dalam kode sebagai;
Alih-alih menggunakan pernyataan di atas
Sunting: Metode ini tidak berfungsi untuk Mi Android Version Mi UI.
sumber
Saya mendapat jawaban yang sangat sederhana, 100% yakin itu berfungsi:
sumber
Saya telah menemukan solusi yang jauh lebih elegan untuk ini. Ini melibatkan menghitung berapa kali ArrayAdapter (dalam kasus Anda "adaptor") telah dipanggil. Katakanlah Anda memiliki 1 pemintal dan Anda menelepon:
Deklarasikan penghitung int setelah onCreate dan kemudian di dalam metode onItemSelected () beri syarat "jika" untuk memeriksa berapa kali atapter telah dipanggil. Dalam kasus Anda, Anda telah memanggilnya sekali saja:
sumber
Kontribusi kecil saya adalah variasi pada beberapa hal di atas yang cocok untuk saya beberapa kali.
Deklarasikan variabel integer sebagai nilai default (atau nilai yang digunakan terakhir disimpan dalam preferensi). Gunakan spinner.setSelection (myDefault) untuk menetapkan nilai itu sebelum pendengar terdaftar. Di cek onItemSelected apakah nilai pemintal baru sama dengan nilai yang Anda tetapkan sebelum menjalankan kode lebih lanjut.
Ini memiliki keuntungan tambahan dari tidak menjalankan kode jika pengguna memilih nilai yang sama lagi.
sumber
Setelah mengalami masalah yang sama, saya sampai pada solusi ini menggunakan tag. Gagasan di baliknya sederhana: Setiap kali pemintal diubah secara programatis, pastikan tag mencerminkan posisi yang dipilih. Di pendengar maka Anda memeriksa apakah posisi yang dipilih sama dengan tag. Jika ya, pemilihan pemintal diubah secara programatik.
Di bawah ini adalah kelas "proxy pemintal" baru saya:
Anda juga akan memerlukan file XML dengan pengaturan tag di
Values
direktori Anda . Saya memberi nama file sayaspinner_tag.xml
, tetapi terserah Anda. Ini terlihat seperti ini:Sekarang ganti
dalam kode Anda dengan
Dan buat pawang Anda terlihat seperti ini:
Fungsi
isUiTriggered()
akan mengembalikan true jika dan hanya jika pemintal telah diubah oleh pengguna. Perhatikan bahwa fungsi ini memiliki efek samping - ia akan mengatur tag, sehingga panggilan kedua dalam panggilan pendengar yang sama akan selalu kembalifalse
.Pembungkus ini juga akan menangani masalah dengan pendengar yang dipanggil selama pembuatan tata letak.
Bersenang-senanglah, Jens.
sumber
Karena tidak ada yang bekerja untuk saya, dan saya memiliki lebih dari 1 pemintal dalam pandangan saya (dan IMHO memegang peta bool adalah kerja keras) Saya menggunakan tag untuk menghitung klik:
sumber
Sudah banyak jawaban, ini milik saya.
Saya memperluas
AppCompatSpinner
dan menambahkan metodepgmSetSelection(int pos)
yang memungkinkan pengaturan seleksi terprogram tanpa memicu callback seleksi. Saya sudah mengkodekan ini dengan RxJava sehingga acara seleksi dikirim melaluiObservable
.Contoh penggunaannya, yang disebut di
onCreateView()
dalamFragment
misalnya:di mana
setSelection()
adalah metode dalam tampilan terlampir yang terlihat seperti ini, dan yang disebut baik dari peristiwa pemilihan pengguna melaluiObservable
dan juga di tempat lain secara terprogram, sehingga logika untuk menangani pemilihan umum untuk kedua metode seleksi.sumber
Saya akan mencoba menelepon
setelah Anda memanggil setAdapter (). Coba juga menelepon sebelum adaptor.
Anda selalu memiliki solusi untuk pergi dengan subclassing, di mana Anda dapat membungkus bendera boolean ke metode setAdapter yang diganti untuk melewati acara tersebut.
sumber
Solusi dengan bendera boolean atau penghitung tidak membantu saya, karena selama perubahan orientasi padaItemSelected () panggilan "meluap" bendera atau penghitung.
Saya subkelas
android.widget.Spinner
dan membuat tambahan kecil. Bagian yang relevan di bawah ini. Solusi ini berhasil untuk saya.sumber
Ini juga bukan solusi yang elegan. Sebenarnya itu agak Rube-Goldberg tetapi tampaknya berhasil. Saya memastikan pemintal telah digunakan setidaknya sekali dengan memperluas adaptor array dan mengesampingkan getDropDownView-nya. Dalam metode getDropDownView baru saya memiliki bendera boolean yang diatur untuk menunjukkan menu dropdown telah digunakan setidaknya sekali. Saya mengabaikan panggilan ke pendengar sampai bendera ditetapkan.
MainActivity.onCreate ():
mengganti susunan adaptor:
pendengar yang dimodifikasi:
sumber
jika Anda perlu membuat kembali aktivitas dengan cepat misalnya: mengubah tema, flag / counter sederhana tidak akan berfungsi
gunakan fungsi onUserInteraction () untuk mendeteksi aktivitas pengguna,
referensi: https://stackoverflow.com/a/25070696/4772917
sumber
Saya telah melakukan dengan cara paling sederhana:
onCreate ();
Selesai
sumber
sumber
Itu solusi terakhir dan mudah saya gunakan:
Gunakan default
setSelection(...)
untuk perilaku default atau gunakansetSelectionWithoutInformListener(...)
untuk memilih item di spinner tanpa memicu callback OnItemSelectedListener.sumber
Saya perlu menggunakan
mSpinner
di ViewHolder, jadi flagmOldPosition
diatur dalam kelas dalam anonim.sumber
Saya akan menyimpan indeks awal selama pembuatan objek onClickListener.
sumber
Solusi saya menggunakan
onTouchListener
tetapi tidak membatasi penggunaannya. Ini membuat pembungkusonTouchListener
jika perlu di mana pengaturanonItemSelectedListener
.sumber
Saya mungkin menjawab terlambat atas posting, namun saya berhasil mencapai ini menggunakan perpustakaan Data mengikat Android Android Databinding . Saya membuat pengikatan khusus untuk memastikan pendengar tidak dipanggil sampai item yang dipilih diubah sehingga meskipun pengguna memilih posisi yang sama berulang kali acara tidak dipecat.
Layout file xml
app:position
adalah posisi yang Anda lewati untuk dipilih.Ikatan khusus
Anda dapat membaca lebih lanjut tentang pengikatan data khusus di sini Android Custom Setter
CATATAN
Jangan lupa untuk mengaktifkan penyatuan data dalam file Gradle Anda
Sertakan file tata letak Anda dalam
<layout>
tagsumber
sumber