当前位置: 移动技术网 > IT编程>移动开发>Android > Android PullToRefreshLayout下拉刷新控件的终结者

Android PullToRefreshLayout下拉刷新控件的终结者

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

渑池县地图,世博会会徽,我们结婚了20130503

       说到下拉刷新控件,网上版本有很多,很多软件也都有下拉刷新功能。有一个叫xlistview的,我看别人用过,没看过是咋实现的,看这名字估计是继承自listview修改的,不过效果看起来挺丑的,也没什么扩展性,太单调了。看了qq2014的列表下拉刷新,发现挺好看的,我喜欢,贴一下图看一下qq的下拉刷新效果:

                                          

    不错吧?嗯,是的。一看就知道实现方式不一样。咱们今天就来实现一个下拉刷新控件。由于有时候不仅仅是listview需要下拉刷新,expandablelistview和gridview也有这个需求,由于listview,gridview都是abslistview的子类,expandablelistview是listview的子类所以也是abslistview的子类。所以我的思路是自定义一个对所有abslistview的子类通用的下拉管理布局,叫pulltorefreshlayout,如果需要gridview,只需要在布局文件里将listview换成gridview就行了,expandablelistview也一样,不需要再继承什么gridview啊listview啊乱七八糟的。


看上图,主要工作就是定义黑色大布局,红色部分是不下拉的时候的可见部分,可以是任意的abslistview的子类(gridview,listview,expandablelistview等等)。其实我已经写好了,先看一下效果图:

正常拉法:

                          

强迫症拉法:

                 

上面是listview的,下面是gridview的

                  

再来看一下expandablelistview的下拉刷新效果:

                              

可以看到,点击事件和长按事件都能正常触发而不会误触发,在使用expandablelistview的时候需要注意禁止展开时自动滚动,否则会出现bug。后面会提供demo源码下载,可以根据自己的需求去修改。

下面讲解pulltorefreshlayout的实现,在贴完整的源码之前先理解整个类的大概思路:

public class pulltorefreshlayout extends relativelayout implements ontouchlistener 
{ 
  
 // 下拉的距离 
 public float movedeltay = 0; 
 // 是否可以下拉 
 private boolean canpull = true; 
  
  
 private void hidehead() 
 { 
  // 在这里开始异步隐藏下拉头,在松手的时候或这刷新完毕的时候隐藏 
 } 
 
  
 public void refreshfinish(int refreshresult) 
 { 
  // 完成刷新操作,显示刷新结果 
 } 
 
 private void changestate(int to) 
 { 
  // 改变当前所处的状态,有四个状态:下拉刷新、释放刷新、正在刷新、刷新完成 
 } 
 
 /* 
  * (非 javadoc)由父控件决定是否分发事件,防止事件冲突 
  * 
  * @see android.view.viewgroup#dispatchtouchevent(android.view.motionevent) 
  */ 
 @override 
 public boolean dispatchtouchevent(motionevent ev) 
 { 
  switch (ev.getactionmasked()) 
  { 
  case motionevent.action_down: 
   /*手指按下的时候,无法判断是否将要下拉,所以这时候break让父类把down事件分发给子view 
   记录按下的坐标*/ 
   break; 
  case motionevent.action_move: 
   /*如果往上滑动且movedetay==0则说明不在下拉,break继续将move事件分发给子view 
   如果往下拉,则计算下拉的距离movedeltay,根据movedeltay重新layout子控件。但是 
   由于down事件传到了子view,如果不清除子view的事件,会导致子view误触发长按事件和点击事件。所以在这里清除子view的事件回调。 
   下拉超过一定的距离时,改变当前状态*/ 
   break; 
  case motionevent.action_up: 
   //根据当前状态执行刷新操作或者hidehead 
  default: 
   break; 
  } 
  // 事件分发交给父类 
  return super.dispatchtouchevent(ev); 
 } 
 
  
 
 /* 
  * (非 javadoc)绘制阴影效果,颜色值可以修改 
  * 
  * @see android.view.viewgroup#dispatchdraw(android.graphics.canvas) 
  */ 
 @override 
 protected void dispatchdraw(canvas canvas) 
 { 
  //在这里用一个渐变绘制分界线阴影 
 } 
 
  
 
