Android listView同时展示多种不同数据的item

引言

———–最近由于项目需求,需要把两种数据揉在一起然后通过两种数据共有的时间来排序,装在listView里面。以前我们看到的多数都是listView里面装一种item所持的数据,或者更甚一点就是加了分割线什么的。

现在我们一起来学习一下怎么把多种数据用不同的item排序后装在同一个listView里面

先上个图

这里写图片描述

开始

首先我们先写两种不同的数据

—————– A类

public class A {
    private String title;

    private int time;

    private String describe;


    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getTime() {
        return time;
    }

    public void setTime(int time) {
        this.time = time;
    }

    public String getDescribe() {
        return describe;
    }

    public void setDescribe(String describe) {
        this.describe = describe;
    }
}

—————– B类

public class B {

    private int imgResourceID;

    private int time;

    private String content;

    public int getImgResourceID() {
        return imgResourceID;
    }

    public void setImgResourceID(int imgResourceID) {
        this.imgResourceID = imgResourceID;
    }

    public int getTime() {
        return time;
    }

    public void setTime(int time) {
        this.time = time;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

A类和B类的数据除了time都是共有的,其他基本都是不一样的,这就是我所谓的两种不同的数据。

我们再来简单地写一下他们的item layout

———-item A 的layout

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="title"
        android:textSize="25sp"
        android:id="@+id/item_list_for_a_title"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="time"
        android:textSize="20sp"
        android:layout_marginRight="50dp"
        android:id="@+id/item_list_for_a_time"
        android:layout_gravity="right" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="describe"
        android:id="@+id/item_list_for_a_dec"
        android:layout_gravity="center_horizontal" />


</LinearLayout>

———-item B 的layout

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

    <ImageView
        android:id="@+id/item_list_for_b_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

    <TextView
        android:id="@+id/item_list_for_b_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:layout_marginight="50dp"
        android:text="time"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/item_list_for_b_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="content"
        android:textAppearance="?      android:attr/textAppearanceMedium" />
</LinearLayout>

然后我们再来看看最重要的ABAdapter

先整体看一下,然后我们再来分解

package com.itspeed.naidou.app.adapter;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.itspeed.naidou.R;
import com.itspeed.naidou.model.bean.A;
import com.itspeed.naidou.model.bean.B;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Created by jafir on 15/9/14.
 */
public class ABAdapter extends BaseAdapter {
    //itemA类的type标志
    private static final int TYPE_A = 0;
    //itemB类的type标志
    private static final int TYPE_B = 1;

    private Context context;

    //整合数据
    private List<Object> data = new ArrayList<>();


    public ABAdapter(Context context, ArrayList<Object> as, ArrayList<Object> bs) {
        this.context = context;

        //把数据装载同一个list里面
        //这里把所有数据都转为object类型是为了装载同一个list里面好进行排序
        data.addAll(as);
        data.addAll(bs);

        //按时间排序来填充数据
        Collections.sort(data, new MyComparator());

    }

    /**
     * 获得itemView的type
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        int result = 0;
        if (data.get(position) instanceof A) {
            result = TYPE_A;
        } else if (data.get(position) instanceof B) {
            result = TYPE_B;
        }
        return result;
    }

    /**
     * 获得有多少中view type
     * @return
     */
    @Override
    public int getViewTypeCount() {
        return 2;
    }

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

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //创建两种不同种类的viewHolder变量
        ViewHolder1 holder1 = null;
        ViewHolder2 holder2 = null;
        //根据position获得View的type
        int type = getItemViewType(position);
        if (convertView == null) {
            //实例化
            holder1 = new ViewHolder1();
            holder2 = new ViewHolder2();
            //根据不同的type 来inflate不同的item layout
            //然后设置不同的tag
            //这里的tag设置是用的资源ID作为Key
            switch (type) {
                case TYPE_A:
                    convertView = View.inflate(context, R.layout.item_list_for_a, null);
                    holder1.title = (TextView) convertView.findViewById(R.id.item_list_for_a_title);
                    holder1.time = (TextView) convertView.findViewById(R.id.item_list_for_a_time);
                    holder1.describe = (TextView) convertView.findViewById(R.id.item_list_for_a_dec);
                    convertView.setTag(R.id.tag_first, holder1);
                    break;
                case TYPE_B:
                    convertView = View.inflate(context, R.layout.item_list_for_b, null);
                    holder2.content = (TextView) convertView.findViewById(R.id.item_list_for_b_content);
                    holder2.time = (TextView) convertView.findViewById(R.id.item_list_for_b_time);
                    holder2.img = (ImageView) convertView.findViewById(R.id.item_list_for_b_img);
                    convertView.setTag(R.id.tag_second, holder2);
                    break;
            }

        } else {
            //根据不同的type来获得tag
            switch (type) {
                case TYPE_A:
                    holder1 = (ViewHolder1) convertView.getTag(R.id.tag_first);
                    break;
                case TYPE_B:
                    holder2 = (ViewHolder2) convertView.getTag(R.id.tag_second);
                    break;
            }
        }

        Object o = data.get(position);
        //根据不同的type设置数据
        switch (type) {
            case TYPE_A:
                A a = (A) o;
                holder1.describe.setText("ADescribe:" + a.getDescribe());
                holder1.time.setText("ATime:" + a.getTime());
                holder1.title.setText("ATitle" + a.getTitle());
                break;

            case TYPE_B:
                B b = (B) o;
                holder2.content.setText("BContent:" + b.getContent());
                holder2.time.setText("BTime:" + b.getTime());
                holder2.img.setImageResource(b.getImgResourceID());
                break;
        }
        return convertView;
    }


    public class MyComparator implements Comparator {

        public int compare(Object arg0, Object arg1) {
            //根据不同的情况来进行排序

            if (arg0 instanceof A && arg1 instanceof B) {

                A a = (A) arg0;
                B b = (B) arg1;
                return Integer.valueOf(a.getTime()).compareTo(b.getTime());

            } else if (arg0 instanceof B && arg1 instanceof A) {
                B b = (B) arg0;
                A a = (A) arg1;
                return Integer.valueOf(b.getTime()).compareTo(Integer.valueOf(a.getTime()));

            } else if (arg0 instanceof A && arg1 instanceof A) {

                A a0 = (A) arg0;
                A a1 = (A) arg1;
                return Integer.valueOf(a0.getTime()).compareTo(Integer.valueOf(a1.getTime()));

            } else {
                B b0 = (B) arg0;
                B b1 = (B) arg1;
                return Integer.valueOf(b0.getTime()).compareTo(Integer.valueOf(b1.getTime()));
            }
        }
    }

    /**
     * item A 的Viewholder
     */
    private static class ViewHolder1 {
        TextView time;
        TextView describe;
        TextView title;

    }

    /**
     * item B 的Viewholder
     */
    private static class ViewHolder2 {
        TextView time;
        TextView content;
        ImageView img;
    }

}

首先是构造方法

public ABAdapter(Context context, ArrayList<Object> as, ArrayList<Object> bs) {
        this.context = context;

        //把数据装载同一个list里面
        //这里把所有数据都转为object类型是为了装载同一个list里面好进行排序
        data.addAll(as);
        data.addAll(bs);

        //按时间排序来填充数据
        Collections.sort(data, new MyComparator());

    }

我们把传进来的俩种数据,进行整合,然后进行排序。

看看排序

