ListView使用总结

ListView使用总结

虽然随着RecyclerView的不断普及,相应的资源也越来越多,许多的项目都在使用RecyclerView,但作为他的前辈ListView,加深对ListView的使用有助于我们更好的适应到RecyclerView的使用中。

首先看一下我们实现的效果一些简单效果

这只是前面的一些简单效果,后面会有一些进阶的效果,希望能耐心的看下去。

ListView的优化

ListView的优化主要包括两个方面,分别是对自身的优化以及其适配器(Adapter)的优化。

ListView自身的优化

主要包括一条。对于ListViewlayout_heightlayout_width设置为match_parent,如果设置为match_parent,一般ListView的宽高会测量三次以上。具体的源码没有深入研究。但为什么会要测量多次,如果对于自定义View稍微有点基础的会知道,对于View的测量大小有三个类型:

- UNSPECIFIED:未指定的,父类不对子类施加任何限制。

- EXACTLY:确定的,父类确定其子类控件的大小。

- AT_MOST:最大值,需要子类去测量自身大小确定。

如果我们设置宽高为wrap_content,即AT_MOST,表示其宽高有控件本身去测量确定,而如果是match_parent,则EXACTLY,表示确定的大小。

Adapter优化。

对于Adapter的优化,主要包括以下几个步骤:

  • 复用convertView,减少子布局的生成。
  • 定义ViewHolder,减少findViewById()的次数。

下面看一下代码


/**
 * 最基础的adapter
 * Created by Alex_MaHao on 2016/5/17.
 */

public class SimpleBaseAdapter extends BaseAdapter {

    private List<String> datas;

    public SimpleBaseAdapter(List<String> datas) {
        this.datas = datas;
    }

    @Override
    public int getCount() {
        return datas==null?0:datas.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

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

        ViewHolder vHolder;

        if(convertView==null){
            //初始化item布局
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_listview_sample,parent,false);

            //创建记事本类
            vHolder = new ViewHolder();

            //查找控件,保存控件的引用
            vHolder.tv = ((TextView) convertView.findViewById(R.id.listview_sample_tv));

            //将当前viewHolder与converView绑定
            convertView.setTag(vHolder);
        }else{
            //如果不为空,获取
            vHolder = (ViewHolder) convertView.getTag();
        }
        vHolder.tv.setText(datas.get(position));

        return convertView;
    }

    /**
     * 笔记本类,保存对象的引用
     */
    class ViewHolder{
        TextView tv;
    }
}

根据代码分析思路:

- ListView中的item滑出屏幕时,滑出的item会在getView()方法中返回,及converView参数。所以在这里判断,是否为null,如果不为null,表示我们可以对该布局重新设置数据并返回到列表中显示。

- ViewHolder,保存item中子控件的引用。因为我们复用了converView,那么对于同一个converView布局,其子控件的引用应该是不变的。所以我们可以获取到其上的所有子控件的引用并通过ViewHolder进行保存。

- setTag(),getTag():该方法实现了ViewHolderconverView的绑定,就类似于通过setTag()方法,将ViewHolder打包成一个包裹,放在了converView上,再通过getTag()方法,将这个包裹取出。

在很多优化中,会将ViewHolder定义为static。但在这里我并没加上。因为经过测试,加或不加,ViewHolder的创建次数不变。网上查了很多资料,也没有找到一个让我信服的理由。唯一有点理的就是基于java的特性。静态内部类的对象不依赖于其所在的外部类对象。

Adapter的封装–BaseAppAdapter

上一节说了Adapter的优化,但如果我们每次写都要写这么多的优化代码,这不符合程序员懒惰的天性。那我们只能把他封装,提取出一个公共的基类,在基类中,我们把布局加载,优化等都默认实现,只需让子类构造布局文件,以及绑定数据。

那么,从我们上一节的代码看,有以下模块都可以提取为基类:

  • 数据集合datas:对于数据集合,我们通常都是一个List集合,在这里定义泛型来表述其所包含的内容。
  • 数据优化:布局的复用以及ViewHolderconverView的绑定。
  • ViewHolder:定义一个ViewHolder,通过map保存控件与id;

子类所需实现的:

  • 数据的初始化
  • 确定item的布局文件
  • 将数据与视图绑定。

那么直接看一下我们继承好的代码:

public abstract  class BaseAppAdapter<T> extends BaseAdapter {

    /**
     * 泛型,保存数据
     */
    protected List<T> datas;

