Myślałem o mniej niż eleganckich sposobach rozwiązania tego problemu, ale wiem, że czegoś mi brakuje.
Mój onItemSelected
wystrzeliwuje natychmiast, bez żadnej interakcji z użytkownikiem, i jest to niepożądane zachowanie. Chcę, aby interfejs użytkownika czekał, aż użytkownik wybierze coś, zanim cokolwiek zrobi.
Próbowałem nawet ustawić słuchacza w onResume()
, mając nadzieję, że to pomoże, ale tak nie jest.
Jak mogę temu zapobiec, zanim użytkownik dotknie kontrolki?
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
źródło
źródło
Spinner
pustym, a wewnątrzonItemSelected
można wykryć, czy łańcuch nie jest pustystartActivity
!Odpowiedzi:
Spodziewałbym się, że twoje rozwiązanie zadziała - myślałem, że zdarzenie wyboru nie uruchomi się, jeśli ustawisz adapter przed skonfigurowaniem detektora.
To powiedziawszy, prosta flaga boolowska pozwoli ci wykryć nieuczciwe zdarzenie pierwszego wyboru i zignorować je.
źródło
onResume()
ionPostResume()
tak wszystkie normalne haki zostały zakończone w momencie układ dzieje.Korzystanie z Runnables jest całkowicie nieprawidłowe.
Użyj
setSelection(position, false);
w początkowej selekcji wcześniejsetOnItemSelectedListener(listener)
W ten sposób ustawiasz swój wybór bez animacji, co powoduje, że wywoływany jest nasłuchiwanie wybranego elementu. Ale słuchacz ma wartość zerową, więc nic nie jest uruchamiane. Następnie twój słuchacz zostaje przypisany.
Postępuj zgodnie z następującą sekwencją:
źródło
Odnosząc się do odpowiedzi Dan Dyer, spróbuj zarejestrowania
OnSelectListener
wpost(Runnable)
metodzie:Robiąc to dla mnie, w końcu pojawiło się pożądane zachowanie.
W tym przypadku oznacza to również, że słuchacz strzela tylko na zmieniony element.
źródło
onCreate()
,onResume()
itp W takim przypadku jego fantastycznej sztuczki, bez niebezpieczeństwa wyścigu. Zwykle używam tej sztuczkionCreate()
zaraz po kodzie układu.Stworzyłem małą użyteczną metodę zmiany
Spinner
wyboru bez powiadamiania użytkownika:Wyłącza nasłuchiwanie, zmienia wybór i ponownie włącza nasłuchiwanie.
Sztuczka polega na tym, że wywołania są asynchroniczne z wątkiem interfejsu użytkownika, więc musisz to zrobić w kolejnych postach modułu obsługi.
źródło
setSpinnerSelectionWithoutCallingListener
dwukrotnie szybko, aby drugie połączenie zostało wykonane, podczas gdy pierwszy już ustawił słuchaczanull
, spinner utknie znull
słuchaczem na zawsze. Proponuję następującą poprawkę: dodajif (listener == null) return;
pospinner.setSelection(selection)
.Niestety wydaje się, że dwa najczęściej sugerowane rozwiązania tego problemu, a mianowicie liczenie wystąpień oddzwaniania i publikowanie elementu Runnable w celu ustawienia oddzwaniania w późniejszym czasie, mogą się nie powieść, gdy na przykład włączone są opcje ułatwień dostępu. Oto klasa pomocnicza, która omawia te problemy. Dalsze wyjaśnienia znajdują się w bloku komentarza.
źródło
Miałem wiele problemów z odpalaniem tarczy, kiedy nie chciałem, a wszystkie odpowiedzi tutaj są niewiarygodne. Działają - ale tylko czasami. W końcu natkniesz się na scenariusze, w których się nie powiedzie i wprowadzisz błędy do swojego kodu.
Dla mnie działało przechowywanie ostatniego wybranego indeksu w zmiennej i ocena go w detektorze. Jeśli jest taki sam jak nowy wybrany indeks, nic nie rób i wróć, w przeciwnym razie kontynuuj od nasłuchiwania. Zrób to:
Zaufaj mi, kiedy to powiem, jest to zdecydowanie najbardziej niezawodne rozwiązanie. Hack, ale działa!
źródło
Byłem w podobnej sytuacji i mam dla siebie proste rozwiązanie.
Wydaje się, że są to metody
setSelection(int position)
isetSelected(int position, boolean animate)
mają różne wewnętrzne wdrożenie.Kiedy używasz drugiej metody
setSelected(int position, boolean animate)
z flagą false animate, otrzymujesz wybór bez odpalaniaonItemSelected
detektora.źródło
setSelection(int position, boolean animate);
onItemSelected
w API23Aby tylko przedstawić wskazówki dotyczące używania onTouchListener do rozróżnienia między automatycznymi wywołaniami setOnItemSelectedListener (które są częścią inicjalizacji aktywności itp.) A wywołaniami wywołanymi przez rzeczywistą interakcję użytkownika, po wypróbowaniu kilku innych sugestii zrobiłem następujące i okazało się, że działa dobrze z najmniejszą liczbą wierszy kodu.
Wystarczy ustawić pole logiczne dla swojej aktywności / fragmentu, takie jak:
Następnie, tuż przed ustawieniem setOnItemSelectedListener błystki, ustaw onTouchListener:
źródło
źródło
Po dłuższym wyciągnięciu włosów stworzyłem własną klasę Spinner. Dodałem do niego metodę, która odpowiednio rozłącza i łączy słuchacza.
Użyj go w swoim XML w następujący sposób:
Wszystko, co musisz zrobić, to pobrać instancję SaneSpinner po inflacji i wybrać zestaw połączeń w następujący sposób:
Dzięki temu żadne zdarzenie nie jest uruchamiane, a interakcja użytkownika nie jest przerywana. To znacznie zmniejszyło złożoność mojego kodu. To powinno być zawarte w magazynie Android, ponieważ tak naprawdę jest to PITA.
źródło
Brak niepożądanych zdarzeń z fazy układu, jeśli odłożysz dodawanie detektora do momentu zakończenia układu:
źródło
ViewTreeObserver.OnGlobalLayoutListener
wersje poniżej J, dzwoniącViewTreeObserver.removeGlobalOnLayoutListener
, która jest przestarzała i ma podobną nazwę do metody używanej przez tę odpowiedź.Stanie się tak, jeśli dokonasz wyboru w kodzie jako;
Zamiast powyższej instrukcji użyj
Edycja: Ta metoda nie działa dla Mi Android w wersji Mi UI.
źródło
Otrzymałem bardzo prostą odpowiedź, 100% pewności, że to działa:
źródło
Znalazłem o wiele bardziej eleganckie rozwiązanie tego problemu. Polega ona na zliczeniu, ile razy ArrayAdapter (w twoim przypadku „adapter”) został wywołany. Powiedzmy, że masz 1 przędzarkę i dzwonisz:
Zadeklaruj licznik int po metodzie onCreate, a następnie w metodzie onItemSelected () umieść warunek „if”, aby sprawdzić, ile razy wywołano ataptera. W twoim przypadku zadzwoniłeś tylko raz, więc:
źródło
Mój niewielki wkład jest odmianą niektórych z powyższych, które mi pasowały kilka razy.
Zadeklaruj zmienną całkowitą jako wartość domyślną (lub ostatnio używaną wartość zapisaną w preferencjach). Użyj spinner.setSelection (myDefault), aby ustawić tę wartość przed zarejestrowaniem detektora. W onItemSelected sprawdź, czy nowa wartość pokrętła jest równa wartości, którą przypisałeś przed uruchomieniem dalszego kodu.
Ma to tę dodatkową zaletę, że nie uruchamia kodu, jeśli użytkownik ponownie wybierze tę samą wartość.
źródło
Po tym samym problemie doszedłem do tego rozwiązania przy użyciu tagów. Pomysł jest prosty: za każdym razem, gdy pokrętło zmienia się programowo, upewnij się, że znacznik odzwierciedla wybraną pozycję. Następnie w odbiorniku sprawdzasz, czy wybrana pozycja jest równa znacznikowi. Jeśli tak, wybór pokrętła został programowo zmieniony.
Poniżej moja nowa klasa „spinner proxy”:
Będziesz także potrzebował pliku XML z ustawieniami znaczników w swoim
Values
katalogu. Nazwałem mój plikspinner_tag.xml
, ale to zależy od ciebie. To wygląda tak:Teraz wymień
w kodzie za pomocą
I spraw, by twój przewodnik wyglądał tak:
Funkcja
isUiTriggered()
zwróci wartość true tylko wtedy, gdy użytkownik zmieni przędzarkę. Zauważ, że ta funkcja ma efekt uboczny - ustawi znacznik, więc drugie połączenie w tym samym wywołaniu nasłuchującym zawsze powrócifalse
.To opakowanie poradzi sobie również z problemem wywołania detektora podczas tworzenia układu.
Miłej zabawy, Jens.
źródło
Ponieważ nic dla mnie nie działało, a moim zdaniem mam więcej niż 1 pokrętło (a IMHO trzymające mapę boolową to przesada), używam tagu do liczenia kliknięć:
źródło
Wiele odpowiedzi już jest, oto moja.
Rozszerzam
AppCompatSpinner
i dodam metodę,pgmSetSelection(int pos)
która umożliwia programowe ustawienie wyboru bez wyzwalania wywołania zwrotnego wyboru. Zakodowałem to za pomocą RxJava, aby zdarzenia selekcji były dostarczane za pośrednictwemObservable
.Przykładem jego użytkowania, zwanej w
onCreateView()
w sposóbFragment
, na przykład:gdzie
setSelection()
jest metoda w otaczającym widoku, która wygląda tak i która jest wywoływana zarówno ze zdarzeń wyboru użytkownika poprzezObservable
programowo, jak i gdzie indziej, więc logika obsługi selekcji jest wspólna dla obu metod selekcji.źródło
Chciałbym zadzwonić
po wywołaniu setAdapter (). Spróbuj także zadzwonić przed adapterem.
Zawsze masz rozwiązanie pozwalające przejść z podklasą, w której możesz zawinąć flagę logiczną do nadpisanej metody setAdapter, aby pominąć zdarzenie.
źródło
Rozwiązanie z flagą logiczną lub licznikiem nie pomogło mi, ponieważ podczas zmiany orientacji onItemSelected () wywołuje „przepełnienie” flagi lub licznika.
Podklasowałem
android.widget.Spinner
i dodawałem małe dodatki. Odpowiednie części są poniżej. To rozwiązanie działało dla mnie.źródło
To też nie jest eleganckie rozwiązanie. W rzeczywistości jest to raczej Rube-Goldberg, ale wydaje się, że działa. Upewniam się, że spinner został użyty przynajmniej raz, rozszerzając adapter macierzy i zastępując jego getDropDownView. W nowej metodzie getDropDownView mam flagę logiczną, która jest ustawiona tak, aby pokazywać, że menu rozwijane zostało użyte co najmniej raz. Ignoruję wywołania do detektora, dopóki flaga nie zostanie ustawiona.
MainActivity.onCreate ():
przesłonięty adapter tablicy:
zmodyfikowany odbiornik:
źródło
jeśli chcesz odtworzyć aktywność w locie, np .: zmiana motywów, zwykła flaga / licznik nie zadziała
użyj funkcji onUserInteraction () do wykrycia aktywności użytkownika,
odniesienie: https://stackoverflow.com/a/25070696/4772917
źródło
Mam zrobić z najprostszy sposób:
onCreate ();
Gotowy
źródło
źródło
To moje ostatnie i łatwe w użyciu rozwiązanie:
Użyj domyślnego
setSelection(...)
dla domyślnego zachowania lub użyjsetSelectionWithoutInformListener(...)
do wyboru elementu w pokrętle bez wyzwalania wywołania zwrotnego OnItemSelectedListener.źródło
Muszę użyć
mSpinner
w ViewHolder, więc flagamOldPosition
jest ustawiona w anonimowej klasie wewnętrznej.źródło
Zapisuję indeks początkowy podczas tworzenia obiektu onClickListener.
źródło
Moje rozwiązanie wykorzystuje,
onTouchListener
ale nie ogranicza jego użycia. Tworzy opakowanie wonTouchListener
razie potrzeby w razie instalacjionItemSelectedListener
.źródło
Być może odpowiadam zbyt późno na ten post, jednak udało mi się to osiągnąć za pomocą biblioteki powiązań danych Android Android Databinding . Utworzyłem niestandardowe powiązanie, aby upewnić się, że słuchacz nie jest wywoływany, dopóki wybrany element nie zostanie zmieniony, więc nawet jeśli użytkownik wybiera tę samą pozycję raz po raz, zdarzenie nie zostanie uruchomione.
Plik XML układu
app:position
to miejsce, w którym podajesz pozycję do wybrania.Wiązanie niestandardowe
Możesz przeczytać więcej o niestandardowym wiązaniu danych tutaj Android Custom Setter
UWAGA
Nie zapomnij włączyć wiązania danych w pliku Gradle
Uwzględnij pliki układu w
<layout>
tagachźródło
źródło