当前位置: 代码迷 >> 综合 >> SwipeRefreshLayout+Recyclerview的刷新加载封装
  详细解决方案

SwipeRefreshLayout+Recyclerview的刷新加载封装

热度:78   发布时间:2023-11-06 07:13:41.0

目录

1.简介

2.自定义的SwipeRefreshLayout

1)全局变量和基础方法

2)onlayout拿到RecyclerView,设置加载更多的监听

3)其余的判断标准

3.Activity中的使用

4.xml布局使用

5.适配器和单行布局


1.简介

当页面展示大量相同布局的数据的时候,公司的接口一般都是一页一页的去请求并拿到数据去展示,防止页面因同时加载大量数据出现内存溢出等问题。

下面demo假设每页最多20条数据,第一页数据或者后面加载的某一页数据有可能数量小于20,当小于20的时候,我们就不接着去触发上拉加载更多的监听。主要介绍封装的SwipeRefreshLayout以及它的使用。

顺便说一下,我们上拉加载更多的判定条件有4个

a.recyclerview的状态为RecyclerView.SCROLL_STATE_IDLE

b.recyclerview滑动到了底部

c.recyclerview未在上拉加载或者下拉刷新状态(免得多次加载或者刷新数据的时候去加载数据)

d.手指做了上划的操作,且滑动距离大于android认定的最小滑动距离

demo地址:https://download.csdn.net/download/qq_37321098/10657545

2.自定义的SwipeRefreshLayout

1)全局变量和基础方法

    private static final String TAG = "测试";//正在加载状态private boolean isLoading = false;//最小滑动距离,手移动的距离大于这个距离才能拖动控件private int mScaledTouchSlop;//加载控件private RecyclerView mRecyclerView;//在分发事件的时候处理子控件的触摸事件private float mDownY, mUpY;private OnLoadMoreListener mListener;//是否还有更多数据private boolean hasMoreDate = true;public MySwipeRefresh(Context context, AttributeSet attrs) {super(context, attrs);mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();}//属性设置public void setInitParams(int bgColor, int proColor) {//下拉进度的背景颜色setProgressBackgroundColorSchemeResource(bgColor);//进度条颜色setColorSchemeResources(proColor);}

注意全局变量hasMoreDate就是我们用来判断是否还有更多加载数据判断的依据。如果没有,是不会去触发加载更多的监听。

2)onlayout拿到RecyclerView,设置加载更多的监听

 @Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);//判断内部是ListView还是RecyclerViewif (getChildCount() > 0) {if (getChildAt(0) instanceof RecyclerView) {mRecyclerView = (RecyclerView) getChildAt(0);// 设置RecyclerView的滑动监听setRecyclerViewOnScroll();}}}//设置RecyclerView的滑动监听private void setRecyclerViewOnScroll() {mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {@Overridepublic void onScrollStateChanged(RecyclerView recyclerView, int newState) {super.onScrollStateChanged(recyclerView, newState);boolean isBottom = isVisBottom(recyclerView);if (newState == RecyclerView.SCROLL_STATE_IDLE &&isBottom &&!isLoading &&(mDownY - mUpY) >= mScaledTouchSlop) {Log.e(TAG, "满足条件->加载数据");if(!hasMoreDate){Log.e(TAG, "没有更多数据了");return;}if (mListener != null) {// 加载状态setLoading(true);mListener.onLoadMore();}}}});}public static boolean isVisBottom(RecyclerView recyclerView) {//屏幕中最后一个可见子项的position=总数-1//当前屏幕所看到的子项个数大于0//RecyclerView的滑动状态为空闲if (linearLayoutManager.getChildCount() > 0 &&linearLayoutManager.findLastVisibleItemPosition() == linearLayoutManager.getItemCount() - 1 &&recyclerView.getScrollState() == recyclerView.SCROLL_STATE_IDLE) {Log.e(TAG, "页面展示到底部条目");return true;} else {Log.e(TAG, "页面未展示到底部条目");return false;}}

