Material Design之RecyclerView基本講解與瀑布流的實(shí)現(xiàn)

RecyclerView簡(jiǎn)介

RecyclerView是一種新的視圖組,目標(biāo)是為任何基于適配器的視圖提供相似的渲染方式。它被作為L(zhǎng)istView和GridView控件的繼承者,具有更優(yōu)的靈活性與可替代性。在最新的support-v7版本中提供支持。本文將講解RecyclerView的簡(jiǎn)單實(shí)現(xiàn),添加刪除條目,點(diǎn)擊事件添加與瀑布流的實(shí)現(xiàn)。

相關(guān)原理與簡(jiǎn)單實(shí)現(xiàn)

添加依賴

在AndroidStudio的build.gradle中添加依賴:

dependencies {
    ...
    compile 'com.android.support:recyclerview-v7:25.3.0'
}

在布局中使用

添加完依賴后就可以在布局中使用RecyclerView了:

<android.support.v7.widget.RecyclerView
    android:id="@+id/main_recyclerview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

RecyclerView.Adapter

RecyclerView封裝了一種新型的適配器,與現(xiàn)在使用的適配器大同小異。它強(qiáng)制用戶使用RecyclerView提供的ViewHolder,使用時(shí)主要需要重寫onCreateViewHolder與onBindViewHolder方法。前者用來(lái)展現(xiàn)視圖及其持有者,且只有真正需要一個(gè)新view時(shí)才會(huì)被回調(diào),不需要檢查是否已經(jīng)被回收。后者用來(lái)綁定數(shù)據(jù)到View上。

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Context context;
    private List<String> list;
    private LayoutInflater inflater;
    
    public MyAdapter(Context context, List<String> list) {
        this.context = context;
        this.list = list;
        inflater = LayoutInflater.from(context);
    }
    
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item_recyclerview, parent, false);
        MyViewHolder holder = new MyViewHolder(view);
        return holder;
    }
    
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textView.setText(list.get(position));
    }
    
    @Override
    public int getItemCount() {
        return list.size();
    }
    
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        public MyViewHolder(View itemView) {
            super(itemView);
            textView = (TextView) itemView.findViewById(R.id.item_textview);
        }
    }
}

LayoutManager

RecyclerView通過(guò)布局管理器LayoutManager控制每一個(gè)item如何進(jìn)行排列擺放,何時(shí)展示和隱藏。回收或重用一個(gè)View時(shí)LayoutManager會(huì)向適配器請(qǐng)求新的數(shù)據(jù)來(lái)替換舊的數(shù)據(jù),這種機(jī)制避免了創(chuàng)建過(guò)多的View和頻繁的調(diào)用findViewById方法,目前其自帶的主要有以下三種:

  • LinearLayoutManager:ListView樣式
  • GridLayoutManager:GridView樣式
  • StaggeredGridLayoutManager:瀑布流樣式

ItemDecoration

RecyclerView并不能像ListView一樣直接在xml布局中修改item分割線樣式。需要在Activity動(dòng)態(tài)設(shè)置,當(dāng)然更推薦在單個(gè)條目布局中設(shè)置margin或者padding來(lái)實(shí)現(xiàn)分割線效果,這里提供一個(gè)分割線。

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;
    private int mOrientation;
    
    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }
    
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
    
    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }
    
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
    
    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
            }
    }
}

ItemAnimator

ItemAnimator會(huì)根據(jù)適配器上收到的通知來(lái)動(dòng)畫顯示視圖組的修改,比如item的添加與刪除。DefaultItemAnimator已經(jīng)能很好的展現(xiàn)動(dòng)畫效果了。

RecyclerView初始化

若想初始化一個(gè)RecyclerView使其進(jìn)入工作狀態(tài),你需要在Activity中做以下的操作:

RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view_main);
MyAdapter adapter = new MyAdapter(this, list);
//設(shè)置RecyclerView保持固定的大小
recyclerView.setHasFixedSize(true);
//設(shè)置適配器
recyclerView.setAdapter(adapter);
//設(shè)置RecyclerView ListView樣式布局管理
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//設(shè)置RecyclerView的Item分割線
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
//設(shè)置RecyclerView的動(dòng)畫
recyclerView.setItemAnimator(new DefaultItemAnimator());

//設(shè)置RecyclerView GridView樣式
//recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 3));
//設(shè)置RecyclerView 水平GridView樣式
//recyclerView.setLayoutManager(new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.HORIZONTAL));
//設(shè)置RecyclerView 瀑布流樣式
//recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

點(diǎn)擊事件與添加刪除Item

