ListView 使用多布局的方法和简单的原理分析

来源:互联网 发布:绵阳广电网络营业厅 编辑:程序博客网 时间:2024/06/08 12:21

很多时候我们需要用到一个listview使用多个布局的情况,例如聊天的界面
这里写图片描述

这是从网上下载的图片,仅供参考。

实现:

下面来分析一下如何实现图中的效果:

如果要滑动的话,可以使用scrollView或者是listview, 但是scrollView 对于内存上无法实现view的复用,所以说不是一个好的方案。

使用listview就要使用多个布局文件,这个在android在baseadapter中已经为我们开放了非常方便的途径:

1、重写 int getItemViewType(int position)
在这个方法中返回我们来区别不同文件的tag,例如左边的聊天放回0, 右边的聊天放回1;

2、重写int getViewTypeCount
在这个方法中放回布局的个数,具体返回的大小,一会再做非常重要的解释。

先上传整体的伪代码:

import java.util.List;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;public class TestAdapter extends BaseAdapter {    private Context context;    private List<ChatMessage> data;    public TestAdapter(Context context, List<ChatMessage> data) {        this.context = context;        this.data = data;    }    @Override    public int getItemViewType(int position) {        // 左边的聊天放回0 ,右边的聊天返回1        return data.get(position).getType();    }    @Override    public int getViewTypeCount() {        return 2;    }    @Override    public int getCount() {        return data.size();    }    @Override    public Object getItem(int arg0) {        return data.get(arg0);    }    @Override    public long getItemId(int arg0) {        return arg0;    }    @Override    public View getView(int position, View convertView, ViewGroup arg2) {        // 获取当前位置的type, 0 为左边聊天, 1为右边的聊天        int type = getItemViewType(position);        switch (type) {        case 0:            // 引用左边聊天的布局            convertView = LayoutInflater.from(context).inflate(..., null);            // 展示内容的操作            ...            break;        case 1:            // 引用右边聊天的布局            convertView = LayoutInflater.from(context).inflate(..., null);            // 展示内容的操作            ...            break;        default:            break;        }        return convertView;    }    private class ChatMessage {        int type;        public int getType() {            return type;        }    }}

分析

代码非常的简单,其实还可以进行Holder这样的优化,这里只是简单说明一下使用的方法,就不具体优化了。

非常初级的api使用方法,完全没有什么难度,但是有一个问题一直困扰我很久:getViewTypeCount()这个方法中返回的这个值到底有什么用?

有的人会说:这里返回的是不同布局的个数。
经过我的测试,发现只要返回的值比 int getItemViewType(int position)这个方法中返回的最大值大就没有问题。例如上面的代码中,我们返回的type最大值是1,所以只要返回比1大的所有值都没问题。那么这种解释就说不通了。

今天又重新看了一下listview的复用机制才忽然意识到这个值到底起到了什么作用:
先简要说明listview的循环复用机制,一个listview至多为自己的item缓存 当前屏幕展示的个数 + 2, 滑动的时候把移除屏幕的item添加到缓冲区,回到屏幕时从缓冲区获取一个item来展示,这样就实现了item的复用,大大降低了内存的开销。具体的分析资料可以在郭霖老师和张鸿洋老师的博客中看到。

那么在listview多个布局的时候会发生怎么样的变化呢?
原理是还是一样的,唯一的不同就是我们之前重写的两个方法的返回了不一样的值。

1、 int getItemViewType(int position)
这个方法帮助listview区分不同的布局文件,缓冲布局文件的是一个数组,返回的int值就是缓冲区不同布局文件的下标,0 就代表 放在数组第一个位置, 1就代表放在第二个位置,需要复用的时候直接根据返回的int下标去获取需要的item, 如果不重写这个方法,那么位置肯定是唯一的,也就只有一个布局文件,我们可以猜测默认值是0;

2、int getViewTypeCount
在这个方法中返回的值就很重要了,他实际上是缓冲区的长度,所以这个值就必须要比getItemViewType(int position)返回的最大值要大,如果小的话,就会报数组越界异常,所以开头的问题就得到解决了。我们也可以猜测这个默认值返回的是1。

个人还是建议发挥恰当的值,例如 我们需要两个布局文件一个类型0, 一个类型100, 那么我们至少要设置长度是100, 中间的98个位置全是空的,作为一个开发者而言,是不允许这种站着茅坑不拉屎的行为发生的。

到这里这篇博客就结束了,希望这篇文章可以帮助大家去理解listview的实现原理。

0 0
原创粉丝点击