当前位置: 移动技术网 > IT编程>移动开发>Android > Android自定义下拉刷新控件RefreshableView

Android自定义下拉刷新控件RefreshableView

2019年07月24日  | 移动技术网IT编程  | 我要评论

dodge是什么车,绝心冷后,eight relovery

这是在了解下拉刷新功能原理下的产物,下拉刷新可以说是国产app里面必有的功能,连google都为此出了swiperefreshlayout,一种md风格的下拉刷新。
不过,md风格在国内似乎很是艰难,不单单是国内系统主流仍是4.4的原因,也有用户习惯的问题,扯的有点多了,在看了许多博客之后,我突然想写一个能仿照 swiperefreshlayout 的兼容所有控件的下拉刷新,不单单只是 listview,希望它也可以包容普通的view和scrollview,经过两天的奋斗,终于搞定了,因为我的目的只是想要下拉刷新,所以功能很少,不过,如果能把下拉刷新搞定了,其它的功能,就可以慢慢堆砌起来了。

新系统的原因,无法给出合适的gif,只能截图了:

第一张图片展示的是textview:

第二章图片展示的是listview:

第三章图片展示的是scrollview:

基本上这就是我测试的成功的控件,兼容普通的view是最简单的,复杂一点的就是listview和scrollview。

思路:我的思路和大部分博客都是一样的,自定义一个viewgroup,然后将包含的要拖拽的控件的touch事件交给 refreshableview 处理,动态改变 headview 的 margintop 的值,以上。当然,这其中会有一些细节要注意,比如 ontouch 方法的返回值的处理,还有子 view 的 margintop 值的处理。

源码

先是headview的布局文件:

<?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="wrap_content"
 android:gravity="center"
 android:orientation="horizontal">


 <imageview
 android:id="@+id/imageview_down"
 android:layout_width="14dp"
 android:layout_height="24dp"
 android:padding="2dp"
 android:src="@drawable/svg_down" />

 <progressbar
 android:visibility="gone"
 android:id="@+id/progressbar"
 style="?android:attr/progressbarstyle"
 android:layout_width="20dp"
 android:layout_height="20dp"
 android:progressdrawable="@drawable/progressbar" />

 <textview
 android:padding="15dp"
 android:id="@+id/textview"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="@string/pull_to_refresh"
 android:textsize="16sp" />

</linearlayout>

接下来就是至关重要的类 refreshableview:

package com.pull2refresh;

import android.animation.objectanimator;
import android.animation.valueanimator;
import android.annotation.targetapi;
import android.content.context;
import android.os.build;
import android.util.attributeset;
import android.util.log;
import android.view.layoutinflater;
import android.view.motionevent;
import android.view.view;
import android.view.viewconfiguration;
import android.view.viewgroup;
import android.view.animation.rotateanimation;
import android.widget.imageview;
import android.widget.linearlayout;
import android.widget.progressbar;
import android.widget.textview;

import shike.xianrou.com.pull2refresh.r;

/**
 * created by cjh on 16-9-6.
 */
public class refreshableview extends linearlayout implements view.ontouchlistener {

 private static final string tag = "refreshableview";

 private static final int refreshing = 0;//正在刷新
 private static final int original = refreshing + 1;//初始状态
 private static final int release_to_refreshing = original + 1;//释放即将刷新的状态

 private int current_status = original;//当前最新状态

 private linearlayout headview;//刷新layout
 private textview textview;//刷新layout中的文字提示
 private imageview imageview;//刷新layout中的箭头
 private progressbar progressbar;//刷新layout中的进度条

 private view view;//手指控制的下拉的view

 private int hideheight;//刷新layout要隐藏的高度

 private boolean isablepull;//是否可以下拉,例如当 current_status = refreshiing 时是不可以下拉拖拽的

 private float ydown;//手指按下的坐标

 private int touchslop = viewconfiguration.get(getcontext()).getscaledtouchslop();//界限值,防止手指误触,过于灵敏

 private boolean firstlayout = true;//第一次调用onlayout的时候置为false

 private int maxmargintop;//刷新layout能拉下的最大距离

 private marginlayoutparams marginlayoutparams;//刷新layout的marginlayoutparams

 private string pull_to_refresh = "下拉可以刷新";

 private string release_to_refresh = "释放立即刷新";

 private string refreshing = "正在刷新…";


