Android 遇坑【6】 - ListView getView()多次调用问题

问题描述

其实这是很久之前准备写的博客...

在学习 Spinner 时仅总结了 静态加载 的方法,动态加载 方法可参照 Android UI - Spinner

而在实际使用中,当存在较多 Spinner 时,使用静态数据显然是不科学的(一个Spinner一个xml文件心好累的 )。

那么我们需要用适配器动态的添加数据,可参考Android 基础知识学习 - spinner 使用

动态添加 Spinner 适配器数据 与 ListView 结合时,由于我是在 ListView getView( ) 方法中实现数据动态加载,实验结果发现Spinner列表项 的数据被多次动态添加(且不规则),导致错乱。

原因

ListView 的高度为 xxdp (固定) 或者 match_parent 的时候,ListView 很容易就能计算出容器内可以显示多少行。但如果我们使用了 wrap_content,这是只有在屏幕内控件完全加载后才知道到底能显示多少行数据,ListView 自身便会做一些尝试性计算,就会导致重复调用多次 getView()

而我在 getView()中调用了一个

spinner_data.getOption();

在这个方法中,我将 Spinner 列表项数据 add( ) 后,再返回该 ArrayList。多次调用时,会造成多次添加的问题。

好了,找到问题所在,对症下药。

解决方法

先说说我的问题

修改 spinner_data.getOption() 方法,让它只返回 ArryList实例;而添加数据的工作,放到构造函数中,这样可以避免多次调用 add() 方法。

当然这只是针对本人 demo 的一些修改,如果你有 Spinner 动态添加数据 + ListView 更好的做法,欢迎留言拍我。

通用解决方法-简单粗暴版

根据上文原因描述,有一个特别简单的解决方法:指定 ListView 的高度为固定值,这样就不会触发多次调用的问题。

且不说高度的计算方法,单单是每次增删 ListView Item项 的时候,都需要手动计算再修改高度属性...我就无法接受。

如果你也和我一样,那么请接往下看。

通用解决方法-动态设置listView高度

任何一个视图都不可能凭空突然出现在屏幕上,它们都是要经过非常科学的绘制流程后才能显示出来的;ListView 中的 Item控件 也一样。每一个视图的绘制过程都必须经历三个最主要的阶段,即 onMeasure ( )onLayout ( ) onDraw ( )

measure 阶段时主要就是为了计算两个参数:height width。而且要注意的是,这是个递归的过程,从顶向下,DecorView 开始依次调用自己子元素的 Measure。计算完成这两个参数后就开始 Layout,最后再是 Draw的调用。

虽然我在源码中并没有找出具体的原因,但如果你自己做一下实验的话就会发现,即使是一个再简单的 View,在展示到界面上之前都会经历至少两次 onMeasure () 和两次 onLayout () 的过程。          ---- 郭霖

对于 ListView,每显示一个 ItemVIew,它都去测量 View 的高度,执行 onMeasure ( ) 方法,导致 getView( ) 执行多次。

那么我们可以通过以下函数 动态设置ListView高度

    private void fixListViewHeight(ListView listView)
    {
        int totalHeight = 0;
        // 如果没有设置数据适配器,则 ListView 没有子项,返回
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null)
            return;

        for (int index = 0,len = listAdapter.getCount(); index < len; index++ )
        {
            View listViewItem = listAdapter.getView(index,null,listView);
            // 计算子项 View 的宽高
            listViewItem.measure(0, 0);
            // 计算所有子项的高度和
            totalHeight += listViewItem.getMeasuredHeight();
        }

        ViewGroup.LayoutParams params = listView.getLayoutParams();
        //listView.getDividerHeight () 获取子项间分隔符的高度
        //params.height 设置 ListView 完全显示需要的高度
        params.height = totalHeight + (listView.getDividerHeight()*(listAdapter.getCount()-1));

        listView.setLayoutParams(params);
    }

使用也很简单

fixListViewHeight(listView);

ps:注意该方法需在ListView设置完设配器后调用。

demo 效果

参考鸣谢

ListView,GridView 的 getView 多次调用问题

解决 listview,gridview 的 getview 多次调用问题

关于 ListView 的 getView 方法被多次重复调用的问题

Android 视图绘制流程完全解析,带你一步步深入了解 View (二)

Android ListView 工作原理完全解析,带你从源码的角度彻底理解

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值