0
点赞
收藏
分享

微信扫一扫

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_移动开发_02

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

正常拉法:

               

Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout_移动开发_03

           

强迫症拉法:

                 

Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout_移动开发_04

上面是ListView的,下面是GridView的

                  

Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout_android_05

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

                              

Android自定义控件实战——下拉刷新控件终结者:PullToRefreshLayout_下拉刷新_06

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

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

 


[java]  view plain copy

    1. public class PullToRefreshLayout extends RelativeLayout implements OnTouchListener  
    2. {  
    3.       
    4. // 下拉的距离  
    5. public float moveDeltaY = 0;  
    6. // 是否可以下拉  
    7. private boolean canPull = true;  
    8.       
    9.      
    10. private void hideHead()  
    11.     {  
    12. // 在这里开始异步隐藏下拉头,在松手的时候或这刷新完毕的时候隐藏  
    13.     }  
    14.   
    15.       
    16. public void refreshFinish(int refreshResult)  
    17.     {  
    18. // 完成刷新操作,显示刷新结果  
    19.     }  
    20.   
    21. private void changeState(int to)  
    22.     {  
    23. // 改变当前所处的状态,有四个状态:下拉刷新、释放刷新、正在刷新、刷新完成  
    24.     }  
    25.   
    26. /*
    27.      * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
    28.      * 
    29.      * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
    30.      */  
    31. @Override  
    32. public boolean dispatchTouchEvent(MotionEvent ev)  
    33.     {  
    34. switch (ev.getActionMasked())  
    35.         {  
    36. case MotionEvent.ACTION_DOWN:  
    37. /*手指按下的时候,无法判断是否将要下拉,所以这时候break让父类把down事件分发给子View
    38.             记录按下的坐标*/  
    39. break;  
    40. case MotionEvent.ACTION_MOVE:  
    41. /*如果往上滑动且moveDetaY==0则说明不在下拉,break继续将move事件分发给子View
    42.             如果往下拉,则计算下拉的距离moveDeltaY,根据moveDeltaY重新Layout子控件。但是
    43.             由于down事件传到了子View,如果不清除子View的事件,会导致子View误触发长按事件和点击事件。所以在这里清除子View的事件回调。
    44.             下拉超过一定的距离时,改变当前状态*/  
    45. break;  
    46. case MotionEvent.ACTION_UP:  
    47. //根据当前状态执行刷新操作或者hideHead  
    48. default:  
    49. break;  
    50.         }  
    51. // 事件分发交给父类  
    52. return super.dispatchTouchEvent(ev);  
    53.     }  
    54.   
    55.       
    56.   
    57. /*
    58.      * (非 Javadoc)绘制阴影效果,颜色值可以修改
    59.      * 
    60.      * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
    61.      */  
    62. @Override  
    63. protected void dispatchDraw(Canvas canvas)  
    64.     {  
    65. //在这里用一个渐变绘制分界线阴影  
    66.     }  
    67.   
    68.       
    69.   
    70. @Override  
    71. protected void onLayout(boolean changed, int l, int t, int r, int b)  
    72.     {  
    73. //这个方法就是重新Layout子View了,根据moveDeltaY来定位子View的位置  
    74.     }  
    75.   
    76.       
    77. @Override  
    78. public boolean onTouch(View v, MotionEvent event)  
    79.     {  
    80. //这个是OnTouchListener的方法,只判断AbsListView的状态来决定是否canPull,除此之外不做其他处理  
    81.     }  
    82. }


    可以看到,这里复写了ViewGroup的dispatchTouchEvent,这样就可以掌控事件的分发,如果不了解这个方法可以看一下这篇Android事件分发、View事件Listener全解析。之所以要控制事件分发是因为我们不可能知道手指down在AbsListView上之后将往上滑还是往下拉,所以down事件会分发给AbsListView的,但是在move的时候就需要看情况了,因为我们不想在下拉的同时AbsListView也在滑动,所以在下拉的时候不分发move事件,但这样问题又来了,前面AbsListView已经接收了down事件,如果这时候不分发move事件给它,它会触发长按事件或者点击事件,所以在这里还需要清除AbsListView消息列表中的callback。

     

    onLayout用于重新布置下拉头和AbsListView的位置的,这个不难理解。

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

     


    [java]  view plain copy

     

    1. package com.jingchen.pulltorefresh;  
    2.   
    3. import java.lang.reflect.Field;  
    4. import java.util.Timer;  
    5. import java.util.TimerTask;  
    6.   
    7. import android.content.Context;  
    8. import android.graphics.Canvas;  
    9. import android.graphics.LinearGradient;  
    10. import android.graphics.Paint;  
    11. import android.graphics.Paint.Style;  
    12. import android.graphics.RectF;  
    13. import android.graphics.Shader.TileMode;  
    14. import android.os.Handler;  
    15. import android.os.Message;  
    16. import android.util.AttributeSet;  
    17. import android.util.Log;  
    18. import android.view.MotionEvent;  
    19. import android.view.View;  
    20. import android.view.View.OnTouchListener;  
    21. import android.view.ViewGroup;  
    22. import android.view.animation.AnimationUtils;  
    23. import android.view.animation.LinearInterpolator;  
    24. import android.view.animation.RotateAnimation;  
    25. import android.widget.AbsListView;  
    26. import android.widget.RelativeLayout;  
    27. import android.widget.TextView;  
    28.   
    29. /**
    30.  * 整个下拉刷新就这一个布局,用来管理两个子控件,其中一个是下拉头,另一个是包含内容的contentView(可以是AbsListView的任何子类)
    31.  * 
    32.  * @author 陈靖
    33.  */  
    34. public class PullToRefreshLayout extends RelativeLayout implements OnTouchListener  
    35. {  
    36. public static final String TAG = "PullToRefreshLayout";  
    37. // 下拉刷新  
    38. public static final int PULL_TO_REFRESH = 0;  
    39. // 释放刷新  
    40. public static final int RELEASE_TO_REFRESH = 1;  
    41. // 正在刷新  
    42. public static final int REFRESHING = 2;  
    43. // 刷新完毕  
    44. public static final int DONE = 3;  
    45. // 当前状态  
    46. private int state = PULL_TO_REFRESH;  
    47. // 刷新回调接口  
    48. private OnRefreshListener mListener;  
    49. // 刷新成功  
    50. public static final int REFRESH_SUCCEED = 0;  
    51. // 刷新失败  
    52. public static final int REFRESH_FAIL = 1;  
    53. // 下拉头  
    54. private View headView;  
    55. // 内容  
    56. private View contentView;  
    57. // 按下Y坐标,上一个事件点Y坐标  
    58. private float downY, lastY;  
    59. // 下拉的距离  
    60. public float moveDeltaY = 0;  
    61. // 释放刷新的距离  
    62. private float refreshDist = 200;  
    63. private Timer timer;  
    64. private MyTimerTask mTask;  
    65. // 回滚速度  
    66. public float MOVE_SPEED = 8;  
    67. // 第一次执行布局  
    68. private boolean isLayout = false;  
    69. // 是否可以下拉  
    70. private boolean canPull = true;  
    71. // 在刷新过程中滑动操作  
    72. private boolean isTouchInRefreshing = false;  
    73. // 手指滑动距离与下拉头的滑动距离比,中间会随正切函数变化  
    74. private float radio = 2;  
    75. // 下拉箭头的转180°动画  
    76. private RotateAnimation rotateAnimation;  
    77. // 均匀旋转动画  
    78. private RotateAnimation refreshingAnimation;  
    79. // 下拉的箭头  
    80. private View pullView;  
    81. // 正在刷新的图标  
    82. private View refreshingView;  
    83. // 刷新结果图标  
    84. private View stateImageView;  
    85. // 刷新结果:成功或失败  
    86. private TextView stateTextView;  
    87. /**
    88.      * 执行自动回滚的handler
    89.      */  
    90. new Handler()  
    91.     {  
    92.   
    93. @Override  
    94. public void handleMessage(Message msg)  
    95.         {  
    96. // 回弹速度随下拉距离moveDeltaY增大而增大  
    97. float) (8 + 5 * Math.tan(Math.PI / 2 / getMeasuredHeight() * moveDeltaY));  
    98. if (state == REFRESHING && moveDeltaY <= refreshDist && !isTouchInRefreshing)  
    99.             {  
    100. // 正在刷新,且没有往上推的话则悬停,显示"正在刷新..."  
    101.                 moveDeltaY = refreshDist;  
    102.                 mTask.cancel();  
    103.             }  
    104. if (canPull)  
    105.                 moveDeltaY -= MOVE_SPEED;  
    106. if (moveDeltaY <= 0)  
    107.             {  
    108. // 已完成回弹  
    109. 0;  
    110.                 pullView.clearAnimation();  
    111. // 隐藏下拉头时有可能还在刷新,只有当前状态不是正在刷新时才改变状态  
    112. if (state != REFRESHING)  
    113.                     changeState(PULL_TO_REFRESH);  
    114.                 mTask.cancel();  
    115.             }  
    116. // 刷新布局,会自动调用onLayout  
    117.             requestLayout();  
    118.         }  
    119.   
    120.     };  
    121.   
    122. public void setOnRefreshListener(OnRefreshListener listener)  
    123.     {  
    124.         mListener = listener;  
    125.     }  
    126.   
    127. public PullToRefreshLayout(Context context)  
    128.     {  
    129. super(context);  
    130.         initView(context);  
    131.     }  
    132.   
    133. public PullToRefreshLayout(Context context, AttributeSet attrs)  
    134.     {  
    135. super(context, attrs);  
    136.         initView(context);  
    137.     }  
    138.   
    139. public PullToRefreshLayout(Context context, AttributeSet attrs, int defStyle)  
    140.     {  
    141. super(context, attrs, defStyle);  
    142.         initView(context);  
    143.     }  
    144.   
    145. private void initView(Context context)  
    146.     {  
    147. new Timer();  
    148. new MyTimerTask(updateHandler);  
    149.         rotateAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.reverse_anim);  
    150.         refreshingAnimation = (RotateAnimation) AnimationUtils.loadAnimation(context, R.anim.rotating);  
    151. // 添加匀速转动动画  
    152. new LinearInterpolator();  
    153.         rotateAnimation.setInterpolator(lir);  
    154.         refreshingAnimation.setInterpolator(lir);  
    155.     }  
    156.   
    157. private void hideHead()  
    158.     {  
    159. if (mTask != null)  
    160.         {  
    161.             mTask.cancel();  
    162. null;  
    163.         }  
    164. new MyTimerTask(updateHandler);  
    165. 0, 5);  
    166.     }  
    167.   
    168. /**
    169.      * 完成刷新操作,显示刷新结果
    170.      */  
    171. public void refreshFinish(int refreshResult)  
    172.     {  
    173.         refreshingView.clearAnimation();  
    174.         refreshingView.setVisibility(View.GONE);  
    175. switch (refreshResult)  
    176.         {  
    177. case REFRESH_SUCCEED:  
    178. // 刷新成功  
    179.             stateImageView.setVisibility(View.VISIBLE);  
    180.             stateTextView.setText(R.string.refresh_succeed);  
    181.             stateImageView.setBackgroundResource(R.drawable.refresh_succeed);  
    182. break;  
    183. case REFRESH_FAIL:  
    184. // 刷新失败  
    185.             stateImageView.setVisibility(View.VISIBLE);  
    186.             stateTextView.setText(R.string.refresh_fail);  
    187.             stateImageView.setBackgroundResource(R.drawable.refresh_failed);  
    188. break;  
    189. default:  
    190. break;  
    191.         }  
    192. // 刷新结果停留1秒  
    193. new Handler()  
    194.         {  
    195. @Override  
    196. public void handleMessage(Message msg)  
    197.             {  
    198.                 state = PULL_TO_REFRESH;  
    199.                 hideHead();  
    200.             }  
    201. 0, 1000);  
    202.     }  
    203.   
    204. private void changeState(int to)  
    205.     {  
    206.         state = to;  
    207. switch (state)  
    208.         {  
    209. case PULL_TO_REFRESH:  
    210. // 下拉刷新  
    211.             stateImageView.setVisibility(View.GONE);  
    212.             stateTextView.setText(R.string.pull_to_refresh);  
    213.             pullView.clearAnimation();  
    214.             pullView.setVisibility(View.VISIBLE);  
    215. break;  
    216. case RELEASE_TO_REFRESH:  
    217. // 释放刷新  
    218.             stateTextView.setText(R.string.release_to_refresh);  
    219.             pullView.startAnimation(rotateAnimation);  
    220. break;  
    221. case REFRESHING:  
    222. // 正在刷新  
    223.             pullView.clearAnimation();  
    224.             refreshingView.setVisibility(View.VISIBLE);  
    225.             pullView.setVisibility(View.INVISIBLE);  
    226.             refreshingView.startAnimation(refreshingAnimation);  
    227.             stateTextView.setText(R.string.refreshing);  
    228. break;  
    229. default:  
    230. break;  
    231.         }  
    232.     }  
    233.   
    234. /*
    235.      * (非 Javadoc)由父控件决定是否分发事件,防止事件冲突
    236.      * 
    237.      * @see android.view.ViewGroup#dispatchTouchEvent(android.view.MotionEvent)
    238.      */  
    239. @Override  
    240. public boolean dispatchTouchEvent(MotionEvent ev)  
    241.     {  
    242. switch (ev.getActionMasked())  
    243.         {  
    244. case MotionEvent.ACTION_DOWN:  
    245.             downY = ev.getY();  
    246.             lastY = downY;  
    247. if (mTask != null)  
    248.             {  
    249.                 mTask.cancel();  
    250.             }  
    251. /*
    252.              * 触碰的地方位于下拉头布局,由于我们没有对下拉头做事件响应,这时候它会给咱返回一个false导致接下来的事件不再分发进来。
    253.              * 所以我们不能交给父类分发,直接返回true
    254.              */  
    255. if (ev.getY() < moveDeltaY)  
    256. return true;  
    257. break;  
    258. case MotionEvent.ACTION_MOVE:  
    259. // canPull这个值在底下onTouch中会根据ListView是否滑到顶部来改变,意思是是否可下拉  
    260. if (canPull)  
    261.             {  
    262. // 对实际滑动距离做缩小,造成用力拉的感觉  
    263.                 moveDeltaY = moveDeltaY + (ev.getY() - lastY) / radio;  
    264. if (moveDeltaY < 0)  
    265. 0;  
    266. if (moveDeltaY > getMeasuredHeight())  
    267.                     moveDeltaY = getMeasuredHeight();  
    268. if (state == REFRESHING)  
    269.                 {  
    270. // 正在刷新的时候触摸移动  
    271. true;  
    272.                 }  
    273.             }  
    274.             lastY = ev.getY();  
    275. // 根据下拉距离改变比例  
    276. float) (2 + 2 * Math.tan(Math.PI / 2 / getMeasuredHeight() * moveDeltaY));  
    277.             requestLayout();  
    278. if (moveDeltaY <= refreshDist && state == RELEASE_TO_REFRESH)  
    279.             {  
    280. // 如果下拉距离没达到刷新的距离且当前状态是释放刷新,改变状态为下拉刷新  
    281.                 changeState(PULL_TO_REFRESH);  
    282.             }  
    283. if (moveDeltaY >= refreshDist && state == PULL_TO_REFRESH)  
    284.             {  
    285.                 changeState(RELEASE_TO_REFRESH);  
    286.             }  
    287. if (moveDeltaY > 8)  
    288.             {  
    289. // 防止下拉过程中误触发长按事件和点击事件  
    290.                 clearContentViewEvents();  
    291.             }  
    292. if (moveDeltaY > 0)  
    293.             {  
    294. // 正在下拉,不让子控件捕获事件  
    295. return true;  
    296.             }  
    297. break;  
    298. case MotionEvent.ACTION_UP:  
    299. if (moveDeltaY > refreshDist)  
    300. // 正在刷新时往下拉释放后下拉头不隐藏  
    301. false;  
    302. if (state == RELEASE_TO_REFRESH)  
    303.             {  
    304.                 changeState(REFRESHING);  
    305. // 刷新操作  
    306. if (mListener != null)  
    307.                     mListener.onRefresh();  
    308. else  
    309.             {  
    310.   
    311.             }  
    312.             hideHead();  
    313. default:  
    314. break;  
    315.         }  
    316. // 事件分发交给父类  
    317. return super.dispatchTouchEvent(ev);  
    318.     }  
    319.   
    320. /**
    321.      * 通过反射修改字段去掉长按事件和点击事件
    322.      */  
    323. private void clearContentViewEvents()  
    324.     {  
    325. try  
    326.         {  
    327. class.getDeclaredFields();  
    328. for (int i = 0; i < fields.length; i++)  
    329. if (fields[i].getName().equals("mPendingCheckForLongPress"))  
    330.                 {  
    331. // mPendingCheckForLongPress是AbsListView中的字段,通过反射获取并从消息列表删除,去掉长按事件  
    332. true);  
    333.                     contentView.getHandler().removeCallbacks((Runnable) fields[i].get(contentView));  
    334. else if (fields[i].getName().equals("mTouchMode"))  
    335.                 {  
    336. // TOUCH_MODE_REST = -1, 这个可以去除点击事件  
    337. true);  
    338. 1);  
    339.                 }  
    340. // 去掉焦点  
    341. new int[]  
    342. 0 });  
    343. catch (Exception e)  
    344.         {  
    345. "error : " + e.toString());  
    346.         }  
    347.     }  
    348.   
    349. /*
    350.      * (非 Javadoc)绘制阴影效果,颜色值可以修改
    351.      * 
    352.      * @see android.view.ViewGroup#dispatchDraw(android.graphics.Canvas)
    353.      */  
    354. @Override  
    355. protected void dispatchDraw(Canvas canvas)  
    356.     {  
    357. super.dispatchDraw(canvas);  
    358. if (moveDeltaY == 0)  
    359. return;  
    360. new RectF(0, 0, getMeasuredWidth(), moveDeltaY);  
    361. new Paint();  
    362. true);  
    363. // 阴影的高度为26  
    364. new LinearGradient(0, moveDeltaY, 0, moveDeltaY - 26, 0x66000000, 0x00000000, TileMode.CLAMP);  
    365.         paint.setShader(linearGradient);  
    366.         paint.setStyle(Style.FILL);  
    367. // 在moveDeltaY处往上变淡  
    368.         canvas.drawRect(rectF, paint);  
    369.     }  
    370.   
    371. private void initView()  
    372.     {  
    373.         pullView = headView.findViewById(R.id.pull_icon);  
    374.         stateTextView = (TextView) headView.findViewById(R.id.state_tv);  
    375.         refreshingView = headView.findViewById(R.id.refreshing_icon);  
    376.         stateImageView = headView.findViewById(R.id.state_iv);  
    377.     }  
    378.   
    379. @Override  
    380. protected void onLayout(boolean changed, int l, int t, int r, int b)  
    381.     {  
    382. if (!isLayout)  
    383.         {  
    384. // 这里是第一次进来的时候做一些初始化  
    385. 0);  
    386. 1);  
    387. // 给AbsListView设置OnTouchListener  
    388. this);  
    389. true;  
    390.             initView();  
    391. 0).getMeasuredHeight();  
    392.         }  
    393. if (canPull)  
    394.         {  
    395. // 改变子控件的布局  
    396. 0, (int) moveDeltaY - headView.getMeasuredHeight(), headView.getMeasuredWidth(), (int) moveDeltaY);  
    397. 0, (int) moveDeltaY, contentView.getMeasuredWidth(), (int) moveDeltaY + contentView.getMeasuredHeight());  
    398. else super.onLayout(changed, l, t, r, b);  
    399.     }  
    400.   
    401. class MyTimerTask extends TimerTask  
    402.     {  
    403.         Handler handler;  
    404.   
    405. public MyTimerTask(Handler handler)  
    406.         {  
    407. this.handler = handler;  
    408.         }  
    409.   
    410. @Override  
    411. public void run()  
    412.         {  
    413.             handler.sendMessage(handler.obtainMessage());  
    414.         }  
    415.   
    416.     }  
    417.   
    418. @Override  
    419. public boolean onTouch(View v, MotionEvent event)  
    420.     {  
    421. // 第一个item可见且滑动到顶部  
    422. null;  
    423. try  
    424.         {  
    425.             alv = (AbsListView) v;  
    426. catch (Exception e)  
    427.         {  
    428.             Log.d(TAG, e.getMessage());  
    429. return false;  
    430.         }  
    431. if (alv.getCount() == 0)  
    432.         {  
    433. // 没有item的时候也可以下拉刷新  
    434. true;  
    435. else if (alv.getFirstVisiblePosition() == 0 && alv.getChildAt(0).getTop() >= 0)  
    436.         {  
    437. // 滑到AbsListView的顶部了  
    438. true;  
    439. else  
    440. false;  
    441. return false;  
    442.     }  
    443. }


    代码中的注释已经写的很清楚了。 

    既然PullToRefreshLayout已经写好了,接下来就来使用这个Layout实现下拉刷新~

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

    [java]  view plain copy

    1. public interface OnRefreshListener {  
    2. void onRefresh();  
    3. }


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

    看一下MainActivity的布局: 

    [html]  view plain copy

      1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"  
      2. android:id="@+id/refresh_view"  
      3. android:layout_width="match_parent"  
      4. android:layout_height="match_parent" >  
      5.   
      6. <include layout="@layout/refresh_head" />  
      7.       
      8. <!-- 支持AbsListView的所有子类 -->  
      9. <ListView  
      10. android:id="@+id/content_view"  
      11. android:layout_width="match_parent"  
      12. android:layout_height="match_parent"  
      13. android:background="@color/white"  
      14. android:divider="@color/gray"  
      15. android:dividerHeight="1dp" >  
      16. </ListView>  
      17.   
      18. </com.jingchen.pulltorefresh.PullToRefreshLayout>


      PullToRefreshLayout只能包含两个子控件:refresh_head和content_view。

      看一下refresh_head的布局:

        [html]  view plain copy


        1. <?xml version="1.0" encoding="utf-8"?>  
        2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        3. android:id="@+id/head_view"  
        4. android:layout_width="match_parent"  
        5. android:layout_height="match_parent"  
        6. android:background="@color/light_blue" >  
        7.   
        8. <RelativeLayout  
        9. android:layout_width="match_parent"  
        10. android:layout_height="wrap_content"  
        11. android:layout_alignParentBottom="true"  
        12. android:paddingBottom="20dp"  
        13. android:paddingTop="20dp" >  
        14.   
        15. <RelativeLayout  
        16. android:layout_width="match_parent"  
        17. android:layout_height="wrap_content"  
        18. android:layout_centerInParent="true" >  
        19.   
        20. <ImageView  
        21. android:id="@+id/pull_icon"  
        22. android:layout_width="wrap_content"  
        23. android:layout_height="wrap_content"  
        24. android:layout_centerVertical="true"  
        25. android:layout_marginLeft="60dp"  
        26. android:background="@drawable/pull_icon_big" />  
        27.   
        28. <ImageView  
        29. android:id="@+id/refreshing_icon"  
        30. android:layout_width="wrap_content"  
        31. android:layout_height="wrap_content"  
        32. android:layout_centerVertical="true"  
        33. android:layout_marginLeft="60dp"  
        34. android:background="@drawable/refreshing"  
        35. android:visibility="gone" />  
        36.   
        37. <TextView  
        38. android:id="@+id/state_tv"  
        39. android:layout_width="wrap_content"  
        40. android:layout_height="wrap_content"  
        41. android:layout_centerInParent="true"  
        42. android:text="@string/pull_to_refresh"  
        43. android:textColor="@color/white"  
        44. android:textSize="16sp" />  
        45.   
        46. <ImageView  
        47. android:id="@+id/state_iv"  
        48. android:layout_width="wrap_content"  
        49. android:layout_height="wrap_content"  
        50. android:layout_centerVertical="true"  
        51. android:layout_marginRight="8dp"  
        52. android:layout_toLeftOf="@id/state_tv"  
        53. android:visibility="gone" />  
        54. </RelativeLayout>  
        55. </RelativeLayout>  
        56.   
        57. </RelativeLayout>

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

        接下来是MainActivity的代码:

         [java]  view plain copy 

          1. package com.jingchen.pulltorefresh;  
          2.   
          3. import java.util.ArrayList;  
          4. import java.util.List;  
          5.   
          6. import android.app.Activity;  
          7. import android.content.Context;  
          8. import android.os.Bundle;  
          9. import android.os.Handler;  
          10. import android.os.Message;  
          11. import android.support.v4.view.PagerAdapter;  
          12. import android.support.v4.view.ViewPager;  
          13. import android.support.v4.view.ViewPager.OnPageChangeListener;  
          14. import android.view.LayoutInflater;  
          15. import android.view.View;  
          16. import android.view.View.OnClickListener;  
          17. import android.view.ViewGroup;  
          18. import android.view.animation.AnimationUtils;  
          19. import android.view.animation.LinearInterpolator;  
          20. import android.view.animation.RotateAnimation;  
          21. import android.widget.AbsListView;  
          22. import android.widget.AdapterView;  
          23. import android.widget.AdapterView.OnItemClickListener;  
          24. import android.widget.AdapterView.OnItemLongClickListener;  
          25. import android.widget.BaseExpandableListAdapter;  
          26. import android.widget.ExpandableListView;  
          27. import android.widget.ExpandableListView.OnChildClickListener;  
          28. import android.widget.ExpandableListView.OnGroupClickListener;  
          29. import android.widget.ListView;  
          30. import android.widget.TextView;  
          31. import android.widget.Toast;  
          32.   
          33. /**
          34.  * 除了下拉刷新,在contenview为ListView的情况下我给ListView增加了FooterView,实现点击加载更多
          35.  * 
          36.  * @author 陈靖
          37.  * 
          38.  */  
          39. public class MainActivity extends Activity implements OnRefreshListener, OnClickListener  
          40. {  
          41. private AbsListView alv;  
          42. private PullToRefreshLayout refreshLayout;  
          43. private View loading;  
          44. private RotateAnimation loadingAnimation;  
          45. private TextView loadTextView;  
          46. private MyAdapter adapter;  
          47. private boolean isLoading = false;  
          48.   
          49. @Override  
          50. protected void onCreate(Bundle savedInstanceState)  
          51.     {  
          52. super.onCreate(savedInstanceState);  
          53.         setContentView(R.layout.activity_main);  
          54.         init();  
          55.     }  
          56.   
          57. private void init()  
          58.     {  
          59.         alv = (AbsListView) findViewById(R.id.content_view);  
          60.         refreshLayout = (PullToRefreshLayout) findViewById(R.id.refresh_view);  
          61. this);  
          62.         initListView();  
          63.   
          64. this, R.anim.rotating);  
          65. // 添加匀速转动动画  
          66. new LinearInterpolator();  
          67.         loadingAnimation.setInterpolator(lir);  
          68.     }  
          69.   
          70. /**
          71.      * ListView初始化方法
          72.      */  
          73. private void initListView()  
          74.     {  
          75. new ArrayList<String>();  
          76. for (int i = 0; i < 30; i++)  
          77.         {  
          78. "这里是item " + i);  
          79.         }  
          80. // 添加head  
          81. null);  
          82. null, false);  
          83. // 添加footer  
          84. null);  
          85.         loading = footerView.findViewById(R.id.loading_icon);  
          86.         loadTextView = (TextView) footerView.findViewById(R.id.loadmore_tv);  
          87. null, false);  
          88. this);  
          89. new MyAdapter(this, items);  
          90.         alv.setAdapter(adapter);  
          91. new OnItemLongClickListener()  
          92.         {  
          93.   
          94. @Override  
          95. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          96.             {  
          97. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          98. return true;  
          99.             }  
          100.         });  
          101. new OnItemClickListener()  
          102.         {  
          103.   
          104. @Override  
          105. public void onItemClick(AdapterView<?> parent, View view, int position, long id)  
          106.             {  
          107. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          108.             }  
          109.         });  
          110.     }  
          111.   
          112. /**
          113.      * GridView初始化方法
          114.      */  
          115. private void initGridView()  
          116.     {  
          117. new ArrayList<String>();  
          118. for (int i = 0; i < 30; i++)  
          119.         {  
          120. "这里是item " + i);  
          121.         }  
          122. new MyAdapter(this, items);  
          123.         alv.setAdapter(adapter);  
          124. new OnItemLongClickListener()  
          125.         {  
          126.   
          127. @Override  
          128. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          129.             {  
          130. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          131. return true;  
          132.             }  
          133.         });  
          134. new OnItemClickListener()  
          135.         {  
          136.   
          137. @Override  
          138. public void onItemClick(AdapterView<?> parent, View view, int position, long id)  
          139.             {  
          140. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          141.             }  
          142.         });  
          143.     }  
          144.   
          145. /**
          146.      * ExpandableListView初始化方法
          147.      */  
          148. private void initExpandableListView()  
          149.     {  
          150. new ExpandableListAdapter(this));  
          151. new OnChildClickListener()  
          152.         {  
          153.   
          154. @Override  
          155. public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)  
          156.             {  
          157. this, " Click on group " + groupPosition + " item " + childPosition, Toast.LENGTH_SHORT).show();  
          158. return true;  
          159.             }  
          160.         });  
          161. new OnItemLongClickListener()  
          162.         {  
          163.   
          164. @Override  
          165. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          166.             {  
          167. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          168. return true;  
          169.             }  
          170.         });  
          171. new OnGroupClickListener()  
          172.         {  
          173.   
          174. @Override  
          175. public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)  
          176.             {  
          177. if (parent.isGroupExpanded(groupPosition))  
          178.                 {  
          179. // 如果展开则关闭  
          180.                     parent.collapseGroup(groupPosition);  
          181. else  
          182.                 {  
          183. // 如果关闭则打开,注意这里是手动打开不要默认滚动否则会有bug  
          184.                     parent.expandGroup(groupPosition);  
          185.                 }  
          186. return true;  
          187.             }  
          188.         });  
          189.     }  
          190.   
          191. @Override  
          192. public void onRefresh()  
          193.     {  
          194. // 下拉刷新操作  
          195. new Handler()  
          196.         {  
          197. @Override  
          198. public void handleMessage(Message msg)  
          199.             {  
          200.                 refreshLayout.refreshFinish(PullToRefreshLayout.REFRESH_SUCCEED);  
          201.             }  
          202. 0, 5000);  
          203.     }  
          204.   
          205. @Override  
          206. public void onClick(View v)  
          207.     {  
          208. switch (v.getId())  
          209.         {  
          210. case R.id.loadmore_layout:  
          211. if (!isLoading)  
          212.             {  
          213.                 loading.setVisibility(View.VISIBLE);  
          214.                 loading.startAnimation(loadingAnimation);  
          215.                 loadTextView.setText(R.string.loading);  
          216. true;  
          217.             }  
          218. break;  
          219. default:  
          220. break;  
          221.         }  
          222.   
          223.     }  
          224.   
          225. class ExpandableListAdapter extends BaseExpandableListAdapter  
          226.     {  
          227. private String[] groupsStrings;// = new String[] { "这里是group 0",  
          228. // "这里是group 1", "这里是group 2" };  
          229. private String[][] groupItems;  
          230. private Context context;  
          231.   
          232. public ExpandableListAdapter(Context context)  
          233.         {  
          234. this.context = context;  
          235. new String[8];  
          236. for (int i = 0; i < groupsStrings.length; i++)  
          237.             {  
          238. new String("这里是group " + i);  
          239.             }  
          240. new String[8][8];  
          241. for (int i = 0; i < groupItems.length; i++)  
          242. for (int j = 0; j < groupItems[i].length; j++)  
          243.                 {  
          244. new String("这里是group " + i + "里的item " + j);  
          245.                 }  
          246.         }  
          247.   
          248. @Override  
          249. public int getGroupCount()  
          250.         {  
          251. return groupsStrings.length;  
          252.         }  
          253.   
          254. @Override  
          255. public int getChildrenCount(int groupPosition)  
          256.         {  
          257. return groupItems[groupPosition].length;  
          258.         }  
          259.   
          260. @Override  
          261. public Object getGroup(int groupPosition)  
          262.         {  
          263. return groupsStrings[groupPosition];  
          264.         }  
          265.   
          266. @Override  
          267. public Object getChild(int groupPosition, int childPosition)  
          268.         {  
          269. return groupItems[groupPosition][childPosition];  
          270.         }  
          271.   
          272. @Override  
          273. public long getGroupId(int groupPosition)  
          274.         {  
          275. return groupPosition;  
          276.         }  
          277.   
          278. @Override  
          279. public long getChildId(int groupPosition, int childPosition)  
          280.         {  
          281. return childPosition;  
          282.         }  
          283.   
          284. @Override  
          285. public boolean hasStableIds()  
          286.         {  
          287. return true;  
          288.         }  
          289.   
          290. @Override  
          291. public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)  
          292.         {  
          293. null);  
          294.             TextView tv = (TextView) view.findViewById(R.id.name_tv);  
          295.             tv.setText(groupsStrings[groupPosition]);  
          296. return view;  
          297.         }  
          298.   
          299. @Override  
          300. public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)  
          301.         {  
          302. null);  
          303.             TextView tv = (TextView) view.findViewById(R.id.name_tv);  
          304.             tv.setText(groupItems[groupPosition][childPosition]);  
          305. return view;  
          306.         }  
          307.   
          308. @Override  
          309. public boolean isChildSelectable(int groupPosition, int childPosition)  
          310.         {  
          311. return true;  
          312.         }  
          313.   
          314.     }  
          315.   
          316. }

          1. package com.jingchen.pulltorefresh;  
          2.   
          3. import java.util.ArrayList;  
          4. import java.util.List;  
          5.   
          6. import android.app.Activity;  
          7. import android.content.Context;  
          8. import android.os.Bundle;  
          9. import android.os.Handler;  
          10. import android.os.Message;  
          11. import android.support.v4.view.PagerAdapter;  
          12. import android.support.v4.view.ViewPager;  
          13. import android.support.v4.view.ViewPager.OnPageChangeListener;  
          14. import android.view.LayoutInflater;  
          15. import android.view.View;  
          16. import android.view.View.OnClickListener;  
          17. import android.view.ViewGroup;  
          18. import android.view.animation.AnimationUtils;  
          19. import android.view.animation.LinearInterpolator;  
          20. import android.view.animation.RotateAnimation;  
          21. import android.widget.AbsListView;  
          22. import android.widget.AdapterView;  
          23. import android.widget.AdapterView.OnItemClickListener;  
          24. import android.widget.AdapterView.OnItemLongClickListener;  
          25. import android.widget.BaseExpandableListAdapter;  
          26. import android.widget.ExpandableListView;  
          27. import android.widget.ExpandableListView.OnChildClickListener;  
          28. import android.widget.ExpandableListView.OnGroupClickListener;  
          29. import android.widget.ListView;  
          30. import android.widget.TextView;  
          31. import android.widget.Toast;  
          32.   
          33. /**
          34.  * 除了下拉刷新,在contenview为ListView的情况下我给ListView增加了FooterView,实现点击加载更多
          35.  * 
          36.  * @author 陈靖
          37.  * 
          38.  */  
          39. public class MainActivity extends Activity implements OnRefreshListener, OnClickListener  
          40. {  
          41. private AbsListView alv;  
          42. private PullToRefreshLayout refreshLayout;  
          43. private View loading;  
          44. private RotateAnimation loadingAnimation;  
          45. private TextView loadTextView;  
          46. private MyAdapter adapter;  
          47. private boolean isLoading = false;  
          48.   
          49. @Override  
          50. protected void onCreate(Bundle savedInstanceState)  
          51.     {  
          52. super.onCreate(savedInstanceState);  
          53.         setContentView(R.layout.activity_main);  
          54.         init();  
          55.     }  
          56.   
          57. private void init()  
          58.     {  
          59.         alv = (AbsListView) findViewById(R.id.content_view);  
          60.         refreshLayout = (PullToRefreshLayout) findViewById(R.id.refresh_view);  
          61. this);  
          62.         initListView();  
          63.   
          64. this, R.anim.rotating);  
          65. // 添加匀速转动动画  
          66. new LinearInterpolator();  
          67.         loadingAnimation.setInterpolator(lir);  
          68.     }  
          69.   
          70. /**
          71.      * ListView初始化方法
          72.      */  
          73. private void initListView()  
          74.     {  
          75. new ArrayList<String>();  
          76. for (int i = 0; i < 30; i++)  
          77.         {  
          78. "这里是item " + i);  
          79.         }  
          80. // 添加head  
          81. null);  
          82. null, false);  
          83. // 添加footer  
          84. null);  
          85.         loading = footerView.findViewById(R.id.loading_icon);  
          86.         loadTextView = (TextView) footerView.findViewById(R.id.loadmore_tv);  
          87. null, false);  
          88. this);  
          89. new MyAdapter(this, items);  
          90.         alv.setAdapter(adapter);  
          91. new OnItemLongClickListener()  
          92.         {  
          93.   
          94. @Override  
          95. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          96.             {  
          97. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          98. return true;  
          99.             }  
          100.         });  
          101. new OnItemClickListener()  
          102.         {  
          103.   
          104. @Override  
          105. public void onItemClick(AdapterView<?> parent, View view, int position, long id)  
          106.             {  
          107. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          108.             }  
          109.         });  
          110.     }  
          111.   
          112. /**
          113.      * GridView初始化方法
          114.      */  
          115. private void initGridView()  
          116.     {  
          117. new ArrayList<String>();  
          118. for (int i = 0; i < 30; i++)  
          119.         {  
          120. "这里是item " + i);  
          121.         }  
          122. new MyAdapter(this, items);  
          123.         alv.setAdapter(adapter);  
          124. new OnItemLongClickListener()  
          125.         {  
          126.   
          127. @Override  
          128. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          129.             {  
          130. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          131. return true;  
          132.             }  
          133.         });  
          134. new OnItemClickListener()  
          135.         {  
          136.   
          137. @Override  
          138. public void onItemClick(AdapterView<?> parent, View view, int position, long id)  
          139.             {  
          140. this, " Click on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          141.             }  
          142.         });  
          143.     }  
          144.   
          145. /**
          146.      * ExpandableListView初始化方法
          147.      */  
          148. private void initExpandableListView()  
          149.     {  
          150. new ExpandableListAdapter(this));  
          151. new OnChildClickListener()  
          152.         {  
          153.   
          154. @Override  
          155. public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id)  
          156.             {  
          157. this, " Click on group " + groupPosition + " item " + childPosition, Toast.LENGTH_SHORT).show();  
          158. return true;  
          159.             }  
          160.         });  
          161. new OnItemLongClickListener()  
          162.         {  
          163.   
          164. @Override  
          165. public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)  
          166.             {  
          167. this, "LongClick on " + parent.getAdapter().getItemId(position), Toast.LENGTH_SHORT).show();  
          168. return true;  
          169.             }  
          170.         });  
          171. new OnGroupClickListener()  
          172.         {  
          173.   
          174. @Override  
          175. public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)  
          176.             {  
          177. if (parent.isGroupExpanded(groupPosition))  
          178.                 {  
          179. // 如果展开则关闭  
          180.                     parent.collapseGroup(groupPosition);  
          181. else  
          182.                 {  
          183. // 如果关闭则打开,注意这里是手动打开不要默认滚动否则会有bug  
          184.                     parent.expandGroup(groupPosition);  
          185.                 }  
          186. return true;  
          187.             }  
          188.         });  
          189.     }  
          190.   
          191. @Override  
          192. public void onRefresh()  
          193.     {  
          194. // 下拉刷新操作  
          195. new Handler()  
          196.         {  
          197. @Override  
          198. public void handleMessage(Message msg)  
          199.             {  
          200.                 refreshLayout.refreshFinish(PullToRefreshLayout.REFRESH_SUCCEED);  
          201.             }  
          202. 0, 5000);  
          203.     }  
          204.   
          205. @Override  
          206. public void onClick(View v)  
          207.     {  
          208. switch (v.getId())  
          209.         {  
          210. case R.id.loadmore_layout:  
          211. if (!isLoading)  
          212.             {  
          213.                 loading.setVisibility(View.VISIBLE);  
          214.                 loading.startAnimation(loadingAnimation);  
          215.                 loadTextView.setText(R.string.loading);  
          216. true;  
          217.             }  
          218. break;  
          219. default:  
          220. break;  
          221.         }  
          222.   
          223.     }  
          224.   
          225. class ExpandableListAdapter extends BaseExpandableListAdapter  
          226.     {  
          227. private String[] groupsStrings;// = new String[] { "这里是group 0",  
          228. // "这里是group 1", "这里是group 2" };  
          229. private String[][] groupItems;  
          230. private Context context;  
          231.   
          232. public ExpandableListAdapter(Context context)  
          233.         {  
          234. this.context = context;  
          235. new String[8];  
          236. for (int i = 0; i < groupsStrings.length; i++)  
          237.             {  
          238. new String("这里是group " + i);  
          239.             }  
          240. new String[8][8];  
          241. for (int i = 0; i < groupItems.length; i++)  
          242. for (int j = 0; j < groupItems[i].length; j++)  
          243.                 {  
          244. new String("这里是group " + i + "里的item " + j);  
          245.                 }  
          246.         }  
          247.   
          248. @Override  
          249. public int getGroupCount()  
          250.         {  
          251. return groupsStrings.length;  
          252.         }  
          253.   
          254. @Override  
          255. public int getChildrenCount(int groupPosition)  
          256.         {  
          257. return groupItems[groupPosition].length;  
          258.         }  
          259.   
          260. @Override  
          261. public Object getGroup(int groupPosition)  
          262.         {  
          263. return groupsStrings[groupPosition];  
          264.         }  
          265.   
          266. @Override  
          267. public Object getChild(int groupPosition, int childPosition)  
          268.         {  
          269. return groupItems[groupPosition][childPosition];  
          270.         }  
          271.   
          272. @Override  
          273. public long getGroupId(int groupPosition)  
          274.         {  
          275. return groupPosition;  
          276.         }  
          277.   
          278. @Override  
          279. public long getChildId(int groupPosition, int childPosition)  
          280.         {  
          281. return childPosition;  
          282.         }  
          283.   
          284. @Override  
          285. public boolean hasStableIds()  
          286.         {  
          287. return true;  
          288.         }  
          289.   
          290. @Override  
          291. public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)  
          292.         {  
          293. null);  
          294.             TextView tv = (TextView) view.findViewById(R.id.name_tv);  
          295.             tv.setText(groupsStrings[groupPosition]);  
          296. return view;  
          297.         }  
          298.   
          299. @Override  
          300. public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)  
          301.         {  
          302. null);  
          303.             TextView tv = (TextView) view.findViewById(R.id.name_tv);  
          304.             tv.setText(groupItems[groupPosition][childPosition]);  
          305. return view;  
          306.         }  
          307.   
          308. @Override  
          309. public boolean isChildSelectable(int groupPosition, int childPosition)  
          310.         {  
          311. return true;  
          312.         }  
          313.   
          314.     }  
          315.   
          316. }

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

          [html]  view plain copy

          1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"  
          2. android:id="@+id/refresh_view"  
          3. android:layout_width="match_parent"  
          4. android:layout_height="match_parent" >  
          5.   
          6. <include layout="@layout/refresh_head" />  
          7. <!-- 支持AbsListView的所有子类 -->  
          8. <GridView  
          9. android:id="@+id/content_view"  
          10. android:layout_width="match_parent"  
          11. android:layout_height="match_parent"  
          12. android:background="@color/white"  
          13. android:columnWidth="90dp"  
          14. android:gravity="center"  
          15. android:horizontalSpacing="10dp"  
          16. android:numColumns="auto_fit"  
          17. android:stretchMode="columnWidth"  
          18. android:verticalSpacing="15dp" >  
          19. </GridView>  
          20.   
          21. </com.jingchen.pulltorefresh.PullToRefreshLayout>

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

           

          [html]  view plain copy

           

          1. <com.jingchen.pulltorefresh.PullToRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"  
          2. android:id="@+id/refresh_view"  
          3. android:layout_width="match_parent"  
          4. android:layout_height="match_parent" >  
          5.   
          6. <include layout="@layout/refresh_head" />  
          7. <!-- 支持AbsListView的所有子类 -->  
          8. <ExpandableListView  
          9. android:id="@+id/content_view"  
          10. android:layout_width="match_parent"  
          11. android:layout_height="match_parent"  
          12. android:background="@color/white" >  
          13. </ExpandableListView>  
          14.   
          15. </com.jingchen.pulltorefresh.PullToRefreshLayout>


           

           

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

          举报

          相关推荐

          0 条评论