Android下拉刷新和上拉加载更多

时间:2022-12-22 20:49:10

Android下拉刷新和上拉加载更多


下拉刷新

通过android系统提供的组件:SwipeRefreshLayout

一、基本使用
1 xml中 添加 SwipeRefreshLayout 组件
该组件包含着要操作下拉刷新的控件 如ListView RecyclerView 等
注意这里的SwipeRefreshLayout组件的子布局只能有一个

<android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_below="@+id/dividerimage0"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            >
        </android.support.v7.widget.RecyclerView>

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

2 java代码中初始化

 private SwipeRefreshLayout swipeRefreshLayout;

swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);

3 设置刷新时的颜色变化

  swipeRefreshLayout.setColorSchemeResources(
                R.color.colorPrimary,
                R.color.green,
                R.color.red
        );

4 所在Activity类也要implements SwipeRefreshLayout.OnRefreshListener


5 重写下拉刷新方法

@Override
    public void onRefresh() {
          // 网络请求
        okHttp.getHandler(handlerForGenJin);
        // 这里用sortWay变量 这样即使下拉刷新也能保持用户希望的排序方式
        askForOkHttp(sortWay);
    }

6 当网络请求完 获取并解析了数据,通知结束下拉刷新

  // 通知结束下拉刷新
  handlerForRefresh.sendEmptyMessage(0x93);

7

Handler handlerForRefresh = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x93: {
                    swipeRefreshLayout.setRefreshing(false);
                }
            }
        }
    };

8 开启监听
onCreate()中

swipeRefreshLayout.setOnRefreshListener(this);

问题:当下拉刷新后,会发现Item之间的间距增大,刷新一次增大一次
分析:因为你每次调用initView ,这样每次都会执行

  mRecyclerView.addItemDecoration(new MyDividerItemDecoration(
                    this, DividerItemDecoration.VERTICAL));

也就是每次都会加一次分隔线,因此要设置一个boolean变量的值,判断是否是第一次加载界面,是的话就加分隔线,否则就不加分隔线了

 if (IsFirstOnCreate) {
            mRecyclerView.addItemDecoration(new MyDividerItemDecoration(
                    this, DividerItemDecoration.VERTICAL));
            IsFirstOnCreate = false;
        }

从别的界面回传通知本界面执行下拉刷新
通过 intent 的 startActivityForResult方式去跳转 设置请求码回传码
在onActivityResult中:

    @Override
    public void onActivityResult(int requestCode,int resultCode,Intent data){
        super.onActivityResult(requestCode,resultCode,data);
        if(requestCode==0x05){
            if(resultCode==0x05){
                swipeRefreshLayout.setOnRefreshListener(this);
                swipeRefreshLayout.post(new Runnable() {
                    @Override
                    public void run() {
                        swipeRefreshLayout.setRefreshing(true);
                    }
                });
                this.onRefresh();
            }
        }
    }

上拉加载更多

主要是利用RecyclerView自带的ScrollListener去监听是否滑动到了底部

设置底部的Item样式

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">

    <TextView  android:id="@+id/foot_tips" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="15sp" android:padding="10dp" android:layout_marginBottom="1dp"/>

</LinearLayout>

Activity中

    private LinearLayoutManager mLayoutManager;               
    private static boolean hasMore = false; // 是否有下一页
    private static int currentPage ;
    // 若是上拉加载更多的网络请求 则不需要删除数据
    private boolean isLoadingMore = false;
    // 最后一个条目位置
    private static int lastVisibleItem = 0;

oncreate中

loadingMore();
// 初始currentPage为1
currentPage = 1;
// 网络请求
askForOKHttp(sortWay);

loadingMore()监听方法:

private void loadingMore(){
// 实现上拉加载重要步骤,设置滑动监听器,RecyclerView自带的ScrollListener
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
   @Override
   public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
       super.onScrollStateChanged(recyclerView, newState);
       if(!isLoadingMore){        // 若不是加载更多 才 加载
       // 在newState为滑到底部时
       if (newState == RecyclerView.SCROLL_STATE_IDLE) {
           // 如果没有隐藏footView,那么最后一个条目的位置(带数据)就比我们的getItemCount少1
           if (!mAdapter.isFadeTips() && lastVisibleItem + 1 == mAdapter.getItemCount()) {
               // 然后调用updateRecyclerview方法更新RecyclerView
               updateRecyclerView();
           }
           // 如果隐藏了提示条,我们又上拉加载时,那么最后一个条目(带数据)就要比getItemCount要少2
           if (mAdapter.isFadeTips() && lastVisibleItem + 2 == mAdapter.getItemCount()) {
               // 然后调用updateRecyclerview方法更新RecyclerView
               updateRecyclerView();    // 要调
           }
       }
     }  
   }
   //滚动监听
 @Override
   public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
       super.onScrolled(recyclerView, dx, dy);
       // 在滑动完成后,拿到最后一个可见的item的位置
       lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
   }

});

