首页 > 系统 > Android > 正文

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

2019-10-24 20:18:17
字体:
来源:转载
供稿:网友
这篇文章主要介绍了Android自定义控件实战中下拉刷新控件终结者PullToRefreshLayout的实现方法,感兴趣的小伙伴们可以参考一下
 

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

                                          Android,PullToRefreshLayout,下拉刷新

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

Android,PullToRefreshLayout,下拉刷新

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

正常拉法:

               Android,PullToRefreshLayout,下拉刷新           

强迫症拉法:

                 Android,PullToRefreshLayout,下拉刷新

上面是ListView的,下面是GridView的

                  Android,PullToRefreshLayout,下拉刷新

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

                              Android,PullToRefreshLayout,下拉刷新

可以看到,点击事件和长按事件都能正常触发而不会误触发,在使用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下拉刷新控件有所帮助。



发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表