加载更多的4个依据,开头已经提到过。isVisBottom()函数就是判断Recyclerview是否滑动到了底部,根据最后显示的数据位置是否等于item总数减一(lastVisibleItemPosition 从0起算的),注意这是判断垂直Recyclerview布局情况下滑动到底部的依据。如果是瀑布流布局,我们需要减的数目是你每一行展示内容的个数。还有一种特殊情况,之前也遇到过。如果是Scrollview嵌套的Recyclerview,需要判断的是Scrollview滑动到底部(你会发现Recyclerview滑动的监听不会触发,因为焦点在Scrollview身上),再去加载更多数据,这个可以去百度下如何判断Scrollview滑动到底部,此情况不多见。

3)其余的判断标准

  @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:// 移动的起点mDownY = ev.getY();break;case MotionEvent.ACTION_MOVE:break;case MotionEvent.ACTION_UP:// 移动的终点mUpY = getY();break;}return super.dispatchTouchEvent(ev);}public void setLoading(boolean loading) {isLoading = loading;//FIXME 可以为recyclerview添加底部布局,通过判断布局type,数据总数+1,加载底部布局mDownY = 0;mUpY = 0;}//是否还有更多数据public void setHasMoreDate(boolean hasMoreDate) {this.hasMoreDate = hasMoreDate;}

选择在dispatchTouchEvent方法中,拿到down和up在Y轴上位置去判断是否上滑。这个判断还是有必要的,不然会出现到达底部,即使做下滑的操作,都会去触发上拉加载数据的监听。

setLoading()函数就是外部用来设置正在刷新数据或者加载数据的标志,让SwipeRefreshLayout不去触发加载的监听。

setHasMoreDate()函数,就是没有更多数据,或者第一页数据不满分页加载时每一页个数的时候,去调用的方法,设置了之后同样也不会触发加载的监听。

3.Activity中的使用

