1. 程式人生 > >Android RecyclerView設計通用Adapter

Android RecyclerView設計通用Adapter

RecylerView 的使用頻率現在也算做是很高了吧?使用起來的確是挺方便的,也容易實現一些比較好看的效果

一、一般步驟

一般的設計流程都是如下所示
首先是需要一個 JavaBean 來承載資料,包含的內容分別是標題還有內容

public class Data {

    private String title;

    private String content;

    public Data(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public
String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }

然後繼承 RecyclerView.Adapter

,並實現幾個固定方法

public class MyRecyclerAdapter extends RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder> {

    private List<Data> dataList;

    private LayoutInflater layoutInflater;

    public MyRecyclerAdapter(Context context, List<Data> dataList) {
        this.dataList = dataList;
        layoutInflater = LayoutInflater.from(context);
    }

    @Override
    public
MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = layoutInflater.inflate(R.layout.item_left, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { holder.tv_title.setText(dataList.get(position).getTitle()); holder.tv_content.setText(dataList.get(position).getContent()); } @Override public int getItemCount() { return dataList.size(); } class MyViewHolder extends RecyclerView.ViewHolder { private TextView tv_title; private TextView tv_content; MyViewHolder(View itemView) { super(itemView); tv_title = (TextView) itemView.findViewById(R.id.tv_title); tv_content = (TextView) itemView.findViewById(R.id.tv_content); } } }

使用到的佈局檔案

<?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="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textSize="15sp" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="15sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#abc" />

</LinearLayout>

然後再來為 RecycleView 填充 Adapter

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        List<Data> dataList = new ArrayList<>();
        for (int i = 0; i < 40; i++) {
            Data data = new Data("葉應是葉", "我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好");
            dataList.add(data);
        }
        MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList);
        recyclerView.setAdapter(adapter);
    }
}

執行效果如下
這裡寫圖片描述

使用多了會發現,其實 MyRecyclerAdapter 中幾個需要實現的方法其實都是步驟都是類似的,如果每次都要來重複這樣的步驟,那真的是在做無用的工作了

這裡,來為 RecycleView 設計一個通用 Adapter,取代掉那些重複的步驟,讓程式碼更加簡潔

二、通用ViewHolder

首先,繼承 RecyclerView.ViewHolder 實現一個通用的 ViewHolder
當中,使用 SparseArray 來存放 View 以減少 findViewById 的次數,SparseArray 比 HashMap 更省記憶體,在某些條件下效能會更好,不過只能儲存 key 為 int 型別的資料,正好用來存放資源ID

因為列表項中一般都是使用 TextView 和 ImageView 兩個控制元件,所以這裡提供兩個控制元件的操作方法。此外,為了監聽列表項單擊和雙擊事件,這裡再來自定義一個介面 onItemCommonClickListener ,用於點選事件回撥

public class CommonViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener, View.OnClickListener {

    // SparseArray 比 HashMap 更省記憶體,在某些條件下效能更好,只能儲存 key 為 int 型別的資料,
    // 用來存放 View 以減少 findViewById 的次數
    private SparseArray<View> viewSparseArray;

    private onItemCommonClickListener commonClickListener;

    public CommonViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);
        itemView.setOnLongClickListener(this);
        viewSparseArray = new SparseArray<>();
    }

    /**
     * 根據 ID 來獲取 View
     *
     * @param viewId viewID
     * @param <T>    泛型
     * @return 將結果強轉為 View 或 View 的子型別
     */
    public <T extends View> T getView(int viewId) {
        // 先從快取中找,找打的話則直接返回
        // 如果找不到則 findViewById ,再把結果存入快取中
        View view = viewSparseArray.get(viewId);
        if (view == null) {
            view = itemView.findViewById(viewId);
            viewSparseArray.put(viewId, view);
        }
        return (T) view;
    }

    public CommonViewHolder setText(int viewId, CharSequence text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    public CommonViewHolder setViewVisibility(int viewId, int visibility) {
        getView(viewId).setVisibility(visibility);
        return this;
    }

    public CommonViewHolder setImageResource(int viewId, int resourceId) {
        ImageView imageView = getView(viewId);
        imageView.setImageResource(resourceId);
        return this;
    }

    protected interface onItemCommonClickListener {

        void onItemClickListener(int position);

        void onItemLongClickListener(int position);

    }

    public void setCommonClickListener(onItemCommonClickListener commonClickListener) {
        this.commonClickListener = commonClickListener;
    }

    @Override
    public void onClick(View v) {
        if (commonClickListener != null) {
            commonClickListener.onItemLongClickListener(getAdapterPosition());
        }
    }

    @Override
    public boolean onLongClick(View v) {
        if (commonClickListener != null) {
            commonClickListener.onItemClickListener(getAdapterPosition());
        }
        return false;
    }
}

