Android:从ListView访问子视图

109

我需要找到使用ListView显示的列表中一个元素的像素位置。似乎应该获取一个TextView,然后使用getTop(),但我无法弄清如何获取ListView的子视图。

更新: 对于ListViewViewGroup的子项与列表中的项目不是一一对应的。相反,ViewGroup的子项仅对应当前可见的视图。因此,getChildAt()操作的是ViewGroup内部的索引,并不一定与ListView使用的列表位置有任何关系。

6个回答

216

参见:Android ListView:获取可见项的数据索引 再结合以上Feet回答的部分内容,您可以得到如下内容:

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

好处是你不会遍历ListView的子元素,这可能会影响性能。


11
回答很好,但是当您的列表中具有标题视图时,它不完全起作用。将赋值给firstPosition的代码更改为 int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount(); 可以解决这个问题。 - pospi
1
很好的答案,在大多数情况下都是有效的,但我不明白为什么您认为子视图按照它们在子视图数组中的顺序出现。由于视图可以被重用,我们怎么能确定第一个视图不代表最后一个可见视图呢? - Victor Denisov
3
这只能在UI线程上发生;因此,当执行此代码时,UI线程将被阻塞,因此子视图是静止的,并且不会被修改。 ListView 已经处理了“移动”旧 convertView 后围绕着子视图的操作,因此您可以保证 ListView.getChildAt(0) 实际上是适配器中第一个附加的视图。它可能不是完全可见的(也可能根本不可见,这取决于 ListView 在回收它认为“滚出视图”的视图之前的“可见性”阈值)。 - Joe
1
@Matheus,这不是Android ListView的工作方式。如果列表项已经离开屏幕,它的视图已经被回收并重用于其他在屏幕上的列表项之一。如果这是一个要求,您应该重新考虑如何使用ListView。 - Joe
在我的情况下,唯一的方法是执行 adapter.notifyDataSetChanged() 并让适配器重新创建视图(并更新必要的内容)。但无法手动更改视图。 - Teo Inke
显示剩余3条评论

18

这段代码更易于使用:

 View rowView = listView.getChildAt(viewIndex);//The item number in the List View
    if(rowView != null)
        {
           // Your code here
        }

10
并不总是有效。getChildAt从第一个可见行开始计数,而不是从数据顶部开始。 - SteelBytes
13
ViewID 参数有些令人困惑,它是一个索引(或位置)。视图 ID 是由 aapt 工具生成的完全随意的整数。 - Johan Pelgrim

6

1
我尝试过这个方法来进行选择。通常情况下它可以工作,但有时会出现偏差一的错误,且不是很可靠。我不建议使用这种方法。 - Timmmm
谢谢Timmmm,我实际上并没有尝试过,只是在文档中找到了这个。 - Feet

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

基本上,需要创建两个Drawable - 一个是透明的,另一个是所需颜色。在点击位置请求焦点(如定义的int位置),并更改所述行的颜色。然后遍历父ListView,并相应地更改所有其他行。这适用于用户多次单击listview的情况。这是使用ListView中每一行的自定义布局完成的(非常简单,只需创建一个带有TextView的新布局文件 - 不要设置为可聚焦或可点击!)。
不需要自定义适配器 - 使用ArrayAdapter即可。

getChildAt 是相对于当前滚动的项目而言的。如果您稍微滚动一下列表,它就会停止工作。 - nurettin

4
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);

1
详细阐述一下。 - innoSPG

-9

这假设您知道ListView中元素的位置:

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

然后您应该能够调用 getLeft() 和 getTop() 方法来确定元素在屏幕上的位置。


43
当ListView填充列表时,getView()将被内部调用。您不应该在该位置使用它来获取列表中的视图,因为如果使用null作为convertView参数调用getView(),则会导致适配器从适配器的布局资源中膨胀出一个新视图(而不是获取已经显示的视图)。 - Joe
1
偶数位置将是视觉屏幕上的一个位置,而不是适配器数据源的位置。 - Vikas
@jasonhudgins 这是我一直在使用的方法,因为我认为它是最好的,但当我尝试使子视图中的进度条不可见时,却使列表视图中的所有进度条都不可见了......总之,被接受的答案解决了这个问题。 - Edijae Crusar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接