Jak dynamicznie aktualizować ListView w systemie Android [zamknięte]

159

W systemie Android, jak mogę to ListViewfiltrować na podstawie danych wejściowych użytkownika, w przypadku gdy wyświetlane elementy są aktualizowane dynamicznie na podstawie TextViewwartości?

Szukam czegoś takiego:

-------------------------
| Text View             |
-------------------------
| List item             |
| List item             |
| List item             |
| List item             |
|                       |
|                       |
|                       |
|                       |
-------------------------
Hamy
źródło
7
Nominację do ponownego otwarcia. Zaktualizowałem tekst, aby uczynić to bardziej pytanie, i jest to oczywiście cenny zasób społeczności, ponieważ odpowiedzi wciąż napływają
Hamy
Otwórz ponownie pytanie. To jest oczywiście pomocne.
SilentNot

Odpowiedzi:

286

Najpierw musisz utworzyć układ XML, który ma zarówno EditText, jak i ListView.

<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <!-- Pretty hint text, and maxLines -->
    <EditText android:id="@+building_list/search_box" 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="type to filter"
        android:inputType="text"
        android:maxLines="1"/>

    <!-- Set height to 0, and let the weight param expand it -->
    <!-- Note the use of the default ID! This lets us use a 
         ListActivity still! -->
    <ListView android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1" 
         /> 

</LinearLayout>

Spowoduje to poprawne ułożenie wszystkiego, z ładnym tekstem edycji nad ListView. Następnie utwórz ListActivity w normalny sposób, ale dodaj setContentView()wywołanie do onCreate()metody, abyśmy używali naszego niedawno zadeklarowanego układu. Pamiętaj, że zidentyfikowaliśmy ListViewspecjalnie, z android:id="@android:id/list". Dzięki temu ListActivitywie, którego ListViewchcemy użyć w naszym zadeklarowanym układzie.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.filterable_listview);

        setListAdapter(new ArrayAdapter<String>(this,
                       android.R.layout.simple_list_item_1, 
                       getStringArrayList());
    }

Uruchomienie aplikacji powinno teraz pokazać poprzednią ListView, z ładnym pudełkiem powyżej. Aby zmusić to pole do zrobienia czegoś, musimy pobrać z niego dane wejściowe i uczynić z tego filtru listę. Chociaż wiele osób próbowało zrobić to ręcznie, większość ListView Adapter klas zawiera Filterobiekt, którego można użyć do automatycznego filtrowania. Musimy tylko rury wejście od EditTextdo Filter. Okazuje się, że jest to całkiem proste. Aby przeprowadzić szybki test, dodaj tę linię do onCreate()rozmowy

adapter.getFilter().filter(s);

Zauważ, że będziesz musiał zapisać swoje ListAdapterw zmiennej, aby to działało - zapisałem moje ArrayAdapter<String>z wcześniej w zmiennej o nazwie „adapter”.

Następnym krokiem jest pobranie danych wejściowych z pliku EditText. To wymaga trochę namysłu. Możesz dodać OnKeyListener()do swojego EditText. Jednak ten odbiornik odbiera tylko niektóre kluczowe zdarzenia . Na przykład, jeśli użytkownik wpisze „wyw”, przewidywany tekst prawdopodobnie zaleci słowo „oko”. Dopóki użytkownik nie wybierze „wyw” lub „eye”, OnKeyListenernie otrzymasz kluczowego zdarzenia. Niektórzy mogą preferować to rozwiązanie, ale uznałem to za frustrujące. Chciałem każdego kluczowego zdarzenia, więc miałem wybór, czy filtrować, czy nie. Rozwiązaniem jest TextWatcher. Po prostu utwórz i dodaj a TextWatcherdo EditTexti przekaż ListAdapter Filterżądanie filtru za każdym razem, gdy zmieni się tekst. Pamiętaj, aby usunąć TextWatcherin OnDestroy()! Oto ostateczne rozwiązanie:

private EditText filterText = null;
ArrayAdapter<String> adapter = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.filterable_listview);

    filterText = (EditText) findViewById(R.id.search_box);
    filterText.addTextChangedListener(filterTextWatcher);

    setListAdapter(new ArrayAdapter<String>(this,
                   android.R.layout.simple_list_item_1, 
                   getStringArrayList());
}