 @override 
 protected void onlayout(boolean changed, int l, int t, int r, int b) 
 { 
  //这个方法就是重新layout子view了,根据movedeltay来定位子view的位置 
 } 
 
  
 @override 
 public boolean ontouch(view v, motionevent event) 
 { 
  //这个是ontouchlistener的方法,只判断abslistview的状态来决定是否canpull,除此之外不做其他处理 
 } 
} 

可以看到,这里复写了viewgroup的dispatchtouchevent,这样就可以掌控事件的分发,如果不了解这个方法可以看一下这篇android事件分发、view事件listener全解析。之所以要控制事件分发是因为我们不可能知道手指down在abslistview上之后将往上滑还是往下拉,所以down事件会分发给abslistview的,但是在move的时候就需要看情况了,因为我们不想在下拉的同时abslistview也在滑动,所以在下拉的时候不分发move事件,但这样问题又来了,前面abslistview已经接收了down事件,如果这时候不分发move事件给它,它会触发长按事件或者点击事件,所以在这里还需要清除abslistview消息列表中的callback。
onlayout用于重新布置下拉头和abslistview的位置的,这个不难理解。

理解了大概思路之后,看一下pulltorefreshlayout完整的源码吧~

package com.jingchen.pulltorefresh; 
 
import java.lang.reflect.field; 
import java.util.timer; 
import java.util.timertask; 
 
import android.content.context; 
import android.graphics.canvas; 
import android.graphics.lineargradient; 
import android.graphics.paint; 
import android.graphics.paint.style; 
import android.graphics.rectf; 
import android.graphics.shader.tilemode; 
import android.os.handler; 
import android.os.message; 
import android.util.attributeset; 
import android.util.log; 
import android.view.motionevent; 
import android.view.view; 
import android.view.view.ontouchlistener; 
import android.view.viewgroup; 
import android.view.animation.animationutils; 
import android.view.animation.linearinterpolator; 
import android.view.animation.rotateanimation; 
import android.widget.abslistview; 
import android.widget.relativelayout; 
import android.widget.textview; 
 
/** 
 * 整个下拉刷新就这一个布局,用来管理两个子控件,其中一个是下拉头,另一个是包含内容的contentview(可以是abslistview的任何子类) 
 * 
 * @author 陈靖 
 */ 
public class pulltorefreshlayout extends relativelayout implements ontouchlistener 
{ 
 public static final string tag = "pulltorefreshlayout"; 
 // 下拉刷新 
 public static final int pull_to_refresh = 0; 
 // 释放刷新 
 public static final int release_to_refresh = 1; 
 // 正在刷新 
 public static final int refreshing = 2; 
 // 刷新完毕 
 public static final int done = 3; 
 // 当前状态 
 private int state = pull_to_refresh; 
 // 刷新回调接口 
 private onrefreshlistener mlistener; 
 // 刷新成功 
 public static final int refresh_succeed = 0; 
 // 刷新失败 
 public static final int refresh_fail = 1; 
 // 下拉头 
 private view headview; 
 // 内容 
 private view contentview; 
 // 按下y坐标,上一个事件点y坐标 
 private float downy, lasty; 
 // 下拉的距离 
 public float movedeltay = 0; 
 // 释放刷新的距离 
 private float refreshdist = 200; 
 private timer timer; 
 private mytimertask mtask; 
 // 回滚速度 
 public float move_speed = 8; 
 // 第一次执行布局 
 private boolean islayout = false; 
 // 是否可以下拉 
 private boolean canpull = true; 
 // 在刷新过程中滑动操作 
 private boolean istouchinrefreshing = false; 
 // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化 
 private float radio = 2; 
 // 下拉箭头的转180°动画 
 private rotateanimation rotateanimation; 
 // 均匀旋转动画 
 private rotateanimation refreshinganimation; 
 // 下拉的箭头 
 private view pullview; 
 // 正在刷新的图标 
 private view refreshingview; 
 // 刷新结果图标 
 private view stateimageview; 
 // 刷新结果:成功或失败 
 private textview statetextview; 
 /** 
  * 执行自动回滚的handler 
  */ 
 handler updatehandler = new handler() 
 { 
 
  @override 
  public void handlemessage(message msg) 
  { 
   // 回弹速度随下拉距离movedeltay增大而增大 
   move_speed = (float) (8 + 5 * math.tan(math.pi / 2 / getmeasuredheight() * movedeltay)); 
   if (state == refreshing && movedeltay <= refreshdist && !istouchinrefreshing) 
   { 
    // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..." 
    movedeltay = refreshdist; 
    mtask.cancel(); 
   } 
   if (canpull) 
    movedeltay -= move_speed; 
   if (movedeltay <= 0) 
   { 
    // 已完成回弹 
    movedeltay = 0; 
    pullview.clearanimation(); 
    // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态 
    if (state != refreshing) 
     changestate(pull_to_refresh); 
    mtask.cancel(); 
   } 
   // 刷新布局,会自动调用onlayout 
   requestlayout(); 
  } 
 
 }; 
 
