Android: Access child views from a ListView
Asked Answered
P

6

109

I need to find out the pixel position of one element in a list that's been displayed using a ListView. It seems like I should get one of the TextView's and then use getTop(), but I can't figure out how to get a child view of a ListView.

Update: The children of the ViewGroup do not correspond 1-to-1 with the items in the list, for a ListView. Instead, the ViewGroup's children correspond to only those views that are visible right now. So getChildAt() operates on an index that's internal to the ViewGroup and doesn't necessarily have anything to do with the position in the list that the ListView uses.

Podgorica answered 2/11, 2008 at 22:40 Comment(0)
D
216

See: Android ListView: get data index of visible item and combine with part of Feet's answer above, can give you something like:

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

The benefit is that you aren't iterating over the ListView's children, which could take a performance hit.

Dalton answered 20/4, 2010 at 23:1 Comment(8)
Great answer, but doesn't quite work right when you have header views in your list. The assignment to firstPosition should be int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount(); to fix this.Icosahedron
@pospi: thanks, good point! I've updated my answer to account for that.Dalton
Good answer and works in majority of cases, but I don't understand why you believe that child views appear in the order they are in the array of child views. Since views can be reused how can we be sure that first view doesn't represent the last visible view?Arteriovenous
@VictorDenisov: this can only happen on the UI thread; therefore the UI thread will be blocked while this code is executing, thus the child views are stationary and will not be modified. ListView is already handling "moving" the child views around after recycling old convertViews, etc, so you can be guaranteed that ListView.getChildAt(0) is in fact the first attached view from the adapter. It might not be fully visible (and might not even be visible at all, depending on ListView's threshold of "visibility" before recycling a view that it considers is "scrolled out of view")Dalton
@Matheus that's not how Android ListView works. If the list item is off-screen, its view has already been recycled and reused for one of the other list items that are on screen. You should re-think how you're using ListView if that's a requirement.Dalton
I've got the same question as Matheus'. Working on an ExpandableListView, if I do something with the child and the groupView is not being shown, I can't change its view. It's not redrawn by the adapter btw, I checked it. I'd like to see a solution for thatClannish
The only way in my case was to do adapter.notifyDataSetChanged() and let the adapter recreate the Views (and update what's necessary). But no way to change the View manually.Clannish
In what method of Activity or Fragment I should use this code?Parcel
P
18

This code is easier to use:

 View rowView = listView.getChildAt(viewIndex);//The item number in the List View
    if(rowView != null)
        {
           // Your code here
        }
Pascale answered 1/1, 2012 at 8:20 Comment(2)
doesn't work always. getChildAt counts from the first visible row, not from the top of the data.Gargantua
The ViewID argument is confusing. It's an index (or position). The view ID is a completely arbitrary integer generated by the aapt tool.Frey
H
6

A quick search of the docs for the ListView class has turned up getChildCount() and getChildAt() methods inherited from ViewGroup. Can you iterate through them using these? I'm not sure but it's worth a try.

Found it here

Halt answered 2/11, 2008 at 23:4 Comment(2)
I've tried this, for doing selections. It usually works but there are off-by-one errors sometimes and it isn't reliable. I wouldn't recommend it.Keratinize
Thanks Timmmm, I hadn't actually tried it just found it in the docs.Halt
H
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));
            }
        }
    }
});

Basically, create two Drawables - one that is transparent, and another that is the desired color. Request focus at the clicked position (int position as defined) and change the color of the said row. Then walk through the parent ListView, and change all other rows accordingly. This accounts for when a user clicks on the listview multiple times. This is done with a custom layout for each row in the ListView. (Very simple, just create a new layout file with a TextView - do not set focusable or clickable!).

No custom adapter required - use ArrayAdapter

Hundredth answered 27/6, 2013 at 13:54 Comment(1)
getChildAt is relative to current scrolled items. This will stop working if you scroll your list just a bit.Disused
S
4
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
Stinking answered 29/1, 2016 at 8:11 Comment(1)
Elaborate a little bitValenba
A
-9

This assumes you know the position of the element in the ListView :

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

Then you should be able to call getLeft() and getTop() to determine the elements on screen position.

Airsick answered 4/12, 2008 at 14:8 Comment(3)
getView() is called internally by the ListView, when populating the list. You should not use it to get the view at that position in the list, as calling getView() with null for the convertView causes the adapter to inflate a new view from the adapter's layout resource (does not get the view that is already being displayed).Dalton
Even position will be a position in visual screen, not to the adapter's data source position.Deflocculate
@Airsick This is the method i have been using for i thought it is the best but when i try to make a progressbar in the child view invisible, but instead it made all progressbars in listvew invisible........all in all the accepted answer did the trickAnglim

© 2022 - 2024 — McMap. All rights reserved.