private TextWatcher filterTextWatcher = new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before,
            int count) {
        adapter.getFilter().filter(s);
    }

};

@Override
protected void onDestroy() {
    super.onDestroy();
    filterText.removeTextChangedListener(filterTextWatcher);
}
Hamy
źródło
7
Czy istnieje prosty sposób na filtrowanie ListView w „zawiera” zamiast „zaczyna się od”, jak to robi to rozwiązanie?
Viktor Brešan
12
Viktor - Jeśli interesujące Cię słowa są oddzielone spacjami, zrobi to automatycznie. W przeciwnym razie nie do końca. Prawdopodobnie najłatwiejszym sposobem byłoby utworzenie podklasy Adaptera przez rozszerzenie go i zastąpienie metody getFilter w celu zwrócenia zdefiniowanego obiektu Filter. Zobacz github.com/android/platform_frameworks_base/blob/master/core/…, aby zrozumieć, jak działa domyślny ArrayFilter - łatwo byłoby skopiować 95% tego kodu i zmienić wiersze 479 i 486
Hamy
2
Hamy, doskonały opis! Mam jednak pytanie: zaimplementowałem to i po każdej literze, którą wpisuję, ListView znika na kilka sekund, a następnie wraca, przefiltrowany. Czy doświadczyłeś tego? Moja intuicja jest taka, że ​​to dlatego, że mam ponad 600 pozycji na liście, z nietrywialnymi funkcjami toString ().
lowellk
2
W trybie poziomym na mniejszym ekranie klawiatura EditText + zajmuje cały ekran, a ListView nie jest widoczny! Jakieś rozwiązanie?
Martin Konicek
4
Czy to naprawdę konieczne? > Pamiętaj, aby usunąć TextWatchera w OnDestroy ()
Jojo,
10

uruchomienie programu spowoduje wymuszone zamknięcie.

Zamieniłem linię:

android: id = "@ + building_list / search_box"

z

android: id = "@ + id / search_box"

czy to może być problem? Do czego służy „@ + building_list”?

j7nn7k
źródło
Johe, kiedy mówisz R.id. Coś, coś istnieje w id, ponieważ powiedziałeś android: id = "@ + id / search_box". Jeśli powiesz android: id = "@ + building_list / search_box", to w kodzie możesz wywołać findViewById (R.building_list.search_box); Jaki jest wyjątek najwyższego poziomu, który otrzymujesz? Ten kod jest kopiowany bez kompilacji testowej, więc prawdopodobnie zostawiłem gdzieś co najmniej jeden błąd
Hamy
Cześć Hamy, w kodzie odwołałeś się do „@ + building_list / search_box” z „filterText = (EditText) findViewById (R.id.search_box);” Dlatego się zastanawiałem.
j7nn7k
1
Występujące forceclose podczas rozpoczynania wpisywania. Część błędu: ERROR / AndroidRuntime (188): java.lang.NullPointerException 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): w xxx.com.ListFilter $ 1. onTextChanged (ListFilter.java:46) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): at android.widget.TextView.sendOnTextChanged (TextView.java:6102) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): w android.widget.TextView.handleTextChanged (TextView.java:6143) 02-11 07: 30: 29.828: ERROR / AndroidRuntime (188): w android.widget.TextView $ ChangeWatcher.onTextChanged (TextView.java : 6286)
j7nn7k
Johe, Ups - wygląda na to, że próbowałem zmodyfikować kod, aby pozbyć się @ + building_list (ponieważ jest to kwestia zamieszania), ale nie wszędzie go złapałem. Dzięki za wskazówkę! Sama zmiana na @ + id powinna to naprawić
Hamy
4

miałem problem z filtrowaniem, wyniki zostały przefiltrowane, ale nie zostały przywrócone !

więc przed filtrowaniem (rozpoczęciem aktywności) utworzyłem kopię zapasową listy ... (tylko kolejna lista, zawierająca te same dane)

podczas filtrowania filtr i listadapter są łączone z listą podstawową.

ale sam filtr używał danych z listy kopii zapasowych.

w moim przypadku zapewniło to, że lista była aktualizowana natychmiastowo i nawet po usunięciu wyszukiwanych haseł lista jest w każdym przypadku przywracana pomyślnie :)

i tak dzięki za to rozwiązanie.

cV2
źródło