    /**
     * 构造方法,子类必须实现其构造方法,并初始化数据
     * @param datas
     */
    public BaseAppAdapter(List<T> datas) {
        this.datas = datas;
    }

    @Override
    public int getCount() {
        return datas==null?0:datas.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

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

        BaseViewHolder vHolder;

        if(convertView==null){
            convertView = LayoutInflater.from(parent.getContext()).inflate(getItemLayoutId(),parent,false);

            vHolder = new BaseViewHolder(convertView);

            convertView.setTag(vHolder);
        }else{
            vHolder = (BaseViewHolder) convertView.getTag();
        }

        /**
         * 数据绑定的回调
         */
        bindData(vHolder,datas.get(position));

        return convertView;
    }

    /**
     * 子类实现,获取item布局文件的id
     * @return
     */
    protected  abstract int getItemLayoutId();

    /**
     * 子类实现,绑定数据
     * @param vHolder  对应position的ViewHolder
     * @param data 对应的数据绑定
     */
    protected abstract void bindData(BaseViewHolder vHolder,T data);

    /**
     * ViewHolder类
     */
    class BaseViewHolder{

        /**
         * 保存view,以id-view的形式
         */
        Map<Integer,View> mapView;
        View rootView;

        public BaseViewHolder(View rootView){
            this.rootView = rootView;
            mapView = new HashMap<Integer,View>();
        }

        /**
         * 通过id查找控件
         * @param id
         * @return
         */
        public View getView(Integer id){
            View view = mapView.get(id);
            if(view==null){
                view = rootView.findViewById(id);
                mapView.put(id,view);
            }

            return view;
        }

    }

}

在以上代码中有几个关键点:

  • 有参的构造方法:子类实现时,必须调用父类的构造方法,用以保存数据。
  • getItemLayoutId():item的布局文件id。抽象方法,子类必须实现。对于每一个BaseAppAdapter的子类,都需要通过该方法返回他们所特有的item的id。
  • bindData(BaseViewHolder vHolder,T data):数据绑定方法,子类必须实现。子类在此方法中将数据设置到试图上
  • BaseViewHolder中的getView(Integer id):该方法设计比较巧妙,因为,对于基类我们不知道有哪些id需要查询,如果直接通过findViewById()方法,则并没有减少查询次数。在这里,通过保存一个map对象,通过id先从map中取,如果不存在,说明是第一次获取,则使用findViewById查找控件,并将控件以键值对的形式保存到map中,则下次查找,会从map中取,减少了findViewById的次数。可能有人会认为,添加了一个map对象,内存占用不是增大了吗,但是,第一,我们map中保存的都是引用。第二,findViewById()是按照深度优先遍历查询的,如果不懂,遍历肯定能明白吧。

ListView 分割线的高度和颜色设置。

简单的两个属性

  • android:divider:设置颜色,背景
  • android:dividerHeight:设置高度

如果设置无分割线,可以设置android:divider="@null"

该两个属性必须同时使用,如果只设置divider,则没有效果,同时默认的分割线也会消失。

当然,我们也可以在item布局文件中添加分割线(在底部添加一个线),麻烦点而已。

隐藏ListView 的滚动条

android:scrollbars="none"

取消ListView的点击反馈效果

对于ListView,在android5.0一下是一个变色,在android5.0以上是一个波纹动画。我们可以通过一些设置取消掉他的反馈效果。使用如下代码

android:listSelector="#0000":点击颜色设置为透明

设置空试图

ListView作为列表显示的控件,那么肯定会存在数据为空的情况,如果我们就直接显示一面白无疑是不友好的,ListView可以通过设置一个视图,当列表为null,显示该视图。

关键方法为setEmpty(View)。具体使用方法如下:

xml文件中,在ListView下设置一个图片,如下

 <com.mahao.alex.systemwidgetdemo.listView.MyListView
        android:id="@+id/listview_lv"
        android:divider="#f00"
        android:scrollbars="none"
        android:listSelector="#0000"
        android:dividerHeight="3dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <ImageView
        android:id="@+id/listview_empty_view"
        android:layout_width="match_parent"
        android:src="@mipmap/ic_launcher"
        android:layout_height="match_parent" />

在java代码中设置如下

        //设置空视图,调用此方法会默认隐藏Empty_view
        mLv.setEmptyView(findViewById(R.id.listview_empty_view));

注意:我们无需设置ImageViewvisible属性,因为通过setEmpty()方法,已将其显示与隐藏于ListView所绑定,由ListView来管理。

