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的布局,
-
< android.support.v7.widget.RecyclerView
-
android:id= "@+id/recyclerView"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent"/>
然后在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 MyRecyclerViewAdapterextends RecyclerView. Adapter< MyAdapter. ViewHolder> {
-
private List<String> list;
-
-
public MyAdapter(List<String> list) {
-
this.list = list;
-
}
-
-
@Override
-
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_base_use, parent, false);
-
MyAdapter.ViewHolder viewHolder = new MyAdapter.ViewHolder(view);
-
return viewHolder;
-
}
-
-
@Override
-
public void onBindViewHolder(MyAdapter.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的布局也非常简单
-
<?xml version= "1.0" encoding= "utf-8" ?>
-
< LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_content"
-
android:layout_height= "wrap_content"
-
android:orientation= "vertical">
-
-
< TextView
-
android:id= "@+id/item_tx"
-
android:layout_width= "match_content"
-
android:layout_height= "wrap_content"
-
android:gravity= "center"
-
android:padding= "10dp"
-
android:layout_gravity= "center_horizontal"
-
android:text= "Item"/>
-
-
</ LinearLayout>
然后我们运行效果如下
从例子也可以看出来,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.
-
*
-
* <p>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)}.</p>
-
*/
-
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))
这里的分割线是默认的,我们可以在主题中去设置分割线的颜色
-
<!-- Base application theme. -->
-
< style name= "AppTheme" parent= "Theme.AppCompat.Light.DarkActionBar">
-
<!-- Customize your theme here. -->
-
< item name= "colorPrimary">@color/colorPrimary</ item>
-
< item name= "colorPrimaryDark">@color/colorPrimaryDark</ item>
-
< item name= "colorAccent">@color/colorAccent</ item>
-
< item name= "android:listDivider">@drawable/bg_recyclerview_divider</ item>
-
</ style>
bg_recyclerview_divider.xml
-
<?xml version= "1.0" encoding= "utf-8" ?>
-
< shape xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:shape= "rectangle">
-
-
< gradient
-
android:centerColor= "#ff00ff00"
-
android:endColor= "#ff0000ff"
-
android:startColor= "#ffff0000"
-
android:type= "linear"/>
-
-
< size
-
android:width= "10dp"
-
android:height= "10dp"/>
-
-
</ shape>
运行后效果如下
以上的分割线只适用在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< MyRecyclerViewAdapter. ViewHolder> {
-
private List<String> 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<String> list) {
-
this.list = list;
-
}
-
-
@Override
-
public MyRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_base_use, 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)
作者地址:https://blog.youkuaiyun.com/tuike/article/details/79064750