ListView相关内容

一、ListView优化

列表的显示需要三个元素:

  1. ListVeiw: 用来展示列表的View。
  2. 适配器 : 用来把数据映射到ListView上
  3. 数据: 具体的将被映射的字符串,图片,或者基本组件。
适配器
  1. ArrayAdapter:只能展示一行字
  2. SimpleAdapter:扩充性最好
  3. BaseAdapter:自定义Adapter

ListView加载数据原理

系统要绘制ListView,首先调用getCount()函数得到ListView要显示的总条目数,再绘制Item条目,调用getView()函数。获得一个View(加载item要显示的布局),然后再实例化并设置各个组件及其数据内容并显示它。

ListView 针对每个item,要求 adapter “返回一个视图” (getView),也就是说ListView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。

ListView优化原理

这里写图片描述

  1. 使用convertView可以避免重复地调用inflate
  2. 使用ViewHolder可以避免重复地调用findViewById

ListView中的每一个Item显示都需要Adapter调用一次getView的方法,这个方法会传入一个convertView的参数,返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存,创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)也是耗时操作,所以必将影响性能。Android提供了一个叫做Recycler(反复循环器)的 构件,就是当ListView的Item从上方滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,相应的会从下方生成一个 Item,而此时调用的getView中的convertView参数就是滚出屏幕的Item的View,所以说如果能重用这个convertView, 就会大大改善性能。

  1. convertView复用:判断convertView是否为空,当convertView为空时不重复使用,为空时初始化,尽可能少创建view(View.inflate(….)的方法,将xml文件解析,并显示到界面上,这是非常消耗资源的)
  2. 定义一个viewHolder,封装Item条目中所需要展示的所有组件,给convertView设置tag(setTag()),传入一个viewHolder对象,用于缓存要显示Item条目,可以达到图像数据异步加载的效果。
  3. 如果listview需要显示的item很多,就要考虑分页加载。

二、ListView分页加载的实现

方式一:当用户滑动到底部时自动加载实现思路

实现OnScrollListener 接口重写onScrollStateChanged 和onScroll方法,使用onscroll方法实现”滑动“后处理检查是否还有新的记录,如果有,添加记录到adapter, adapter调用 notifyDataSetChanged 更新数据;如果没有记录了,则不再加载数据。使用onScrollStateChanged可以检测是否滚到最后一行且停止滚动然后执行加载.

    /**
     * 滚动监听方法
     * 
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        isLoad = ((firstVisibleItem + visibleItemCount) == totalItemCount);
    }

    /**
     * 滑动改变的监听事件
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (isLoad
                && scrollState == android.widget.NumberPicker.OnScrollListener.SCROLL_STATE_IDLE) {
        }
    }

方式二:在ListView底部设置一个按钮,用户点击即加载实现思路

        /**
         * 1、Button布局不能和ListView不在在同一布局 
         * 2、通过inflate方法添加带有button的布局
         * 3、通过ListView的addFooterView()方法将其添加到listView底部
         * 4、设置button的点击事件,切不能再onScrollStateChanged()方法中 
         * 5、创建Hander来更新加载出来的数据
         */
        View view = View.inflate(getApplicationContext(),
                R.layout.footerlayout, null);
        bt = (Button) view.findViewById(R.id.bt);
        lv.addFooterView(view);
        bt.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                handler.postDelayed(new Runnable() {

                    @Override
                    public void run() {
                        // 添加更新的数据
                        // 其方法中的list集合必须创建成全局的
                        initData("string" + count);
                        count++;
                        // 加载更多数据
                        bt.setVisibility(View.VISIBLE);
                        // pg.setVisibility(View.GONE);
                        // 通知listView刷新数据
                        adapter.notifyDataSetChanged();
                    }
                }, 2000);
            }
        });

三、ListView图片异步加载实现思路

  1. 先从内存缓存中获取图片显示(内存缓冲)
  2. 获取不到的话从SD卡里获取(SD卡缓冲,,从SD卡获取图片是放在子线程里执行的,否则快速滑屏的话会不够流畅)
  3. 都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示(视情况看是否要显示)