三、通用RecyclerAdapter

再來實現一個通用的 RecyclerView.Adapter
因為不知道要使用到的資料型別是哪一種,也為了更好的適配各種資料型別,所以這裡需要用到泛型

當中,onBindViewHolder(CommonViewHolder holder, int position) 需要我們自己來操作,所以這裡再來宣告一個抽象方法 bindData(CommonViewHolder holder, T data) ,由子類來負責實現繫結操作

public abstract class CommonRecycleAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> {

    protected LayoutInflater layoutInflater;

    protected List<T> dataList;

    protected int layoutId;

    public CommonRecycleAdapter(Context context, List<T> dataList, int layoutId) {
        this.layoutInflater = LayoutInflater.from(context);
        this.dataList = dataList;
        this.layoutId = layoutId;
    }

    @Override
    public int getItemViewType(int position) {
        return super.getItemViewType(position);
    }

    @Override
    public CommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = layoutInflater.inflate(layoutId, parent, false);
        return new CommonViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(CommonViewHolder holder, int position) {
        bindData(holder, dataList.get(position));
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    abstract void bindData(CommonViewHolder holder, T data);

}

這樣,就有了一個實現了基本操作的通用 Adapter
這裡再來看看如何使用它

四、使用通用 Adapter

需要先來繼承 CommonRecycleAdapter ,只需要實現一個方法即可,看起來簡潔多了吧
程式碼中聲明瞭兩個建構函式,根據是否需要用到點選事件監聽來選擇

public class MyAdapter extends CommonRecycleAdapter<Data> {

    private CommonViewHolder.onItemCommonClickListener commonClickListener;

    public MyAdapter(Context context, List<Data> dataList) {
        super(context, dataList, R.layout.item_left);
    }

    public MyAdapter(Context context, List<Data> dataList, CommonViewHolder.onItemCommonClickListener commonClickListener) {
        super(context, dataList, R.layout.item_left);
        this.commonClickListener = commonClickListener;
    }

