最近工作中用到了RecyclerView,所以写一篇关于RecyclerView的总结文章热热身
本文主要实现3主要功能
- item上下移动与滑动删除
- emptyView与RecyclerView的简单绑定
- RecyclerView实现流畅分页加载
功能不是很复杂,稍微介绍下之后就直接上代码。
RecyclerView实现item上下移动、滑动删除
先看下效果图
这个功能主要通过ItemTouchHelper.Callback和ItemTouchHelper这两个类实现。ItemTouchHelper是官方支持包提供的一个辅助RecyclerView实现动态拖拽的工具库。ItemTouchHelper.Callback会接收ItemTouchHelper的操作回调,通过这个类可以使得用户的触摸行反馈到对应的ViewHodler上。ItemTouchHelper要与ItemTouchHelper.Callback、RecyclerView配合使用。关键代码如下
newsAdapter = new NewsAdapter();
ItemTouchHelper.Callback callback = new MyItemTouchHelper(newsAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
MyItemTouchHelper继承了ItemTouchHelper.Callback,当用户操作完毕之后要通知adapter去更新数据与UI,这里使用了接口的方式,让NewsAdapter继承了自定义的OnItemMoveListener。MyItemTouchHelper代码如下
public class MyItemTouchHelper extends ItemTouchHelper.Callback {
private static final String TAG = "MyItemTouchHelper";
private OnItemMoveListener moveListener;
public MyItemTouchHelper(OnItemMoveListener onItemMoveListener) {
moveListener = onItemMoveListener;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int upFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(upFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
moveListener.onItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
Log.e(TAG, "onMove: " + viewHolder.getAdapterPosition() + ",target position: " + target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
moveListener.onItemRemoved(viewHolder.getAdapterPosition());
Log.e(TAG, "onSwiped: " + viewHolder.getAdapterPosition());
}
NewsAdapter继承了OnItemMoveListener,并且实现了onItemMoved和onSwiped。在onItemMoved中需要进行数据交换和UI更新,在onSwiped需要进行数据删除与UI更新,关键代码如下
public class NewsAdapter extends RecyclerView.Adapter<NewsHolder> implements OnItemMoveListener {
···
@Override
public void onItemMoved(int fromPosition, int toPosition) {
//数据交换
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(dataList, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(dataList, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onItemRemoved(int position) {
//数据删除
dataList.remove(position);
notifyItemRemoved(position);
}
···
}
emptyView的简单实现
一般RecyclerView中没有数据时需要展示一个emptyView,本次使用RecyclerView.AdapterDataObserver来实现此功能。
RecyclerView.AdapterDataObserver时一个能够监听adapter数据变化的观察者
看下布局
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.afun.recycleviewdemo.MainActivity">
<LinearLayout
android:id="@+id/emptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/empty_view_bg" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingBottom="8dp"
android:paddingTop="4dp"
android:text="没东西啦~~"
android:textColor="#727272"
android:textSize="16sp" />
</LinearLayout>
<com.afun.recycleviewdemo.ui.MyRecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarSize="5dp"
android:scrollbarThumbVertical="@color/colorAccent"
android:scrollbars="vertical">
</com.afun.recycleviewdemo.ui.MyRecyclerView>
</android.support.design.widget.CoordinatorLayout>
这新建了一个AdapterObserver类并且继承了RecyclerView.AdapterDataObserver,通过这个类通知RecyclerView是否展示emptyView
public class AdapterObserver extends RecyclerView.AdapterDataObserver {
private MyRecyclerView mRecyclerView;
public void onAttach(MyRecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
@Override
public void onChanged() {
super.onChanged();
mRecyclerView.showEmptyView();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
mRecyclerView.showEmptyView();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
super.onItemRangeRemoved(positionStart, itemCount);
mRecyclerView.showEmptyView();
}
}
然后将AdapterObserver与RecyclerView关联起来
//设置数据观察者
AdapterObserver observer = new AdapterObserver();
observer.onAttach(recyclerView);
recyclerView.onAttach(findViewById(R.id.emptyView), observer);
最后看下MyRecyclerView的关键代码
public class MyRecyclerView extends RecyclerView {
···
public void onAttach(View emptyView, AdapterObserver adapterObserver) {
mEmptyView = emptyView;
mObserver = adapterObserver;
}
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
mObserver.onChanged();
}
}
public void showEmptyView() {
Adapter<?> adapter = getAdapter();
if (adapter != null && mEmptyView != null) {
if (adapter.getItemCount() == 0) {
mEmptyView.setVisibility(VISIBLE);
this.setVisibility(GONE);
} else {
mEmptyView.setVisibility(GONE);
this.setVisibility(VISIBLE);
}
}
}
}
RecyclerView实现流畅分页加载
现在很多app都是分页加载,主要是通过重写recyclerView的OnScrollListener,看下介绍
public abstract static class OnScrollListener {
/**
* Callback method to be invoked when RecyclerView's scroll state changes.
*
* @param recyclerView The RecyclerView whose scroll state has changed.
* @param newState The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
* {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
*/
public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
/**
* Callback method to be invoked when the RecyclerView has been scrolled. This will be
* called after the scroll has completed.
* <p>
* This callback will also be called if visible item range changes after a layout
* calculation. In that case, dx and dy will be 0.
*
* @param recyclerView The RecyclerView which scrolled.
* @param dx The amount of horizontal scroll.
* @param dy The amount of vertical scroll.
*/
public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
}
使用下来,感觉onScrollStateChanged比较适合拖动到最后展示加载更多View(带个progressBar的View),然后再显示新的一页的场景,onScrolled适合大幅度滑动实现流畅加载更多的场景,本次主要使用onScrolled来实现自动加载更多,效果图如下
本次涉及的关键代码如下
//实现加载更多
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
final int childCount = recyclerView.getChildCount();
if (childCount > 0) {
View lastChild = recyclerView.getChildAt(childCount - 1);
RecyclerView.Adapter outerAdapter = recyclerView.getAdapter();
int lastVisible = recyclerView.getChildAdapterPosition(lastChild);
if (lastVisible >= outerAdapter.getItemCount() - 3) {
if (dataSource.canLoadMore()) {
++current;
loadData();
}
}
}
}
});
private void loadData() {
int length = dataSource.getLoadLength(current);
newsAdapter.notifyLoadMoreChange(dataSource.getNewsList(currentPosition, length));
currentPosition = currentPosition + length;
}
//newsAdapter.notifyLoadMoreChange方法
public void notifyLoadMoreChange(List<NewsBean.NewsListBean> dataList) {
if (dataList != null && dataList.size() != 0) {
this.dataList.addAll(dataList);
int oldSize = getItemCount();
//使用notifyItemRangeInserted插入新数据
notifyItemRangeInserted(oldSize, this.dataList.size());
}
}
OK,全部介绍完毕,最后附上代码有兴趣的同学可以看看。