这里可能出现图片错位的现象

图片错位问题的本质源于我们的listview使用了缓存convertView,假设一种场景,一个listview一屏显示九个item,那么在拉出第十个item的时候,事实上该item是重复使用了第一个item,也就是说在第一个item从网络中下载图片并最终要显示的时候,其实该item已经不在当前显示区域内了,此时显示的后果将可能在第十个item上输出图像,这就导致了图片错位的问题。所以解决之道在于可见则显示,不可见则不显示。在ImageLoader里有个imageViews的map对象,就是用于保存当前显示区域图像对应的url集,在显示前判断处理一下即可。

四、ListView 加载不同条目布局

ListView 显示的每个条目都是通过 baseAdapter 的

getView(int position, View convertView,ViewGroup parent)

来展示的,除此之外 adapter 还提供了getViewTypeCount()和 getItemViewType(int position) 两个方法。在 getView 方法中我们可以根据不同 的viewtype 加载不同的布局文件。

五、ListView 数据集改变后,如何更新 ListView

使用该 ListView 的 adapter 的 notifyDataSetChanged()方法。该方法会使 ListView 重新绘制

六、ListView 如何定位到指定位置

可以通过 ListView 提供的 lv.setSelection(48);方法

七、ScrollView和ListView的冲突问题

http://bbs.anzhuo.cn/thread-982250-1-1.html
1. 不光是ListView,其他继承自AbsListView的类也会出现相同的问题,包括:ExpandableListView、GridView等,为了方便说明,以下均用ListView来代表
2. ScrollView中只能放一个控件,一般都放LinearLayout中,orientation属性值为vertical。

scrollview和listview的冲突 listview显示不全

方法一: 默认情况下Android是禁止在ScrollView中放入另外的ScrollView,因为它的高度无法计算

解决方法:ListView设置完Adapter后,根据ListView的子条目重新计算ListView的高度,然后把高度再作为LayoutParms设置给ListIView

public class Utility {  
        public static void setListViewHeightBasedOnChildren(ListView listView) {  
        // 获取ListView对应的Adapter  
        ListAdapter listAdapter = listView.getAdapter();  
        if(listAdapter == null) {  
            return;  
        }  
        int totalHeight = 0;  
        for(int i = 0, len = listAdapter.getCount(); i < len; i++) { // listAdapter.getCount()返回数据项的数目  
            View listItem = listAdapter.getView(i, null, listView);  
            listItem.measure(0, 0); // 计算子项View 的宽高  
            totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度  
        }  
        ViewGroup.LayoutParams params = listView.getLayoutParams();  
        params.height = totalHeight  
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));  
        // listView.getDividerHeight()获取子项间分隔符占用的高度  
        // params.height最后得到整个ListView完整显示需要的高度  
        listView.setLayoutParams(params);  
        }  
      } 

只要在设置ListView的Adapter后调用此静态方法即可让ListView正确的显示在其父ListView的ListItem中。但是要注意的是,子ListView的每个Item必须是LinearLayout,不能是其他的,因为其他的Layout(如RelativeLayout)没有重写onMeasure(), 所以会在onMeasure()时抛出异常。

方法二: 自定义ListView,重写onMeasure方法,引用该ListView即可。

public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, mExpandSpec);
    }

scrollview和listview的滑动冲突

给ScrollView 设置 android:fillViewport="true"
//当ScrollView里的元素想填满ScrollView时,使用"fill_parent"是不管用的,必需为ScrollView设置:android:fillViewport="true"
自定义ListView,在listView的onTouch里面拦截ScrollView的事件,使得scrollView不可以获得点击事件,这样只有listView获得点击事件!

listView.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
                scrollView.requestDisallowInterceptTouchEvent(true);
                return false;
            }
        });

方法二:也可以不自定义ListView,为ListView设置OnTouchListener()的OnTouch方法中调用sv.requestDisallowInterceptTouchEvent(true);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值