Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android RecyclerView使用詳解

Android RecyclerView使用詳解

編輯:關於Android編程

簡介

RecyclerView是Google在android-supportv7包中推出的一個新的控件,該控件的主要作用是用於替代ListView、GridView,相比較於這兩種控件RecyclerView有以下有點:

RecyclerView內部封裝了ViewHolder,更加方便Item的復用 提供了一種插拔式的體驗,高度解耦,異常靈活,增加控件的擴展性。

RecyclerView的靈活性可以體現在以下幾點

通過RecyclerView.setLayoutManager()方法添加布局管理器 通過RecyclerView.addItemDecoration()方法添加item分割線 通過RecyclerView.setItemAnimator()方法添加item增刪動畫

LayoutManager布局管理器介紹

LinearLayoutManager:線性布局 GridLayoutManager:網格布局 StaggeredGridLayoutManager:瀑布流布局

RecyclerView使用

導入類庫

對於使用eclipse開發用戶,需要導入sdk/extras/android/support/v7目錄下recyclerview類庫 對於Android studio開發用戶,直接通過修改build.gradle添加類庫即可

通過RecyclerView實現ListView:

效果如下:
這裡寫圖片描述

實現代碼如下:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> package com.example.zhangke.recyclerviewdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private ArrayList mDatas; private RecyclerViewAdapter mAdapter; private StaggeredGridLayoutAdapter mStagAdapter; private RecyclerView.ItemDecoration mItemDecoration; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mRecyclerView = (RecyclerView) findViewById(R.id.recycleview); initData(); //垂直listview mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); //設置分割線 mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL); mRecyclerView.addItemDecoration(mItemDecoration); // 設置增刪動畫 mRecyclerView.setItemAnimator(new DefaultItemAnimator()); mAdapter = new RecyclerViewAdapter(this, mDatas); //設置點擊事件 mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() { @Override public void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show(); } }); mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() { @Override public void onLongItemClick(View view, int position) { Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show(); } }); //設置Adapter mRecyclerView.setAdapter(mAdapter); } /** * 初始化數據 */ private void initData() { mDatas = new ArrayList(); for (int i = 0; i < 100; i++) { mDatas.add("item " + i); } } public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.add: mAdapter.addItem(1, "item" + 1); break; case R.id.remove: mAdapter.removeItem(1); break; } return true; } }

RecyclerView.Adapter說明

同ListView一樣,RecyclerView現實數據需要通過Adapter實現,Google已給我們提供了RecyclerView.Adapter的內部抽象類,所以必須通過繼承該類實現一個Adapter。繼承該類需要復寫三個方法:

public int getItemCount():獲得item數目 public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType):創建一個ViewHolder public void onBindViewHolder(final RecyclerViewHolder holder, int position):綁定Viewholder即對Item進行具體操作

注意:由於Adapter會在內部封裝一個ViewHolder,所以我們還要自定義一個ViewHolder。

對於RecyviewView,系統並沒有提供item點擊事件,所以在自定義Adapter時,通常會自己定義Item的點擊事件的接口以實現item點擊。

具體代碼如下:

package com.example.zhangke.recyclerviewdemo;

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

import java.util.ArrayList;

/**
 * Created by zhangke on 16/6/15.
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter {


    private Context mContext;
    private ArrayList mDatas;
    private OnItemClickListener mOnItemClickListener;
    private OnLongItemClickListener mOnLongItemClickListener;

    public RecyclerViewAdapter(Context context, ArrayList datas) {
        this.mContext = context;
        this.mDatas = datas;
    }


    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        RecyclerViewHolder holder = new RecyclerViewHolder(View.inflate(mContext, R.layout.item, null));
        return holder;
    }

    @Override
    public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
        holder.mTextView.setText(mDatas.get(position));
        if (mOnItemClickListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnItemClickListener.onItemClick(v, layoutPosition);
                }
            });
        }
        if (mOnLongItemClickListener != null) {
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    int layoutPosition = holder.getLayoutPosition();
                    mOnLongItemClickListener.onLongItemClick(v, layoutPosition);
                    return true;
                }
            });
        }
    }

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

    /**
     * viewholder
     */
    class RecyclerViewHolder extends RecyclerView.ViewHolder {

        private TextView mTextView;

        public RecyclerViewHolder(View itemView) {
            super(itemView);

            mTextView = (TextView) itemView.findViewById(R.id.textview);
        }
    }

    /**
     * 添加
     * @param position
     * @param item
     */
    public void addItem(int position, String item){
        mDatas.add(position, item+position);
        notifyItemInserted(position);
    }

    /**
     * 刪除
     * @param position
     */
    public void removeItem(int position){
        mDatas.remove(position);
        notifyItemRemoved(position);
    }

    /**
     * item點擊事件
     */
    public interface OnItemClickListener {
        /**
         * item點擊事件
         *
         * @param view
         * @param position
         */
        void onItemClick(View view, int position);
    }

    public interface OnLongItemClickListener {
        /**
         * item長按點擊事件
         *
         * @param view
         * @param position
         */
        void onLongItemClick(View view, int position);
    }


    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

    public void setOnLongItemClickListener(OnLongItemClickListener listener) {
        this.mOnLongItemClickListener = listener;
    }
}

