zoukankan      html  css  js  c++  java
  • android ListView 在初始化时多次调用getView()原因分析

       今天在做一个功能:在初始化ListView时,把第一行背景置为黄色,同时保存第一行对象,用于在点击其他行时将该行重新置为白色。

    if(position==0){
                    convertView.setBackgroundColor(Color.YELLOW);
                    lastconvertView=convertView;
                }

    结果运行时发现第一行的颜色一直会是黄色而无法改变。调试了之后发现getView中 if(position==0) 居然会多次进入,最终导致的结果便是我最后一次取得的lastconvertView并非listview上面的第一行。网上查了之后发现原因是因为未固定listview的高度导致的,但是root cause却找不到说明。于是去翻阅了源码+大量调试,大概推算出了原因,在此记录。

    首先是说明下ListView的显示机制,listview的机制是这样子的:

    假如你有1000条数据,但是屏幕只能显示10条,那么当你第一次加载显示的时候,会先创建10个View,1-10,当你拖动Listview,使1隐藏而11显示的时候,系统会自动把填充1的View传递过来,注意看代码Adapter的getView方法

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) 

    这里的converView就是1的view,一般的做法会把这个view拿来复用,作为11的view。

    当我们固定listview的高度时(fill_parent或直接固定高度),那么listview很容易就能计算出容器内可以显示多少行。但如果我们使用了“wrap_content”,只有在屏幕内控件完全加载后才知道到底能显示多少行数据时,ListView自身便会做一些尝试性计算。在源码中可以发现一些叫做onMeasure的方法,目测是做此用处(源码略显复杂,没读透)。

    当listview计算出屏幕一共需要多少行后,如果listview自身高度不变,那么它的容纳的行数就不会变,使用getChildCount()可以得到它的最大行数。

    再回到原来的问题,为什么最后一次取得的结果不是listview的第一行呢? 将listview设置为“wrap_content”后用下面的测试代码,看下输出。

    //获取当前listview的个数 相等输出个数和站点名 不相等输出个数和"无"
                if(listView.getChildCount() == position)
                {
                    //child个数 当前position位置 +站名
                    Log.i("", listView.getChildCount()+" "+position+" "+coordInfo.stationname);
                }
                else {
                    //child个数 当前position位置+无
                    Log.i("", listView.getChildCount()+" "+position+" "+"无");
                }

    在我的测试应用中,listview刚好可以放11个view,看下输出发现,listview在开始时,实例化了11个view进行填充,即前面10个“无”结尾的+第一个”客运中心“结尾的view,由此测量出了listview的容量。换句话说,这11个view都只是用于测量的临时view。另外在正是填充完之后,listview再次创建了11个临时view用于确认高度是否正确。而由于我的代码逻辑设计失误,在

    进行到这一步时,由于position会再次等于0,因此会把一个临时的view赋值给lastconvertView。

    到此原因找到,同时将listview的高度设为fill_parent后,问题解决。

    另外说下网上说的另外一个解决方法

    if(parent.getchildcount() == position)
    {
           正常情况下应该执行的代码
    }
    else
    {
         这里就是多次加载的问题,可以不用理这里面的 代码,
    }

    这个方法是不可行的,因为在不改变listview高度的情况下,listview的getchildcount()在加载完成后是固定的,position指的却是在adapter中的位置,当adapter的个数大于listview的容纳个数时,该判断条件不会成立,即滑动listview时,不会成立。

  • 相关阅读:
    SQL Server误区30日谈Day15CheckPoint只会将已提交的事务写入磁盘
    SQL Server误区30日谈Day16数据的损坏和修复
    【译】Windows Azure迁移生命周期概览
    【译】实现为Windows Azure制定的迁移计划
    SQL Server误区30日谈Day18有关FileStream的存储,垃圾回收以及其它
    【译】为迁移到Windows Azure制定规划
    SQL Server误区30日谈Day14清除日志后会将相关的LSN填零初始化
    SQL PASS北京用户群成功举办第一次线下活动,性能调优PPT分享
    Windows Azure虚拟机概览
    SQL Pass北京将举办第一次线下活动,欢迎大家报名
  • 原文地址:https://www.cnblogs.com/linjzong/p/3494090.html
Copyright © 2011-2022 走看看