ListView的滚动

对于显示数据,我们可能会有一些特殊的要求,比如初始显示到第几列,或者要调到第几条显示。对于此种要求,ListView已经实现了相应的方法让我调用。

  • public void setSelection(int position):position为需要显示在第一条的数据。该方法为瞬间滚动,无滑动效果,类似图片上瞬间滑动按钮实现的效果。
  • public void smoothScrollToPosition(int position):与上面方法作用相同,不过其有过度效果,类似平滑滚动实现的效果。

ListView 下拉顶部空白回弹效果

ListView中,有如下方法,他控制了回弹设置的值:

 protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)

maxOverScrollY参数便是设置我们可以下拉的空白区域的高度。具体实现如下

public class MyListView extends ListView {

    /**
     * 下拉回弹效果,下拉的最大距离
     */
    private int mMaxOverDistance;

    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //初始化最大距离
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        float density = metrics.density;
        mMaxOverDistance = (int) (100*density);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {

        //注意第九个参数,设置为了我们自定义的值
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxOverDistance, isTouchEvent);
    }
}

该方法是保护类型的,所以我们需要通过继承ListView来修改此方法的实现。

在看到此方法时,第一反应是ScrollView有没有该方法,查了以后,发现ScrollView也有该方法。那么,同理,ScrollView也能通过重写overScrollBy实现下拉回弹的效果。

ListView 进阶–实现跟随滑动消失和显现的ToolBar

首先看一下效果图:

分析其逻辑:

  • 当我们向上滑动列表时,toolbar慢慢消失到顶部。
  • 当我们向下滑动列表时,toolbar慢慢的从顶部显现。

实现逻辑:

- setOnTouchListener,设置触摸监听。

- 对当前手指移动事件进行判断。

- 如果是向下移动,且ToolBar是隐藏的,则给toolbar设置一个向下显现的动画。

- 如果是向上移动,且toolbar是显现的,则给toolbar设置一个向上隐藏的动画。

看一下实现代码:

/**
 * Created by Alex_MaHao on 2016/5/17.
 */
public class ToorBarListViewActivity extends AppCompatActivity implements View.OnTouchListener {

    private Toolbar mToolBar;

    private ListView mLv;

    private SimpleBaseAdapter mAdapter;

    private List<String> datas  = new ArrayList<>();

    //系统默认的滑动最小偏移量
    private int mTouchSlop;

    //手指初次触摸时的Y坐标
    private float mFirstY;

    //当前手指触摸的Y坐标
    private float mCurrentY;

    //手指移动的方向,0代表向下滑动,1代表向上滑动
    private int direction;

    //toobar的显示状态
    private boolean isShow = true;

    //toobar显示和隐藏的动画
    private Animator mAnimator;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_listview_toobar);

        // 把toolbar替代ActionBar
        mToolBar = ((Toolbar) findViewById(R.id.toolbar));
        setSupportActionBar(mToolBar);

        mLv = ((ListView) findViewById(R.id.listview_toolbar_lv));
        initDatas();

        mAdapter = new SimpleBaseAdapter(datas);

        mLv.setAdapter(mAdapter);

        /**
         * 添加一个头部View,不然使用RelativeLayout,会被遮挡
         */
        addHeadView();

        //最小移动距离,判断是否滑动
        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();

        //设置触摸监听
        mLv.setOnTouchListener(this);
    }

    /**
     * 添加头布局,避免被遮挡
     */
    private void addHeadView() {

        //创建一个与ToolBar等高的空白view,添加到ListView的头布局中
        View head = new View(this);
        TypedArray actionbarSizeTypedArray = getApplicationContext().obtainStyledAttributes(new int[] { android.R.attr.actionBarSize });
        head.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, (int) actionbarSizeTypedArray.getDimension(0, 0)));

        mLv.addHeaderView(head);
    }

    private void initDatas() {
        for(int i = 0;i<20;i++){
            datas.add("添加数据~~"+i);
        }
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){

            case MotionEvent.ACTION_DOWN:
                //记录初次触摸的Y坐标
                mFirstY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                mCurrentY = event.getY();
                if(mCurrentY-mFirstY>mTouchSlop){
                    //向下滑动
                    direction = 0;
                }else if(mFirstY-mCurrentY>mTouchSlop){
                    //向上滑动
                    direction = 1;
                }
                if(direction==1){
                    //向上滑动,且toolbar在显示状态,则隐藏
                    if(isShow){
                        toolbarAnim(false);
                        isShow = !isShow;
                    }
                }else if(direction==0){
                    //向下滑动,且toolbar在隐藏状态,则显示
                    if(!isShow){
                        toolbarAnim(true);
                        isShow = !isShow;
                    }
                }

                break;
        }

        return false;
    }

    public void toolbarAnim(boolean isShow){
        if(mAnimator!=null&&mAnimator.isRunning()){
            mAnimator.cancel();
        }

        if(isShow){
            //显现toobar
            mAnimator = ObjectAnimator.ofFloat(mToolBar,"translationY",mToolBar.getTranslationY(),0);
        }else{
            //隐藏toobar
            mAnimator = ObjectAnimator.ofFloat(mToolBar,"translationY",mToolBar.getTranslationY(),-mToolBar.getHeight());
        }
        //启动动画
        mAnimator.start();
    }
}