ItemDecoration分割線

在之前已經提到,RecyclerView有addItemDecoration()的方法,該方法的作用是為RecyclerView添加item分割線,為此我們不難想象RecyclerView默認是沒有分割線的。但是google官方並沒有提供具體ItemDecoration的方法,僅僅是定義了一個ItemDecoration的抽象類,所以要想實現分割線效果,必須要自己實現一個ItemDecoration。

ItemDecoration抽象方法介紹:

public void onDraw(Canvas c, RecyclerView parent, State state):繪制分割線 public void onDrawOver(Canvas c, RecyclerView parent, State state):繪制分割線,該方法後於onDraw,一般實現一個即可 public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state):用於計算item分割線的偏移量

以下是通過ItemDecoration實現ListView和GridView的分割線:

ListView分割線:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割線
 * <p/>
 * Created by zhangke on 16/6/15.
 */
public class DividerItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 分割線
     */
    private static int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 水平分割線
     */
    public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    /**
     * 垂直分割線
     */
    public static final int VERTICAL = LinearLayoutManager.VERTICAL;
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 分割線
     */
    private Drawable mDivider;

    private int mOrientation;

    /**
     * @param context     上下文
     * @param orientation 分割線方向
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DividerItemDecoration(Context context, int orientation) {
        this.mContext = context;
//        TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS);
//        mDivider = typedArray.getDrawable(0);
//        typedArray.recycle();

        mDivider = context.getDrawable(R.drawable.divider_view);
        setOrientation(orientation);
    }

    /**
     * 設置分割線方向
     *
     * @param orientation
     */
    private void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException("參數異常");
        }
        this.mOrientation = orientation;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == HORIZONTAL) {
            drawHorizontal(c, parent, state);
        } else {
            drawVertical(c, parent, state);
        }

        super.onDraw(c, parent, state);
    }

    /**
     * 繪制垂直方向分割線
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);

            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int top = childView.getBottom() + layoutParams.bottomMargin + Math.round(ViewCompat.getTranslationY(childView));
            int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 繪制水平方向分割線
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childView = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) childView.getLayoutParams();
            int left = childView.getRight() + layoutParams.rightMargin + Math.round(ViewCompat.getTranslationX(childView));
            int right = left + mDivider.getIntrinsicWidth();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 設置分割線寬偏移量:
     * 通過RecyclerView源碼可知:getItemOffset方法會在measureChild方法中調用,我們通過給方法的參數outRect
     * 設置left、top、right、bottom,設置的值會被增加到childView的padding值中。
     *
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

        if (mOrientation == HORIZONTAL) {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        }
    }


}

GridView分割線:

package com.example.zhangke.recyclerviewdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * item分割線
 * <p/>
 * Created by zhangke on 16/6/15.
 */
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {

    /**
     * 分割線
     */
    private static int[] ATTRS = new int[]{android.R.attr.listDivider};

    /**
     * 水平分割線
     */
    public static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
    /**
     * 垂直分割線
     */
    public static final int VERTICAL = LinearLayoutManager.VERTICAL;
    /**
     * 上下文
     */
    private Context mContext;
    /**
     * 分割線
     */
    private Drawable mDivider;

    private int mOrientation;
    private int spanCount;

    /**
     * @param context     上下文
     * @param orientation 分割線方向
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public DividerGridItemDecoration(Context context, int orientation) {
        this.mContext = context;
//        TypedArray typedArray = mContext.obtainStyledAttributes(ATTRS);
//        mDivider = typedArray.getDrawable(0);
//        typedArray.recycle();

        mDivider = context.getDrawable(R.drawable.divider_view);
        setOrientation(orientation);
    }

    /**
     * 設置分割線方向
     *
     * @param orientation
     */
    private void setOrientation(int orientation) {
        if (orientation != HORIZONTAL && orientation != VERTICAL) {
            throw new IllegalArgumentException("參數異常");
        }
        this.mOrientation = orientation;
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        drawHorizontal(c, parent, state);
        drawVertical(c, parent, state);
    }

    /**
     * 繪制垂直方向分割線
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawVertical(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams();
            int left = child.getLeft() - params.leftMargin;
            int right = child.getRight()+ params.rightMargin;
            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }

    }

    /**
     * 繪制水平方向分割線
     *
     * @param c
     * @param parent
     * @param state
     */
    private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (GridLayoutManager.LayoutParams) child.getLayoutParams();
            int left = child.getRight() + params.rightMargin;
            int right = left + mDivider.getIntrinsicWidth();
            int top = child.getTop() - params.topMargin;
            int bottom = child.getBottom() + params.bottomMargin;

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    /**
     * 設置分割線寬偏移量:
     * 通過RecyclerView源碼可知:getItemOffset方法會在measureChild方法中調用,我們通過給方法的參數outRect
     * 設置left、top、right、bottom,設置的值會被增加到childView的padding值中。
     */
    @Override
    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
        if (isLastColum(itemPosition, parent)) {
            //最後一列
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else if (isLastRow(itemPosition, parent)) {
            //最後一行
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());
        }

    }

    /**
     * 是否是最後一行
     *
     * @param itemPosition
     * @param parent
     * @return
     */
    private boolean isLastRow(int itemPosition, RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int childCount = parent.getAdapter().getItemCount();

            int lastRowCount = childCount %  spanCount;
            if (lastRowCount == 0) {
                return false;
            } else if(itemPosition >= (childCount - lastRowCount)){
                return true;
            }
        }
        return false;
    }

    /*
    * 是否是最後一列
     */
    private boolean isLastColum(int itemPosition, RecyclerView parent) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            int spanCount = getSpanCount(parent);
            if ((itemPosition + 1) % spanCount == 0) {
                return true;
            }
        }
        return false;
    }

    private int getSpanCount(RecyclerView parent) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            GridLayoutManager lm = (GridLayoutManager) layoutManager;
            int spanCount = lm.getSpanCount();
            return spanCount;
        }
        return 0;
    }

}