mLayoutManager要为 LinearLayoutManager 类型的

  // 上拉加载时调用的更新RecyclerView的方法
    private void updateRecyclerView() {
      if(hasMore){
          // 还有下一页 网络请求 第二页 第三页
          currentPage++;    // 加1
           isLoadingMore = true;
          askForOKHttp(sortWay);
     }

Adapter中:
声明:

    private int normalType = 0;     // 第一种ViewType,正常的item
    private int footType = 1;       // 第二种ViewType,底部的提示View
    private static boolean hasMore = true;   // 变量,是否有更多数据
    private boolean fadeTips = false; // 变量,是否隐藏了底部的提示

构造方法的形参 除了传入数据,增加一个hasMore变量 用于判断是否有更多

更新数据也是

    /** * 更新数据 */
    public void updateData(ArrayList<String> array0,
                            ArrayList<String> array1,ArrayList<String> array2,
                            ArrayList<String> array3,boolean mHasMore) {
        this.arrayList0 = array0;  
        this.arrayList5 = array1;   
        this.arrayList2 = array2;   
        this.arrayList3 = array3;   
        hasMore = mHasMore;
        notifyDataSetChanged();
    }

onCreateViewHolder 中: 设置不同的viewHolder (分数据item和底部foot item)

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   if (viewType == normalType) {
       ViewHolder viewHolder = null;
          // 实例化展示的view
         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_choose_customer, parent, false);
         // 实例化viewholder
          viewHolder = new ViewHolder(v, mItemClickListener);
       return viewHolder;
   }else {
       View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_foot_layout, parent, false);
       FootHolder footHolder = new FootHolder(v);
       return footHolder;
   }
}

onBindViewHolder中:

@Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        // 如果是正常的imte,直接设置TextView的值
        if (holder instanceof ViewHolder) {
           ((ViewHolder) holder).customerName.setText(arrayList0.get(position));
          ((ViewHolder) holder).qiangduo.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            mItemInnerDeleteListener.onItemInnerDeleteClick(position);
                        }
                    });
    }else {
            if (hasMore) {
                // 不隐藏footView提示
                fadeTips = false;
                if (arrayList0.size() > 0) {
                    // 如果查询数据发现增加之后,就显示正在加载更多
                    ((FootHolder) holder).tips.setVisibility(View.VISIBLE);
                    ((FootHolder) holder).tips.setText("正在加载更多...");
                }
            } else {
                if (arrayList0.size() > 0) {
                    // 如果查询数据发现并没有增加时,就显示没有更多数据了
                    ((FootHolder) holder).tips.setText("暂无更多数据");
                    // 然后通过主线程延时让这个提示消失,在1000ms后执行
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            // 隐藏提示条
                            ((FootHolder) holder).tips.setVisibility(View.GONE);
                            // 将fadeTips设置true
                            fadeTips = true;
                            // hasMore设为true是为了让再次拉到底时,会先显示正在加载更多
                            hasMore = true;
                        }
                    }, 1000);
                }
            }
        }
    }            
 @Override
    public int getItemCount() {
        // 获取item的数量 计上footView
        return arrayList0 == null ? 0 : arrayList0.size()+1;
    }

    // 自定义方法,获取数据的最后一个位置,不计上footView
    public int getRealLastPosition() {
        return arrayList0.size();
    }
// 根据条目位置返回ViewType,以供onCreateViewHolder方法内获取不同的Holder
    @Override
    public int getItemViewType(int position) {
        if (position == getItemCount() - 1) {
            return footType;
        } else {
            return normalType;
        }
    }
 // 暴露接口,改变fadeTips的方法
    public boolean isFadeTips() {
        return fadeTips;
    }
   // 正常item的ViewHolder,用以缓存findView操作
     class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
      TextView XXX XXX XXX ;
        private MyItemClickListener mListener;
        ...
        ...
        }
   // 底部footView的ViewHolder,用以缓存findView操作
    class FootHolder extends RecyclerView.ViewHolder {
        private TextView tips;

        FootHolder(View itemView) {
            super(itemView);
            tips = itemView.findViewById(R.id.foot_tips);
        }
    }

也可以使用第三方库实现上拉加载 下拉刷新

参考:https://github.com/scwang90/SmartRefreshLayout

上部和下部的会隐藏 然后下拉或者上拉 都会显示 然后过一定时间又会收回

和RecyclerView结合,则会有默认的一个 刷新样式

包括RecyclerView和其他组件,recyclerView控件上方/下方的组件 会作为 刷新的控件,设置控件的动画样式即为刷新或者加载的动画样式

只能设置3个子View 否则报错
Caused by: java.lang.RuntimeException: 最多只支持3个子View,Most only support three sub view
如:

<com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:id="@+id/smartRefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <ImageView
            android:id="@+id/imageView0"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            />

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        </android.support.v7.widget.RecyclerView>

        <com.scwang.smartrefresh.layout.footer.BallPulseFooter
            android:layout_width="match_parent"
            android:layout_height="50dp"/>

    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

待处理:限制下拉和上拉加载的高度限制


有三种添加的方式:

1 在 App extends Application的类中
static静态代码块中
优先级最低
2 在XML中

<com.scwang.smartrefresh.layout.SmartRefreshLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

使用 提供的 一些 好看的效果
如上方的

<com.scwang.smartrefresh.layout.footer.BallPulseFooter

也可以自己设置为自定义的 组件
如上方的

 <ImageView

java中开启帧动画

 ImageView imageView = findViewById(R.id.imageView0);
        imageView.setImageResource(R.drawable.run_animation_list);
        AnimationDrawable animationDrawable = (AnimationDrawable) imageView.getDrawable();
        animationDrawable.start();

3 java代码设置

final RefreshLayout refreshLayout = (RefreshLayout) findViewById(R.id.refreshLayout);
//设置 Header 为 贝塞尔雷达 样式
refreshLayout.setRefreshHeader(new BezierRadarHeader(this).setEnableHorizontalDrag(true));
//设置 Footer 为 球脉冲 样式
refreshLayout.setRefreshFooter(new BallPulseFooter(this).setSpinnerStyle(SpinnerStyle.Scale));