代码注释非常详细,需要注意的有以下三点:

  • 因为我们的父布局是RelativeLayout,如果不给ListView设置一个headView,则在最顶部时,ListView顶部数据会被隐藏。当然,可能会想到为什么不用垂直布局,把ListView放在toolbar的下面呢,这样会导致另一个问题,即当toobar消失时,会导致顶部有一个空白效果。如下图所示:

  • 监听两个条件,滑动方向和toolbar当前显示的状态,如果不监听当前toobar状态,会导致动画重复调用。
  • 会存在一种情况,及toolbar处在某一个动画的时候,突然该表方向,我们需要先停止当前动画,并以当前偏移量上为基础,创建新的动画。这也是创建动画时使用mToolBar.getTranslationY()作为参数的原因。

ListView 进阶–聊天列表的实现

在显示数据中,可能会有这种需求,显示的列表的布局不同,例如聊天界面,根据不同的聊天人,显示的位置不同,及加载不同的itemView那么我们如何实现呢。先看一下实现效果:

请忽略他的丑陋,目的是为了实现效果,下面直接上源码,看一下实现过程,最后在总结实现的关键点:

聊天的实体类,在聊天实体类中,我们必须通过类型区分该聊天内容是自己还是朋友。

/**
 * 聊天的实体类
 * Created by Alex_MaHao on 2016/5/18.
 */
public class ChatBean {

    /**
     * 聊天的两种类型,自己和朋友
     */
    public static final int CHAT_MYSELF = 0;
    public static final int CHAT_FIRENDS = 1;

    /**
     * 聊天的头像
     */
    private Drawable userIcon;

    /**
     * 发送的消息
     */
    private String message;

    /**
     * 确定是否为当前聊天者
     */
    private int type;

    public ChatBean() {
    }

    public ChatBean(String message, int type, Drawable userIcon) {
        this.message = message;
        this.type = type;
        this.userIcon = userIcon;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public Drawable getUserIcon() {
        return userIcon;
    }

    public void setUserIcon(Drawable userIcon) {
        this.userIcon = userIcon;
    }
}

ListView所在的布局文件activity_listview_chat.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ListView
        android:id="@+id/listview_chat_lv"
        android:divider="#0000"
        android:listSelector="#0000"
        android:dividerHeight="10dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="20dp"
        />

</RelativeLayout>

因为聊天条目没有点击反馈效果,所以通过android:listSelector设置无反馈效果。使用android:divider="#0000"android:dividerHeight="10dp"设置每一个聊天条目的间隔。

我们需要加载不同的布局,所以我们有两个item布局文件,用来显示自己的消息和朋友的消息:

item_listview_chat_left,显示在左边,朋友消息显示布局

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_alignParentRight="true"
        android:layout_marginRight="20dp"
        android:id="@+id/chat_right_icon"
        android:src="@mipmap/qq"
        android:scaleType="fitXY"
        android:layout_width="40dp"
        android:layout_height="40dp" />

    <TextView
        android:id="@+id/chat_right_msg"
        android:gravity="center"
        android:text="dsadsadsasdasdasd"
        android:textColor="#fff"
        android:layout_marginLeft="10dp"
        android:background="@drawable/listview_chat_right"
        android:layout_toLeftOf="@id/chat_right_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

item_listview_chat_right,显示在右边,自己消息的显示布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_alignParentRight="true"
        android:layout_marginRight="20dp"
        android:id="@+id/chat_right_icon"
        android:src="@mipmap/qq"
        android:scaleType="fitXY"
        android:layout_width="40dp"
        android:layout_height="40dp" />

