ListView getChildAt返回可见孩子的null


68

我从listview / getChildAt方法得到一些奇怪的行为。

我有一个HashSet,iconsToUpdate,包含数据库中已更改的图标。我想遍历可见行以查看是否需要更新其任何图标以反映新图标。我不需要测试当前不在视图中的图标,因为它们在渲染时会正确绘制。

我的问题是,getChildAt似乎不应该返回null。我知道getChildAt只能返回当前可见的视图,但是对于某些可见行它返回null。

这是我的代码,遍历可见行:

Logger.debug("First visible index: " + f_listView.getFirstVisiblePosition());
Logger.debug("Last visible index: " + f_listView.getLastVisiblePosition());
for (int i = f_listView.getFirstVisiblePosition(); i <= f_listView.getLastVisiblePosition(); i++) {
    String tag = "asdf"; // Remove when bug is fixed.
    if (f_listView == null) {
        Logger.debug("f_listView is null");
    } else if (f_listView.getChildAt(i) == null) {
        Logger.debug("Child at index " + i + " is null");
    } else {
        tag = (String) f_listView.getChildAt(i).getTag();
        Logger.debug("Successful at index " + i + ", tag is: " + tag);
    }
    if (iconsToUpdate.contains(tag)) {
        setIcon(i, f_aim.getInHouseIcon(tag));
    }
}

这是与此循环运行相对应的日志:

D/...: First visible index: 3
D/...: Last visible index: 8
D/...: Successful at index 3, tag is: ...
D/...: Successful at index 4, tag is: ...
D/...: Successful at index 5, tag is: ...
D/...: Child at index 6 is null
D/...: Child at index 7 is null
D/...: Child at index 8 is null

应当注意,正确地报告了第一个和最后一个可见索引,因为我在运行此行时正在查看第3-8行。第6、7、8行的渲染正确。如果它们为null,如何显示?

另外,我不知道这是否重要,但是当我位于列表视图的顶部时,第5行是最后可见的行。

关于为什么将这些行返回为null的任何信息,将不胜感激。

谢谢!

Answers:


135

listView.getChildAt(i)起作用,其中0是第一可见行,而(n-1)是最后可见行(其中n是您看到的可见视图数)。

get last / first visible返回您在dataAdapter中的位置。因此,由于您从位置3开始,看起来像6个可见视图,所以当获得位置6-8时,您将获得null。

在您的示例中,getChildAt(0)将返回位置3。通常,我将位置存储在视图中,以便以后在需要值的情况下在dataAdapter上进行查找。

我认为您的for循环应如下所示:

for (int i = 0; i <= f_listView.getLastVisiblePosition() - f_listView.getFirstVisiblePosition(); i++) 

希望这可以帮助。


太好了,很高兴我能提供帮助。如果可以的话,也请接受,我当然喜欢获得积分:)
C Nick

67
啊哈!谢谢。如果文档明确指出getChildAt(i)使用可见位置而不是逻辑位置,将很有用。本来可以节省很多头发。
SMBiggs 2012年

3
确实,文档中没有对此进行描述。@CNick:对此有任何参考吗?
pfalcon

我尝试了同样的for循环,但返回null。有什么想法吗?
tejas

哇,您解决了一个我整整工作半天的错误
Pete 2014年


3

当您调用listView1.getLastVisiblePosition()和时listView1.getFirstVisiblePosition()listview返回在列表视图中部分可见的项目的位置。例如,第一项可能是一半可见的,而最后一项可能是一半可见的。在这种情况下,即使您可以在列表视图中看到该项目的一部分,适配器仍未调用getView()该项目的函数,因此该项目仍被视为空。


谁能确认这是真的吗?
LarsH

2

就我而言,我有一个,listView并且第一个项目是完全可见的,但是当我这样做时getFirstVisiblePosition(),结果是1!

当我滚动并使第一个项目可见一半时,它变得更加奇怪,然后我的getView显示getFirstVisiblePosition()为0。

这是我的代码:

public View getView(final int position, View convertView, final ViewGroup parent) {
        row = convertView;
        final AtomPaymentHolder holder = new AtomPaymentHolder();

        LayoutInflater inflater = ((Activity) context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);
        row.setTag(items.get(position));
        holder.songitem = items.get(position);
        final int firstPosition = MainActivity.listviewSong.getFirstVisiblePosition() - MainActivity.listviewSong.getHeaderViewsCount(); 
        final int lastPosition = MainActivity.listviewSong.getLastVisiblePosition();

        if(MusicService.indexService>= firstPosition && MusicService.indexService<= lastPosition){
            MainActivity.listviewSong.getChildAt(MusicService.indexService-firstPosition).setBackgroundColor(getContext().getResources().getColor(R.color.pressed_color));
        }

(当我选择下一个项目并滚动时,效果很好)


0

他们也许是部分可见的。但是,当getView()尝试回收视图时,将获得完全可见的视图,而其他视图则将其视为null。例如,如果您滚动列表,并且顶部项目是部分可见的,底部项目是部分可见的,则这些为null,但您仍然可以看到它们。


嗯,所有第3-8行都是完全可见的。无法拥有三个连续的部分可见的行,因此我认为这里还有更多事情要做。谢谢您的回应!
克里斯,

尝试从i = 0开始?并检查输出
Nikola Despotoski 2011年

0

我曾经在OnListItemClickListener()中尝试过,但是失败了。最后,我在用于列表视图的自定义适配器中进行了一些修改。在getView()中,我将clickListener应用于经常添加到列表中的项目。n在此执行所有必需的功能。这是我的代码,我在列表n中添加了Image View,因此在imageview上应用了监听器。

我认为这将对那些希望在选择特定“列表项”时更改颜色的人有所帮助。去吧..

在自定义适配器的getView()中// --------------------------------- code ------- -----------------------------------

LayoutInflater inflater =(LayoutInflater)上下文 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

View rowView = inflater.inflate(R.layout.icon_image_layout, parent, false); ImageView imageView = (ImageView) rowView.findViewById(R.id.Icon_ImageView); imageView.setClickable(true); final int pos=position; imageView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub // TODO Auto-generated method stub try{ if(previous_view!=null) previous_view.setBackgroundColor(Color.WHITE); }catch (Exception e) { System.out.println("Exception Occurs Previous View"); } v.setBackgroundColor(Color.RED); MainActivity.imageView.setImageResource(MainActivity.Image_Name[pos]); previous_view=v; return false; } });

0

我知道这是很老的帖子。但是我之所以回答是因为人们仍在寻找有关ListViewgetChildAt()空指针异常的解决方法。

这是因为ArrayApdater正在删除和回收由于高度而在ListView上尚不可见的视图。这样,如果您有10个项目视图,并且ListView一次可以显示4-5:

适配器在位置5到9处删除项目视图,因此任何尝试adapter.getChildAt(5... to 9)都会导致空指针异常

适配器还会回收项目视图,因此,当您向下滚动至5到9时,例如在位置3上所做的任何引用都将丢失,并且在位置3上进行的任何输入(EditText,Checkbox等)也将丢失。向下滚动至5到9时将被回收,以后将在另一个位置(例如1、2或3等)重复使用,其值相同

我发现控制此问题的唯一方法是忘记获取View并拥有:

HashMap<Integer, ImageView> iconViews您想要处理列表中每个项目要使用的值的属性或任何类型。第一种类型对于像item->getId()或的项目必须是唯一的position。用new HashMap<>()构造函数初始化它;在getViews中,使“iconViews.put(position, iconView); 防止适配器”使用回收的convertView,删除条件,if(convertView == null)以便适配器始终为全新的视图实例充气。因为视图实例每次都是新的,所以您必须每次都从HashMap设置值,就像它已经包含key一样if(iconViews.containsKey(position)){iconView = iconViews.get(position))};。在这种情况下,可能没有大量的项,因此不必进行平滑滚动。

最后创建公共方法,以将item->getId()Integer作为参数传递给Adapter之外的值。例如:public ImageView getIconViewAt(int position) { return iconViews.get(position); }。然后很容易从适配器中选择项目

从我的答案中了解更多。


-2

不是因为你在说

f_listView.getChildAt(i) 

并且您应该在那个位置取回物品?

f_listView.getItemAtPosition(i) 

1
我觉得不是。我相信getItemAtPosition会从适配器(而不是视图)获取该行的数据
SteelBytes 2012年

是的,steelbytes。如果使用CursorAdapter,它将返回光标。
WindRider 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.