RecyclerView的使用全面解析
来源:互联网 发布:python os.popen 编辑:程序博客网 时间:2024/06/06 12:03
RecyclerView的使用全面解析
1 RecyclerView的基本使用
引入RecyclerView
由于该控件并不在Andorid SDK中的,而是在support v7包中,因此我们要手动添加该控件。
在build.gradle中添加如下依赖:
dependencies {
...
compile 'com.android.support:appcompat-v7:23.1.1' //Toolbar
compile 'com.android.support:recyclerview-v7:23.1.1' //RecyclerView
}
几个重要的类
在我们开始讲解RecyclerView的用法之前,我们要先认识几个它常用的内部类,因为这几个内部类很重要,贯穿整个使用过程:
1、RecyclerView.Adapter:抽象类,为RecyclerView提供数据,一般根据不同的业务需求来编写具体的实现类。
2、RecyclerView.LayoutManager:抽象类,主要用于测量RecyclerView的子Item,以及根据不同的布局方式来实现Item的布局效果,v 7包自带的实现类有:LinearLayoutManager、StaggeredGridLayoutManager、GridLayoutManager。
3、RecyclerView.ItemDecoration:抽象类,这个主要用于不同的Item之间添加分割线(可选)。官方没有实现类,所以如果要添加分割线,我们需要手动实现这个抽象类。
4、RecyclerView.ItemAnimator:抽象类,这个主要用于当一个item添加或者删除的时候出现的动画效果,官方提供一个默认的实现类。如果想要使我们的RecyclerView在添加、删除数据的时候有炫酷的动画,可以实现这个抽象类。
可以看到,这些重要的类都是抽象类,这就为RecyclerView的高度可定制性打下了坚实的基础,利用不同的实现类,能自由、灵活地使用RecyclerView,这也是RecyclerView的迷人之处。
基本使用
1、布局
在main.xml文件中进行如下布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:toolbar="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
toolbar:titleTextColor="@android:color/white"
toolbar:subtitleTextColor="@android:color/white"
toolbar:popupTheme="@style/ToolbarPopupTheme">
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:layout_margin="5dp">
<TextView
android:id="@+id/num"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="20sp"/>
</LinearLayout>
2、创建Adapter适配器
public static abstract class Adapter<VH extends ViewHolder> {
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); // 1
public abstract void onBindViewHolder(VH holder, int position); // 2
public abstract int getItemCount(); // 3
}
①onCreateViewHolder:创建ViewHolder,该方法会在RecyclerView需要展示一个item的时候回调。重写该方法时,应该使ViewHolder加载item view的布局。这个能发避免了不必要的findViewById操作,提高了性能。如果熟悉ListView的ViewHolder操作那么也能很容易理解这个。
②onBindeViewHolder:该方法在RecyclerView在特定位置展示数据时候回调,顾名思义,把数据绑定、填充到相应的item view中。
③getItemCount:返回数据的数量。
我们注意到,继承该类的时候需要声明它的泛型类型VH,VH继承自RecyclerView.ViewHolder,同时第①个方法的返回值也是VH,由于ViewHolder是根据业务需求而变化的,不同的业务需求而需要的ViewHolder不尽相同,所以没必要单独写一个ViewHolder.java文件,因此我们可以在Adapter子类内部实现一个内部类ViewHolder,这样也符合单一职责原则。具体的Adapter适配器代码如下所示,MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mDataSet;
//构造器,接受数据集
public MyAdapter(List<String> data){
mDataSet = data;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//加载布局文件
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.itemlayout,parent,false);
ViewHolder vh = new ViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
//将数据填充到具体的view中
holder.mTextView.setText(mDataSet.get(position));
}
@Override
public int getItemCount() {
return mDataSet.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
//由于itemView是item的布局文件,我们需要的是里面的textView,因此利用itemView.findViewById获
//取里面的textView实例,后面通过onBindViewHolder方法能直接填充数据到每一个textView了
mTextView = (TextView) itemView.findViewById(R.id.num);
}
}
}
3、MainActivity
一般在java代码中引用RecyclerView,要做如下工作:
①获取RecyclerView实例,通过findViewById()方法。
②为RecyclerView设置布局管理器
③为RecyclerView设置数据适配器Adapter。
我们来看看具体代码:
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private MyAdapter mAdapter;
private List<String> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initToolbar();
initData();
initRecyclerView();
}
private void initData() {
mData = new ArrayList<>();
for(int i=0;i<20;i++){
mData.add("Item "+i);
}
}
private void initRecyclerView() {
//1 实例化RecyclerView
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mLayoutManager = new LinearLayoutManager(this);
//2 为RecyclerView创建布局管理器,这里使用的是LinearLayoutManager,表示里面的Item排列是线性排列
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new MyAdapter(mData);
//3 设置数据适配器
mRecyclerView.setAdapter(mAdapter);
}
private void initToolbar() {
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("RecyclerView");
toolbar.setSubtitle("demo");
//设置导航图标、添加菜单点击事件要在setSupportActionBar方法之后
setSupportActionBar(toolbar);
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
Toast.makeText(MainActivity.this, "Search !", Toast.LENGTH_LONG).show();
break;
case R.id.action_notifications:
Toast.makeText(MainActivity.this, "Notificationa !", Toast.LENGTH_SHORT).show();
break;
case R.id.action_settings:
Toast.makeText(MainActivity.this, "Settings !", Toast.LENGTH_SHORT).show();
break;
}
return true;
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
}
线性的listview就出来了
更改布局管理器
以上使用了LinearLayoutManager,线性布局管理器,使得item呈竖直排列或者水平排列,除了这种线性布局之外,官方还提供了另外两种布局方式,分别是GridLayoutManager和StaggeredGridLayoutManager。前者容易理解,是表格形式的布局,类似于GridView,而后者则是瀑布流表格布局。
1、GridLayoutManager
要改变布局管理器很简单,我们只需要实例化一个表格布局管理器即可,我们先看看它的构造函数:
/**
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns or rows in the grid
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
* #VERTICAL}.
* @param reverseLayout When set to true, layouts from end to start.
*/
public GridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setSpanCount(spanCount);
}
我们在MainActivity中作出如下修改:
mRecyclerView.setLayoutManager(new GridLayoutManager(this,4,VERTICAL,false));
瀑布流效果现在经常会见到,如果只有ListView或者GridView实现的话,有一定难度,但是如果使用RecyclerView则会简单很多,只需要使用StaggeredGridLayoutManager即可实现。
我们在MainActivity中,更改使用的布局管理器如下:
//构造器中,第一个参数表示列数或者行数,第二个参数表示滑动方向
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mDataSet;
private List<Integer> mHeight;
public MyAdapter(List<String> data){
mDataSet = data;
mHeight = new ArrayList<Integer>();
//随机获取一个mHeight值
for (int i = 0; i < mDataSet.size(); i++)
{
mHeight.add( (int) (100 + Math.random() * 300));
}
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mTextView.setText(mDataSet.get(position));
//绑定数据的同时,修改每个ItemView的高度
ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
lp.height = mHeight.get(position);
holder.itemView.setLayoutParams(lp);
}
...
}
二:处理RecyclerView的点击事件
方法一:利用View.onClickListener及onLongClickListener
利用了java回调机制,这里我们依赖于子Item View的onClickListener及onLongClickListener。
使用接口回调
首先对MyAdapter.java代码做出如下修改:
①新建两个内部接口:
public interface OnItemClickListener{ void onItemClick(View view,int position); } public interface OnItemLongClickListener{ void onItemLongClick(View view,int position); }
private OnItemClickListener mOnItemClickListener; private OnItemLongClickListener mOnItemLongClickListener; public void setOnItemClickListener(OnItemClickListener mOnItemClickListener){ this.mOnItemClickListener = mOnItemClickListener; } public void setOnItemLongClickListener(OnItemLongClickListener mOnItemLongClickListener) { this.mOnItemLongClickListener = mOnItemLongClickListener; }
③在onBindViewHolder方法内,实现回调:
@Overridepublic void onBindViewHolder(final ViewHolder holder, int position) { holder.mTextView.setText(mDataSet.get(position)); //判断是否设置了监听器if(mOnItemClickListener != null){ //为ItemView设置监听器 holder.itemView.setOnClickListener(new View.OnClickListener() { @Overridepublic void onClick(View v) { int position = holder.getLayoutPosition(); // 1 mOnItemClickListener.onItemClick(holder.itemView,position); // 2 } }); } if(mOnItemLongClickListener != null){ holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { @Overridepublic boolean onLongClick(View v) { int position = holder.getLayoutPosition(); mOnItemLongClickListener.onItemLongClick(holder.itemView,position); //返回true 表示消耗了事件 事件不会继续传递return true; } }); }}
修改完MyAdapter.java后,我们接着在MainActivity.java中设置监听器,采用匿名内部类的形式实现了onItemClickListener、onItemLongClickListener接口,这种写法与一般的设置监听器的流程相同:
mAdapter = new MyAdapter(mData);mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() { @Overridepublic void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "click " + mData.get(position), Toast.LENGTH_SHORT).show(); }});mAdapter.setOnItemLongClickListener(new MyAdapter.OnItemLongClickListener() { @Overridepublic void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this,"long click "+mData.get(position),Toast.LENGTH_SHORT).show(); }});mRecyclerView.setAdapter(mAdapter);
方法二:利用RecyclerView.OnItemTouchListener
官方虽然没有提供现成的监听器,但是提供了一个内部接口:OnItemTouchListener,我们看看官方文档对它的描述:An OnItemTouchListener allows the application to intercept touch events in progress at the view hierarchy level of the RecyclerView before those touch events are considered for RecyclerView's own scrolling behavior。大概意思是说该接口允许我们对RecyclerView的触摸事件进行拦截,我们看看它的几个接口方法:
@Overridepublic boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { return false; } @Overridepublic void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
新建一个RecyclerViewClickListener.java:
说一下这三个方法 有问题的话可以参照这个
onInterceptTouchEvent() 拦截事件
onTouchEvent() 处理事件
onRequestDisallowInterceptTouchEvent() 请求不要拦截事件
dispatchTouchEvent() 分发事件
/** * @author ChenYu * 2016-05-10 */public class RecyclerViewClickListener implements RecyclerView.OnItemTouchListener { private int mLastDownX,mLastDownY; //该值记录了最小滑动距离private int touchSlop ; private OnItemClickListener mListener; //是否是单击事件private boolean isSingleTapUp = false; //是否是长按事件private boolean isLongPressUp = false; private boolean isMove = false; private long mDownTime; //内部接口,定义点击方法以及长按方法public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } public RecyclerViewClickListener(Context context,OnItemClickListener listener){ touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mListener = listener; } @Overridepublic boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { int x = (int) e.getX(); int y = (int) e.getY(); switch (e.getAction()){ /** * 如果是ACTION_DOWN事件,那么记录当前按下的位置, * 记录当前的系统时间。 */case MotionEvent.ACTION_DOWN: mLastDownX = x; mLastDownY = y; mDownTime = System.currentTimeMillis(); isMove = false; break; /** * 如果是ACTION_MOVE事件,此时根据TouchSlop判断用户在按下的时候是否滑动了, * 如果滑动了,那么接下来将不处理点击事件 */case MotionEvent.ACTION_MOVE: if(Math.abs(x - mLastDownX)>touchSlop || Math.abs(y - mLastDownY)>touchSlop){ isMove = true; } break; /** * 如果是ACTION_UP事件,那么根据isMove标志位来判断是否需要处理点击事件; * 根据系统时间的差值来判断是哪种事件,如果按下事件超过1ms,则认为是长按事件, * 否则是单击事件。 */case MotionEvent.ACTION_UP: if(isMove){ break; } if(System.currentTimeMillis()-mDownTime > 1000){ isLongPressUp = true; }else { isSingleTapUp = true; } break; } if(isSingleTapUp ){ //根据触摸坐标来获取childView View childView = rv.findChildViewUnder(e.getX(),e.getY()); isSingleTapUp = false; if(childView != null){ //回调mListener#onItemClick方法 mListener.onItemClick(childView,rv.getChildLayoutPosition(childView)); return true; } return false; } if (isLongPressUp ){ View childView = rv.findChildViewUnder(e.getX(),e.getY()); isLongPressUp = false; if(childView != null){ mListener.onItemLongClick(childView, rv.getChildLayoutPosition(childView)); return true; } return false; } return false; } @Overridepublic void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }}
//调用RecyclerView#addOnItemTouchListener方法能添加一个RecyclerView.OnItemTouchListener对象 mRecyclerView.addOnItemTouchListener(new RecyclerViewClickListener(this,new RecyclerViewClickListener.OnItemClickListener() { @Overridepublic void onItemClick(View view, int position) { Toast.makeText(MainActivity.this,"Click "+mData.get(position),Toast.LENGTH_SHORT).show(); } @Overridepublic void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this,"Long Click "+mData.get(position),Toast.LENGTH_SHORT).show(); }}));
那么方法一和方法二有何区别呢?
首先,方法一我们是直接在MyAdapter数据适配器中,为itemview设置了内置监听器,再通过这个监听器实现我们的回调方法,相当于回调了两次,同时这个方法与MyAdapter的耦合度比较高,也违反了单一职责原则,当然其简易性也是突出的优点。而方法二,我们利用了onTouchListener接口对事件进行了拦截,在拦截中处理我们的点击事件,实现了与适配器的解耦,但是复杂程度会比方法一大。总地来说,如果RecyclerView需要处理的点击事件逻辑很简单,那么可以使用方法一;如果需要处理比较复杂的点击事件,比如说,双击、长按等点击事件,则需要使用方法二去实现各种复杂的逻辑。
对方法二的优化
在实现方法二的RecyclerViewClickListener的时候,在内部对事件的实现了单击、长按的判断,但是这个长按事件不是标准的,只有松开手指的时候才会触发长按事件,这也算是一点瑕疵,同时如果要增加别的事件,比如说双击事件,则需要增加相应的逻辑,如果需要判断的事件种类变多则会给我们的代码编写带来困难,那么有没有更加简便的方法呢?其实安卓SDK为我们提供了一个手势检测类:GestureDetector来处理各种不同的手势,那么我们完全可以利用GestureDetector来对方法二进行改进。
新建RecyclerViewClickListener2.java:
public class RecyclerViewClickListener2 implements RecyclerView.OnItemTouchListener { private GestureDetector mGestureDetector; private OnItemClickListener mListener; //内部接口,定义点击方法以及长按方法public interface OnItemClickListener { void onItemClick(View view, int position); void onItemLongClick(View view, int position); } public RecyclerViewClickListener2(Context context, final RecyclerView recyclerView,OnItemClickListener listener){ mListener = listener; mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener(){ //这里选择SimpleOnGestureListener实现类,可以根据需要选择重写的方法//单击事件@Overridepublic boolean onSingleTapUp(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(),e.getY()); if(childView != null && mListener != null){ mListener.onItemClick(childView,recyclerView.getChildLayoutPosition(childView)); return true; } return false; } //长按事件@Overridepublic void onLongPress(MotionEvent e) { View childView = recyclerView.findChildViewUnder(e.getX(),e.getY()); if(childView != null && mListener != null){ mListener.onItemLongClick(childView,recyclerView.getChildLayoutPosition(childView)); } } }); } @Overridepublic boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { //把事件交给GestureDetector处理if(mGestureDetector.onTouchEvent(e)){ return true; }elsereturn false; } @Overridepublic void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Overridepublic void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }}
在MainActivity.java改动如下:mRecyclerView.addOnItemTouchListener(new RecyclerViewClickListener2(this, mRecyclerView, new RecyclerViewClickListener2.OnItemClickListener() { @Overridepublic void onItemClick(View view, int position) { Toast.makeText(MainActivity.this,"Click "+mData.get(position),Toast.LENGTH_SHORT).show(); } @Overridepublic void onItemLongClick(View view, int position) { Toast.makeText(MainActivity.this,"Long Click "+mData.get(position),Toast.LENGTH_SHORT).show(); }}));
三:操作数据及添加分割线
操作数据
首先我们看看官方提供了什么API给我们调用,我们看RecyclerView.Adapter中提供的四个方法如下:
//该方法用于当增加一个数据的时候,position表示新增数据显示的位置final void notifyItemInserted(int position)//该方法用于删除一个数据的时候,position表示数据删除的位置final void notifyItemRemoved(int position)//该方法表示所在position对应的item位置不会改变,但是该item内容发生变化final void notifyItemChanged(int position)//该方法一般用于:适配器之前装载的数据大部分已经过时了,需要重新更新数据//调用该方法的时候,recyclerView会重新计算子item及所有子item重新布局//出于效率考虑,官方建议用更加精确的方法(比如上面三个方法)来取代这个方法final void notifyDataSetChanged()
首先对MyAdapter.java修改,新增三个方法: //移除数据
public void removeData(int position) { mDataSet.remove(position); notifyItemRemoved(position); } //新增数据public void addData(int position){ mDataSet.add(position,"Add One"); notifyItemInserted(position); } //更改某个位置的数据public void changeData(int position){ mDataSet.set(position,"Item has changed"+ count++); notifyItemChanged(position); }
mRecyclerView.addOnItemTouchListener(new RecyclerViewClickListener2(this, mRecyclerView, new RecyclerViewClickListener2.OnItemClickListener() { @Overridepublic void onItemClick(View view, int position) { Toast.makeText(MainActivity.this, "Click " + mData.get(position), Toast.LENGTH_SHORT).show(); } @Overridepublic void onItemLongClick(View view, int position) { //长按某个item后,将移除这个item mAdapter.removeData(position); } }));...toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Overridepublic boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_search: Toast.makeText(MainActivity.this, "Search !", Toast.LENGTH_LONG).show(); break; case R.id.action_add: mAdapter.addData(1); break; case R.id.action_change: mAdapter.changeData(2); break; } return true; }});
//这一句不是必要的,因为RecyclerView会默认使用mRecyclerView.setItemAnimator(new DefaultItemAnimator());
添加分割线
细心的同学会发现,笔者的RecyclerView每个Item之间是用magin属性来分开的,那么RecycleView有没有像ListView那样可以直接在xml属性中添加android:divider呢?答案是没有的,可能是考虑到RecycleView灵活多变的特点,官方没有添加这个属性,不过我们还是有办法可以手动去添加。在第一节有提到过: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); }}
onDraw和onDrawOver,显然,这两个方法是用于绘制的,那么绘制分割线的逻辑可以放在这里面,它们二者的具体区别是:onDraw是在item view绘制之前调用,而onDrawOver是在item view绘制之后调用,因此我们一般选择重写其中一个方法即可。getItemOffsets,这个方法是告诉RecyclerView在绘制完一个item view的时候,应该留下多少空位,以便于绘制分割线。
既然知道了这三个方法的作用,那么我们来写一个实现类,新建DividerItemDecoration(注:该类参考自Android官方):
public class DividerItemDecoration extends RecyclerView.ItemDecoration { //使用系统自带的listDividerprivate 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 mDivider; private int mOrientation; public DividerItemDecoration(Context context,int orientation){ //使用TypeArray加载该系统资源final TypedArray ta = context.obtainStyledAttributes(ATTRS); mDivider = ta.getDrawable(0); //缓存 ta.recycle(); setOrientation(orientation); } public void setOrientation(int orientation){ if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){ throw new IllegalArgumentException("invalid orientation"); } mOrientation = orientation; } @Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { if(mOrientation == VERTICAL_LIST){ drawVertical(c,parent); }else{ drawHorizontal(c,parent); } } public void drawVertical(Canvas c,RecyclerView parent){ //获取分割线的左边距,即RecyclerView的padding值final int left = parent.getPaddingLeft(); //分割线右边距final int right = parent.getWidth() - parent.getPaddingRight(); final int childCount = parent.getChildCount(); //遍历所有item view,为它们的下方绘制分割线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 + mDivider.getIntrinsicHeight(); mDivider.setBounds(left,top,right,bottom); mDivider.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 + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } @Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { if(mOrientation == VERTICAL_LIST){ //设置偏移的高度是mDivider.getIntrinsicHeight,该高度正是分割线的高度 outRect.set(0,0,0,mDivider.getIntrinsicHeight()); }else{ outRect.set(0,0,mDivider.getIntrinsicWidth(),0); } }}
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
- RecyclerView的使用全面解析
- RecyclerView的使用解析
- ListView、AdapterView、RecyclerView全面解析
- ListView、AdapterView、RecyclerView全面解析
- RecyclerView的使用与解析
- 最全面的RecyclerView源码解析(一)
- Android开发:ListView、AdapterView、RecyclerView全面解析
- Android开发:ListView、AdapterView、RecyclerView全面解析
- Android开发:ListView、AdapterView、RecyclerView全面解析
- Android中RecyclerView的使用与解析
- Android:最全面的Socket使用解析
- 最全面的Socket使用解析
- Android:最全面的Socket使用解析
- RecyclerView CardView使用解析
- Android RecyclerView 使用解析
- Android RecyclerView 使用解析
- RecyclerView CardView使用解析
- RecyclerView使用解析
- Linux环境下服务器利用组播来获取客户端IP
- 数字滤波器回顾与解析
- 浏览器对象模型BOM之location对象
- 手机映射到大屏幕上
- 群智能优化之果蝇算法
- RecyclerView的使用全面解析
- 欢迎使用CSDN-markdown编辑器
- pat甲1004. Counting Leaves
- Scikit-learn实战之SVM分类
- shiro认证框架
- PHP E-mail
- CSS:按一定比例布局;某个元素填满剩余空间
- Java序列化与反序列化
- 3秒鲁一个简单的百分比布局