    @Override
    void bindData(CommonViewHolder holder, Data data) {
        holder.setText(R.id.tv_title, data.getTitle())
                .setText(R.id.tv_content, data.getContent())
                .setCommonClickListener(commonClickListener);
    }

}

這裡選擇要監聽點選事件

public class MainActivity extends AppCompatActivity implements CommonViewHolder.onItemCommonClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        List<Data> dataList = new ArrayList<>();
        for (int i = 0; i < 40; i++) {
            Data data = new Data("葉應是葉", "我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好");
            dataList.add(data);
        }
        //MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList);
        MyAdapter adapter = new MyAdapter(this, dataList, this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClickListener(int position) {
        Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onItemLongClickListener(int position) {
        Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show();
    }

}

執行效果
這裡寫圖片描述

五、佈局多樣化

前面,CommonRecycleAdapter 已經可以為我們節省很多程式碼了,免去了一些重複性操作
這裡,可以再來更進一步使 CommonRecycleAdapter 得以更加通用

舉個例子,類似微信App的聊天介面那樣,自己傳送的資訊和對方傳送來的資訊位置是一左一右的,也就是說,這個聊天列表使用了兩個不同的佈局檔案
那麼,可以也為 CommonRecycleAdapter 加入支援多佈局的功能

為了標識資料的不同,在 Data 類中加入一個新的變數 location

/**
 * Created by 葉應是葉 on 2017/2/25.
 */

public class Data {

    private String title;

    private String content;

    private String location;

    public Data(String title, String content) {
        this.title = title;
        this.content = content;
    }

    public String getTitle() {
        return title;
    }

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

    public String getContent() {
        return content;
    }

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

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

}

之前使用的佈局中標題是靠左的,這裡再定義一個佈局,使標題靠右

<?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="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#abc" />

</LinearLayout>

然後,需要有一個方法來判斷哪種資料型別需要使用哪種佈局,所以再來定義一個介面,getLayoutId() 用於返會佈局檔案ID

public interface MultiTypeSupport<T> {

    int getLayoutId(T item, int position);

}

修改 CommonRecycleAdapter
如果 multiTypeSupport 不為 null,意思就是要使用到不同的佈局檔案了,則呼叫 getLayoutId() 方法,將其返回值作為 ItemViewType

public abstract class CommonRecycleAdapter<T> extends RecyclerView.Adapter<CommonViewHolder> {

    protected LayoutInflater layoutInflater;

    protected List<T> dataList;

    protected int layoutId;

    protected MultiTypeSupport<T> multiTypeSupport;

    public CommonRecycleAdapter(Context context, List<T> dataList, int layoutId) {
        this.layoutInflater = LayoutInflater.from(context);
        this.dataList = dataList;
        this.layoutId = layoutId;
    }

    @Override
    public int getItemViewType(int position) {
        if (multiTypeSupport != null) {
            return multiTypeSupport.getLayoutId(dataList.get(position), position);
        }
        return super.getItemViewType(position);
    }

    @Override
    public CommonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (multiTypeSupport != null) {
            layoutId = viewType;
        }
        View itemView = layoutInflater.inflate(layoutId, parent, false);
        return new CommonViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(CommonViewHolder holder, int position) {
        bindData(holder, dataList.get(position));
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    abstract void bindData(CommonViewHolder holder, T data);

}

修改 MyAdapter 類,實現 MultiTypeSupport 介面,根據 Data 物件的 location 欄位的值,來決定返回哪個佈局檔案的ID

public class MyAdapter extends CommonRecycleAdapter<Data> implements MultiTypeSupport<Data> {

    private CommonViewHolder.onItemCommonClickListener commonClickListener;

    public MyAdapter(Context context, List<Data> dataList) {
        super(context, dataList, R.layout.item_left);
    }

    public MyAdapter(Context context, List<Data> dataList, CommonViewHolder.onItemCommonClickListener commonClickListener) {
        super(context, dataList, R.layout.item_left);
        this.commonClickListener = commonClickListener;
        this.multiTypeSupport = this;
    }


    @Override
    void bindData(CommonViewHolder holder, Data data) {
        holder.setText(R.id.tv_title, data.getTitle())
                .setText(R.id.tv_content, data.getContent())
                .setCommonClickListener(commonClickListener);
    }

    @Override
    public int getLayoutId(Data item, int position) {
        if (item.getLocation().equals("left")) {
            return R.layout.item_left;
        }
        return R.layout.item_right;
    }

}

再來修改 Activity 中的程式碼

public class MainActivity extends AppCompatActivity implements CommonViewHolder.onItemCommonClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycleView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        List<Data> dataList = new ArrayList<>();
        boolean bool = false;
        for (int i = 0; i < 40; i++) {
            Data data = new Data("葉應是葉", "我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好,我也不知道說什麼好");
            if (!bool) {
                data.setLocation("left");
            } else {
                data.setLocation("right");
            }
            bool = !bool;
            dataList.add(data);
        }
        //MyRecyclerAdapter adapter = new MyRecyclerAdapter(this, dataList);
        MyAdapter adapter = new MyAdapter(this, dataList, this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClickListener(int position) {
        Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onItemLongClickListener(int position) {
        Toast.makeText(this, "position:" + position, Toast.LENGTH_SHORT).show();
    }

}

執行效果如下
這裡寫圖片描述

可以看到,每隔一行使用到的佈局都不一樣

這樣一來,通用的 Adapter 也設計完成了,以後即使需要使用到很多種不同的資料型別,只要繼承 CommonRecycleAdapter ,實現一個或兩個方法,就可以搞定 Adapter 了

相關推薦

Android RecyclerView設計通用Adapter

RecylerView 的使用頻率現在也算做是很高了吧?使用起來的確是挺方便的,也容易實現一些比較好看的效果 一、一般步驟 一般的設計流程都是如下所示 首先是需要一個 JavaBean 來承載資料,包含的內容分別是標題還有內容 public clas

[Android]RecyclerView基本使用+adapter回撥介面實現點選事件

一、概述 RrcyclerView是ListView的加強版,不僅可以輕鬆實現和ListView同樣的效果,還優化了ListView中存在的各種不同之處。 Android官方推薦我們使用RecyclerView。 二、基本用法 1.首先需要在專案的build.gradle中新增相

RecyclerView打造通用AdapterRecyclerView更加好用

一、概述 記得好久以前針對ListView類控制元件寫過一篇打造萬能的ListView GridView 介面卡,如今RecyclerView異軍突起,其Adapter的用法也與ListView類似,那麼我們也可以一步一步的為其打造通用的Adapter

Android快速生成通用Adapter

原始碼與示例apk 快速開始 Android Studio - 在build.gradle中引入 //必選 compile 'com.dyhdyh.adapters:ada

Android RecyclerView簡單通用介面卡

一直都想寫一個通用的RecyclerView介面卡,但是一直都無從下手,後來看了鴻洋大神的部落格後才知道怎麼寫,並且在此基礎上添加了點自己的東西,終於算是大功告成。先上程式碼看看 public class ViewHolder extends Recycler

Android RecyclerView通用Adapter及item拖拽、滑動刪除

package test.gzy.qqtest.RecyclerView; import android.app.Service; import android.graphics.Color; import android.os.Bundle; import android.os.Vibrator; impo

Android 解析RecyclerView(1)——帶點選事件監聽的通用Adapter

按照最原始的做法,構建一個RecyclerView Adapter需要寫挺多程式碼的,而通過對Java泛型的使用,可以使程式碼量變得只需十幾行即可。此外,由於RecyclerView不像ListView那樣支援直接新增頂部View,也不支援直接新增點選事件監

Android簡單開發之 通用Adapter ViewHolder

chap int mage getitem fail abs earch this get 我們尋常使用Adapter的方式 public class BusbaseSearchApadter extends SimpleBaseApadter { priva

RecyclerView 通用Adapter通用ViewHolder

背景 在使用RecyclerView時,經常需要針對特定的頁面寫viewHolder,相當繁瑣。後來看到專案中採用了統一通用的viewHolder,使用相當簡單。為了更加通用,對原有程式碼進行了抽象簡化,做成了一個依賴庫,直接呼叫即可。 先上效果圖: 開原始碼地址(一共

Android UI設計RecyclerView

                     RecyclerView簡介  RecyclerView是繼ListView和GridView後Google又一力作,它不僅可以很方便的實現瀑布流效果,而且大幅度降低了檢視的耦合性,在設計上有很高的自由度。本文主要分析RecyclerView的使用技巧以及優化。使用前

Android RecyclerView Adapter及Holder的封裝【原創】

專案中使用到了橫向的ListView樣子的功能,一開始使用Gallery做,由於Gallery的自帶彈性功能,效果不理想,於是使用網上的一些開源的橫向ListView,但是效果很卡, 最後使用了v7包中的RecyclerView,初次使用,不太熟練,對其進行了簡單的封裝

RecyclerView通用adapter以及item點選事件的實現

  今天實現內容如下: 1.實現recyclerview通用的adapter CommonAdapter 2.實現recyclerview item的點選事件 先上效果圖 viewpager+f

Android RecyclerView通用介面卡,支援Header、Footer、LoadMore、EmptyView等功能

前言 RecyclerView已經發布很久了,高度的解耦性和靈活的定製性使得其一經發布就廣受好評,但在使用過程中會發現,編寫介面卡的程式碼量貌似有點多,而且不支援新增HeaderView、FooterView等功能,甚至連Item點選監聽都沒有,這當然是不能忍

極簡的Android RecyclerView Adapter(使用DataBinding)

簡介我們知道,DataBinding的核心理念是資料驅動。資料驅動驅動的目標就是View,使用DataBinding,我們通過新增、修改、刪除資料來源,View就會自動予以相關變化。Android RecyclerView的Adapter起的作用就是連線資料和View。一個最

Android RecyclerView嵌套RecyclerView

eat inf idg create 首頁 ber mage asi adapt 原理 RecyclerView嵌套RecyclerView的條目,項目中可能會經常有這樣的需求,但是我們將子條目設置為RecyclerView之後,卻顯示不出來。自己試了很久,終於找到了原因

Android課程設計第二天界面排版

fit span widget mipmap only get pro 其他 orien 註意:課程設計只為完成任務,不做細節描述~ 老師叫我們做一個這個樣子,然後.. 1 <?xml version="1.0" encoding="utf-8"?>

Android課程設計第四天ListView運用

cnblogs super ces getc clas odin long sad tools 註意:課程設計只為完成任務,不做細節描述~ 效果圖 1 <?xml version="1.0" encoding="utf-8"?> 2 <Relat

Android課程設計第五天歡迎界面(滑動)

col color oncreate apt cli ctsc star .cn ges 註意:課程設計只為完成任務,不做細節描述~ 滑動界面 1 package com.example.myapplication; 2 3 import android

aNDROID五子棋設計思路

android 五子棋 hao123 music 5% http baidu .com com %E3%80%8AaNDROID%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91%E6%8F%AD%E7%A7%98%E3%80%8B http://m

Android RecyclerView 設置item間隔的方法

== ews lis lar override top spec clas margin RecyclerView大家常用,但是如何給加載出來的item增加間隔很多人都不知道,下面是方法,直接上代碼了: LinearLayoutManager layoutManage