自从用了RecyclerView,腰再也不痛了,手也不酸了

本文已同步发表到我的技术微信公众号,扫一扫文章底部的二维码或在微信搜索 “程序员驿站”即可关注,不定期更新优质技术文章。同时,也欢迎加入QQ技术群(群号:650306310)一起交流学习!

RecyclerView是support:recyclerview-v7中提供的控件,最低兼容到android 3.0版本。

官方介绍RecyclerView为在有限的窗口展现大量数据的控件。拥有类似功能的控件有ListView、GridView以及被Google遗弃的Gallery等,为毛已经有了它们,Google还推出RecyclerView呢,那就要说说RecyclerView所具有的一些优势了。

那RecyclerView到底有啥优势呢?总结起来六颗字:低耦合高类聚。RecyclerView已经标准化ViewHolder,我们自定义的ViewHoler需要继承 RecyclerView.ViewHolder,然后在构造方法中初始化控件,后面会有具体介绍。通过设置不同的LayoutManager,以及结合ItemDecoration , ItemAnimator,ItemTouchHelper,可以实现非常炫酷的效果,这些是ListView等控件难以企及的。

基本使用:

1.使用前需要在在gradle中添加依赖

 

implementation 'com.android.support:recyclerview-v7:27.0.2'

2.编写代码,首先我们需要在Xml中写RecyclerView的布局,

    

然后在activity中获取RecyclerView,并设置LayoutManager以及adapter

 

//通过findViewById拿到RecyclerView实例
mRecyclerView =   findViewById(R.id.recyclerView);
//设置RecyclerView管理器
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
//初始化适配器
mAdapter = new MyRecyclerViewAdapter(list); 
//设置添加或删除item时的动画,这里使用默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//设置适配器
mRecyclerView.setAdapter(mAdapter);

下面是MyRecyclerViewAdapter的代码:

package com.sharejoys.recyclerviewdemo.actvity;

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

import com.sharejoys.recyclerviewdemo.R;

import java.util.List;

/**
 * Created by 青青-子衿 on 2018/1/15.
 */


public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
    private List list;
    
    public MyRecyclerViewAdapter(List list) {
        this.list = list;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_normal, parent, false);
        MyAdapter.ViewHolder viewHolder = new MyRecyclerViewAdapter.ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(MyRecyclerViewAdapter.ViewHolder holder, int position) {
        holder.mText.setText(list.get(position));
    }

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

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView mText;
        ViewHolder(View itemView) {
            super(itemView);
            mText = itemView.findViewById(R.id.item_tx);
        }
    }
}

这里item_normal的布局也非常简单




    

然后我们运行效果如下

从例子也可以看出来,RecyclerView的用法并不比ListView复杂,反而更灵活好用,它将数据、排列方式、数据的展示方式都分割开来,因此可定制型,自定义的形式也非常多,非常灵活。

 

设置横向布局:

 

 mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));

 

设置网格布局:

 

 

 mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));

设置瀑布流:

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

如果第二个参数可以设置为横向的,则效果如下:

从以上可知,我们可以通过设置不同的管理器,实现不同的效果
LinearLayoutManager:以线性布局展示,可以设置横向和纵向
GridLayoutManager:以网格形式展示,类似GridView效果
StaggeredGridLayoutManager:以瀑布流形式的效果

RecyclerView条目之间默认没有分割线,那是否可以像ListView一样设置divider以及dividerHight搞一条分割线出来呢,答案是不可以的,google并没有提供这样的属性。但是谷歌为我们提供了可以定制的解决办法,那就是以下要说ItemDecoration

 

利用ItemDecoration实现条目分割线

ItemDecoration是谷歌定义的可用于画分割线的类, 是抽象的,需要我们自己去实现

 

  /**
     * An ItemDecoration allows the application to add a special drawing and layout offset
     * to specific item views from the adapter's data set. This can be useful for drawing dividers
     * between items, highlights, visual grouping boundaries and more.
     *
     * 

All ItemDecorations are drawn in the order they were added, before the item * views (in {@link ItemDecoration#onDraw(Canvas, RecyclerView, RecyclerView.State) onDraw()} * and after the items (in {@link ItemDecoration#onDrawOver(Canvas, RecyclerView, * RecyclerView.State)}.

*/ public abstract static class ItemDecoration { public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); } @Deprecated public void onDraw(Canvas c, RecyclerView parent) { } public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); } @Deprecated public void onDrawOver(Canvas c, RecyclerView parent) { } @Deprecated public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { outRect.set(0, 0, 0, 0); } public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent); } }