public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private MySwipeRefresh mySwipeRefresh;private TextDateAdapter textDateAdapter;private LinearLayoutManager linearLayoutManager;//是否刷新状态中private boolean isRefreshing = false;//接口中每一页的数据private int pageSize = 20;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initRefreshView();}private void initRefreshView() {recyclerView = (RecyclerView) findViewById(R.id.rv);linearLayoutManager = new LinearLayoutManager(MainActivity.this);recyclerView.setLayoutManager(linearLayoutManager);mySwipeRefresh = (MySwipeRefresh) findViewById(R.id.refresh);mySwipeRefresh.setInitParams(android.R.color.white, R.color.colorAccent);//分割recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,linearLayoutManager.getOrientation()));//刷新加载回调initLoadCallback();//数据初始加载initData();}private void initLoadCallback() {// 下拉时触发SwipeRefreshLayout的下拉动画,动画完毕之后就会回调这个方法mySwipeRefresh.setOnRefreshListener(new MySwipeRefresh.OnRefreshListener() {@Overridepublic void onRefresh() {if (isRefreshing) {Log.e("测试: ", "下拉刷新中,return");return;}initData();}});// 设置下拉加载更多mySwipeRefresh.setOnLoadMoreListener(new MySwipeRefresh.OnLoadMoreListener() {@Overridepublic void onLoadMore() {loadMoreData();}});}private void initData() {isRefreshing = true;mySwipeRefresh.setRefreshing(true);mySwipeRefresh.setLoading(true);new Handler().postDelayed(new Runnable() {@Overridepublic void run() {//FIXME 设置是否还有更多数据,没有的话,不去调用加载的监听List<String> list = getRefreshDate();if (list.size() != pageSize) {mySwipeRefresh.setHasMoreDate(false);} else {mySwipeRefresh.setHasMoreDate(true);}//初始化数据加载if (textDateAdapter == null) {textDateAdapter = new TextDateAdapter(MainActivity.this, list);recyclerView.setAdapter(textDateAdapter);} else {textDateAdapter.refreshData(list);}// 收起下拉进度条if (mySwipeRefresh.isRefreshing()) {Log.e("测试", "收起下拉进度条");mySwipeRefresh.setRefreshing(false);mySwipeRefresh.setLoading(false);isRefreshing = false;}}}, 1000);}private void loadMoreData() {//FIXME 设置关卡,没有更多数据或者数据个数小于分页加载中每一页的数据,则不去加载更多数据//FIXME textDateAdapter.addBottomData前,swipererefresh中设置标志,不去触发loadMore的监听//FIXME 注意在刷新的时候,去变更适配器中的这个标志//mySwipeRefresh内部设置了loading状态isRefreshing = true;mySwipeRefresh.setLoading(true);new Handler().postDelayed(new Runnable() {@Overridepublic void run() {if (textDateAdapter != null) {List<String> list = getUpLoadDate();//FIXME 标志的设置if (list.size() != pageSize) {mySwipeRefresh.setHasMoreDate(false);}textDateAdapter.addBottomData(list);//未刷新状态mySwipeRefresh.setLoading(false);isRefreshing = false;}}}, 100);}private List<String> getRefreshDate() {//底部添加的数据集合List<String> list = new ArrayList<>();//大于90.模拟后台数据少于分页加载时候每一页的数据int num = new Random().nextInt(100);if (num > 80) {for (int i = 0; i < 10; i++) {list.add("最后一页数据,少于20:" + i);}return list;}for (int i = 0; i < 20; i++) {list.add("分页加载数据:" + i);}return list;}private List<String> getUpLoadDate() {//底部添加的数据集合List<String> list = new ArrayList<>();//大于90.模拟后台数据少于分页加载时候每一页的数据int num = new Random().nextInt(100);if (num > 80) {for (int i = 0; i < 10; i++) {list.add("最后一页数据,少于20:" + i);}return list;}for (int i = 0; i < 20; i++) {list.add("分页加载数据:" + i);}return list;}
}

4.xml布局使用

<?xml version="1.0" encoding="utf-8"?>
<com.bihucj.mcandroid.view.MySwipeRefresh 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"android:orientation="vertical"android:id="@+id/refresh"tools:context="com.bihucj.mcandroid.ui.MainActivity"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv"android:layout_width="match_parent"android:layout_height="match_parent" /></com.bihucj.mcandroid.view.MySwipeRefresh>

5.适配器和单行布局

public class TextDateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {private List<String> mList;private Context context;public TextDateAdapter(Context context, List<String> list) {this.context = context;this.mList = list;}public void clearData() {if (mList.size() > 0 && mList != null) {mList.clear();notifyDataSetChanged();}}public void addBottomData(List<String> list) {if (list.size() > 0 && list != null) {mList.addAll(list);notifyDataSetChanged();}}public int getListSize() {return (mList == null) ? 0 : mList.size();}public void refreshData(List<String> list) {if (list.size() > 0 && mList != null) {mList.clear();mList.addAll(list);notifyDataSetChanged();}}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.singleitem_text_date, null);return new ImgsViewHolder(view);}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {holder.setIsRecyclable(false);if (holder instanceof ImgsViewHolder) {TextView tv_text = ((ImgsViewHolder) holder).tv_text;tv_text.setHeight(50);tv_text.setGravity(Gravity.CENTER);tv_text.setText(mList.get(position));}}@Overridepublic int getItemCount() {return mList.size();}private class ImgsViewHolder extends RecyclerView.ViewHolder {private TextView tv_text;public ImgsViewHolder(View itemView) {super(itemView);tv_text = itemView.findViewById(R.id.tv_text);}}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="50dp"><TextViewandroid:id="@+id/tv_text"android:textColor="#00aaff"android:textSize="@dimen/_15dp"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center"android:text="asdas"android:textStyle="bold" /></LinearLayout>

 

  相关解决方案