 public void setonrefreshlistener(onrefreshlistener listener) 
 { 
  mlistener = listener; 
 } 
 
 public pulltorefreshlayout(context context) 
 { 
  super(context); 
  initview(context); 
 } 
 
 public pulltorefreshlayout(context context, attributeset attrs) 
 { 
  super(context, attrs); 
  initview(context); 
 } 
 
 public pulltorefreshlayout(context context, attributeset attrs, int defstyle) 
 { 
  super(context, attrs, defstyle); 
  initview(context); 
 } 
 
 private void initview(context context) 
 { 
  timer = new timer(); 
  mtask = new mytimertask(updatehandler); 
  rotateanimation = (rotateanimation) animationutils.loadanimation(context, r.anim.reverse_anim); 
  refreshinganimation = (rotateanimation) animationutils.loadanimation(context, r.anim.rotating); 
  // 添加匀速转动动画 
  linearinterpolator lir = new linearinterpolator(); 
  rotateanimation.setinterpolator(lir); 
  refreshinganimation.setinterpolator(lir); 
 } 
 
 private void hidehead() 
 { 
  if (mtask != null) 
  { 
   mtask.cancel(); 
   mtask = null; 
  } 
  mtask = new mytimertask(updatehandler); 
  timer.schedule(mtask, 0, 5); 
 } 
 
 /** 
  * 完成刷新操作,显示刷新结果 
  */ 
 public void refreshfinish(int refreshresult) 
 { 
  refreshingview.clearanimation(); 
  refreshingview.setvisibility(view.gone); 
  switch (refreshresult) 
  { 
  case refresh_succeed: 
   // 刷新成功 
   stateimageview.setvisibility(view.visible); 
   statetextview.settext(r.string.refresh_succeed); 
   stateimageview.setbackgroundresource(r.drawable.refresh_succeed); 
   break; 
  case refresh_fail: 
   // 刷新失败 
   stateimageview.setvisibility(view.visible); 
   statetextview.settext(r.string.refresh_fail); 
   stateimageview.setbackgroundresource(r.drawable.refresh_failed); 
   break; 
  default: 
   break; 
  } 
  // 刷新结果停留1秒 
  new handler() 
  { 
   @override 
   public void handlemessage(message msg) 
   { 
    state = pull_to_refresh; 
    hidehead(); 
   } 
  }.sendemptymessagedelayed(0, 1000); 
 } 
 
 private void changestate(int to) 
 { 
  state = to; 
  switch (state) 
  { 
  case pull_to_refresh: 
   // 下拉刷新 
   stateimageview.setvisibility(view.gone); 
   statetextview.settext(r.string.pull_to_refresh); 
   pullview.clearanimation(); 
   pullview.setvisibility(view.visible); 
   break; 
  case release_to_refresh: 
   // 释放刷新 
   statetextview.settext(r.string.release_to_refresh); 
   pullview.startanimation(rotateanimation); 
   break; 
  case refreshing: 
   // 正在刷新 
   pullview.clearanimation(); 
   refreshingview.setvisibility(view.visible); 
   pullview.setvisibility(view.invisible); 
   refreshingview.startanimation(refreshinganimation); 
   statetextview.settext(r.string.refreshing); 
   break; 
  default: 
   break; 
  } 
 } 
 
