欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android教你打造独一无二的上拉下拉刷新加载框架

程序员文章站 2023-12-02 14:52:40
其实早在去年七月,群里小伙伴就有让我共享这个。但我当时绝的技术不纯熟。代码有bug什么的。没有写出来。现在感觉整理的差不多了。就写出来让大家看看,有问题一起讨论解决。 说...

其实早在去年七月,群里小伙伴就有让我共享这个。但我当时绝的技术不纯熟。代码有bug什么的。没有写出来。现在感觉整理的差不多了。就写出来让大家看看,有问题一起讨论解决。

说到刷新加载,我们第一个想到啥,对了就是swiperefreshlayout,还有什么superswiperefreshlayout,xrecyclerview等等。反正老多了,我还是之前那句话,不管用什么,我们需要知道他的原理。

打造框架开始

对于刷新加载的实现,你们第一个想到的是什么?是用swiperefresh然后在recyclerview底部加一个不同type?还是用事件拦截呢?那必须是事件拦截啊,虽然现在swiperefreshlayout很火,基本很多app都能看到他。但是遇到那种坑比公司说刷新要用自己公司logo你也没辙。对把。。好了,感觉得罪了好多公司,不管他,我们继续。

我们先看下我们的效果图:

android教你打造独一无二的上拉下拉刷新加载框架

老铁,没毛病。下面我介绍如何实现的。

下拉刷新

首先我们给出如下几个参数,后面要用:

  private nestedscrollingparenthelper helper = null;
  private boolean isrefresh = true;
  private boolean isload = true;
  //滑动的总距离
  private int totaly = 0;
  private linearlayout headerlayout = null;
  private myrecyclerview myrecyclerview = null;
  private linearlayout footerlayout = null;

既然是刷新,我们的滚动肯定是在父view之前的。所以我们需要在onnestedprescroll这个方法里面写上我们所需要改动的x,y值。

我们需要用父view去拦截它。

我们需要判断dy的值是否大于0,因为大于0是刷新操作,小于0是加载操作。然后我们需要判断recyclerview是否是纵向的而不是横向的。

public void onnestedprescroll(view target, int dx, int dy, int[] consumed) {
    if (isrefresh) {
      if (dy > 0) {
        if (myrecyclerview.isorientation(0)) {
          totaly += dy;
          if ((totaly / 2) <= 0) {
            scrollto(0, totaly / 2);
            consumed[1] = dy;
          } else {
            scrollto(0, 0);
            consumed[1] = 0;
          }
        }
        return;
      }
    }

上拉加载

上面我也说了onnestedprescroll这个方法中判断dy<0才是加载操作。所以综上所述,代码变成了这样:

 public void onnestedprescroll(view target, int dx, int dy, int[] consumed) {
    if (totaly < 0 && myrecyclerview.isorientation(0) || totaly > 0 && myrecyclerview.isorientation(1)) {
      isfling = true;
    }
    if (isrefresh) {
      if (dy > 0) {
        if (myrecyclerview.isorientation(0)) {
          totaly += dy;
          if ((totaly / 2) <= 0) {
            scrollto(0, totaly / 2);
            consumed[1] = dy;
          } else {
            scrollto(0, 0);
            consumed[1] = 0;
          }
        }
        return;
      }
    }
    if (isload) {
      if (dy < 0) {
        if (myrecyclerview.isorientation(1)) {
          totaly += dy;
          if ((totaly / 2) >= 0) {
            scrollto(0, totaly / 2);
            consumed[1] = dy;
          } else {
            scrollto(0, 0);
            consumed[1] = 0;
          }
        }
        return;
      }
    }
  }

最后我们需要在子view滑动结束后,实行如下操作:

 //子view滑动结束调用
  //dyunconsumed < 0 向下滚
  //dyunconsumed > 0 向上滚
  public void onnestedscroll(view target, int dxconsumed, int dyconsumed, int dxunconsumed, int dyunconsumed) {
    if (dyunconsumed != 0) {
      totaly += dyunconsumed;
      scrollto(0, totaly / 2);
    }
  }

其实最主要的两个方法已经解决了,其他到没什么了,这边,我把nestedscrolling的8个接口的功能和自定义recyclerview放出来。已变大家参考。希望大家都能实现自己的刷新加载。告别swiperefreshlayout。

添加header和footer

这里我们参考listview自带的addheaderview和addfooterview。代码如下:

 public void addheaderview(view headerview, int headerheight) {
    this.headerlayout.removeallviews();
    this.headerlayout.addview(headerview);
    layoutparams layoutparams = new layoutparams(layoutparams.match_parent, headerheight);
    layoutparams.topmargin = -headerheight;
    this.headerlayout.setlayoutparams(layoutparams);
  }

  public void addfooterview(view footerview, int footerheight) {
    this.footerlayout.removeallviews();
    this.footerlayout.addview(footerview);
    this.footerlayout.setlayoutparams(new layoutparams(layoutparams.match_parent, footerheight));
  }

几个接口的实现

 public boolean onstartnestedscroll(view child, view target, int nestedscrollaxes) {
    return true;
  }

  public void onnestedscrollaccepted(view child, view target, int axes) {
    helper.onnestedscrollaccepted(child, target, axes);
  }

  //父view拦截子view的滚动
  public void onnestedprescroll(view target, int dx, int dy, int[] consumed) {
    if (totaly < 0 && myrecyclerview.isorientation(0) || totaly > 0 && myrecyclerview.isorientation(1)) {
      isfling = true;
    }
    if (isrefresh) {
      if (dy > 0) {
        if (myrecyclerview.isorientation(0)) {
          totaly += dy;
          if ((totaly / 2) <= 0) {
            scrollto(0, totaly / 2);
            consumed[1] = dy;
          } else {
            scrollto(0, 0);
            consumed[1] = 0;
          }
        }
        return;
      }
    }
    if (isload) {
      if (dy < 0) {
        if (myrecyclerview.isorientation(1)) {
          totaly += dy;
          if ((totaly / 2) >= 0) {
            scrollto(0, totaly / 2);
            consumed[1] = dy;
          } else {
            scrollto(0, 0);
            consumed[1] = 0;
          }
        }
        return;
      }
    }
  }

  //子view滑动结束调用
  //dyunconsumed < 0 向下滚
  //dyunconsumed > 0 向上滚
  public void onnestedscroll(view target, int dxconsumed, int dyconsumed, int dxunconsumed, int dyunconsumed) {
    if (dyunconsumed != 0) {
      totaly += dyunconsumed;
      scrollto(0, totaly / 2);
    }
  }

  public void onstopnestedscroll(view child) {
    helper.onstopnestedscroll(child);
    if (ontouchuplistener != null) {
      isfling = false;
      ontouchuplistener.touchup();
    }
  }

  public boolean onnestedfling(view target, float velocityx, float velocityy, boolean consumed) {
    return isfling;
  }

  public boolean onnestedprefling(view target, float velocityx, float velocityy) {
    return isfling;
  }

  public int getnestedscrollaxes() {
    return helper.getnestedscrollaxes();
  }

自定义recyclerview

既然是自己写的刷新加载框架,总不能还有自定义layout中在放个recyclerview。多麻烦,自定义一个,直接放在里面,然后分别放个header和footer 就没必要每次有页面用到刷新都要写一个布局。3个布局解决整个项目的刷新和加载。话不多说,代码如下:

  private class myrecyclerview extends recyclerview {
    private staggeredgridlayoutmanager staggeredgridlayoutmanager = null;
    private linearlayoutmanager linearlayoutmanager = null;
    private gridlayoutmanager gridlayoutmanager = null;
    private boolean isscrollload = false;
    private boolean isscrollrefresh = false;

    public myrecyclerview(context context) {
      super(context);
      setverticalfadingedgeenabled(false);
      sethorizontalfadingedgeenabled(false);
      setverticalscrollbarenabled(false);
      sethorizontalscrollbarenabled(false);
      setoverscrollmode(over_scroll_never);
      setitemanimator(new defaultitemanimator());
    }

    private void setmylayoutmanager(layoutmanager layoutmanager) {
      if (layoutmanager instanceof staggeredgridlayoutmanager) {
        staggeredgridlayoutmanager = (staggeredgridlayoutmanager) layoutmanager;
      } else if (layoutmanager instanceof gridlayoutmanager) {
        gridlayoutmanager = (gridlayoutmanager) layoutmanager;
      } else if (layoutmanager instanceof linearlayoutmanager) {
        linearlayoutmanager = (linearlayoutmanager) layoutmanager;
      }
      setlayoutmanager(layoutmanager);
      if (!isvertical()) {
        throw new nullpointerexception("vertical!");
      }
    }

    private boolean isorientation(int orientation) {//orientation,0代表向下,1代表向上
      if (orientation == 0)
        return iscanpulldown();
      else if (orientation == 1)
        return iscanpullup();
      return false;
    }

    private boolean iscanpulldown() {
      return !canscrollvertically(-1);
    }

    private boolean iscanpullup() {
      return !canscrollvertically(1);
    }

//    private int scrollload() {
//      int lastitem = 0;
//      int itemcount = 0;
//      int spancount = 1;
//      if (staggeredgridlayoutmanager != null) {
//        lastitem = staggeredgridlayoutmanager.findlastvisibleitempositions(null)[0];
//        itemcount = staggeredgridlayoutmanager.getitemcount();
//        spancount = staggeredgridlayoutmanager.getspancount();
//      } else if (linearlayoutmanager != null) {
//        lastitem = linearlayoutmanager.findlastvisibleitemposition();
//        itemcount = linearlayoutmanager.getitemcount();
//        spancount = 1;
//      } else if (gridlayoutmanager != null) {
//        lastitem = gridlayoutmanager.findlastvisibleitemposition();
//        itemcount = gridlayoutmanager.getitemcount();
//        spancount = gridlayoutmanager.getspancount();
//      }
//      return ((itemcount - 1) / spancount + 1) - (lastitem / spancount + 1);
//    }

    private boolean isvertical() {
      if (staggeredgridlayoutmanager != null)
        return staggeredgridlayoutmanager.getorientation() == staggeredgridlayoutmanager.vertical;
      else if (linearlayoutmanager != null)
        return linearlayoutmanager.getorientation() == linearlayoutmanager.vertical;
      else if (gridlayoutmanager != null)
        return gridlayoutmanager.getorientation() == gridlayoutmanager.vertical;
      return false;
    }

//    public void onscrolled(int dx, int dy) {
//      if (dy > 0 && !isscrollload) {
//        if (olior != null) {
//          onscrolllistener.scrollload(sc````````
`
``
llload());//传递滚动到倒数第几行
//        }
//      }
//    }
  }

这样我们变实现了自己的刷新加载框架,代码我已上传:swpullrecyclerlayout_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。