 public class MyComparator implements Comparator {

        public int compare(Object arg0, Object arg1) {
            //根据不同的情况来进行排序

            if (arg0 instanceof A && arg1 instanceof B) {

                A a = (A) arg0;
                B b = (B) arg1;
                return Integer.valueOf(a.getTime()).compareTo(b.getTime());

            } else if (arg0 instanceof B && arg1 instanceof A) {
                B b = (B) arg0;
                A a = (A) arg1;
                return Integer.valueOf(b.getTime()).compareTo(Integer.valueOf(a.getTime()));

            } else if (arg0 instanceof A && arg1 instanceof A) {

                A a0 = (A) arg0;
                A a1 = (A) arg1;
                return Integer.valueOf(a0.getTime()).compareTo(Integer.valueOf(a1.getTime()));

            } else {
                B b0 = (B) arg0;
                B b1 = (B) arg1;
                return Integer.valueOf(b0.getTime()).compareTo(Integer.valueOf(b1.getTime()));
            }
        }
    }

是按照时间顺序来排列的,重写了Comparator的compare方法

看看那两个最重要的方法

getItemViewType

如果通过数据来判断是哪种type

/**
     * 获得itemView的type
     * @param position
     * @return
     */
    @Override
    public int getItemViewType(int position) {
        int result = 0;
        if (data.get(position) instanceof A) {
            result = TYPE_A;
        } else if (data.get(position) instanceof B) {
            result = TYPE_B;
        }
        return result;
    }

getViewTypeCount

/**
     * 获得有多少中view type
     * @return
     */
    @Override
    public int getViewTypeCount() {
        return 2;
    }

attention!

这里有个很容易犯的错,我就犯了,花了我很久的时间才排查出来。