 /* 
  * (非 javadoc)由父控件决定是否分发事件,防止事件冲突 
  * 
  * @see android.view.viewgroup#dispatchtouchevent(android.view.motionevent) 
  */ 
 @override 
 public boolean dispatchtouchevent(motionevent ev) 
 { 
  switch (ev.getactionmasked()) 
  { 
  case motionevent.action_down: 
   downy = ev.gety(); 
   lasty = downy; 
   if (mtask != null) 
   { 
    mtask.cancel(); 
   } 
   /* 
    * 触碰的地方位于下拉头布局,由于我们没有对下拉头做事件响应,这时候它会给咱返回一个false导致接下来的事件不再分发进来。 
    * 所以我们不能交给父类分发,直接返回true 
    */ 
   if (ev.gety() < movedeltay) 
    return true; 
   break; 
  case motionevent.action_move: 
   // canpull这个值在底下ontouch中会根据listview是否滑到顶部来改变,意思是是否可下拉 
   if (canpull) 
   { 
    // 对实际滑动距离做缩小,造成用力拉的感觉 
    movedeltay = movedeltay + (ev.gety() - lasty) / radio; 
    if (movedeltay < 0) 
     movedeltay = 0; 
    if (movedeltay > getmeasuredheight()) 
     movedeltay = getmeasuredheight(); 
    if (state == refreshing) 
    { 
     // 正在刷新的时候触摸移动 
     istouchinrefreshing = true; 
    } 
   } 
   lasty = ev.gety(); 
   // 根据下拉距离改变比例 
   radio = (float) (2 + 2 * math.tan(math.pi / 2 / getmeasuredheight() * movedeltay)); 
   requestlayout(); 
   if (movedeltay <= refreshdist && state == release_to_refresh) 
   { 
    // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新 
    changestate(pull_to_refresh); 
   } 
   if (movedeltay >= refreshdist && state == pull_to_refresh) 
   { 
    changestate(release_to_refresh); 
   } 
   if (movedeltay > 8) 
   { 
    // 防止下拉过程中误触发长按事件和点击事件 
    clearcontentviewevents(); 
   } 
   if (movedeltay > 0) 
   { 
    // 正在下拉,不让子控件捕获事件 
    return true; 
   } 
   break; 
  case motionevent.action_up: 
   if (movedeltay > refreshdist) 
    // 正在刷新时往下拉释放后下拉头不隐藏 
    istouchinrefreshing = false; 
   if (state == release_to_refresh) 
   { 
    changestate(refreshing); 
    // 刷新操作 
    if (mlistener != null) 
     mlistener.onrefresh(); 
   } else 
   { 
 
   } 
   hidehead(); 
  default: 
   break; 
  } 
  // 事件分发交给父类 
  return super.dispatchtouchevent(ev); 
 } 
 
 /** 
  * 通过反射修改字段去掉长按事件和点击事件 
  */ 
 private void clearcontentviewevents() 
 { 
  try 
  { 
   field[] fields = abslistview.class.getdeclaredfields(); 
   for (int i = 0; i < fields.length; i++) 
    if (fields[i].getname().equals("mpendingcheckforlongpress")) 
    { 
     // mpendingcheckforlongpress是abslistview中的字段,通过反射获取并从消息列表删除,去掉长按事件 
     fields[i].setaccessible(true); 
     contentview.gethandler().removecallbacks((runnable) fields[i].get(contentview)); 
    } else if (fields[i].getname().equals("mtouchmode")) 
    { 
     // touch_mode_rest = -1, 这个可以去除点击事件 
     fields[i].setaccessible(true); 
     fields[i].set(contentview, -1); 
    } 
   // 去掉焦点 
   ((abslistview) contentview).getselector().setstate(new int[] 
   { 0 }); 
  } catch (exception e) 
  { 
   log.d(tag, "error : " + e.tostring()); 
  } 
 } 
 
