刚学习了RecyclerView,在这里对自己学习的内容进行一个总结,方便以后查看。
现在学习的知识很浅,只是知道如何去使用,而没有去研究它的源码,不知道它的内部具体原理是如何。
这篇文章大概有些说法是不对的,希望下次有机会用到RecyclerView来看这篇文章可以修改里面描述不对的语句。
希望看到这篇文章的人有不对之处可以帮助指出,谢谢。
正文开始。
导入RecyclerView的依赖包
实现类似ListView的功能
RecyclerView与ListView类似,需要适配器来填充数据。具体的步骤与非常熟悉的ListView一样。
首先activity的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></android.support.v7.widget.RecyclerView>
</LinearLayout>
item的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="text"
android:textColor="@color/textColor"
android:padding="10dp"
android:gravity="center"
android:textSize="18sp"
android:background="@color/textBackground"/>
</LinearLayout>
很简单,item就是一个TextView。
适配器
package example.com.rvdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
/**
* Created by csjy on 2017/4/18.
*/
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder>{
private List<String> mList;
private LayoutInflater inflater;
public RecyclerAdapter(Context context, List<String> mList){
inflater = LayoutInflater.from(context);
this.mList = mList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(inflater.inflate(R.layout.item_recycler_view,parent,false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.tvText.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
private TextView tvText;
public MyViewHolder(View itemView) {
super(itemView);
tvText = (TextView) itemView.findViewById(R.id.tv_text);
}
}
}
它需要继承RecyclerView的Adapter<VH extends ViewHolder>,所以写一个自己的ViewHolder类继承自RecyclerView的ViewHolder。重写三个方法,方法的功能一看名字就了然。
Activity
package example.com.rvdemo;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by csjy on 2017/4/18.
*/
public class ListViewActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private Context context;
private RecyclerAdapter adapter;
private List<String> mList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_view);
initDatas();
initView();
}
private void initDatas() {
mList = new ArrayList<String>();
for (int i = 0; i < 100; i++){
mList.add(""+ i);
}
}
private void initView() {
context = MyApplication.getContext();
mRecyclerView = (RecyclerView) this.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
adapter = new RecyclerAdapter(context,mList);
mRecyclerView.setAdapter(adapter);
}
}
initDatas()方法必须在initView()方法前,不然适配器中getItemCount()返回值为空,很简单的问题,确很容易犯。
效果图
可以看到无分割线。现在给item添加分割线
package example.com.rvdemo;
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.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
/**
* Created by csjy on 2017/4/19.
*/
public class RecyclerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
/**
* 分割线
*/
private Drawable mDivider;
/**
* 方向,水平/垂直
*/
private int mOrientation;
RecyclerItemDecoration(Context context,int orientation){
TypedArray ta = context.obtainStyledAttributes(ATTRS);
mDivider = ta.getDrawable(0);
ta.recycle();
this.mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if(mOrientation == LinearLayout.HORIZONTAL){
drawHorizontal(c,parent);
}else if(mOrientation == LinearLayout.VERTICAL){
drawVertical(c,parent);
}
}
private void drawVertical(Canvas c, RecyclerView parent) {
int left = parent.getLeft();
int right = parent.getRight();
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;
int buttom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left,top,right,buttom);
mDivider.draw(c);
}
}
private void drawHorizontal(Canvas c, RecyclerView parent) {
int top = parent.getTop();
int bottom = parent.getBottom();
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 = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left,top,right,bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if(mOrientation == LinearLayout.VERTICAL){
outRect.set(0,0,0,mDivider.getIntrinsicHeight());
}else if(mOrientation == LinearLayout.HORIZONTAL){
outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
}
}
}
首先在onDraw()方法里做一个判断,它的方向是垂直还是水平
drawVertical()与drawHorizontal()方法见图
看到图就知道里面的left、top、right、bottom是怎么算的。
最后调用getItemOffset()方法,画出分割线。
效果图
在这里说几个RecyclerView常用的方法。
- setLayoutManager() RecyclerView显示的方式,主要用到有LinearLayoutManager,GridLayoautManager,StaggeredGridLayoutManager(必须)
- addItemDecoration() 添加分割线(不是必须)
- setAnimation() item增删动画(不是必须)
- setAdapter() 适配器,不用说(必须)
实现类似GridView的功能
只需要修改分割线的代码,GridView垂直水平两个方向都有。
package example.com.rvdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
/**
* Created by csjy on 2017/4/19.
*/
public class RecyclerGridItemDecoration extends RecyclerView.ItemDecoration {
// private static final int [] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDividerV,mDividerH;
// RecyclerGridItemDecoration(Context context){
//
// TypedArray ta = context.obtainStyledAttributes(ATTRS);
// mDividerV = ta.getDrawable(0);
// ta.recycle();
// }
RecyclerGridItemDecoration(Context context,int drawableVertical,int drawableHorizontal){
mDividerV = context.getResources().getDrawable(drawableVertical);
mDividerH = context.getResources().getDrawable(drawableHorizontal);
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
drawVertical(c,parent);
drawHorizontal(c,parent);
}
private void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
View child = parent.getChildAt(i);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) parent.getLayoutParams();
final int left = child.getLeft() + params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDividerH.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDividerH.getIntrinsicHeight();
mDividerH.setBounds(left, top, right, bottom);
mDividerH.draw(c);
}
}
private void drawVertical(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++){
View child = parent.getChildAt(i);
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) parent.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDividerV.getIntrinsicWidth();
mDividerV.draw(c);
mDividerV.setBounds(left,top,right,bottom);
}
}
/**
* 获取行/列数
* @param parent
* @return
*/
private int getSpanCount(RecyclerView parent){
int spanCount = 0;
RecyclerView.LayoutManager manager = parent.getLayoutManager();
if(manager instanceof GridLayoutManager){
spanCount = ((GridLayoutManager) manager).getSpanCount();
}else if(manager instanceof StaggeredGridLayoutManager){
spanCount = ((StaggeredGridLayoutManager) manager).getSpanCount();
}
return spanCount;
}
/**
* 判断是否是最后一列
* 如果是最后一列就不画最右边的分割线
* true:是最后一列
* false: 不是最后一列
* @param parent
* @param position item的位置
* @param childCount item的总数
* @param spanCount 行/列数
* @return
*/
private boolean isLastColum(RecyclerView parent, int position, int childCount, int spanCount){
RecyclerView.LayoutManager manager = parent.getLayoutManager();
if(manager instanceof GridLayoutManager){
if((position + 1) % spanCount == 0){
return true;
}
}else if(manager instanceof StaggeredGridLayoutManager){
int orientation = ((StaggeredGridLayoutManager) manager).getOrientation();
if(orientation == StaggeredGridLayoutManager.VERTICAL){
if((position + 1) % spanCount == 0){
return true;
}
}else if(orientation == StaggeredGridLayoutManager.HORIZONTAL){
childCount = (childCount % spanCount == 0)?(childCount - spanCount):(childCount - childCount % spanCount);
if (position >= childCount){
return true;
}
}
}
return false;
}
/**
* 判断是否是最后一行
* 如果是最后一行,就不画最下边的分割线
* true :是最后一行
* false: 不是最后一行
* @param parent
* @param position 当前的位置
* @param childCount item的总数
* @param spanCount 行/列数
* @return
*/
private boolean isLastRow(RecyclerView parent, int position, int childCount, int spanCount){
RecyclerView.LayoutManager manager = parent.getLayoutManager();
if(manager instanceof GridLayoutManager){
childCount = (childCount % spanCount == 0)?(childCount - spanCount):(childCount - childCount % spanCount);
if (position >= childCount){
return true;
}
}else if(manager instanceof StaggeredGridLayoutManager){
int orientation = ((StaggeredGridLayoutManager) manager).getOrientation();
if(orientation == StaggeredGridLayoutManager.VERTICAL){
childCount = (childCount % spanCount == 0)?(childCount - spanCount):(childCount - childCount % spanCount);
if (position >= childCount){
return true;
}
}else if(orientation == StaggeredGridLayoutManager.HORIZONTAL){
if((position + 1) % spanCount == 0){
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
super.getItemOffsets(outRect, itemPosition, parent);
Log.d("Tag","itemPosition====" + itemPosition);
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
outRect.set(0, 0, isLastColum(parent,itemPosition,childCount,spanCount) ? 0 : mDividerV.getIntrinsicWidth(),
isLastRow(parent,itemPosition,childCount,spanCount) ? 0 : mDividerH.getIntrinsicHeight());
}
}
Activity的代码:
package example.com.rvdemo;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by csjy on 2017/4/18.
*/
public class GridViewActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private Context context;
private RecyclerAdapter adapter;
private List<String> mList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_view);
initDatas();
initView();
}
private void initDatas() {
mList = new ArrayList<String>();
for (int i = 0; i < 100; i++){
mList.add(""+ i);
}
}
private void initView() {
context = MyApplication.getContext();
mRecyclerView = (RecyclerView) this.findViewById(R.id.recycler_view);
mRecyclerView.setLayoutManager(new GridLayoutManager(context,4));
adapter = new RecyclerAdapter(context,mList);
mRecyclerView.setAdapter(adapter);
RecyclerGridItemDecoration decoration = new RecyclerGridItemDecoration(context,R.drawable.divider_vertical,R.drawable.divider);
mRecyclerView.addItemDecoration(decoration);
}
}
两个drawable:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#0011ff"/>
<size android:height="4dp"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#FFFF0004"/>
<size android:width="4dp"/>
</shape>
看图一目了然,接下来getSpanCount()方法,用来获取行数或者列数,首先实例化LayoutManager,判断LayoutManager的实例为GridLayoutManager还是StaggeredGridLayoutManager,调用相应LayoutManager的getSpanCount()获取行/列数。
isLastColumn()与isLastRow()方法判断是否为最后一列,最后一行。
首先先解释GridLayoutManger,如果是垂直的情况,见下图
判断是否为最后一列:
这里假设有3列,spanCount = 3; 用position + 1 与spanCount求余,第一行:3%3 =0;第二行:6%3=0,所以,如果为零,说明为最后一列,返回true。
判断是否为最后一行:
假设item的总数为100,childCount = 100; 用childCount与spanCount求余,childCount % spanCount == 0,就是标准的column*row;childCount % spanCount == 1:见上图,以此类推。
进行一个判断,如果childCount % spanCount == 0,就是标准的column*row,将childCount - spanCount 赋值给childCount,至于为什么要这样赋值,是因为
childCount - spanCount 就相当于把最后一行减去,一旦当前item的位置大于倒数第二行,就说明是最后一行(这说的有点抽象。。但是挺好理解的)。如果
childCount % spanCount != 0,用childCount - childCount % spanCount也是把最后一行减去,那么一旦当前item的位置大于倒数第二行,就说明是最后一行。
最后调用getItemOffet()方法做一个小偏移。
水平的情况:
判断最后一列的情况就与垂直情况下的判断最后一行的情况一样,见代码
判断最后一行的情况就与垂直情况下的判断最后一列的情况一样。
StaggeredGridLayoutManager的判断方式与GridLayoutManager的方式一样,看到代码就可以理解。
效果图:
实现类似瀑布流的功能
只需要简单修改适配器的代码,给item加上随机的高度就可以,修改几句代码,改为:
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(6,StaggeredGridLayoutManager.VERTICAL));
adapter = new StaggeredAdapter(context,mList);
mRecyclerView.setAdapter(adapter);
修改后的适配器代码:
package example.com.rvdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
/**
* Created by csjy on 2017/4/19.
*/
public class StaggeredAdapter extends RecyclerView.Adapter<StaggeredAdapter.MyViewHolder>{
private List<String> mList;
private List<Integer> mHeight;
private LayoutInflater inflater;
StaggeredAdapter(Context context,List<String> mList){
inflater = LayoutInflater.from(context);
this.mList = mList;
mHeight = new ArrayList<Integer>();
for(int i = 0; i < mList.size(); i++){
mHeight.add((int)(200 + Math.random() * 500));
Log.d("Tag", String.valueOf((100 + Math.random() * 300)));
}
}
@Override
public StaggeredAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(inflater.inflate(R.layout.item_recycler_view,parent,false));
return holder;
}
@Override
public void onBindViewHolder(StaggeredAdapter.MyViewHolder holder, int position) {
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.tvText.getLayoutParams();
params.height = mHeight.get(position);
holder.tvText.setLayoutParams(params);
holder.tvText.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
private TextView tvText;
public MyViewHolder(View itemView) {
super(itemView);
tvText = (TextView) itemView.findViewById(R.id.tv_text);
}
}
}
只是新增了一个给item设置随机的高。
效果图:
实现类似Gallery的功能
先看效果图
思路如下:自定义一个RecyclerView,定义一个接口,当RecyclerView发生滚动时回调。
自定义RecyclerView:
package example.com.rvdemo;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by csjy on 2017/4/20.
*/
public class MyRecyclerView extends RecyclerView {
private View currentView;
public MyRecyclerView(Context context,AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (onItemScrollListener != null) {
currentView = getChildAt(0);
onItemScrollListener.OnChange(currentView,getChildAdapterPosition(currentView));
}
}
public interface OnItemScrollListener{
void OnChange(View view,int position);
}
private OnItemScrollListener onItemScrollListener;
public void setOnItemScrollListener(OnItemScrollListener onItemScrollListener) {
this.onItemScrollListener = onItemScrollListener;
}
@Override
public void onScrolled(int dx, int dy) {
super.onScrolled(dx, dy);
View newView;
newView = getChildAt(0);
if(onItemScrollListener != null){
if (newView != null && newView != currentView){
currentView = newView;
onItemScrollListener.OnChange(currentView,getChildAdapterPosition(currentView));
}
}
}
}
adapter:
package example.com.rvdemo;
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.ImageView;
import java.util.List;
/**
* Created by csjy on 2017/4/19.
*/
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.MyViewHolder>{
private List<Integer> mList;
private LayoutInflater inflater;
GalleryAdapter(Context context, List<Integer> mList) {
inflater = LayoutInflater.from(context);
this.mList = mList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
MyViewHolder holder = new MyViewHolder(inflater.inflate(R.layout.item_recycler_gallery,parent,false));
return holder;
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.ivImage.setBackgroundResource(mList.get(position));
if (onItemClickListener != null){
holder.ivImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(position);
}
});
}
}
@Override
public int getItemCount() {
return mList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder{
private ImageView ivImage;
public MyViewHolder(View itemView) {
super(itemView);
ivImage = (ImageView) itemView.findViewById(R.id.iv_image);
}
}
public interface OnItemClickListener{
void onItemClick(int position);
}
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}
adapter中自定义了一个接口,当有点击事件时回调。
Activity:
package example.com.rvdemo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by csjy on 2017/4/19.
*/
public class GalleryActivity extends AppCompatActivity{
private MyRecyclerView mRecyclerView;
private Context context;
private GalleryAdapter adapter;
private List<Integer> mList;
private ImageView ivBig;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recycler_gallery);
initDatas();
initView();
initEvent();
}
private void initEvent() {
adapter.setOnItemClickListener(new GalleryAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
// Toast.makeText(context, "you click" + position, Toast.LENGTH_SHORT).show();
ivBig.setBackgroundResource(mList.get(position));
}
});
mRecyclerView.setOnItemScrollListener(new MyRecyclerView.OnItemScrollListener() {
@Override
public void OnChange(View view, int position) {
ivBig.setBackgroundResource(mList.get(position));
}
});
}
private void initDatas() {
mList = new ArrayList<Integer>(Arrays.asList(R.mipmap.a,R.mipmap.b,R.mipmap.c,R.mipmap.d,R.mipmap.e,
R.mipmap.f,R.mipmap.g,R.mipmap.h,R.mipmap.i,R.mipmap.j,R.mipmap.k,R.mipmap.l,R.mipmap.m));
}
private void initView() {
context = MyApplication.getContext();
mRecyclerView = (MyRecyclerView) this.findViewById(R.id.gallery_recycler);
LinearLayoutManager manager = new LinearLayoutManager(context);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(manager);
adapter = new GalleryAdapter(context,mList);
mRecyclerView.setAdapter(adapter);
ivBig = (ImageView)this.findViewById(R.id.iv_big_image);
}
}
activity很简单,一看就明白,这里不做记录。
参考自:【张鸿洋的博客】http://blog.csdn.net/lmj623565791/article/details/38173061
完毕。