看过我这边文章的RecyclerView.Adapter的优化与封装的真的想说声抱歉,其实不需要继承BaseBean,只需要继承Object就可以了,而且更灵活了,当时不知道咋了,脑袋抽风了,多次了这一举。
好了,今天来一个更牛逼的神器RefreshLayout
效果图:
- 实现了上拉刷新和下拉加载功能
- 封装好的BaseAdapter和BaseViewHolder,将adapter交给Activity来管理
- 实现了单击事件和长单击事件
类结构图:
这个demo我把之前的BaseAdapter和BaseViewHolder的封装都重新整理了一边,然后把BaseAdapter抽象给Activity来管理,因为我发现我在之前的博文中封装的BaseAdapter,然后让其他的adapter去继承他去操作,这个adapter根本就不需要多少的操作,而且有点冗余,好多的adapter,烦,所以我打算把BaseAdapter的管理交给Activity去实现,这样,整个项目只需要一个BaseAdapter和BaseViewHolder去操作就行了,真的是万能的适配器啊,项目瞬间清爽了起来。好了,今天先来说说实现思路:
因为下拉加载SwipeRefreshLayout大家都知道的,官方的,没什么好讲,但是在处理上拉加载的时候这个下拉在后面还需要去处理,所以,先来讲上拉加载更多。
上拉加载更多需要哪些实现呢?
- 计算所有的RecycleView的item数量(即layoutManager.getItemCount())
- 获取当前页面底部可见的item的position位置(即LayoutManager.findLastVisibleItemPosition())
- 判断这个可见的position是否大于等于RecycleView的item数量减1(因为position是从0开始的)
- 我们还需要一个getChildCount辅助方法,获取当前整个屏幕显示的item数量,目的是判断有无item,来处理item不满一屏幕的时候处理。
- 当数据不满一屏幕的时候,SwipeRefreshLayout下拉的时候会触发我在onScrollStateChanged里面做的判断,然而这个判断是满足上拉加载更多的,会出现上拉加载刷新和下拉加载更多同时调用,然后看见数据突然的闪一下恢复到原来的数据,为了解决数据不满一屏的操作的bug,我在onTouch里面做了一个手势的处理,如果是向下拉,则给上拉加载更多的判断设置个flag为false,不让他去处理,亲测完美解决。
上代码,来看看RefreshRecycleView.java
/**
* Created by wangqi on 2016/11/8.
*/
public class RefreshRecycleView extends RecyclerView {
public RefreshRecycleView(Context context) {
super(context);
initView(context);
}
public RefreshRecycleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
private ProgressDialog progressDialog;
private void initView(Context context) {
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("加载更多...");
}
@Override
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
//获取在各个不同的LayoutManager中可见的position位置
if (state == RecyclerView.SCROLL_STATE_IDLE && loadMoreListner != null && loadingMoreEnabled) {
LayoutManager layoutManager = getLayoutManager();
int lastVisibleItemPosition;
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int[] into = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(into);
lastVisibleItemPosition = findMax(into);
} else {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
}
//触发加载更多事件
if (flag && loadingMoreEnabled && layoutManager.getChildCount() > 0 && lastVisibleItemPosition >= layoutManager.getItemCount() - 1 && layoutManager.getItemCount() >= layoutManager.getChildCount()) {
progressDialog.show();
loadMoreListner.onLoad();
}
}
}
public boolean flag;
float startY = 0;
float moveY = 0;
/**
*
* @param e
* @return 处理数据不满一屏的手势判断
*/
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
startY = e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
moveY = e.getRawY();
if (moveY > startY)
flag = false;
else
flag = true;
startY = moveY;
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(e);
}
public void setLoadMoreComplete() {
progressDialog.dismiss();
}
//设置不给上拉加载
public void setLoadingMoreEnabled(boolean loadingMoreEnabled) {
this.loadingMoreEnabled = loadingMoreEnabled;
}
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value > max) {
max = value;
}
}
return max;
}
boolean loadingMoreEnabled = true;
LoadMoreListner loadMoreListner;
public void setLoadMoreListner(LoadMoreListner loadMoreListner) {
this.loadMoreListner = loadMoreListner;
}
public interface LoadMoreListner {
void onLoad();
}
}
代码特别简单,
- 在滑动事件里面去判断当前上否是滑动状态,然后判断上拉加载更多是否可用,当前的回调是否已经注册。
- 获取RecycleView在不同LayoutManager的时候返回的底部可见的position的位置
- layoutManager.getChildCount()是获取当前可见界面的item数量
- lastVisibleItemPosition获取item可见的最底部的position位置
- layoutManager.getItemCount()是获取整个RecycleView的item的总数
- 然后对当前进行判断,
- falg是处理onTouch返回的数据不满一屏幕的判断,
- 其他的判断大家看看就懂的,不难
- 然后显示dialog,回调load接口,给别人去处理
好了,这时候来看看包装RefreshRecycleView的类RefreshLayout.java了
看看xml就懂了
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/refresh_swip"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.e8net.myapplication.RefreshRecycleView
android:id="@+id/refresh_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
RefreshLayout只是对RecycleView的一种包装,专门处理上拉刷新和下拉加载更多,将所有事件的处理通过这个辅助类传递给Activity的调用者,外者只需要调用,不需要管理实现原理,简洁了代码。来看看代码
/**
* Created by wangqi on 2016/11/8.
*/
public class RefreshLayout extends LinearLayout {
RefreshRecycleView refreshRecycleView;
SwipeRefreshLayout swipeRefreshLayout;
public RefreshLayout(Context context) {
super(context);
initView(context);
}
public RefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public RefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(final Context context) {
View v = LayoutInflater.from(context).inflate(R.layout.refresh_view, this, false);
refreshRecycleView = (RefreshRecycleView) v.findViewById(R.id.refresh_recycler);
swipeRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.refresh_swip);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
this.addView(v, params);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
onRefreshListner.refresh();
}
});
//加载更多
refreshRecycleView.setLoadMoreListner(new RefreshRecycleView.LoadMoreListner() {
@Override
public void onLoad() {
onRefreshListner.loadMore();
}
});
}
public void setLoadingMoreEnabled(boolean flag) {
refreshRecycleView.setLoadingMoreEnabled(flag);
}
public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
refreshRecycleView.setLayoutManager(layoutManager);
}
public void setAdapter(RecyclerView.Adapter<BaseViewHolder> adapter) {
refreshRecycleView.setAdapter(adapter);
}
public void setRefreshComplete() {
swipeRefreshLayout.setRefreshing(false);
}
public void setLoadMoreComplete() {
refreshRecycleView.setLoadMoreComplete();
}
private RefreshListner onRefreshListner;
public void setOnRefreshListner(RefreshListner onRefreshListner) {
this.onRefreshListner = onRefreshListner;
}
public interface RefreshListner {
void refresh();
void loadMore();
}
}
- 初始化xml,将view界面inflater到RefreshLayout
- 给下拉刷新和上拉加载进行注册
- 然后设置下拉刷新完成和上拉加载完成,这个大家应该都懂的,全是包装
- 然后注册个接口,将下拉和上拉的触发交个回调者来处理
这时候包装都结束了,最后来看看Activity来调用看看
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
refreshLayout = (RefreshLayout) findViewById(R.id.refresh_layout);
list = new ArrayList<>();
addList();
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
refreshLayout.setLayoutManager(layoutManager);
myRecycleAdapter = new BaseRecycleAdapter(this, R.layout.recycle_item, list);
refreshLayout.setAdapter(myRecycleAdapter);
refreshLayout.setOnRefreshListner(new RefreshLayout.RefreshListner() {
@Override
public void refresh() {
refreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
list.clear();
addList();
myRecycleAdapter.notifyDataSetChanged();
refreshLayout.setRefreshComplete();
}
}, 2000);
}
@Override
public void loadMore() {
refreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
addList();
myRecycleAdapter.notifyDataSetChanged();
refreshLayout.setLoadMoreComplete();
}
}, 2000);
}
});
}
public void addList() {
for (int i = 0; i < 10; i++)
list.add(""); }
下拉和上拉就这么搞定了,哈哈,不对不对,你这个adapter还没讲呢,对吼,那赶紧的跟上步伐
说说我这个牛X的adapter,所有项目的RecycleView都只需要这一个adapter就可以搞定了,如果item需要复杂的处理,可以拓展BaseViewHolder方法,主要思路就是,将数据交给adapter,adapter不做处理,只是对数据的size进行排版item,数据的设置回调给Activity去实现,Activity拿到数据去BaseViewHolder里面去设置界面显示数据,adapter里面实现了单击和长单击事件,和我之前那篇博文一样的思路,那来看看代码吧
BaseRecycleAdapter.java
/**
* Created by wangqi on 2016/7/16.
*/
public class BaseRecycleAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private int layoutId;
private List<? extends Object> data;
public Context context;
private OnItemClickListner onItemClickListner;//单击事件
private OnItemLongClickListner onItemLongClickListner;//长按单击事件
private boolean clickFlag = true;//单击事件和长单击事件的屏蔽标识
/**
* @param context //上下文
* @param layoutId //布局id
* @param data //数据源
*/
public BaseRecycleAdapter(Context context, int layoutId, List<? extends Object> data) {
this.layoutId = layoutId;
this.data = data;
this.context = context;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(context).inflate(layoutId, parent, false);
final BaseViewHolder holder = new BaseViewHolder(v, context);
//单击事件回调
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListner == null)
return;
if (clickFlag) {
onItemClickListner.onItemClickListner(v, holder.getLayoutPosition());
}
clickFlag = true;
}
});
//单击长按事件回调
v.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (onItemLongClickListner == null)
return false;
onItemLongClickListner.onItemLongClickListner(v, holder.getLayoutPosition());
clickFlag = false;
return false;
}
});
return holder;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
if (mCallBack != null)
mCallBack.convert(holder, data.get(position), position);
}
@Override
public int getItemCount() {
return data.size();
}
public void setOnItemClickListner(OnItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public void setOnItemLongClickListner(OnItemLongClickListner onItemLongClickListner) {
this.onItemLongClickListner = onItemLongClickListner;
}
public interface OnItemClickListner {
void onItemClickListner(View v, int position);
}
public interface OnItemLongClickListner {
void onItemLongClickListner(View v, int position);
}
CallBack mCallBack;
public void setCallBack(CallBack CallBack) {
this.mCallBack = CallBack;
}
public interface CallBack {
<T extends Object> void convert(BaseViewHolder holder, T bean, int position);
}
}
这一次adapter的修改,主要是对当初的抽象onBindViewHolder方法变成了接口回调,当初需要继承这个Base,然后实现这个convert方法来对数据进行操作,但这个时候感觉继承这个adapter的使用者,完全不需要那么多的操作,只是对数据操作了下,大功能基本上是在BaseViewHolder处理,这一次整改,将这个设置器交给Activity来处理,还有就是数据的泛型,上一遍博文让所有的Model都继承BaseBean,这个真是多此一举啊,哎,失败,这里我给改成Object,方便传递,其他的和我上篇博文一样,可以看看,文章开头给了链接地址。
然后来看看BaseViewHolder.java
/**
* Created by wangqi on 2016/7/16.
*/
public class BaseViewHolder extends RecyclerView.ViewHolder {
View convertView;
Context context;
public BaseViewHolder(View itemView, Context context) {
super(itemView);
this.convertView = itemView;
this.context = context;
}
public View getItemView() {
return convertView;
}
public void setText(int id, String text) {
TextView tx = (TextView) convertView.findViewById(id);
tx.setText(text);
}
public void setText(int id, String text, final OnClickListener onClickListener) {
TextView tx = (TextView) convertView.findViewById(id);
tx.setText(text);
tx.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickListener.onClickListner(v);
}
});
}
public void setImageListner(int id, final OnClickListener onClickListener) {
ImageView img = (ImageView) convertView.findViewById(id);
img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickListener.onClickListner(v);
}
});
}
public void setImageResource(int id, int resouceId) {
ImageView img = (ImageView) convertView.findViewById(id);
img.setImageResource(resouceId);
}
public void setImageResource(int id, int resouceId, final OnClickListener onClickListener) {
ImageView img = (ImageView) convertView.findViewById(id);
img.setImageResource(resouceId);
img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickListener.onClickListner(v);
}
});
}
public interface OnClickListener {
void onClickListner(View v);
}
}
这里面的方法是需要开发者根据需求自己去定制添加方法的,我这里只是列举了一些常见的,大家可以自己扩展,大家可能看到我给的回调,如果你当前界面不需要设置监听,你完全可以直接调用设置数据源的方法,如果你需要item的子View触发事件,也可以调用有回调接口的方法,这个回调也是交给Activity去处理
来看看完整的MainActivity的代码
public class MainActivity extends AppCompatActivity {
private RefreshLayout refreshLayout;
List<String> list;
BaseRecycleAdapter myRecycleAdapter;
public void addList() {
for (int i = 0; i < 10; i++)
list.add("");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
refreshLayout = (RefreshLayout) findViewById(R.id.refresh_layout);
list = new ArrayList<>();
addList();
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
refreshLayout.setLayoutManager(layoutManager);
myRecycleAdapter = new BaseRecycleAdapter(this, R.layout.recycle_item, list);
refreshLayout.setAdapter(myRecycleAdapter);
refreshLayout.setOnRefreshListner(new RefreshLayout.RefreshListner() {
@Override
public void refresh() {
refreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
list.clear();
addList();
myRecycleAdapter.notifyDataSetChanged();
refreshLayout.setRefreshComplete();
}
}, 2000);
}
@Override
public void loadMore() {
refreshLayout.postDelayed(new Runnable() {
@Override
public void run() {
addList();
myRecycleAdapter.notifyDataSetChanged();
refreshLayout.setLoadMoreComplete();
}
}, 2000);
}
});
myRecycleAdapter.setOnItemClickListner(new BaseRecycleAdapter.OnItemClickListner() {
@Override
public void onItemClickListner(View v, int position) {
Toast.makeText(MainActivity.this, "click---" + position, Toast.LENGTH_SHORT).show();
}
});
myRecycleAdapter.setOnItemLongClickListner(new BaseRecycleAdapter.OnItemLongClickListner() {
@Override
public void onItemLongClickListner(View v, int position) {
Toast.makeText(MainActivity.this, "longClick---" + position, Toast.LENGTH_SHORT).show();
}
});
myRecycleAdapter.setCallBack(new BaseRecycleAdapter.CallBack() {
@Override
public <T> void convert(BaseViewHolder holder, T bean, final int position) {
holder.setText(R.id.txt, "明天会更好" + position, new BaseViewHolder.OnClickListener() {
@Override
public void onClickListner(View v) {
Toast.makeText(MainActivity.this, "明天会更好" + position, Toast.LENGTH_SHORT).show();
}
});
holder.setImageListner(R.id.img, new BaseViewHolder.OnClickListener() {
@Override
public void onClickListner(View v) {
Toast.makeText(MainActivity.this, "img" + position, Toast.LENGTH_SHORT).show();
}
});
}
});
}
}
看完后有没有为之一振的感觉呢,Activity不去管那些复杂的操作,只做一些简单结果处理,确实有那么点意思,adapter也只需要给xml布局和数据源,不再去管理复杂操作,想怎么玩就怎么玩,所有项目的RecycleView都可以用这一个adapter,Activity立马解决数据操作,神器,绝对的神器,为了实现效果早点装逼,代码质量还需要一些规范分类(关键是懒,哈哈),里面用了好多的接口回调,也是醉了,不过这种结果处理,真是屡试不爽啊,好咯,下午马上就要上实验课了,作业还一大堆,不会操作就只会下位来装逼指导学生,每天上课之前ppt放映到”系主任”几个字的那个页面,真是受够了——————————愤青少年
最后附上github项目链接来star吧