 /* 
  * (非 javadoc)绘制阴影效果,颜色值可以修改 
  * 
  * @see android.view.viewgroup#dispatchdraw(android.graphics.canvas) 
  */ 
 @override 
 protected void dispatchdraw(canvas canvas) 
 { 
  super.dispatchdraw(canvas); 
  if (movedeltay == 0) 
   return; 
  rectf rectf = new rectf(0, 0, getmeasuredwidth(), movedeltay); 
  paint paint = new paint(); 
  paint.setantialias(true); 
  // 阴影的高度为26 
  lineargradient lineargradient = new lineargradient(0, movedeltay, 0, movedeltay - 26, 0x66000000, 0x00000000, tilemode.clamp); 
  paint.setshader(lineargradient); 
  paint.setstyle(style.fill); 
  // 在movedeltay处往上变淡 
  canvas.drawrect(rectf, paint); 
 } 
 
 private void initview() 
 { 
  pullview = headview.findviewbyid(r.id.pull_icon); 
  statetextview = (textview) headview.findviewbyid(r.id.state_tv); 
  refreshingview = headview.findviewbyid(r.id.refreshing_icon); 
  stateimageview = headview.findviewbyid(r.id.state_iv); 
 } 
 
 @override 
 protected void onlayout(boolean changed, int l, int t, int r, int b) 
 { 
  if (!islayout) 
  { 
   // 这里是第一次进来的时候做一些初始化 
   headview = getchildat(0); 
   contentview = getchildat(1); 
   // 给abslistview设置ontouchlistener 
   contentview.setontouchlistener(this); 
   islayout = true; 
   initview(); 
   refreshdist = ((viewgroup) headview).getchildat(0).getmeasuredheight(); 
  } 
  if (canpull) 
  { 
   // 改变子控件的布局 
   headview.layout(0, (int) movedeltay - headview.getmeasuredheight(), headview.getmeasuredwidth(), (int) movedeltay); 
   contentview.layout(0, (int) movedeltay, contentview.getmeasuredwidth(), (int) movedeltay + contentview.getmeasuredheight()); 
  }else super.onlayout(changed, l, t, r, b); 
 } 
 
 class mytimertask extends timertask 
 { 
  handler handler; 
 
  public mytimertask(handler handler) 
  { 
   this.handler = handler; 
  } 
 
  @override 
  public void run() 
  { 
   handler.sendmessage(handler.obtainmessage()); 
  } 
 
 } 
 
 @override 
 public boolean ontouch(view v, motionevent event) 
 { 
  // 第一个item可见且滑动到顶部 
  abslistview alv = null; 
  try 
  { 
   alv = (abslistview) v; 
  } catch (exception e) 
  { 
   log.d(tag, e.getmessage()); 
   return false; 
  } 
  if (alv.getcount() == 0) 
  { 
   // 没有item的时候也可以下拉刷新 
   canpull = true; 
  } else if (alv.getfirstvisibleposition() == 0 && alv.getchildat(0).gettop() >= 0) 
  { 
   // 滑到abslistview的顶部了 
   canpull = true; 
  } else 
   canpull = false; 
  return false; 
 } 
} 

代码中的注释已经写的很清楚了。
既然pulltorefreshlayout已经写好了,接下来就来使用这个layout实现下拉刷新~

首先得写个onrefreshlistener接口来回调刷新操作:

public interface onrefreshlistener { 
 void onrefresh(); 
} 

就一个刷新操作的方法,待会儿让activity实现这个接口就可以在activity中执行刷新操作了。

看一下mainactivity的布局:

<com.jingchen.pulltorefresh.pulltorefreshlayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/refresh_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" > 
 
 <include layout="@layout/refresh_head" /> 
  
 <!-- 支持abslistview的所有子类 --> 
 <listview 
  android:id="@+id/content_view" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@color/white" 
  android:divider="@color/gray" 
  android:dividerheight="1dp" > 
 </listview> 
 
</com.jingchen.pulltorefresh.pulltorefreshlayout> 

pulltorefreshlayout只能包含两个子控件:refresh_head和content_view。
看一下refresh_head的布局:

<?xml version="1.0" encoding="utf-8"?> 
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/head_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="@color/light_blue" > 
 
 <relativelayout 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:layout_alignparentbottom="true" 
  android:paddingbottom="20dp" 
  android:paddingtop="20dp" > 
 
  <relativelayout 
   android:layout_width="match_parent" 
   android:layout_height="wrap_content" 
   android:layout_centerinparent="true" > 
 
   <imageview 
    android:id="@+id/pull_icon" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centervertical="true" 
    android:layout_marginleft="60dp" 
    android:background="@drawable/pull_icon_big" /> 
 
   <imageview 
    android:id="@+id/refreshing_icon" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centervertical="true" 
    android:layout_marginleft="60dp" 
    android:background="@drawable/refreshing" 
    android:visibility="gone" /> 
 
   <textview 
    android:id="@+id/state_tv" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centerinparent="true" 
    android:text="@string/pull_to_refresh" 
    android:textcolor="@color/white" 
    android:textsize="16sp" /> 
 
   <imageview 
    android:id="@+id/state_iv" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centervertical="true" 
    android:layout_marginright="8dp" 
    android:layout_toleftof="@id/state_tv" 
    android:visibility="gone" /> 
  </relativelayout> 
 </relativelayout> 
 
</relativelayout> 

可以根据需要修改refresh_head的布局然后在pulltorefreshlayout中处理,但是相关view的id要和pulltorefreshlayout中用到的保持同步!

接下来是mainactivity的代码:

package com.jingchen.pulltorefresh; 
 
import java.util.arraylist; 
import java.util.list; 
 
import android.app.activity; 
import android.content.context; 
import android.os.bundle; 
import android.os.handler; 
import android.os.message; 
import android.support.v4.view.pageradapter; 
import android.support.v4.view.viewpager; 
import android.support.v4.view.viewpager.onpagechangelistener; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.view.onclicklistener; 
import android.view.viewgroup; 
import android.view.animation.animationutils; 
import android.view.animation.linearinterpolator; 
import android.view.animation.rotateanimation; 
import android.widget.abslistview; 
import android.widget.adapterview; 
import android.widget.adapterview.onitemclicklistener; 
import android.widget.adapterview.onitemlongclicklistener; 
import android.widget.baseexpandablelistadapter; 
import android.widget.expandablelistview; 
import android.widget.expandablelistview.onchildclicklistener; 
import android.widget.expandablelistview.ongroupclicklistener; 
import android.widget.listview; 
import android.widget.textview; 
import android.widget.toast; 
 
/** 
 * 除了下拉刷新,在contenview为listview的情况下我给listview增加了footerview,实现点击加载更多 
 * 
 * @author 陈靖 
 * 
 */ 
public class mainactivity extends activity implements onrefreshlistener, onclicklistener 
{ 
 private abslistview alv; 
 private pulltorefreshlayout refreshlayout; 
 private view loading; 
 private rotateanimation loadinganimation; 
 private textview loadtextview; 
 private myadapter adapter; 
 private boolean isloading = false; 
 
 @override 
 protected void oncreate(bundle savedinstancestate) 
 { 
  super.oncreate(savedinstancestate); 
  setcontentview(r.layout.activity_main); 
  init(); 
 } 
 
 private void init() 
 { 
  alv = (abslistview) findviewbyid(r.id.content_view); 
  refreshlayout = (pulltorefreshlayout) findviewbyid(r.id.refresh_view); 
  refreshlayout.setonrefreshlistener(this); 
  initlistview(); 
 
  loadinganimation = (rotateanimation) animationutils.loadanimation(this, r.anim.rotating); 
  // 添加匀速转动动画 
  linearinterpolator lir = new linearinterpolator(); 
  loadinganimation.setinterpolator(lir); 
 } 
 