通過LayoutManager實現不同效果

效果圖如下:
這裡寫圖片描述

代碼如下:

package com.example.zhangke.recyclerviewdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private RecyclerView mRecyclerView;
    private ArrayList mDatas;
    private RecyclerViewAdapter mAdapter;
    private StaggeredGridLayoutAdapter mStagAdapter;
    private RecyclerView.ItemDecoration mItemDecoration;

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

        mRecyclerView = (RecyclerView) findViewById(R.id.recycleview);
        initData();

        //垂直listview
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        //設置分割線
        mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        mRecyclerView.addItemDecoration(mItemDecoration);
        // 設置增刪動畫
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        mAdapter = new RecyclerViewAdapter(this, mDatas);

        //設置點擊事件
        mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
            @Override
            public void onLongItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
            }
        });

        //設置Adapter
        mRecyclerView.setAdapter(mAdapter);

    }

    /**
     * 初始化數據
     */
    private void initData() {
        mDatas = new ArrayList();
        for (int i = 0; i < 100; i++) {
            mDatas.add("item " + i);
        }

    }


    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mItemDecoration != null) {
            mRecyclerView.removeItemDecoration(mItemDecoration);
        }
        switch (item.getItemId()) {
            case R.id.add:

                mAdapter.addItem(1, "item" + 1);
                break;

            case R.id.remove:
                mAdapter.removeItem(1);
                break;

            case R.id.showVertical:
                //垂直listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mAdapter.setOnItemClickListener(new RecyclerViewAdapter.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mAdapter.setOnLongItemClickListener(new RecyclerViewAdapter.OnLongItemClickListener() {
                    @Override
                    public void onLongItemClick(View view, int position) {
                        Toast.makeText(MainActivity.this, "onLongItemClick " + position, Toast.LENGTH_SHORT).show();
                    }
                });

                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showHorizontal:
                //水平listview
                mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridHorizontal:
                // 水平gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4, LinearLayoutManager.HORIZONTAL, false));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.HORIZONTAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showGridVertical:
                // 垂直gridview
                mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4));
                mItemDecoration = new DividerGridItemDecoration(this, DividerGridItemDecoration.VERTICAL);
                mRecyclerView.addItemDecoration(mItemDecoration);
                mAdapter = new RecyclerViewAdapter(this, mDatas);
                mRecyclerView.setAdapter(mAdapter);
                break;

            case R.id.showStaggle:
                // 瀑布流
                mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(3, LinearLayoutManager.VERTICAL));
                mStagAdapter = new StaggeredGridLayoutAdapter(this, mDatas);
                mRecyclerView.setAdapter(mStagAdapter);
                break;
        }
        return true;
    }
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved