Android: dostęp do widoków podrzędnych z ListView

108

Muszę znaleźć pozycję piksela jednego elementu na liście, która została wyświetlona przy użyciu ListView. Wygląda na to, że powinienem pobrać jeden z TextView, a następnie użyć getTop(), ale nie mogę dowiedzieć się, jak uzyskać widok podrzędny pliku ListView.

Aktualizacja: elementy podrzędne elementu ViewGroupnie odpowiadają 1-do-1 elementom na liście, dla a ListView. Zamiast tego ViewGroupdzieci odpowiadają tylko tym widokom, które są teraz widoczne. Więc getChildAt()działa na indeksie, który jest wewnętrzny w stosunku do indeksu ViewGroupi nie musi mieć nic wspólnego z pozycją na liście, której ListViewużywa.

lakierować
źródło

Odpowiedzi:

215

Zobacz: Android ListView: pobierz indeks danych widocznego elementu i połącz z częścią odpowiedzi Feeta powyżej, może dać ci coś takiego:

int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount(); // This is the same as child #0
int wantedChild = wantedPosition - firstPosition;
// Say, first visible position is 8, you want position 10, wantedChild will now be 2
// So that means your view is child #2 in the ViewGroup:
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
  Log.w(TAG, "Unable to get view for desired position, because it's not being displayed on screen.");
  return;
}
// Could also check if wantedPosition is between listView.getFirstVisiblePosition() and listView.getLastVisiblePosition() instead.
View wantedView = listView.getChildAt(wantedChild);

Zaletą jest to, że nie wykonujesz iteracji po elementach podrzędnych ListView, co może mieć wpływ na wydajność.

Joe
źródło
10
Świetna odpowiedź, ale nie działa dobrze, gdy na liście są widoki nagłówków. Zadaniem firstPositionpowinno być int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();naprawienie tego.
pospi
@pospi: dzięki, słuszna uwaga! Zaktualizowałem swoją odpowiedź, aby to wyjaśnić.
Joe
Dobra odpowiedź i działa w większości przypadków, ale nie rozumiem, dlaczego uważasz, że widoki dzieci pojawiają się w kolejności, w jakiej znajdują się w tablicy widoków potomnych. Ponieważ widoki można ponownie wykorzystać, jak możemy mieć pewność, że pierwszy widok nie reprezentuje ostatniego widocznego widoku?
Victor Denisov
2
@VictorDenisov: może się to zdarzyć tylko w wątku interfejsu użytkownika; w związku z tym wątek interfejsu użytkownika zostanie zablokowany podczas wykonywania tego kodu, a zatem widoki podrzędne są stacjonarne i nie zostaną zmodyfikowane. ListViewjuż jest handling „ruchomych” poglądy dzieci dookoła po recyklingu starych convertViews, itp, więc może być zagwarantowane, że ListView.getChildAt(0) jest w rzeczywistości pierwszym załączeniu widok z adapterem. Może nie być w pełni widoczny (i może nawet nie być widoczny, w zależności od ListViewprogu „widoczności” przed odtworzeniem widoku, który uważa za „przewinięty z widoku”)
Joe
1
@Matheus, tak nie działa Android ListView. Jeśli element listy znajduje się poza ekranem, jego widok został już przetworzony i ponownie wykorzystany dla jednego z pozostałych elementów listy, które na ekranie. Powinieneś ponownie przemyśleć, jak używasz ListView, jeśli jest to wymagane.
Joe
17

Ten kod jest łatwiejszy w użyciu:

 View rowView = listView.getChildAt(viewIndex);//The item number in the List View
    if(rowView != null)
        {
           // Your code here
        }
Kalimah
źródło
9
nie zawsze działa. getChildAt liczy od pierwszego widocznego wiersza, a nie od góry danych.
SteelBytes,
13
Argument ViewID jest mylący. To indeks (lub pozycja). Identyfikator widoku to całkowicie dowolna liczba całkowita generowana przez narzędzie aapt.
Johan Pelgrim,
6

Szybkie przeszukanie dokumentacji dla klasy ListView pozwoliło uzyskać metody getChildCount () i getChildAt () odziedziczone po ViewGroup. Czy możesz je powtórzyć za pomocą tych? Nie jestem pewien, ale warto spróbować.

Znalazłem to tutaj

Stopy
źródło
1
Próbowałem tego, do robienia selekcji. Zwykle działa, ale czasami zdarzają się pojedyncze błędy i nie jest niezawodne. Nie polecałbym tego.
Timmmm,
Dzięki Timmmm, właściwie nie próbowałem tego właśnie znalazłem w dokumentacji.
Stopy
5
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
        View v;
        int count = parent.getChildCount();
        v = parent.getChildAt(position);
        parent.requestChildFocus(v, view);
        v.setBackground(res.getDrawable(R.drawable.transparent_button));
        for (int i = 0; i < count; i++) {
            if (i != position) {
                v = parent.getChildAt(i);
                v.setBackground(res.getDrawable(R.drawable.not_clicked));
            }
        }
    }
});

Zasadniczo utwórz dwa Drawables - jeden, który jest przezroczysty, a drugi o pożądanym kolorze. Poproś o zaznaczenie klikniętej pozycji ( int positionzgodnie z definicją) i zmień kolor wspomnianego wiersza. Następnie przejdź przez rodzica ListViewi odpowiednio zmień wszystkie pozostałe wiersze. Uwzględnia to sytuacje, gdy użytkownik kliknie listviewwiele razy. Odbywa się to za pomocą niestandardowego układu dla każdego wiersza w ListView. (Bardzo proste, po prostu utwórz nowy plik układu z TextView- nie ustawiaj fokusu ani klikalności!).

Nie jest wymagany niestandardowy adapter - użyj ArrayAdapter

Wes
źródło
getChildAt jest zależne od aktualnie przewijanych elementów. To przestanie działać, jeśli przewiniesz listę tylko trochę.
nurettin
4
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
Milaaaad
źródło
1
Rozwiń
-9

Zakłada się, że znasz pozycję elementu w ListView:

  View element = listView.getListAdapter().getView(position, null, null);

Wtedy powinieneś móc wywołać getLeft () i getTop () w celu określenia pozycji elementów na ekranie.

jasonhudgins
źródło
43
getView()jest wywoływana wewnętrznie przez ListView podczas wypełniania listy. Nie należy go używać do pobierania widoku na tej pozycji na liście, ponieważ wywołanie getView()z wartością null dla argumentów convertViewpowoduje, że adapter wypełnia nowy widok z zasobu układu adaptera (nie uzyskuje widoku, który jest już wyświetlany).
Joe
1
Pozycja równa będzie pozycją na ekranie wizualnym, a nie pozycją źródła danych adaptera.
Vikas
@jasonhudgins Jest to metoda, której używałem, ponieważ uważałem ją za najlepszą, ale kiedy próbuję uczynić pasek postępu w widoku dziecka niewidocznym, ale zamiast tego wszystkie paski postępu w listvew stają się niewidoczne ........ wszystko w wszystkie zaakceptowane odpowiedzi
załatwiały sprawę