当我们通过

mRecyclerView.addItemDecoration();

    onDraw: 该方法可以在RecyclerView的画布上画任何装饰,且是在 the item views 被绘制之前回调
    onDrawOver:该方法可以在RecyclerView的画布上画任何装饰,且是在 the item views 被绘制之后回调
    getItemOffsets :可以在该方法中为the item views添加偏移量

下面我们可以就通过继承ItemDecoration为RecyclerView添加分割线。
DividerItemDecoration的代码如下:

 

 

package com.sharejoys.mvpdemo.ui.customview;

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.annotation.IntDef;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Date: 2018/1/14
 *
 * @author 青青-子衿
 * @since 1.0
 */

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    @OrientationType
    private int mOrientation = LinearLayoutManager.VERTICAL;
    private Drawable mDivider;

    private int[] attrs = new int[]{
            android.R.attr.listDivider
    };

    public DividerItemDecoration(Context context, @OrientationType int orientation) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
        setOrientation(orientation);
    }

    private void setOrientation(@OrientationType int orientation) {
        if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
            throw new IllegalArgumentException("传入的布局类型不合法");
        }
        this.mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        //调用这个绘制方法,RecyclerView会回调该绘制方法,需要我们自己去绘制条目的间隔线
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            //垂直
            drawVertical(c, parent);
        } else {
            //水平
            drawHorizontal(c, parent);
        }
    }

    private void drawVertical(Canvas c, RecyclerView parent) {
        // 画水平线
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

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

    private void drawHorizontal(Canvas c, RecyclerView parent) {
        int top = parent.getPaddingTop();
        int bottom = parent.getHeight() - parent.getPaddingBottom();
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);

            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int left = child.getRight() + params.rightMargin + Math.round(ViewCompat.getTranslationX(child));
            int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        //获得条目的偏移量(所有的条目都会回调一次该方法)
        if (mOrientation == LinearLayoutManager.VERTICAL) {
            //垂直
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            //水平
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }

    @IntDef({LinearLayoutManager.VERTICAL, LinearLayoutManager.HORIZONTAL})
    public @interface OrientationType {
    }
}

然后在activity设置水平方向

mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); 
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));


竖直方向:

 

 

 

 

mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONAL, false); 
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.HORIZONAL))

这里的分割线是默认的,我们可以在主题中去设置分割线的颜色

   
    

bg_recyclerview_divider.xml




    

    

运行后效果如下

以上的分割线只适用在LinearLayoutManager的相关布局中。
对于GridLayoutManager布局是不适用的。需要我们单独写一个。

 

以下是对于GridLayoutManager布局的分割线代码

 

package com.sharejoys.recyclerviewdemo.view;

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.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * Date: 2018/1/14 
 *
 * @author 青青-子衿
 * @since 1.0
 */

public class DividerGridViewItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable mDivider;
    private int[] attrs = new int[]{
            android.R.attr.listDivider};

    public DividerGridViewItemDecoration(Context context) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
    }

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

    private void drawVertical(Canvas c, RecyclerView parent) {
        //绘制垂直间隔线(垂直的矩形)
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.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);
        }
    }

    private void drawHorizontal(Canvas c, RecyclerView parent) {
        //绘制水平分割线
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            int left = child.getLeft() - params.leftMargin;
            int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth();
            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + mDivider.getIntrinsicHeight();

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

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        // 四个方向的偏移值
        int right = mDivider.getIntrinsicWidth();
        int bottom = mDivider.getIntrinsicHeight();

        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
        int itemPosition = params.getViewAdapterPosition();
        if (isLastColum(itemPosition, parent)) {
            right = 0;
        }

        if (isLastRow(itemPosition, parent)) {
            bottom = 0;
        }
        outRect.set(0, 0, right, bottom);
    }

    /**
     * 是否最后一行
     */
    private boolean isLastRow(int itemPosition, RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        if (spanCount != -1) {
            int childCount = parent.getAdapter().getItemCount();
            int lastRowCount = childCount % spanCount;
            //最后一行的数量小于spanCount
            if (lastRowCount == 0 || lastRowCount < spanCount) {
                return true;
            }
        }

        return false;
    }


    /**
     * 根据parent获取到列数
     */
    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 -1;
    }

    /**
     * 判断是否是最后一列
     */
    private boolean isLastColum(int itemPosition, RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        if (spanCount != -1) {
            if ((itemPosition + 1) % spanCount == 0) {
                return true;
            }
        }
        return false;
    }
}

