您的位置:首页 > 移动开发 > Android开发

RecyclerView的痛点使用

2016-04-08 11:59 459 查看

前言

申明:这里不提供封装好的RecylerView库,碍于作者能力,只能提供一些思路和想法。同时,这不是一篇基础性的RecylerView的使用文章,文章中我将主要对我本人在使用过程遇到的通点进行归纳,并提供自己的解决方法。

众所周知,在Android 5.0之后,Google引入Material Design设计,同时为我们带来了ListView的替代品RecylerView。对比ListView, RecyclerView在可扩展性,灵活性上更方便我们自己去定制。话不多说,下面我将从下拉刷新上拉加载更多自定义分割线item点击事件阐述我的跟人想法。

下拉刷新

本着用最简便的方式实现需求的原则,我们在下拉刷新这块主要是借助Google提供的SwipeRefreshLayout控件。

SwipeRefreshLayout使用起来很简单,代码如下:

<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp" />

</android.support.v4.widget.SwipeRefreshLayout>


实现下拉刷新,只需要调用SwipeRefreshLayout的监听事件OnRefreshListener,在监听事件方法里实现响应的逻辑就行了

mRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
texts.clear();
i = 0;
setData();
mAdapter.notifyDataSetChanged();
mRefresh.setRefreshing(false);
}
},2000);
}
});


自定义分割线

使用RecyclerView时,为了控制item之间的距离,我们既可以通过设置item的margin调整item之间的距离,也可以调用RecyclerView的addItemDecoration()方法设置分割线来控制item之间的距离。这里我们使用的是后者。

我们需要自定义一个分割线类继承自RecyclerView.ItemDecoration,重写onDraw()方法(绘制分割线)以及getItemOffsets()方法(设置分割线的大小)。

此处给出我的自定义分割线源码

public class DividerItemDecoration extends RecyclerView.ItemDecoration {

private int orientation;

//转换成整型后分割线尺寸
private int itemSize;
//待传入的分割线尺寸,如果没有传入,初始化的时候默认16
private float size;

private Paint mPaint;
private Context context;

public DividerItemDecoration(Context context) {
this(context, 16, LinearLayoutManager.VERTICAL);
}

public DividerItemDecoration(Context context, float size) {
this(context, size, LinearLayoutManager.VERTICAL);
}

public DividerItemDecoration(Context context, int orientation) {
this(context, 16, orientation);
}

public DividerItemDecoration(Context context, float size, int orientation) {
if (orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL) {
throw new IllegalArgumentException("the orientation is not VERTICAL or HORIZONTAL!");
}
this.context = context;
this.size = size;
this.orientation = orientation;
initAttrs();
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (orientation == LinearLayoutManager.VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (orientation == LinearLayoutManager.VERTICAL) {
outRect.set(0, 0, 0, itemSize);
} else {
outRect.set(0, 0, itemSize, 0);
}
}

private void initAttrs() {
itemSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, context.getResources().getDisplayMetrics());
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.TRANSPARENT);
mPaint.setStyle(Paint.Style.FILL);
}

/**
* 绘制横向item分割线
*
* @param canvas 画布
* @param parent recyclerView
*/
private void drawVertical(Canvas canvas, 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;
int bottom = top + itemSize;
canvas.drawRect(left, top, right, bottom, mPaint);
}
}

/**
* 绘制横向item分割线
*
* @param canvas 画布
* @param parent recyclerView
*/
public void drawHorizontal(Canvas canvas, 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;
int right = left + itemSize;
canvas.drawRect(left, top, right, bottom, mPaint);
afae

}
}
}


item点击事件

RecyclerView本身没有给我们提供类似ListView的setOnItemClickListener的监听事件,所以我们只能自己去实现这样一个监听事件。

在我们定义的Adapter中定义一个OnItemClickListener接口,接口中定义一个OnItemClick()方法,之后我们需要对外提供一个方法去实现这个接口。

public interface OnItemClickListener {
void onItemClick(View view, int position);
}

private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}


然后在onBindViewHolder中添加如下代码:

if (onItemClickListener != null) {
((ItemViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(((ItemViewHolder) holder).itemView, holder.getAdapterPosition());
}
});
}


最后,如果需要给item实现点击事件,利用adapter.setOnItemClickListener()实现就可以了

mAdapter.setOnItemClickListener(new RecyclerAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, "click " + (position + 1), Toast.LENGTH_SHORT).show();
}
});


上拉加载更多

上拉加载更多这块,主要是给RecyclerView添加一个底部布局。RecyclerView.Adapter给我们提供了getItemViewType方法来告诉我们每个item是属于正常布局还是属于底部布局

@Override
public int getItemViewType(int position) {
if (position == texts.size()) {
return FOOT_TYPE;//说明该item是底部
}
return NORMAL_TYPE;
}


同时为了实现加载数据,我们通过调用RecyclerView的addOnScrollListener()方法,实现onScrolled()方法和onScrollStateChanged()方法。

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
int itemLastIndex = mAdapter.getItemCount() - 1;//recyclerView最后一项位置
if (newState  == RecyclerView.SCROLL_STATE_IDLE  && visibleLastIndex == itemLastIndex) {
if (i < 100) {
mAdapter.setLoading(true);
mAdapter.notifyDataSetChanged();
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
setData();
mAdapter.setLoading(false);
mAdapter.notifyDataSetChanged();
}
}, 2000);
} else {
mAdapter.setAll(true);
mAdapter.notifyDataSetChanged();
}
}
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleLastIndex = manager.findLastCompletelyVisibleItemPosition();
}
});


简单说下思路,调用此监听事件后,当RecyclerView在滑动过程中会不断回调一些数据来表示当前RecyclerView的状态。我们为了实现上拉加载更多,我们需要知道,当RecyclerView滑到底部时,同时继续向上滑动我们就需要加载更多数据,所以在onScrolled()方法中我们利用findLastCompleteVisibleItemPosition()方法来获得当前RecyclerView停止滑动时最后一个可见项的位置是多少,当RecyclerView在滑动状态改变时,我们判断如果RecylerView改变的状态是停止滑动同时当前可见项位置是Adapter除底部项外最后一个,那么这个时候我们就需要加载数据。

关于自定义分割线的部分主要参见该博客http://blog.csdn.net/jxxfzgy/article/details/43736385

由于作者能力有限,文章若有不足或者错误之处望指正交流

项目的Github地址:https://github.com/ReedGit/RecyclerViewDemo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android