Android学习之RecyclerView
来源:互联网 发布:上海大学乐乎论坛圈子 编辑:程序博客网 时间:2024/06/05 18:41
概述
RecyclerView,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它的不同的LayoutManager,ItemDecoration,ItemAnimator实现各种效果
- 想要控制其显示方法,请通过布局管理器LayoutManager
- 想要控制Item间的间隔(可绘制),请通过ItemDecoration
- 想要控制Item增删的动画,请通过ItemAnimator
- 想要控制点击,长按事件,自己写去
基本使用
public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; MyAdapter adapter; List<String> data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); recyclerView = (RecyclerView)findViewById(R.id.recycler); adapter = new MyAdapter(data,this); recyclerView.setLayoutManager(new LinearLayoutManager(this)); //设置布局管理器 recyclerView.setAdapter(adapter); //设置适配器 } private void init(){ data = new ArrayList<>(); for (int i = 'A'; i < 'z'; i++) { data.add("" + (char) i); } }}public class MyAdapter extends RecyclerView.Adapter<MyAdapter.myHolder> { List<String> data ; Context context; public MyAdapter(List<String> data, Context context) { this.data =data; this.context = context; } @Override public void onBindViewHolder(myHolder holder, int position) { holder.textView.setText(data.get(position)); Log.d("onBind","aaaaaaaaaaa"); } @Override public int getItemCount() { return data.size(); } @Override public myHolder onCreateViewHolder(ViewGroup parent, int viewType) { myHolder holder = new myHolder(LayoutInflater.from(context). inflate(R.layout.recycleritem,parent,false)); Log.d("onCreate","bbbbbbbbb"); return holder; } class myHolder extends RecyclerView.ViewHolder{ TextView textView; public myHolder(View itemView) { super(itemView); textView = (TextView) itemView.findViewById(R.id.text); } }}
在第一次创建item的时候,是先调用onCreateViewHolder再调用onBindViewHolder的,在滑动中,只要不是再创建新的,一直调用的是onBingViewHolder。
关于其他:
mRecyclerView = findView(R.id.id_recyclerview);//设置布局管理器mRecyclerView.setLayoutManager(layout);//设置adaptermRecyclerView.setAdapter(adapter)//设置Item增加、移除动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());//添加分割线mRecyclerView.addItemDecoration(new DividerItemDecoration( getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
看上面例子的效果:
这样好丑,感觉item之间应该有分割线,可是RecyclerView并没有支持divider这样的属性
我们可以通过添加mRecyclerView.addItemDecoration()
来添加分割线
该方法的参数为RecyclerView.ItemDecoration,该类为抽象类,目前官方没有提供默认的实现类
该类源码:
public static abstract class ItemDecoration {public void onDraw(Canvas c, RecyclerView parent, State state) { onDraw(c, parent); }public void onDrawOver(Canvas c, RecyclerView parent, State state) { onDrawOver(c, parent); }public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { getItemOffsets(outRect, ((LayoutParams) view.getLayoutParams()).getViewLayoutPosition(), parent);}@Deprecatedpublic void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { outRect.set(0, 0, 0, 0); }
当我们调用recyclerview.addItemDecoration()方法添加decoration的时候,Recycler在绘制的时候,会去绘制decorator,即调用该类的onDraw和onDrawOver方法:
- onDraw方法先于drawChildern
- onDrawOver在drawChildern之后,一般我们选择复写一个即可
- getItemOffset可以通过outRect.set()为每一个Item设置一定的偏移量,主要用户绘制Decorator。
下面是一个例子:
public class DividerItemDecaration extends RecyclerView.ItemDecoration { private static final int[] ATTRS = new int[]{ android.R.attr.listDivider }; public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL; public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL; private Drawable divider; private int orientation; public DividerItemDecaration(Context context,int orientation) { //取默认的listdivider的属性 final TypedArray a = context.obtainStyledAttributes(ATTRS); //得到这个属性 divider = a.getDrawable(0); a.recycle(); setOrientation(orientation); } public void setOrientation(int orientation){ if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){ throw new IllegalArgumentException("invalid orientation"); } this.orientation = orientation; } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if(orientation == VERTICAL_LIST){ drawVertical(c,parent); }else { drawHorizontal(c,parent); } } public void drawVertical(Canvas c,RecyclerView parent){ final int left = parent.getPaddingLeft(); final int riht = parent.getWidth()-parent.getPaddingRight(); final int childCount = parent.getChildCount(); for(int i = 0;i<childCount;i++){ final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int top = child.getBottom()+params.bottomMargin; final int bottom = top +divider.getIntrinsicHeight(); divider.setBounds(left,top,riht,bottom); divider.draw(c); } } public void drawHorizontal(Canvas c, RecyclerView parent) { final int top = parent.getPaddingTop(); final int bottom = parent.getHeight() - parent.getPaddingBottom(); final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin; final int right = left + divider.getIntrinsicHeight(); divider.setBounds(left, top, right, bottom); divider.draw(c); } } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { //是向上偏移还是向左偏移 if(orientation == VERTICAL_LIST){ outRect.set(0,0,0,divider.getIntrinsicHeight()); }else { outRect.set(0,0,divider.getIntrinsicWidth(),0); } }}
效果:
该实现类通过读取系统主题中的Android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向
获取到listdivider后,该属性的值是个Drawable,在getItemOffsets中,outRect设置绘制范围。onDraw中实现了绘制
然后在原来的代码中加:
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
该分割线是系统默认的,我们可以在theme.xml中找到该属性的使用的情况,并且可以进行改变
<resources> <!-- 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/divider</item> </style></resources>
在style中找一个item,叫android:listDivider
然后自己写一个drawable
<?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:height="4dp"/></shape>
LayoutManager
上面的例子是通过使用默认的LinearLayoutManager实现的
RecyclerView.LayoutManager,是一个抽象类,系统提供了三个实现类:
- LinearLyaoutManager 线性布局,支持横向,纵向
- GridLayoutManager 网格布局
- StaggeredGridLayoutManager 瀑布流布局
GridLayoutManager
mRecyclerView.setLayoutManager(new GridLayoutManager(this,4));
重新写适合于这个的divider
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration{ private static final int[] ATTRS = new int[] { android.R.attr.listDivider }; private Drawable mDivider; public DividerGridItemDecoration(Context context) { final TypedArray a = context.obtainStyledAttributes(ATTRS); mDivider = a.getDrawable(0); a.recycle(); } @Override public void onDraw(Canvas c, RecyclerView parent, State state) { drawHorizontal(c, parent); drawVertical(c, parent); } private int getSpanCount(RecyclerView parent) { // 列数 int spanCount = -1; LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { spanCount = ((GridLayoutManager) layoutManager).getSpanCount(); } else if (layoutManager instanceof StaggeredGridLayoutManager) { spanCount = ((StaggeredGridLayoutManager) layoutManager) .getSpanCount(); } return spanCount; } public void drawHorizontal(Canvas c, RecyclerView parent) { int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getLeft() - params.leftMargin; final int right = child.getRight() + params.rightMargin + mDivider.getIntrinsicWidth(); final int top = child.getBottom() + params.bottomMargin; final int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } public void drawVertical(Canvas c, RecyclerView parent) { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .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 + mDivider.getIntrinsicWidth(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); if (orientation == StaggeredGridLayoutManager.VERTICAL) { if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边 { return true; } } else { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一列,则不需要绘制右边 return true; } } return false; } private boolean isLastRaw(RecyclerView parent, int pos, int spanCount, int childCount) { LayoutManager layoutManager = parent.getLayoutManager(); if (layoutManager instanceof GridLayoutManager) { childCount = childCount - childCount % spanCount; if (pos >= childCount)// 如果是最后一行,则不需要绘制底部 return true; } else if (layoutManager instanceof StaggeredGridLayoutManager) { int orientation = ((StaggeredGridLayoutManager) layoutManager) .getOrientation(); // StaggeredGridLayoutManager 且纵向滚动 if (orientation == StaggeredGridLayoutManager.VERTICAL) { childCount = childCount - childCount % spanCount; // 如果是最后一行,则不需要绘制底部 if (pos >= childCount) return true; } else // StaggeredGridLayoutManager 且横向滚动 { // 如果是最后一行,则不需要绘制底部 if ((pos + 1) % spanCount == 0) { return true; } } } return false; } @Override public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) { int spanCount = getSpanCount(parent); int childCount = parent.getAdapter().getItemCount(); if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部 { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边 { outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); } else { outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight()); } }}
注意,当开始我的左边的分割线怎么都画不出来,但是用系统默认的就可以,是因为在我的drawable文件下的画图,没有规定width,在规定width后,好了
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > <gradient android:startColor="#ffff0000" android:centerColor="#ff00ff00" android:endColor="#ff0000ff" android:type="linear"/> <size android:height="1dp" android:width="1dp" /></shape>
StaggeredGridLayoutManager
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.VERTICAL));
这种写法和上面是的效果是一致的,但是第二个参数传的是一个orientation,如果传入的是StaggeredGridLayoutManager.VERTICAL代表有多少列;那么传入的如果是StaggeredGridLayoutManager.HORIZONTAL就代表有多少行
如果改为:
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL));
可以进行左右滑动
我们都是固定了高度
现在我们再onBindViewHolder只能怪为我们的Item设置个随机高度
@Override public void onBindViewHolder(myHolder holder, int position) { holder.textView.setText(data.get(position)); LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.textView.getLayoutParams(); params.height = size[position%6]; }
ItemAnimator
ItemAnimator也是个抽象类,好在系统为我们提供了一种默认的实现类
借助默认的实现,当Item添加和移除的时候,添加动画效果很简单
// 设置item动画recyclerView.setItemAnimator(new DefaultItemAnimator());
如果是GridLayoutManger:
注意,这里更新数据集不是用adapter.notifyDataSetChanged()而是
notifyItemInserted(position)与notifyItemRemoved(position)
为adapter添加两个方法:
public void addData(int position) { mDatas.add(position, "Insert One"); notifyItemInserted(position); } public void removeData(int position) { mDatas.remove(position); notifyItemRemoved(position); }
在Main中点击MenuItem触发:
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.id_action_add: mAdapter.addData(1); break; case R.id.id_action_delete: mAdapter.removeData(1); break; } return true; }
Click and LingClick
recyclerview没有提供ClickListener和LongClickListener
不过我们可以自己添加
我选择通过adapter中提供回调
首先在Adaptet中定一个接口和方法
public interface onItemClickListener{ void onClick(int position); void LongClivk(int position); } public void SetOnItmeClickListener(onItemClickListener listener){ this.listener = listener; }
然后在onBindViewHolder中为每一个Item设置onClickListener和OnLongClickListener
@Override public void onBindViewHolder(final myHolder holder, int position) { holder.textView.setText(data.get(position)); LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.textView.getLayoutParams(); params.height = size[position%6]; if(listener != null){ holder.itemView .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { listener.onClick(holder.getLayoutPosition()); } }); holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { listener.LongClivk(holder.getLayoutPosition()); return false; } }); } }
然后在MainActivity中设置Listener
adapter.SetOnItmeClickListener(new MyAdapter.onItemClickListener() { @Override public void onClick(int position) { Toast.makeText(MainActivity.this,"onClick",Toast.LENGTH_SHORT).show(); } @Override public void LongClivk(int position) { adapter.remove(position); } });
刚开始我对于画分割线充满了问号,下来自己解析解析,以画水平分割线为例:
public void drawVertical(Canvas c,RecyclerView parent){ final int left = parent.getPaddingLeft(); final int riht = parent.getWidth()-parent.getPaddingRight(); final int childCount = parent.getChildCount(); for(int i = 0;i<childCount;i++){ final View child = parent.getChildAt(i); final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); final int top = child.getBottom()+params.bottomMargin; final int bottom = top +divider.getIntrinsicHeight(); divider.setBounds(left,top,riht,bottom); divider.draw(c); Log.d("wnw",String.valueOf(top)+" "+String.valueOf(bottom)+" "+String.valueOf(left)+ " "+String.valueOf(riht) ); } }
final View child = parent.getChildAt(i);得到的是LinearLayout整个布局。
left是图中的红点,我只是大概画了个位置,right是绿点,
确定了两个X轴上的点,现在是确定Y轴。
top的长度其实就是图中margin的长度,所以矩形第一个左上角的(left,top)就确定了
bottom就是top加上分割线的高度,所以矩形右下角的(right,bottom)就确定了。这样我们分割线画的位置和大小也确定了
然后再通过getItemOffsets()将整个LinearLayout布局向上平移分割线的高度,这样我们分割线的画的空间就有了,就可以画了
参考于:
http://blog.csdn.net/lmj623565791/article/details/45059587;
本文出自:【张鸿洋的博客】
- Android学习之RecyclerView
- Android学习之RecyclerView
- Android学习之RecyclerView
- Android学习之RecyclerView
- Android学习之RecyclerView
- Android学习之RecyclerView
- Android学习之RecyclerView学习
- Android学习笔记之RecyclerView
- Android——RecyclerView入门学习之RecyclerView.Adapter
- Android学习之RecyclerView(三)-ItemDecoration
- Android学习笔记之RecyclerView详解
- android学习之RecyclerView的简单使用
- Android学习之SwipeRefreshLayout+RecyclerView+CardView
- #Android学习#RecyclerView基础
- Android学习RecyclerView小结
- android RecyclerView学习笔记
- Android--RecyclerView学习
- Android RecyclerView源码学习
- ThreadLocal 线程局部变量
- 我的Android逆向之路(一)
- 软件联盟社团介绍
- hmtl
- 杭电acm1236 排名
- Android学习之RecyclerView
- hdu 1047 Integer Inquiry 大数加法
- 二进制与十进制的互相转换
- 简单学习struts标签中使用ognl表达式
- jquery
- 16 Three.js使用dat.GUI简化试验流程
- yum安装gcc和gcc-c++
- HDU 2276-Kiki & Little Kiki 2(矩阵快速幂+位运算)
- 时间中间键总结