 //itemA类的type标志
    private static final int TYPE_A = 0;
    //itemB类的type标志
    private static final int TYPE_B = 1;

就是在写type的时候 这里的int类型的标志一定要从0 开始
我之前就是从1 开始,然后就会报一个超出数组的错误

09-14 22:13:04.039    3158-3158/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.itspeed.naidou, PID: 3158
    java.lang.ArrayIndexOutOfBoundsException: length=2; index=2
            at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:6739)
            at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5091)
            at android.widget.AbsListView.onGenericMotionEvent(AbsListView.java:3811)
            at android.view.View.dispatchGenericMotionEventInternal(View.java:7801)
            at android.view.View.dispatchGenericMotionEvent(View.java:7782)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:1824)
            at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:1777)
            at android.view.View.dispatchGenericMotionEvent(View.java:7775)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchGenericMotionEvent(PhoneWindow.java:2076)
            at com.android.internal.policy.impl.PhoneWindow.superDispatchGenericMotionEvent(PhoneWindow.java:1525)
            at android.app.Activity.dispatchGenericMotionEvent(Activity.java:2494)
            at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchGenericMotionEvent(PhoneWindow.java:2030)
            at android.view.View.dispatchPointerEvent(View.java:7888)
            at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3947)
            at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3826)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
            at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3518)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
            at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3575)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
            at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3442)
            at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3411)
            at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3419)
            at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3392)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5532)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5512)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5483)
            at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5612)
            at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:138)
            at android.os.Looper.loop(Looper.java:123)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.int

所以一定要注意了。

接下来就是我们最重要的部分getView

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //创建两种不同种类的viewHolder变量
        ViewHolder1 holder1 = null;
        ViewHolder2 holder2 = null;
        //根据position获得View的type
        int type = getItemViewType(position);
        if (convertView == null) {
            //实例化
            holder1 = new ViewHolder1();
            holder2 = new ViewHolder2();
            //根据不同的type 来inflate不同的item layout
            //然后设置不同的tag
            //这里的tag设置是用的资源ID作为Key
            switch (type) {
                case TYPE_A:
                    convertView = View.inflate(context, R.layout.item_list_for_a, null);
                    holder1.title = (TextView) convertView.findViewById(R.id.item_list_for_a_title);
                    holder1.time = (TextView) convertView.findViewById(R.id.item_list_for_a_time);
                    holder1.describe = (TextView) convertView.findViewById(R.id.item_list_for_a_dec);
                    convertView.setTag(R.id.tag_first, holder1);
                    break;
                case TYPE_B:
                    convertView = View.inflate(context, R.layout.item_list_for_b, null);
                    holder2.content = (TextView) convertView.findViewById(R.id.item_list_for_b_content);
                    holder2.time = (TextView) convertView.findViewById(R.id.item_list_for_b_time);
                    holder2.img = (ImageView) convertView.findViewById(R.id.item_list_for_b_img);
                    convertView.setTag(R.id.tag_second, holder2);
                    break;
            }

        } else {
            //根据不同的type来获得tag
            switch (type) {
                case TYPE_A:
                    holder1 = (ViewHolder1) convertView.getTag(R.id.tag_first);
                    break;
                case TYPE_B:
                    holder2 = (ViewHolder2) convertView.getTag(R.id.tag_second);
                    break;
            }
        }

        Object o = data.get(position);
        //根据不同的type设置数据
        switch (type) {
            case TYPE_A:
                A a = (A) o;
                holder1.describe.setText("ADescribe:" + a.getDescribe());
                holder1.time.setText("ATime:" + a.getTime());
                holder1.title.setText("ATitle" + a.getTitle());
                break;

            case TYPE_B:
                B b = (B) o;
                holder2.content.setText("BContent:" + b.getContent());
                holder2.time.setText("BTime:" + b.getTime());
                holder2.img.setImageResource(b.getImgResourceID());
                break;
        }
        return convertView;
    }