我们在activity中使用该分割线

 

 

        mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        mRecyclerView.addItemDecoration(new DividerGridViewItemDecoration(this));

点击事件


RecyclerView并没有像ListView的那样可以设置点击事件以及长按点击事件,这个需要我们可以在adapter中去设置回调的方式实现,具体代码如下:
MyRecyclerViewAdapter的代码如下:

 

package com.sharejoys.recyclerviewdemo.actvity;

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

import com.sharejoys.recyclerviewdemo.R;

import java.util.List;

/**
 * Created by 青青-子衿 on 2018/1/15.
 */


public class MyRecyclerViewAdapter extends RecyclerView.Adapter {
    private List list;
    private OnItemClickListener onItemClickListener;
    private OnItemLongClickListener onItemLongClickListener;

    /**
     * 设置点击事件
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    /**
     * 设置长按点击事件
     */
    public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
        this.onItemLongClickListener = onItemLongClickListener;
    }

    public MyRecyclerViewAdapter(List list) {
        this.list = list;
    }

    @Override
    public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_normal, parent, false);
        MyRecyclerViewAdapter.ViewHolder viewHolder = new MyRecyclerViewAdapter.ViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(MyRecyclerViewAdapter.ViewHolder holder, int position) {
        holder.mText.setText(list.get(position));
        int adapterPosition = holder.getAdapterPosition();
        if (onItemClickListener != null) {
            holder.itemView.setOnClickListener(new MyOnClickListener(position, list.get(adapterPosition)));
        }
        if (onItemLongClickListener != null) {
            holder.itemView.setOnLongClickListener(new MyOnLongClickListener(position, list.get(adapterPosition)));
        }
    }

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

    class ViewHolder extends RecyclerView.ViewHolder {
        TextView mText;

        ViewHolder(View itemView) {
            super(itemView);
            mText = itemView.findViewById(R.id.item_tx);
        }
    }

    private class MyOnLongClickListener implements View.OnLongClickListener {
        private int position;
        private String data;

        public MyOnLongClickListener(int position, String data) {
            this.position = position;
            this.data = data;
        }

        @Override
        public boolean onLongClick(View v) {
            onItemLongClickListener.onItemLongClick(v, position, data);
            return true;
        }
    }

    private class MyOnClickListener implements View.OnClickListener {
        private int position;
        private String data;

        public MyOnClickListener(int position, String data) {
            this.position = position;
            this.data = data;
        }

        @Override
        public void onClick(View v) {
            onItemClickListener.onItemClick(v, position, data);
        }
    }


    public interface OnItemClickListener {
        void onItemClick(View view, int position, String data);
    }

    public interface OnItemLongClickListener {
        void onItemLongClick(View view, int position, String data);
    }

}

activity中设置监听:

 

    mAdapter.setOnItemClickListener(new MyRecyclerViewAdapter.OnItemClickListener() {
            @Override
         public void onItemClick(View view, int position, String data) {
                Toast.makeText(MainActivity.this, "您点击了:  " + data, Toast.LENGTH_SHORT).show();
            }
        });
        mAdapter.setOnItemLongClickListener(new MyRecyclerViewAdapter.OnItemLongClickListener() {
            @Override
            public void onItemLongClick(View view, int position, String data) {
                Toast.makeText(MainActivity.this, "您长按点击了:  " + data, Toast.LENGTH_SHORT).show();
            }
        });

运行后效果如下:

ItemAnimator

 

我们可以为RecyclerView设置增加和删除动画,这里我们可以使用默认动画
 

//设置添加或删除item时的动画,这里使用默认动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());

然后在Adapter中增加删除和添加数据的方法

   /**
     * 插入一条数据
     *
     * @param index 下标
     * @param s     数据
     */
    public void addItem(int index, String s) {
        list.add(index, s);
        notifyItemInserted(index);
    }

    /**
     * 删除一条数据
     *
     * @param index 下标
     */
    public void deleteItem(int index) {
        list.remove(index);
        notifyItemRemoved(index);
    }

activty调用删除和添加方法后效果如下

RecycleView还有一些其他用法,比如结合ItemTouchHelper实现item的拖拽效果,可以自定义增加header和footer(类似Listview),有时间我就会补上!

最后附上demo地址

关注我的技术公众号"程序员驿站",每天都有优质技术文章推送,微信扫一扫下方二维码即可关注:


自从用了RecyclerView,腰再也不痛了,手也不酸了_第1张图片

你可能感兴趣的:(Android开发之高级UI)