美中不足的是RecyclerView并沒(méi)有提供像ListView一樣的Item點(diǎn)擊與Item長(zhǎng)點(diǎn)擊事件,不提供咱們就自己造,通過(guò)接口回調(diào)來(lái)實(shí)現(xiàn)。

Adapter中

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private Context context;
    private LayoutInflater inflater;
    protected ArrayList<String> datas;
    private onItemClickedListener onItemClickedListener;
    
    public MyAdapter(Context context, ArrayList<String> datas) {
        this.context = context;
        this.datas = datas;
        inflater = LayoutInflater.from(context);
    }
    
    public void setOnItemClickedListener(MyAdapter.onItemClickedListener onItemClickedListener) {
        this.onItemClickedListener = onItemClickedListener;
    }
    
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textView.setText(datas.get(position));
    }
    
    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }
    
    @Override
    public int getItemCount() {
        return datas.size();
    }
    
   /**
    * 添加條目
    */
    public void addItem(int position) {
        datas.add(position, "xulei");
    //        notifyDataSetChanged();
        notifyItemInserted(position);//調(diào)用這個(gè)才有動(dòng)畫效果
    }
    
   /**
    * 移除條目
    */
    public void removeItem(int position) {
        datas.remove(position);
        notifyItemRemoved(position);
    }
    
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView textView;
        
        public MyViewHolder(View itemView) {
            super(itemView);
            //初始化控件
            textView = (TextView) itemView.findViewById(R.id.item_textview);
            //設(shè)置當(dāng)前條目單擊監(jiān)聽(tīng)
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (onItemClickedListener != null)
                        onItemClickedListener.onClick(view, getAdapterPosition());//可立刻獲取到當(dāng)前position
//                        onItemClickedListener.onClick(view, getLayoutPosition());//需等當(dāng)前視圖更新完才能獲取到當(dāng)前position,<16ms。
                }
            });
            //設(shè)置當(dāng)前條目長(zhǎng)按監(jiān)聽(tīng)
            itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View view) {
                    if (onItemClickedListener != null)
                        onItemClickedListener.onLongClick(view, getAdapterPosition());
                    return false;
                }
            });
        }
    }
    
   /**
    * 點(diǎn)擊回調(diào)的接口
    */
    interface onItemClickedListener {
        void onClick(View view, int position);
        
        void onLongClick(View view, int position);
    }
}

Activity中

在Activity中實(shí)例化Adapter之后添加如下代碼:

adapter.setOnItemClickedListener(new MyAdapter.onItemClickedListener() {
    @Override
    public void onClick(View view, int position) {
        adapter.addItem(position);
        Toast.makeText(MainActivity.this, "點(diǎn)擊click:" + position, Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public void onLongClick(View view, int position) {
        adapter.removeItem(position);
        Toast.makeText(MainActivity.this, "長(zhǎng)按click:" + position, Toast.LENGTH_SHORT).show();
    }
});

瀑布流的實(shí)現(xiàn)

想實(shí)現(xiàn)瀑布流的樣式通過(guò)使用RecyclerView也很容易就能實(shí)現(xiàn)。

首先在Activity中設(shè)置LayoutManager時(shí)選擇:

recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

修改Adapter

瀑布流自然是每個(gè)條目的高度不同才能出現(xiàn)瀑布的效果(水平布局則是寬度不同),那么只需在每個(gè)條目綁定數(shù)據(jù)時(shí)動(dòng)態(tài)改變下其高度即可,貼出瀑布流Adapter代碼:

import android.content.Context;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class StaggerAdapter extends MyAdapter {
    private List<Integer> heights;
    public StaggerAdapter(Context context, ArrayList<String> datas) {
        super(context, datas);
        heights = new ArrayList<>();
        for (int i = 0; i < datas.size(); i++) {
            heights.add((int) (100 + Math.random() * 300));
        }
    }
    
    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        layoutParams.height = heights.get(position);
//        layoutParams.width = heights.get(position);
        holder.itemView.setLayoutParams(layoutParams);
        holder.textView.setText(datas.get(position));
    }
}

總結(jié)

通過(guò)實(shí)踐發(fā)現(xiàn)RecyclerView相較于ListView與GridView確實(shí)強(qiáng)大很多,更加的靈活與方便,提高了開(kāi)發(fā)效率。但也有不足之處,如并未封裝點(diǎn)擊事件的回調(diào),確實(shí)是比較頭疼。期待Google的完善。

附上GitHub源碼:
RecyclerViewDemo
RecyclerViewDevelop

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容