 private int original_margin = 0;//针对下拉的view存在margintop这中特殊值的处理

 public interface pulltorefreshlistener {

 void onrefresh();
 }

 private pulltorefreshlistener pulltorefreshlistener;

 public void addpulltorefreshlistener(pulltorefreshlistener pulltorefreshlistener) {
 this.pulltorefreshlistener = pulltorefreshlistener;
 }

 public refreshableview(context context) {
 super(context);
 init();
 }

 public refreshableview(context context, attributeset attrs) {
 super(context, attrs);
 init();
 }

 public refreshableview(context context, attributeset attrs, int defstyleattr) {
 super(context, attrs, defstyleattr);
 init();
 }

 @targetapi(build.version_codes.lollipop)
 public refreshableview(context context, attributeset attrs, int defstyleattr, int defstyleres) {
 super(context, attrs, defstyleattr, defstyleres);
 init();
 }

 private void init() {
 headview = (linearlayout) layoutinflater.from(getcontext()).inflate(r.layout.refresh_layout, null, true);
 imageview = (imageview) headview.findviewbyid(r.id.imageview_down);
 progressbar = (progressbar) headview.findviewbyid(r.id.progressbar);
 textview = (textview) headview.findviewbyid(r.id.textview);
 progressbar.setvisibility(view.gone);
 setorientation(vertical);
 addview(headview, 0);
 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
 super.onlayout(changed, l, t, r, b);
 log.d(tag, "onlayout");
 if (changed && firstlayout) {
  //将view的touch时间的处理交给refreshableview去处理
  view = getchildat(1);
  view.setontouchlistener(this);

  //刷新layout的 margintop 的最大值设为刷新头的高度
  maxmargintop = headview.getheight();

  //要将控件完全隐藏起来,那么隐藏的高度就设置为控件的高度
  hideheight = -headview.getheight();
  marginlayoutparams = (marginlayoutparams) headview.getlayoutparams();
  marginlayoutparams.topmargin = hideheight;
  headview.setlayoutparams(marginlayoutparams);

  //这里必须将firstlayout设置为false,否则在处理touch是件的过程中,headview在怎么调用setlayoutparams都会被置为初始的隐藏状态
  firstlayout = false;

  //如果子view是一个viewgroup 那么就需要计算出子view的margintop的值,因为如果margintop不为0,那么子view的y轴坐标和父view的坐标是不一样的
  if (view instanceof viewgroup) {
  int[] childlocations = new int[2];
  int[] viewlocations = new int[2];
  view.getlocationonscreen(viewlocations);
  ((viewgroup) view).getchildat(0).getlocationonscreen(childlocations);
  original_margin = childlocations[1] - viewlocations[1];
  log.d(tag, "onlayout viewlocations[1] " + viewlocations[1]);
  log.d(tag, "onlayout locations[1] " + childlocations[1]);
  log.d(tag, "onlayout original_margin " + original_margin);
  }
 }
 }

 @override
 public boolean ontouch(view view, motionevent motionevent) {
 if (pulltorefreshlistener != null) {
  isablepull();
  if (isablepull) {
  switch (motionevent.getaction()) {
   case motionevent.action_down:
   ydown = motionevent.getrawy();
   break;

   case motionevent.action_move:
   float ymove = motionevent.getrawy();
   float distance = ymove - ydown;

   //如果手势是向上的,并且手势在y轴的移动距离小于界限值,那么就不处理
   if (distance < 0 || math.abs(distance) < touchslop)
    return false;

   //margintop的距离是手势距离的1/2,形成费力延迟的效果
   marginlayoutparams.topmargin = (int) (distance / 2 + hideheight);
   log.d(tag, "topmargin " + marginlayoutparams.topmargin);

   //如果大于最大的margintop的值的时候,就将值置为 maxmargintop
   if (marginlayoutparams.topmargin >= maxmargintop)
    marginlayoutparams.topmargin = maxmargintop;


   if (marginlayoutparams.topmargin >= 0) {
    //当刷新头完全显示的时候,改变状态,置为 释放刷新的状态
    if (current_status != release_to_refreshing) {
    rotate(0, 180);
    textview.settext(release_to_refresh);
    }
    current_status = release_to_refreshing;
   } else {
    //否则就置为初始状态
    if (current_status != original) {
    rotate(180, 360);
    textview.settext(pull_to_refresh);
    }
    current_status = original;

   }
   headview.setlayoutparams(marginlayoutparams);
   break;

   case motionevent.action_cancel:
   case motionevent.action_up:
   float yup = motionevent.getrawy();
   float dis = yup - ydown;
   if (dis > 0 && math.abs(dis) > touchslop)
    switch (current_status) {
    //释放刷新
    case release_to_refreshing:
     animatemargintop(marginlayoutparams.topmargin, 0);
     imageview.clearanimation();
     imageview.setvisibility(view.gone);
     progressbar.setvisibility(view.visible);
     textview.settext(refreshing);
     pulltorefreshlistener.onrefresh();
     break;

    //初始化
    case original:
     reset();
     //从当前的margintop的值,转变到隐藏所需的 margintop
     animatemargintop(marginlayoutparams.topmargin, hideheight);
     break;
    }
   else
    return false;

   break;
  }
  return true;
  }
 }
 return false;
 }