    <TextView
        android:id="@+id/chat_right_msg"
        android:gravity="center"
        android:text="dsadsadsasdasdasd"
        android:textColor="#fff"
        android:layout_marginLeft="10dp"
        android:background="@drawable/listview_chat_right"
        android:layout_toLeftOf="@id/chat_right_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

关键点来了,构造ChatAdapter,用以显示数据。

/**
 * 聊天的Adapter,实现加载不同的布局
 * Created by Alex_MaHao on 2016/5/18.
 */
public class ChatAdapter extends BaseAdapter {

    List<ChatBean> chatBeens ;

    public ChatAdapter(List<ChatBean> chatBeens) {
        this.chatBeens = chatBeens;
    }

    @Override
    public int getCount() {
        return chatBeens.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public int getItemViewType(int position) {
        //返回值需要从0开始,对应数据所要加载的对应布局标识
        return chatBeens.get(position).getType();
    }

    @Override
    public int getViewTypeCount() {
        //不同布局的种类数量
        return 2;
    }

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

        //获取当前数据的类型
        int type = getItemViewType(position);

        ViewHolder vHolder = null;
        if(convertView==null){
            switch (type){
                case ChatBean.CHAT_MYSELF:
                    //加载自己消息显示的布局
                    convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_listview_chat_right,parent,false);
                    vHolder = new ViewHolder();
                    vHolder.icon = ((ImageView) convertView.findViewById(R.id.chat_right_icon));
                    vHolder.msg = ((TextView) convertView.findViewById(R.id.chat_right_msg));
                    break;
                case ChatBean.CHAT_FIRENDS:
                    //加载朋友消息显示的布局
                    convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_listview_chat_left,parent,false);
                    vHolder = new ViewHolder();
                    vHolder.icon = ((ImageView) convertView.findViewById(R.id.chat_left_icon));
                    vHolder.msg = ((TextView) convertView.findViewById(R.id.chat_left_msg));
                    break;
            }
            //控件应用与控件绑定
            convertView.setTag(vHolder);
        }else{
            //获取控件引用
            vHolder = (ViewHolder) convertView.getTag();
        }

        //设置数值
        vHolder.icon.setImageDrawable(chatBeens.get(position).getUserIcon());
        vHolder.msg.setText(chatBeens.get(position).getMessage());

        return convertView;
    }

    class ViewHolder{
        ImageView icon;
        TextView msg;
    }
}

adapter中有两个关键的方法:

  • getViewTypeCount():返回布局的种类数,比如当前我们分为自己的消息和朋友的消息两个布局,那么就返回2.

-getItemViewType():返回对应条目下,item布局的类型。ListView通过该方法,实现在getView()中返回相同类型的converView复用。注意,该返回类型要从0开始,不然会数组越界。

因为在这里,比较巧合,我们的item布局控件都一样,只不过显示不一样,所以只定义了一个ViewHolder类。

关键的实现了,那么看一下activity中的代码吧

public class ChatListViewActivity extends AppCompatActivity {

    private ListView mChatLv;

    private List<ChatBean> chatBeanList = new ArrayList<>();

    private ChatAdapter adapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_listview_chat);

        mChatLv = ((ListView) findViewById(R.id.listview_chat_lv));

        initChatBeanList();

        adapter = new ChatAdapter(chatBeanList);

        mChatLv.setAdapter(adapter);

        mChatLv.postDelayed(new Runnable() {
            @Override
            public void run() {
                //设置我们ListView显示在底部
                mChatLv.setSelection(adapter.getCount());
            }
        },200);
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    /**
     * 初始化聊天内容
     */
    private void initChatBeanList() {
        for (int i = 0;i<10;i++){
            ChatBean chat = new ChatBean();
            if(i%2==0){
                chat.setType(ChatBean.CHAT_MYSELF);
                chat.setUserIcon(getApplicationContext().getResources().getDrawable(R.mipmap.qq));
                chat.setMessage("微信,微信,我是QQ");
            }else{
                chat.setType(ChatBean.CHAT_FIRENDS);
                chat.setUserIcon(getApplicationContext().getResources().getDrawable(R.mipmap.weixin));
                chat.setMessage("QQ,QQ,我是微信");
            }

            chatBeanList.add(chat);
        }
    }
}

实现该布局的两个关键方法(adapter中的方法)

- getViewTypeCount():返回布局的种类数,比如当前我们分为自己的消息和朋友的消息两个布局,那么就返回2.