 /** 
  * listview初始化方法 
  */ 
 private void initlistview() 
 { 
  list<string> items = new arraylist<string>(); 
  for (int i = 0; i < 30; i++) 
  { 
   items.add("这里是item " + i); 
  } 
  // 添加head 
  view headview = getlayoutinflater().inflate(r.layout.listview_head, null); 
  ((listview) alv).addheaderview(headview, null, false); 
  // 添加footer 
  view footerview = getlayoutinflater().inflate(r.layout.load_more, null); 
  loading = footerview.findviewbyid(r.id.loading_icon); 
  loadtextview = (textview) footerview.findviewbyid(r.id.loadmore_tv); 
  ((listview) alv).addfooterview(footerview, null, false); 
  footerview.setonclicklistener(this); 
  adapter = new myadapter(this, items); 
  alv.setadapter(adapter); 
  alv.setonitemlongclicklistener(new onitemlongclicklistener() 
  { 
 
   @override 
   public boolean onitemlongclick(adapterview<?> parent, view view, int position, long id) 
   { 
    toast.maketext(mainactivity.this, "longclick on " + parent.getadapter().getitemid(position), toast.length_short).show(); 
    return true; 
   } 
  }); 
  alv.setonitemclicklistener(new onitemclicklistener() 
  { 
 
   @override 
   public void onitemclick(adapterview<?> parent, view view, int position, long id) 
   { 
    toast.maketext(mainactivity.this, " click on " + parent.getadapter().getitemid(position), toast.length_short).show(); 
   } 
  }); 
 } 
 
 /** 
  * gridview初始化方法 
  */ 
 private void initgridview() 
 { 
  list<string> items = new arraylist<string>(); 
  for (int i = 0; i < 30; i++) 
  { 
   items.add("这里是item " + i); 
  } 
  adapter = new myadapter(this, items); 
  alv.setadapter(adapter); 
  alv.setonitemlongclicklistener(new onitemlongclicklistener() 
  { 
 
   @override 
   public boolean onitemlongclick(adapterview<?> parent, view view, int position, long id) 
   { 
    toast.maketext(mainactivity.this, "longclick on " + parent.getadapter().getitemid(position), toast.length_short).show(); 
    return true; 
   } 
  }); 
  alv.setonitemclicklistener(new onitemclicklistener() 
  { 
 
   @override 
   public void onitemclick(adapterview<?> parent, view view, int position, long id) 
   { 
    toast.maketext(mainactivity.this, " click on " + parent.getadapter().getitemid(position), toast.length_short).show(); 
   } 
  }); 
 } 
 
 /** 
  * expandablelistview初始化方法 
  */ 
 private void initexpandablelistview() 
 { 
  ((expandablelistview) alv).setadapter(new expandablelistadapter(this)); 
  ((expandablelistview) alv).setonchildclicklistener(new onchildclicklistener() 
  { 
 
   @override 
   public boolean onchildclick(expandablelistview parent, view v, int groupposition, int childposition, long id) 
   { 
    toast.maketext(mainactivity.this, " click on group " + groupposition + " item " + childposition, toast.length_short).show(); 
    return true; 
   } 
  }); 
  ((expandablelistview) alv).setonitemlongclicklistener(new onitemlongclicklistener() 
  { 
 
   @override 
   public boolean onitemlongclick(adapterview<?> parent, view view, int position, long id) 
   { 
    toast.maketext(mainactivity.this, "longclick on " + parent.getadapter().getitemid(position), toast.length_short).show(); 
    return true; 
   } 
  }); 
  ((expandablelistview) alv).setongroupclicklistener(new ongroupclicklistener() 
  { 
 
   @override 
   public boolean ongroupclick(expandablelistview parent, view v, int groupposition, long id) 
   { 
    if (parent.isgroupexpanded(groupposition)) 
    { 
     // 如果展开则关闭 
     parent.collapsegroup(groupposition); 
    } else 
    { 
     // 如果关闭则打开,注意这里是手动打开不要默认滚动否则会有bug 
     parent.expandgroup(groupposition); 
    } 
    return true; 
   } 
  }); 
 } 
 
 @override 
 public void onrefresh() 
 { 
  // 下拉刷新操作 
  new handler() 
  { 
   @override 
   public void handlemessage(message msg) 
   { 
    refreshlayout.refreshfinish(pulltorefreshlayout.refresh_succeed); 
   } 
  }.sendemptymessagedelayed(0, 5000); 
 } 
 
 @override 
 public void onclick(view v) 
 { 
  switch (v.getid()) 
  { 
  case r.id.loadmore_layout: 
   if (!isloading) 
   { 
    loading.setvisibility(view.visible); 
    loading.startanimation(loadinganimation); 
    loadtextview.settext(r.string.loading); 
    isloading = true; 
   } 
   break; 
  default: 
   break; 
  } 
 
 } 
 