我们创建了两种viewholder 来装不同item layout里面的view

/**
     * item A 的Viewholder
     */
    private static class ViewHolder1 {
        TextView time;
        TextView describe;
        TextView title;

    }

    /**
     * item B 的Viewholder
     */
    private static class ViewHolder2 {
        TextView time;
        TextView content;
        ImageView img;
    }

跟往常的 setTag 和 getTag一样,只是这里需要判断一个type ,通过getItemViewType 来对不同的item inflate不同的layout

这里还要说一下

这里的setTag方法 有两个参数 第一个是key,
不是String 而是 int ,是一个资源ID的key,之前我就随便定了一个1 或者2,然而那样并不行,因为ID可能冲突或者产生其他错误

所以我用了一个方法
在String 里面 写了2个item ,让系统自己分配资源ID ,就完美解决了

<resources>
    <string name="app_name">Naidou</string>

    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>


    <!-- topic-->


    <!-- title-->

    <!-- tab-->
    <string name="tab_chide">吃的</string>
    <string name="tab_liaode">聊的</string>
    <string name="tab_guangde">逛的</string>
    <string name="tab_wode">我的</string>


    <item name="tag_first" type="id"></item>
    <item name="tag_second" type="id"></item>

</resources>

然后是通过type来设置不同的数据

Object o = data.get(position);
        //根据不同的type设置数据
        switch (type) {
            case TYPE_A:
                A a = (A) o;
                holder1.describe.setText("ADescribe:" + a.getDescribe());
                holder1.time.setText("ATime:" + a.getTime());
                holder1.title.setText("ATitle" + a.getTitle());
                break;

            case TYPE_B:
                B b = (B) o;
                holder2.content.setText("BContent:" + b.getContent());
                holder2.time.setText("BTime:" + b.getTime());
                holder2.img.setImageResource(b.getImgResourceID());
                break;
        }

这样一来我们的内容就讲完了。
多的话不需要说,大家一看代码便知

剩下最后一点就是设置一下假数据吧,然后测试一下效果

 private ABAdapter abAdapter;
    private ArrayList<Object> as = new ArrayList<>();
    private ArrayList<Object> bs = new ArrayList<>();

这里的俩个arrayList 数组 都是Object类型的,目的就是为了后面的数据整合,因为要把两种数据装在同一个数组里面进行排序,所以这样做。

但是其实数据的原来类型A或者B,还是可以通过instance of 来辨别的。

private void setDate() {
        //这里的time是从100开始+2*i
        for(int i = 0;i < 10;i++){
            CookBook cookBook = new CookBook();
            cookBook.setPortraitUrl("wwww");
            cookBook.setTime(""+(100+2*i));
            cookBooks.add(cookBook);
        }
        //这里是从95 开始+3*i,
        //目的是为了 让他们的时间有交叉,然后才能更好的体现排序
        for(int i = 0;i < 10;i++){
            Topic topic = new Topic();
            topic.setTitle("topic");
            topic.setTime(""+(95+3*i));
            topics.add(topic);
        }
    }
        setData();
        abAdapter = new ABAdapter(aty,as,bs);
        listView.setAdapter(abAdapter);

好啦,大功告成!!!!!

最后

希望这个东西有能帮助到你们,多谢大家的支持!
如果有问题,请不吝多提,我们一起交流学习。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章