- getItemViewType():返回对应条目下,item布局的类型。ListView通过该方法,实现在getView()中返回相同类型的converView复用。注意,该返回类型要从0开始,不然会数组越界。

ListView 的上拉加载和下拉刷新

决定使用多种方式实现下拉刷新和上拉加载,会在下一篇博客中实现。绝对满满的干货。

ListView上拉加载和下拉刷新多种实现方式已经发表,欢迎观看,学习,并留下您宝贵的意见。

该项目源码已经共享到我的github,在模块systemwidgetdemo中。

时间: 2024-10-12 16:17:30

ListView使用总结的相关文章

Android 监听ListView、GridView滑动到底部

// 监听listview滚到最底部mIndexList.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { // 当不滚动时 case OnScrollListener.SCROLL_STATE_IDLE: // 判断滚动到底部 if (view.get

C# 将Access中时间段条件查询的数据添加到ListView中

C# 将Access中时间段条件查询的数据添加到ListView中 一.让ListView控件显示表头的方法 在窗体中添加ListView 空间,其属性中设置:View属性设置为:Detail,Columns集合中添加表头中的文字. 二.利用代码给ListView添加Item. 首先,ListView的Item属性包括Items和SubItems.必须先实例化一个ListIteView对象.具体如下: ListViewItem listViewItem=new ListViewItem(); l

[ ObjectListView ] - ListView的增强控件 - 前言 (翻译)

********************************************************************************** 原  标 题: A Much Easier to Use ListView 原文地址: https://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView 翻       译: 于国栋 http://www.shannon.net.cn *********

Android ListView工作原理完全解析(转自 郭霖老师博客)

原文地址:http://blog.csdn.net/guolin_blog/article/details/44996879 在Android所有常用的原生控件当中,用法最复杂的应该就是ListView了,它专门用于处理那种内容元素很多,手机屏幕无法展示出所有内容的情况.ListView可以使用列表的形式来展示内容,超出屏幕部分的内容只需要通过手指滑动就可以移动到屏幕内了. 另外ListView还有一个非常神奇的功能,我相信大家应该都体验过,即使在ListView中加载非常非常多的数据,比如达到

360手机助手(二):侧拉栏drawerLayout+Tab+Viewpager+ListView+Pulltorefresh+imageloder

整个项目的框架在ContentPager,要熟悉这个类. 简介 完成侧拉栏的显示,侧拉首页对应的:首页+应用+游戏这3个 tab,包含的知识点有:请求数据 +pullTofresh + ImageLoader +轮播图 + listView 效果图: 侧拉栏的实现 V4包中的DrawerLayout实现侧拉效果,每个侧拉的item对应一个fragment,点击该item时,先把所有的fragment隐藏 + 文字设置未选中,然后才创建对应的fragment并显示 怎么实现侧拉栏的显示与隐藏? 点

ListView

一.ListView入门 1.定义ListView在布局中 2.定义ListView的数据适配器 3.实现baseAdapter的getCount方法和getView方法. 二.ListView的优化 1.缓存对象 //convertView 历史缓存对象 @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv; if(convertView == null){ /

Android——ListView布局+适配器(三)

Android--ListView布局+适配器(三) package com.example.administrator.newstop; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import andro

Android——ListView多布局+适配器(二)

Android--ListView多布局+适配器(二) <span style="font-size:18px;">package com.example.administrator.newstop.entity; /** * Created by Administrator on 2016/8/4. */ public class News { private String title; private String pubDate; private int img; p

C#如何解决对ListView控件更新以及更新时界面闪烁问题

第一个问题:如何更新ListView控件内容 很多时候运行窗体程序时,由于程序中使用了多线程加之操作不当,所以在对控件操作时会出现下面这样的异常: 这是因为我们在窗体中添加的控件都有属于自己的线程,所以不能从其它线程来访问它. 那要如何解决? 使用委托:MethodInvoker,我用这个挺方便的.下面举一个用例: 1 //调用InitListView(),便可以对ListView控件进行自由更新啦 2 private void InitListView() 3 { 4 MethodInvoke

【黑马Android】(04)数据库的创建和sql语句增删改查/LinearLayout展示列表数据/ListView的使用和BaseAdater/内容提供者创建

数据库的创建和sql语句增删改查 1. 加载驱动. 2. 连接数据库. 3. 操作数据库. 创建表: create table person( _id integer primary key, name varchar(20), age integer ); 添加: insert into person(name, age) values('lisi', 19); 删除: delete from person where _id = 1; 修改: update person set name =