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);

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

最後

希望這個東西有能幫助到你們,多謝大家的支持!
如果有問題,請不吝多提,我們一起交流學習。

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