Android自定义ListView实现下拉刷新

Android自定义ListView实现下拉刷新,第1张

概述首先呈上效果图当今APP,哪个没有点滑动刷新功能,简直就太落伍了。正因为需求多,因此自然而然开源的也就多。但是若想引用开源库,则很麻烦,比如PullToRefreshView这个库,如果把开源代码都移植到项目中,这是件

首先呈上效果图


当今APP,哪个没有点滑动刷新功能,简直就太落伍了。正因为需求多,因此自然而然开源的也就多。但是若想引用开源库,则很麻烦,比如PullToRefreshVIEw这个库,如果把开源代码都移植到项目中,这是件很繁琐的事,如果用依赖功能的话,对于强迫症的我,又很不爽。现在也有各种自定义ListVIEw实现PullToRefreshListVIEw的控件,无非就是在header加入一个控件,通过setpadding的方式来改变显示效果。效果已经太out了,如意中发现Google自带的swiperefreshlayout实现的效果挺不错,但是我发现这个控件在部分手机上的效果不一样,估计和v7包相关。因此就有了这篇文章自定义这个喜欢的效果。
 首先大概描述一下实现原理: 
1、重写ListVIEw的ontouchEvent,在方法中根据手指滑动的距离与临界值判断,决定当前的状态,分为四个状态:RELEASE_TO_REFRESH、PulL_TO_REFRESH、REFRESHING、DONE四个状态,分别代表释放刷新、拉动刷新、正在刷新、默认状态。 
2、重写ListVIEw的onDraw方法,根据不同的状态值,显示不同的图形表示。 
3、根据滑动距离不同,显示不同的透明度、圆弧角度值、整体图形的坐标等等。 
4、图形的变化分为两种:1、手动触发,滑动一点距离就更新一点坐标。比如PulL_TO_REFRESH状态,适合在ontouchEvent中的ACTION_MOVE中触发。2、动画自动触发,比如REFRESHING状态和DONE状态,适合在ontouchEvent中的ACTION_UP方法中触发,手指一松开就自动触发动画效果。 
5、必须在设置了刷新监听器才可以滑动,否则就是一个普通的ListVIEw。