 class expandablelistadapter extends baseexpandablelistadapter 
 { 
  private string[] groupsstrings;// = new string[] { "这里是group 0", 
          // "这里是group 1", "这里是group 2" }; 
  private string[][] groupitems; 
  private context context; 
 
  public expandablelistadapter(context context) 
  { 
   this.context = context; 
   groupsstrings = new string[8]; 
   for (int i = 0; i < groupsstrings.length; i++) 
   { 
    groupsstrings[i] = new string("这里是group " + i); 
   } 
   groupitems = new string[8][8]; 
   for (int i = 0; i < groupitems.length; i++) 
    for (int j = 0; j < groupitems[i].length; j++) 
    { 
     groupitems[i][j] = new string("这里是group " + i + "里的item " + j); 
    } 
  } 
 
  @override 
  public int getgroupcount() 
  { 
   return groupsstrings.length; 
  } 
 
  @override 
  public int getchildrencount(int groupposition) 
  { 
   return groupitems[groupposition].length; 
  } 
 
  @override 
  public object getgroup(int groupposition) 
  { 
   return groupsstrings[groupposition]; 
  } 
 
  @override 
  public object getchild(int groupposition, int childposition) 
  { 
   return groupitems[groupposition][childposition]; 
  } 
 
  @override 
  public long getgroupid(int groupposition) 
  { 
   return groupposition; 
  } 
 
  @override 
  public long getchildid(int groupposition, int childposition) 
  { 
   return childposition; 
  } 
 
  @override 
  public boolean hasstableids() 
  { 
   return true; 
  } 
 
  @override 
  public view getgroupview(int groupposition, boolean isexpanded, view convertview, viewgroup parent) 
  { 
   view view = layoutinflater.from(context).inflate(r.layout.list_item_layout, null); 
   textview tv = (textview) view.findviewbyid(r.id.name_tv); 
   tv.settext(groupsstrings[groupposition]); 
   return view; 
  } 
 
  @override 
  public view getchildview(int groupposition, int childposition, boolean islastchild, view convertview, viewgroup parent) 
  { 
   view view = layoutinflater.from(context).inflate(r.layout.list_item_layout, null); 
   textview tv = (textview) view.findviewbyid(r.id.name_tv); 
   tv.settext(groupitems[groupposition][childposition]); 
   return view; 
  } 
 
  @override 
  public boolean ischildselectable(int groupposition, int childposition) 
  { 
   return true; 
  } 
 
 } 
 
} 

在mainactivity中判断contentview是listview的话给listview添加了footerview实现点击加载更多的功能。这只是一个演示pulltorefreshlayout使用的demo,可以参照一下修改。我已经在里面写了listview,gridview和expandablelistview的初始化方法,根据自己使用的是哪个来调用吧。那么这是listview的下拉刷新和加载更多。如果我要gridview也有下拉刷新功能呢?那就把mainactivity的布局换成这样:

<com.jingchen.pulltorefresh.pulltorefreshlayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/refresh_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" > 
 
 <include layout="@layout/refresh_head" /> 
 <!-- 支持abslistview的所有子类 --> 
 <gridview 
  android:id="@+id/content_view" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@color/white" 
  android:columnwidth="90dp" 
  android:gravity="center" 
  android:horizontalspacing="10dp" 
  android:numcolumns="auto_fit" 
  android:stretchmode="columnwidth" 
  android:verticalspacing="15dp" > 
 </gridview> 
 
</com.jingchen.pulltorefresh.pulltorefreshlayout> 

如果是expandablelistview则把布局改成这样:

<com.jingchen.pulltorefresh.pulltorefreshlayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/refresh_view" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" > 
 
 <include layout="@layout/refresh_head" /> 
 <!-- 支持abslistview的所有子类 --> 
 <expandablelistview 
  android:id="@+id/content_view" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@color/white" > 
 </expandablelistview> 
 
</com.jingchen.pulltorefresh.pulltorefreshlayout> 

怎么样?很简单吧?简单易用,不用再去继承修改了。

本文已经被整理到《android下拉刷新上拉加载效果》,欢迎大家学习研究。

希望本文所述对大家学习android下拉刷新控件有所帮助。

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

相关文章:

验证码:
移动技术网