SwipeRefreshLayout详解和自定义上拉加载更多
2016-09-28 13:36 阅读(248)

个人主页
演示Demo下载


本文重点介绍了SwipeRefreshLayout的使用和自定View继承SwipeRefreshLayout添加上拉加载更多的功能。

一、SwipeRefreshLayout简单介绍

二、SwipeRefreshLayout主要方法介绍

翻看官方的文档,可以看到方法有很多,这里只介绍五个经常用到的方法。

三、SwipeRefreshLayout的基本使用

3.1 设置布局

<!--使用谷歌官方的下拉刷新组件,只有下拉刷新功能-->
  <!--
      <android.support.v4.widget.SwipeRefreshLayout
          android:id="@+id/srl"
          android:layout_width="match_parent"
          android:layout_height="match_parent">

          <ListView
              android:id="@+id/lv"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>

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

3.2 在代码中使用

// 不能在onCreate中设置,这个表示当前是刷新状态,如果一进来就是刷新状态,SwipeRefreshLayout会屏蔽掉下拉事件
  //swipeRefreshLayout.setRefreshing(true);

  // 设置颜色属性的时候一定要注意是引用了资源文件还是直接设置16进制的颜色,因为都是int值容易搞混
  // 设置下拉进度的背景颜色,默认就是白色的
  swipeRefreshView.setProgressBackgroundColorSchemeResource(android.R.color.white);
  // 设置下拉进度的主题颜色
  swipeRefreshView.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark);

  // 下拉时触发SwipeRefreshLayout的下拉动画,动画完毕之后就会回调这个方法
  swipeRefreshView.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
      @Override
      public void onRefresh() {

          // 开始刷新,设置当前为刷新状态
          //swipeRefreshLayout.setRefreshing(true);

          // 这里是主线程
          // 一些比较耗时的操作,比如联网获取数据,需要放到子线程去执行
          // TODO 获取数据
          final Random random = new Random();
          new Handler().postDelayed(new Runnable() {
              @Override
              public void run() {
                  mList.add(0, "我是天才" + random.nextInt(100) + "号");
                  mAdapter.notifyDataSetChanged();

                  Toast.makeText(MainActivity.this, "刷新了一条数据", Toast.LENGTH_SHORT).show();

                  // 加载完数据设置为不刷新状态,将下拉进度收起来
                  swipeRefreshView.setRefreshing(false);
              }
          }, 1200);

          // System.out.println(Thread.currentThread().getName());

          // 这个不能写在外边,不然会直接收起来
          //swipeRefreshLayout.setRefreshing(false);
      }
  });

四、自定义View继承SwipeRefreshLayout,添加上拉加载更多功能

由于谷歌并没有提供上拉加载更多的布局,所以我们只能自己去定义布局实现这个功能。

这里通过自定义View继承SwipeRefreshLayout容器,然后添加上拉加载更多的功能。

4.1 定义View继承SwipeRefreshLayout,添加上拉加载功能

代码中的注释比较详细,这里就不一一解释了,说一下大概的实现思路,主要分为四步。

4.1.1 获取子控件ListView

// 获取ListView,设置ListView的布局位置
  if (mListView == null) {
      // 判断容器有多少个孩子
      if (getChildCount() > 0) {
          // 判断第一个孩子是不是ListView
          if (getChildAt(0) instanceof ListView) {
              // 创建ListView对象
              mListView = (ListView) getChildAt(0);

              // 设置ListView的滑动监听
              setListViewOnScroll();
          }
      }
  }

4.1.2 对ListView设置滑动监听

/**
   * 设置ListView的滑动监听
   */
  private void setListViewOnScroll() {

      mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(AbsListView view, int scrollState) {
              // 移动过程中判断时候能下拉加载更多
              if (canLoadMore()) {
                  // 加载数据
                  loadData();
              }
          }

          @Override
          public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

          }
      });
  }

4.1.3 处理SwipeRefreshView容器的分发事件

/**
   * 在分发事件的时候处理子控件的触摸事件
   *
   * @param ev
   * @return
   */
  private float mDownY, mUpY;
  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {

      switch (ev.getAction()) {
          case MotionEvent.ACTION_DOWN:
              // 移动的起点
              mDownY = ev.getY();
              break;
          case MotionEvent.ACTION_MOVE:
              // 移动过程中判断时候能下拉加载更多
              if (canLoadMore()) {
                  // 加载数据
                  loadData();
              }

              break;
          case MotionEvent.ACTION_UP:
              // 移动的终点
              mUpY = getY();
              break;
      }
      return super.dispatchTouchEvent(ev);
  }

4.1.4 判断条件,满足就用回调去加载数据

/**
   * 判断是否满足加载更多条件
   *
   * @return
   */
  private boolean canLoadMore() {
      // 1. 是上拉状态
      boolean condition1 = (mDownY - mUpY) >= mScaledTouchSlop;
      if (condition1) {
          System.out.println("是上拉状态");
      }

      // 2. 当前页面可见的item是最后一个条目
      boolean condition2 = false;
      if (mListView != null && mListView.getAdapter() != null) {
          condition2 = mListView.getLastVisiblePosition() == (mListView.getAdapter().getCount() - 1);
      }

      if (condition2) {
          System.out.println("是最后一个条目");
      }
      // 3. 正在加载状态
      boolean condition3 = !isLoading;
      if (condition3) {
          System.out.println("不是正在加载状态");
      }
      return condition1 && condition2 && condition3;
  }

  /**
   * 处理加载数据的逻辑
   */
  private void loadData() {
      System.out.println("加载数据...");
      if (mOnLoadListener != null) {
          // 设置加载状态,让布局显示出来
          setLoading(true);
          mOnLoadListener.onLoad();
      }

  }

  /**
   * 设置加载状态,是否加载传入boolean值进行判断
   *
   * @param loading
   */
  public void setLoading(boolean loading) {
      // 修改当前的状态
      isLoading = loading;
      if (isLoading) {
          // 显示布局
          mListView.addFooterView(mFooterView);
      } else {
          // 隐藏布局
          mListView.removeFooterView(mFooterView);

          // 重置滑动的坐标
          mDownY = 0;
          mUpY = 0;
      }
  }

4.2 使用自定义View

4.2.1. 书写布局

<!--自定义View实现SwipeRefreshLayout,添加上拉加载更多的功能-->
  <com.pinger.swiperefreshdemo.view.SwipeRefreshView
      android:id="@+id/srl"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <ListView
          android:id="@+id/lv"
          android:layout_width="match_parent"
          android:layout_height="match_parent"/>

  </com.pinger.swiperefreshdemo.view.SwipeRefreshView>

4.2.2. 在代码中使用

// 设置下拉加载更多
  swipeRefreshView.setOnLoadListener(new SwipeRefreshView.OnLoadListener() {
      @Override
      public void onLoad() {
          new Handler().postDelayed(new Runnable() {
              @Override
              public void run() {

                  // 添加数据
                  for (int i = 30; i < 35; i++) {
                      mList.add("我是天才" + i+ "号");
                      // 这里要放在里面刷新,放在外面会导致刷新的进度条卡住
                      mAdapter.notifyDataSetChanged();
                  }

                  Toast.makeText(MainActivity.this, "加载了" + 5 + "条数据", Toast.LENGTH_SHORT).show();

                  // 加载完数据设置为不加载状态,将加载进度收起来
                  swipeRefreshView.setLoading(false);
              }
          }, 1200);
      }
  });

个人主页
演示Demo下载

以上纯属于个人平时工作和学习的一些总结分享,如果有什么错误欢迎随时指出,大家可以讨论一起进步。