代码很简单,只有两个文件,并且有很详细的注释:
PullToRefreshListVIEw类:

 package cc.wxf.vIEw.pull; import androID.content.Context;import androID.graphics.Canvas;import androID.util.AttributeSet;import androID.vIEw.MotionEvent;import androID.vIEw.VIEw;import androID.Widget.AbsListVIEw;import androID.Widget.ListVIEw; /** * Created by ccwxf on 2016/3/30. */public class PullToRefreshListVIEw extends ListVIEw implements AbsListVIEw.OnScrollListener {   public final static int RELEASE_TO_REFRESH = 0;  public final static int PulL_TO_REFRESH = 1;  public final static int REFRESHING = 2;  public final static int DONE = 3;   // 达到刷新条件的滑动距离  public final static int touch_SLOP = 160;  // 判断是否记录了最开始按下时的Y坐标  private boolean isRecored;  // 记录最开始按下时的Y坐标  private int startY;  // ListVIEw第一个Item  private int firstItemIndex;  // 当前状态  private int state;  // 是否可刷新,只有设置了监听器才能刷新  private boolean isRefreshable;  // 刷新标记  private PullMark mark;   private OnRefreshListener refreshListener;  private OnScrollButtomListener scrollButtomListener;   public PullToRefreshListVIEw(Context context) {    super(context);    init(context);  }   public PullToRefreshListVIEw(Context context,AttributeSet attrs) {    super(context,attrs);    init(context);  }   private voID init(Context context) {    //关闭硬件加速,否则PullMark的阴影不会出现    setLayerType(VIEw.LAYER_TYPE_SOFTWARE,null);    setonScrollListener(this);    mark = new PullMark(this);    state = DONE;    isRefreshable = false;  }   @OverrIDe  public voID onScrollStateChanged(AbsListVIEw vIEw,int scrollState) {    if (scrollButtomListener != null) {      if (scrollState == OnScrollListener.SCRolL_STATE_IDLE) {        if (vIEw.getLastVisibleposition() == vIEw.getAdapter().getCount() - 1) {          scrollButtomListener.onScrollToButtom();        }      }    }  }   @OverrIDe  public voID onScroll(AbsListVIEw vIEw,int firstVisibleItem,int visibleItemCount,int totalitemCount) {    firstItemIndex = firstVisibleItem;  }   @OverrIDe  protected voID onDraw(Canvas canvas) {    super.onDraw(canvas);    mark.onDraw(canvas);  }   @OverrIDe  protected voID onMeasure(int wIDthMeasureSpec,int heightmeasureSpec) {    int wIDth = MeasureSpec.getSize(wIDthMeasureSpec);    mark.setCenterX(wIDth / 2);    super.onMeasure(wIDthMeasureSpec,heightmeasureSpec);  }   @OverrIDe  public boolean ontouchEvent(MotionEvent event) {    if (!isRefreshable) {      return super.ontouchEvent(event);    }    switch (event.getAction()) {      case MotionEvent.ACTION_DOWN:        handleActionDown(event);        break;       case MotionEvent.ACTION_UP:        handleActionUp();        break;       case MotionEvent.ACTION_MOVE:        handleActionMove(event);        break;      default:        break;    }    return super.ontouchEvent(event);  }   private voID handleActionMove(MotionEvent event) {    int tempY = (int) event.getY();     if (!isRecored && firstItemIndex == 0) {      isRecored = true;      startY = tempY;    }     if (state != REFRESHING && isRecored) {      if (state == RELEASE_TO_REFRESH) {        setSelection(0);        if ((tempY - startY < touch_SLOP) && (tempY - startY) > 0) {          state = PulL_TO_REFRESH;        }      }      if (state == PulL_TO_REFRESH) {        setSelection(0);        if (tempY - startY >= touch_SLOP) {          state = RELEASE_TO_REFRESH;        } else if (tempY - startY <= 0) {          state = DONE;        }      }       if (state == DONE) {        if (tempY - startY > 0) {          state = PulL_TO_REFRESH;        }      }      mark.change(state,tempY - startY);    }  }   private voID handleActionUp() {    if (state == PulL_TO_REFRESH) {      state = DONE;      mark.changeByAnimation(state);    } else if (state == RELEASE_TO_REFRESH) {      state = REFRESHING;      mark.changeByAnimation(state);      onRefresh();    }    isRecored = false;  }   private voID handleActionDown(MotionEvent event) {    if (firstItemIndex == 0 && !isRecored) {      isRecored = true;      startY = (int) event.getY();    }  }   private voID onRefresh() {    if (refreshListener != null) {      refreshListener.onRefresh();    }  }   public voID startRefresh() {    state = REFRESHING;    mark.changeByAnimation(state);    onRefresh();  }   public voID stopRefresh() {    state = DONE;    mark.changeByAnimation(state);  }   public voID setonRefreshListener(OnRefreshListener refreshListener) {    this.refreshListener = refreshListener;    isRefreshable = true;  }   /**   * 刷新监听器   */  public interface OnRefreshListener {    public voID onRefresh();  }   public voID setonScrollButtomListener(OnScrollButtomListener scrollButtomListener) {    this.scrollButtomListener = scrollButtomListener;  }   /**   * 滑动到最低端触发监听器   */  public interface OnScrollButtomListener {    public voID onScrollToButtom();  } }

刷新标志类:

 package cc.wxf.vIEw.pull; import androID.graphics.Canvas;import androID.graphics.color;import androID.graphics.Paint;import androID.graphics.RectF;import androID.os.Handler; /** * Created by ccwxf on 2016/3/30. */public class PullMark {  //背景面板的半径、颜色  private static final int RADIUS_PAN = 40;  private static final int color_PAN = color.parsecolor("#fafafa");  //面板阴影的半径、颜色  private static final int RADIUS_SHADOW = 5;  private static final int color_SHADOW = color.parsecolor("#d9d9d9");  //面板中间的圆弧的半径、颜色、粗度、开始绘制角度  private static final int RADIUS_ARROWS = 20;  private static final int color_ARROWS = color.GREEN;  private static final int BOUND_ARROWS = 6;  private static final int START_ANGLE = 0;  // 开始绘制角度的变化率、总体绘制角度、总体绘制透明度  private static final int RATIO_SATRT_ANGLE = 3;  private static final int ALL_ANGLE = 270;  private static final int ALL_Alpha = 255;  // 动画的高度渐变比率、时间刷新间隔  private static final float RATIO_touch_SLOP = 7f;  private static final long RATIO_ANIMATION_DURATION = 10;   private PullToRefreshListVIEw ListVIEw;  // 中点的X、Y坐标、初始隐藏时的Y坐标  private float doneCenterY = -(RADIUS_PAN + RADIUS_SHADOW) / 2;  private float centerX;  private float centerY = doneCenterY;  // 开始绘制的角度、需要绘制的角度、透明度  private int startAngle = START_ANGLE;  private int sweepAngle = startAngle;  private int Alpha;  // 弧度变化比率,根据总体高度与总体弧度角度的比例决定  private float radioAngle = ALL_ANGLE * 1.0f / PullToRefreshListVIEw.touch_SLOP;  // 透明度变化比率,根据总体高度与总体透明度的比例决定  private float radioAlpha = ALL_Alpha * 1.0f / PullToRefreshListVIEw.touch_SLOP;  // PullToRefreshListVIEw的状态  private int state;  // 当前手指滑动的距离  private float mtouchLength;  // 是否启动旋转动画  private boolean isRotateAnimation = false;  // 画笔  private Paint mPaint = new Paint(Paint.ANTI_AliAS_FLAG);  private Handler handler = new Handler();   public PullMark(PullToRefreshListVIEw ListVIEw) {    this.ListVIEw = ListVIEw;  }   /**   * 设置绘制的中点X坐标,在PullToRefreshListVIEw的onMeasure中实现   * @param centerX   */  public voID setCenterX(int centerX){    this.centerX = centerX;  }   /**   * 表示一次普通的数据变化,在ontouchEvent中的ACTION_MOVE中触发   * @param state   * @param mtouchLength   */  public voID change(int state,float mtouchLength){    this.state = state;    this.mtouchLength = mtouchLength;    // 改变绘制的Y坐标    centerY = doneCenterY + mtouchLength;    // 改变绘制的透明度    Alpha = (int) (mtouchLength * radioAlpha);    if(Alpha > ALL_Alpha){      Alpha = ALL_Alpha;    }else if(Alpha < 0){      Alpha = 0;    }    //改变绘制的起始角度    startAngle = startAngle + RATIO_SATRT_ANGLE;    if(startAngle >= 360){      startAngle = 0;    }    //改变绘制的弧度角度    sweepAngle = (int) (mtouchLength * radioAngle);    if(sweepAngle > ALL_ANGLE){      sweepAngle = ALL_ANGLE;    }else if(sweepAngle < 0){      sweepAngle = 0;    }    ListVIEw.invalIDate();  }   /**   * 表示一次动画的变化,在ontouchEvent的ACTION_UP中或者手动startRefresh以及手动stopRefresh中触发   * @param state   */  public voID changeByAnimation(final int state){    this.state = state;    if(state == PullToRefreshListVIEw.DONE){      //结束旋转动画(关闭正在刷新的效果)      isRotateAnimation = false;    }    //慢慢变化到起始位置    handler.postDelayed(new RunnableMove(state),RATIO_ANIMATION_DURATION);  }   /**   * 启动移动的处理   */  public class RunnableMove implements Runnable{     private int state;    private int destination;    private float slop;     public RunnableMove(int state) {      this.state = state;      if(state == PullToRefreshListVIEw.DONE){        destination = 0;        slop = RATIO_touch_SLOP;      }else if(state == PullToRefreshListVIEw.REFRESHING){        destination = PullToRefreshListVIEw.touch_SLOP;        slop = RATIO_touch_SLOP * 5;      }    }     @OverrIDe    public voID run() {      if(mtouchLength > destination){        mtouchLength -= slop;        change(state,mtouchLength);        handler.postDelayed(this,RATIO_ANIMATION_DURATION);      }else{        if(state == PullToRefreshListVIEw.DONE){          // 直接将坐标初始化,否则会有一点点误差          centerY = doneCenterY;          ListVIEw.invalIDate();        }else if(state == PullToRefreshListVIEw.REFRESHING){          //启动旋转的动画效果          isRotateAnimation = true;          handler.postDelayed(new RunnableRotate(),RATIO_ANIMATION_DURATION);        }      }    }  }   /**   * 旋转动画的处理   */  public class RunnableRotate implements Runnable{     @OverrIDe    public voID run() {      if(isRotateAnimation){        //启动动画旋转效果        startAngle = startAngle + RATIO_SATRT_ANGLE;        if(startAngle >= 360){          startAngle = 0;        }        ListVIEw.invalIDate();        handler.postDelayed(this,RATIO_ANIMATION_DURATION);      }else{        //回到初始位置        handler.postDelayed(new RunnableMove(state),RATIO_ANIMATION_DURATION);      }    }  }   /**   * 绘制刷新图标的标志   * @param mCanvas   */  public voID onDraw(Canvas mCanvas){    //绘制背景圆盘和阴影    mPaint.setStyle(Paint.Style.FILL);    mPaint.setcolor(color_PAN);    mPaint.setShadowLayer(RADIUS_SHADOW,color_SHADOW);    mCanvas.drawCircle(centerX,centerY,RADIUS_PAN,mPaint);    //绘制圆弧    mPaint.setStyle(Paint.Style.stroke);    mPaint.setcolor(color_ARROWS);    mPaint.setstrokeWIDth(BOUND_ARROWS);    mPaint.setAlpha(Alpha);    mCanvas.drawArc(new RectF(centerX - RADIUS_ARROWS,centerY - RADIUS_ARROWS,centerX + RADIUS_ARROWS,centerY + RADIUS_ARROWS),startAngle,sweepAngle,false,mPaint);  }}

使用的时候,必须要设置了监听器才能有效的滑动: 

final PullToRefreshListVIEw ListVIEw = (PullToRefreshListVIEw) findVIEwByID(R.ID.ListVIEw);    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,androID.R.layout.simple_List_item_1,new String[]{      "测试1","测试2","测试3","测试4","测试5","测试6",});    ListVIEw.setAdapter(adapter);    ListVIEw.setonRefreshListener(new PullToRefreshListVIEw.OnRefreshListener() {      @OverrIDe      public voID onRefresh() {        new Handler().postDelayed(new Runnable() {          @OverrIDe          public voID run() {            ListVIEw.stopRefresh();          }        },2000);      }    });

 两个源代码文件就搞定了,demo工程就不提供了,很简单的。

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

总结

以上是内存溢出为你收集整理的Android自定义ListView实现下拉刷新全部内容,希望文章能够帮你解决Android自定义ListView实现下拉刷新所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1141670.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存