刚学习了RecyclerView,在这里对自己学习的内容进行一个总结,方便以后查看。
现在学习的知识很浅,只是知道如何去使用,而没有去研究它的源码,不知道它的内部具体原理是如何。
这篇文章大概有些说法是不对的,希望下次有机会用到RecyclerView来看这篇文章可以修改里面描述不对的语句。
希望看到这篇文章的人有不对之处可以帮助指出,谢谢。
正文开始。
导入RecyclerView的依赖包
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垂直水平两个方向都有。
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设置随机的高。效果图:
先看效果图
思路如下:自定义一个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
完毕。