 /**
 * 判断是否可以下拉
 *
 * @return
 */
 public boolean isablepull() {
 //统一判断,其实主要是对于view是普通view的判断
 if (current_status != refreshing)
  isablepull = true;
 else
  isablepull = false;

 if (view instanceof viewgroup) {
  isablepull = false;
  view childview = ((viewgroup) view).getchildat(0);
  int[] viewlocations = new int[2];
  int[] childviewlocations = new int[2];
  view.getlocationonscreen(viewlocations);
  childview.getlocationonscreen(childviewlocations);

  //这一步中的 original_margin 至关重要,就是用来兼容 子view 有margintop属性的值,当childview 的y轴坐标 和 计算出了 margin 值后的父view坐标相等时,说明此时处于可下拉的状态
  if (viewlocations[1] + original_margin == childviewlocations[1])
  isablepull = true;
  else
  isablepull = false;
 }

 return isablepull;
 }

 private void rotate(int from, int to) {
 rotateanimation rotateanimation = new rotateanimation(from, to, imageview.getwidth() / 2, imageview.getheight() / 2);
 rotateanimation.setduration(100);
 rotateanimation.setfillafter(true);
 imageview.startanimation(rotateanimation);
 }

 private void animatemargintop(int from, int to) {
 objectanimator objectanimator = objectanimator.ofint(headview, "cjh", from, to);
 objectanimator.setduration(300);
 objectanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
  @override
  public void onanimationupdate(valueanimator valueanimator) {
  int margin = (int) valueanimator.getanimatedvalue();
  marginlayoutparams.topmargin = margin;
  headview.setlayoutparams(marginlayoutparams);
  }
 });
 objectanimator.start();
 }

 public void complete() {
 animatemargintop(0, hideheight);
 reset();
 }

 private void reset() {
 rotate(180, 360);
 textview.settext(pull_to_refresh);
 imageview.setvisibility(view.visible);
 progressbar.setvisibility(view.gone);
 }

}

使用:

 <com.pull2refresh.refreshableview
 android:id="@+id/refreshableview"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:gravity="center">

 <com.pull2refresh.mtextview
  android:id="@+id/mtextview"
  android:layout_width="match_parent"
  android:layout_height="100dp"
  android:background="@color/colorprimary"
  android:gravity="center"
  android:text="hello world!"
  android:textsize="22sp" />

 </com.pull2refresh.refreshableview>

...
 refreshableview.addpulltorefreshlistener(new refreshableview.pulltorefreshlistener() {
  @override
  public void onrefresh() {
  setdata();
  }
 });
...
 private void setdata() {
 objectanimator objectanimator = objectanimator.offloat(textview, "text", 1f, 100f);
 objectanimator.setduration(3000);
 objectanimator.addupdatelistener(new valueanimator.animatorupdatelistener() {
  @override
  public void onanimationupdate(valueanimator valueanimator) {
  textview.settext((float) valueanimator.getanimatedvalue());
  }
 });
 objectanimator.addlistener(new animator.animatorlistener() {
  @override
  public void onanimationstart(animator animator) {

  }

  @override
  public void onanimationend(animator animator) {
  refreshableview.complete();
  }

  @override
  public void onanimationcancel(animator animator) {

  }

  @override
  public void onanimationrepeat(animator animator) {

  }
 });
 objectanimator.start();
 }

在我自定义的 refreshableview 中,如果不设置下拉的监听,就没有下拉的效果,也就是不支持下拉

源码下载:android下拉刷新控件

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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网