Android中实现下拉刷新

分类:Android 1404人阅读 评论(1) 收藏 举报

需求:项目中的消息列表界面要求实现类似sina微博的下拉刷新;

思路:一般的消息列表为ListView类型,将list加载到adapter中,再将adapter加载到ListView中,从而实现消息列表的展示。而下拉刷新要求给消息列表加一个头部,其中有图片(向上/向下箭头)和提示字样(下拉刷新/松开刷新),从而我们需要做的事情:1.需要做一个head.xm来实现头部的样式定义;2.需要自定义一个继承了ListView的MsgListView,在该类中将head加在MsgListView对象的头部;3.将原有的消息界面布局文件中的ListView改为cn.xd.microblogging.tools.MsgListView(包名.MsgListView);4.将原有的消息界面后台代码中创建的ListView对象改为MsgListView对象,注意消息界面Activity要继承ListActivity,并且实现其刷新监听。

说明:实现下拉刷新很简单,仅需要4个文件:MsgRcvListActivity.java(消息列表界面的后台文件,真正的刷新动作在这里完成,下拉刷新相关code下面列出),msgrcvlistactivity.xml(消息列表界面的前台文件,下拉刷新相关code下面列出),MsgListView.java(本文件拿来主义即可不用改,下拉刷新自定义的列表类,监听并执行下拉刷新、松开刷新、正在刷新、完成刷新等状态的改变,但真正的刷新动作不在这里完成,完整code下面列出),head.xml(本文件拿来主义即可不用改,下拉刷新的样式定义文件,包括向上、向下箭头,刷新状态提示等,完整code下面列出);

效果

Android中实现下拉刷新_第1张图片

代码

1.MsgRcvListActivity.java :

[java] view plain copy
  1. publicclassMsgRcvListActivityextendsListActivity{//注意:要继承ListActivity
  2. …………//变量定义,略
  3. MsgListViewlist;
  4. @Override
  5. protectedvoidonCreate(BundlesavedInstanceState){
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.msgrcvlistactivity);
  8. list=(MsgListView)findViewById(android.R.id.list);//继承ListActivity,id要写成android.R.id.list,否则报异常
  9. …………//其他代码,略
  10. //刷新监听,此处实现真正刷新
  11. list.setonRefreshListener(newOnRefreshListener(){
  12. publicvoidonRefresh(){
  13. newAsyncTask<Void,Void,Void>(){
  14. protectedVoiddoInBackground(Void...params){
  15. try{
  16. Thread.sleep(1000);
  17. }catch(Exceptione){
  18. e.printStackTrace();
  19. }
  20. returnnull;
  21. }
  22. @Override
  23. protectedvoidonPostExecute(Voidresult){
  24. adapter.notifyDataSetChanged();
  25. newMsgLoad().execute();//刷新监听中,真正执行刷新动作
  26. list.onRefreshComplete();
  27. }
  28. }.execute(null);
  29. }
  30. });
  31. list.setItemsCanFocus(false);
  32. list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
  33. }
  34. …………//其他代码,略
  35. publicclassMsgLoadextendsAsyncTask<Void,Void,List<MsgBean>>{
  36. …………//其他代码,略
  37. }
  38. }

2.msgrcvlistactivity.xml:

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <LinearLayoutandroid:orientation="vertical"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. android:background="#000000">
  11. //将ListView改为包名.自定义的列表类名,MsgListView.java代码见后,此代码不需改通用,下拉刷新相关即此处,其他无关
  12. <cn.xd.microblogging.tools.MsgListView
  13. android:id="@android:id/list"
  14. android:layout_height="wrap_content"
  15. android:layout_width="fill_parent"
  16. android:layout_weight="1.0"
  17. android:drawSelectorOnTop="false"
  18. android:scrollbars="vertical"
  19. android:fadingEdgeLength="0dip"
  20. android:divider="@drawable/line"
  21. android:dividerHeight="3.0dip"
  22. android:cacheColorHint="#00000000"
  23. android:paddingBottom="40dp"/>
  24. </LinearLayout>
  25. <LinearLayout
  26. android:id="@+id/msg_list_load"
  27. android:layout_width="fill_parent"
  28. android:layout_height="fill_parent">
  29. <LinearLayoutandroid:id="@android:id/empty"
  30. android:layout_width="fill_parent"
  31. android:layout_height="fill_parent">
  32. <include
  33. android:id="@android:id/empty"
  34. layout="@layout/empty_loading"
  35. android:layout_width="fill_parent"
  36. android:layout_height="fill_parent"/>
  37. </LinearLayout>
  38. </LinearLayout>
  39. </RelativeLayout>

3.MsgListView.java,拷进项目基本不需改:

[java] view plain copy
  1. packagecn.xd.microblogging.tools;
  2. importjava.util.Date;
  3. importcn.xd.microblogging.R;
  4. importandroid.content.Context;
  5. importandroid.util.AttributeSet;
  6. importandroid.util.Log;
  7. importandroid.view.LayoutInflater;
  8. importandroid.view.MotionEvent;
  9. importandroid.view.View;
  10. importandroid.view.ViewGroup;
  11. importandroid.view.animation.LinearInterpolator;
  12. importandroid.view.animation.RotateAnimation;
  13. importandroid.widget.AbsListView;
  14. importandroid.widget.BaseAdapter;
  15. importandroid.widget.ImageView;
  16. importandroid.widget.LinearLayout;
  17. importandroid.widget.ListView;
  18. importandroid.widget.AbsListView.OnScrollListener;
  19. importandroid.widget.ProgressBar;
  20. importandroid.widget.TextView;
  21. /**
  22. *
  23. *自定义MsgListView,继承了ListView,
  24. *但填充了listview的头部,即下拉刷新样式,并实现其功能
  25. *@authoryanbo
  26. *
  27. */
  28. publicclassMsgListViewextendsListViewimplementsOnScrollListener{
  29. privatefinalstaticintRELEASE_To_REFRESH=0;
  30. privatefinalstaticintPULL_To_REFRESH=1;
  31. privatefinalstaticintREFRESHING=2;
  32. privatefinalstaticintDONE=3;
  33. privateLayoutInflaterinflater;
  34. privateLinearLayoutheadView;//头部
  35. privateTextViewtipsTextview;//下拉刷新
  36. privateTextViewlastUpdatedTextView;//最新更新
  37. privateImageViewarrowImageView;//箭头
  38. privateProgressBarprogressBar;//刷新进度条
  39. privateRotateAnimationanimation;//旋转特效刷新中箭头翻转向下变向上
  40. privateRotateAnimationreverseAnimation;
  41. //用于保证startY的值在一个完整的touch事件中只被记录一次
  42. privatebooleanisRecored;
  43. privateintheadContentWidth;//头部宽度
  44. privateintheadContentHeight;//头部高度
  45. privateintstartY;//高度起始位置,用来记录与头部距离
  46. privateintfirstItemIndex;//列表中首行索引,用来记录其与头部距离
  47. privateintstate;//下拉刷新中、松开刷新中、正在刷新中、完成刷新
  48. privatebooleanisBack;
  49. publicOnRefreshListenerrefreshListener;//刷新监听
  50. privatefinalstaticStringTAG="abc";
  51. publicMsgListView(Contextcontext,AttributeSetattrs){
  52. super(context,attrs);
  53. init(context);
  54. }
  55. privatevoidinit(Contextcontext){
  56. inflater=LayoutInflater.from(context);
  57. headView=(LinearLayout)inflater.inflate(R.layout.head,null);//listview拼接headview
  58. arrowImageView=(ImageView)headView
  59. .findViewById(R.id.head_arrowImageView);//headview中各view
  60. arrowImageView.setMinimumWidth(50);
  61. arrowImageView.setMinimumHeight(50);
  62. progressBar=(ProgressBar)headView
  63. .findViewById(R.id.head_progressBar);//headview中各view
  64. tipsTextview=(TextView)headView.findViewById(R.id.head_tipsTextView);//headview中各view
  65. lastUpdatedTextView=(TextView)headView
  66. .findViewById(R.id.head_lastUpdatedTextView);//headview中各view
  67. measureView(headView);
  68. headContentHeight=headView.getMeasuredHeight();//头部高度
  69. headContentWidth=headView.getMeasuredWidth();//头部宽度
  70. headView.setPadding(0,-1*headContentHeight,0,0);//setPadding(intleft,inttop,intright,intbottom)
  71. headView.invalidate();//Invalidatethewholeview
  72. Log.v("size","width:"+headContentWidth+"height:"
  73. +headContentHeight);
  74. addHeaderView(headView);//添加进headview
  75. setOnScrollListener(this);//滚动监听
  76. animation=newRotateAnimation(0,-180,
  77. RotateAnimation.RELATIVE_TO_SELF,0.5f,
  78. RotateAnimation.RELATIVE_TO_SELF,0.5f);
  79. animation.setInterpolator(newLinearInterpolator());
  80. animation.setDuration(250);
  81. animation.setFillAfter(true);//特效animation设置
  82. reverseAnimation=newRotateAnimation(-180,0,
  83. RotateAnimation.RELATIVE_TO_SELF,0.5f,
  84. RotateAnimation.RELATIVE_TO_SELF,0.5f);
  85. reverseAnimation.setInterpolator(newLinearInterpolator());
  86. reverseAnimation.setDuration(250);
  87. reverseAnimation.setFillAfter(true);//特效reverseAnimation设置
  88. }
  89. publicvoidonScroll(AbsListViewarg0,intfirstVisiableItem,intarg2,//滚动事件
  90. intarg3){
  91. firstItemIndex=firstVisiableItem;//得到首item索引
  92. }
  93. publicvoidonScrollStateChanged(AbsListViewarg0,intarg1){
  94. }
  95. publicbooleanonTouchEvent(MotionEventevent){//触摸事件
  96. switch(event.getAction()){
  97. caseMotionEvent.ACTION_DOWN://手按下对应下拉刷新状态
  98. if(firstItemIndex==0&&!isRecored){//如果首item索引为0,且尚未记录startY,则在下拉时记录之,并执行isRecored=true;
  99. startY=(int)event.getY();
  100. isRecored=true;
  101. Log.v(TAG,"在down时候记录当前位置‘");
  102. }
  103. break;
  104. caseMotionEvent.ACTION_UP://手松开对应松开刷新状态
  105. if(state!=REFRESHING){//手松开有4个状态:下拉刷新、松开刷新、正在刷新、完成刷新。如果当前不是正在刷新
  106. if(state==DONE){//如果当前是完成刷新,什么都不做
  107. }
  108. if(state==PULL_To_REFRESH){//如果当前是下拉刷新,状态设为完成刷新(意即下拉刷新中就松开了,实际未完成刷新),执行changeHeaderViewByState()
  109. state=DONE;
  110. changeHeaderViewByState();
  111. Log.v(TAG,"由下拉刷新状态,到done状态");
  112. }
  113. if(state==RELEASE_To_REFRESH){//如果当前是松开刷新,状态设为正在刷新(意即松开刷新中松开手,才是真正地刷新),执行changeHeaderViewByState()
  114. state=REFRESHING;
  115. changeHeaderViewByState();
  116. onRefresh();//真正刷新,所以执行onrefresh,执行后状态设为完成刷新
  117. Log.v(TAG,"由松开刷新状态,到done状态");
  118. }
  119. }
  120. isRecored=false;//手松开,则无论怎样,可以重新记录startY,因为只要手松开就认为一次刷新已完成
  121. isBack=false;
  122. break;
  123. caseMotionEvent.ACTION_MOVE://手拖动,拖动过程中不断地实时记录当前位置
  124. inttempY=(int)event.getY();
  125. if(!isRecored&&firstItemIndex==0){//如果首item索引为0,且尚未记录startY,则在拖动时记录之,并执行isRecored=true;
  126. Log.v(TAG,"在move时候记录下位置");
  127. isRecored=true;
  128. startY=tempY;
  129. }
  130. if(state!=REFRESHING&&isRecored){//如果状态不是正在刷新,且已记录startY:tempY为拖动过程中一直在变的高度,startY为拖动起始高度
  131. //可以松手去刷新了
  132. if(state==RELEASE_To_REFRESH){//如果状态是松开刷新
  133. //往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
  134. if((tempY-startY<headContentHeight)//如果实时高度大于起始高度,且两者之差小于头部高度,则状态设为下拉刷新
  135. &&(tempY-startY)>0){
  136. state=PULL_To_REFRESH;
  137. changeHeaderViewByState();
  138. Log.v(TAG,"由松开刷新状态转变到下拉刷新状态");
  139. }
  140. //一下子推到顶了
  141. elseif(tempY-startY<=0){//如果实时高度小于等于起始高度了,则说明到顶了,状态设为完成刷新
  142. state=DONE;
  143. changeHeaderViewByState();
  144. Log.v(TAG,"由松开刷新状态转变到done状态");
  145. }
  146. //往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
  147. else{//如果当前拖动过程中既没有到下拉刷新的地步,也没有到完成刷新(到顶)的地步,则保持松开刷新状态
  148. //不用进行特别的操作,只用更新paddingTop的值就行了
  149. }
  150. }
  151. //还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
  152. if(state==PULL_To_REFRESH){//如果状态是下拉刷新
  153. //下拉到可以进入RELEASE_TO_REFRESH的状态
  154. if(tempY-startY>=headContentHeight){//如果实时高度与起始高度之差大于等于头部高度,则状态设为松开刷新
  155. state=RELEASE_To_REFRESH;
  156. isBack=true;
  157. changeHeaderViewByState();
  158. Log.v(TAG,"由done或者下拉刷新状态转变到松开刷新");
  159. }
  160. //上推到顶了
  161. elseif(tempY-startY<=0){//如果实时高度小于等于起始高度了,则说明到顶了,状态设为完成刷新
  162. state=DONE;
  163. changeHeaderViewByState();
  164. Log.v(TAG,"由DOne或者下拉刷新状态转变到done状态");
  165. }
  166. }
  167. //done状态下
  168. if(state==DONE){//如果状态是完成刷新
  169. if(tempY-startY>0){//如果实时高度大于起始高度了,则状态设为下拉刷新
  170. state=PULL_To_REFRESH;
  171. changeHeaderViewByState();
  172. }
  173. }
  174. //更新headView的size
  175. if(state==PULL_To_REFRESH){//如果状态是下拉刷新,更新headview的size?
  176. headView.setPadding(0,-1*headContentHeight
  177. +(tempY-startY),0,0);
  178. headView.invalidate();
  179. }
  180. //更新headView的paddingTop
  181. if(state==RELEASE_To_REFRESH){//如果状态是松开刷新,更新headview的paddingtop?
  182. headView.setPadding(0,tempY-startY-headContentHeight,
  183. 0,0);
  184. headView.invalidate();
  185. }
  186. }
  187. break;
  188. }
  189. returnsuper.onTouchEvent(event);
  190. }
  191. //当状态改变时候,调用该方法,以更新界面
  192. privatevoidchangeHeaderViewByState(){
  193. switch(state){
  194. caseRELEASE_To_REFRESH:
  195. arrowImageView.setVisibility(View.VISIBLE);
  196. progressBar.setVisibility(View.GONE);
  197. tipsTextview.setVisibility(View.VISIBLE);
  198. lastUpdatedTextView.setVisibility(View.VISIBLE);
  199. arrowImageView.clearAnimation();
  200. arrowImageView.startAnimation(animation);
  201. tipsTextview.setText("松开刷新");
  202. Log.v(TAG,"当前状态,松开刷新");
  203. break;
  204. casePULL_To_REFRESH:
  205. progressBar.setVisibility(View.GONE);
  206. tipsTextview.setVisibility(View.VISIBLE);
  207. lastUpdatedTextView.setVisibility(View.VISIBLE);
  208. arrowImageView.clearAnimation();
  209. arrowImageView.setVisibility(View.VISIBLE);
  210. //是由RELEASE_To_REFRESH状态转变来的
  211. if(isBack){
  212. isBack=false;
  213. arrowImageView.clearAnimation();
  214. arrowImageView.startAnimation(reverseAnimation);
  215. tipsTextview.setText("下拉刷新");
  216. }else{
  217. tipsTextview.setText("下拉刷新");
  218. }
  219. Log.v(TAG,"当前状态,下拉刷新");
  220. break;
  221. caseREFRESHING:
  222. headView.setPadding(0,0,0,0);
  223. headView.invalidate();
  224. progressBar.setVisibility(View.VISIBLE);
  225. arrowImageView.clearAnimation();
  226. arrowImageView.setVisibility(View.GONE);
  227. tipsTextview.setText("正在刷新...");
  228. lastUpdatedTextView.setVisibility(View.VISIBLE);
  229. Log.v(TAG,"当前状态,正在刷新...");
  230. break;
  231. caseDONE:
  232. headView.setPadding(0,-1*headContentHeight,0,0);
  233. headView.invalidate();
  234. progressBar.setVisibility(View.GONE);
  235. arrowImageView.clearAnimation();
  236. arrowImageView
  237. .setImageResource(R.drawable.ic_pulltorefresh_arrow);
  238. tipsTextview.setText("下拉刷新");
  239. lastUpdatedTextView.setVisibility(View.VISIBLE);
  240. Log.v(TAG,"当前状态,done");
  241. break;
  242. }
  243. }
  244. publicvoidsetonRefreshListener(OnRefreshListenerrefreshListener){
  245. this.refreshListener=refreshListener;
  246. }
  247. publicinterfaceOnRefreshListener{
  248. publicvoidonRefresh();
  249. }
  250. publicvoidonRefreshComplete(){
  251. state=DONE;
  252. lastUpdatedTextView.setText("最近更新:"+newDate().toLocaleString());//刷新完成时,头部提醒的刷新日期
  253. changeHeaderViewByState();
  254. }
  255. privatevoidonRefresh(){
  256. if(refreshListener!=null){
  257. refreshListener.onRefresh();
  258. }
  259. }
  260. //此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
  261. privatevoidmeasureView(Viewchild){
  262. ViewGroup.LayoutParamsp=child.getLayoutParams();
  263. if(p==null){
  264. p=newViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
  265. ViewGroup.LayoutParams.WRAP_CONTENT);
  266. }
  267. intchildWidthSpec=ViewGroup.getChildMeasureSpec(0,0+0,p.width);
  268. intlpHeight=p.height;
  269. intchildHeightSpec;
  270. if(lpHeight>0){
  271. childHeightSpec=MeasureSpec.makeMeasureSpec(lpHeight,
  272. MeasureSpec.EXACTLY);
  273. }else{
  274. childHeightSpec=MeasureSpec.makeMeasureSpec(0,
  275. MeasureSpec.UNSPECIFIED);
  276. }
  277. child.measure(childWidthSpec,childHeightSpec);
  278. }
  279. }

4.head.xml,拷进项目基本不需改:

[html] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <!--ListView的头部-->
  3. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  4. android:id="@+id/head_rootLayout"
  5. android:layout_width="fill_parent"
  6. android:layout_height="wrap_content">
  7. <!--内容-->
  8. <RelativeLayout
  9. xmlns:android="http://schemas.android.com/apk/res/android"
  10. android:id="@+id/head_contentLayout"
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:paddingLeft="30dp">
  14. <!--箭头图像、进度条-->
  15. <FrameLayout
  16. android:layout_width="wrap_content"
  17. android:layout_height="wrap_content"
  18. android:layout_alignParentLeft="true"
  19. android:layout_centerVertical="true">
  20. <!--箭头-->
  21. <ImageView
  22. android:id="@+id/head_arrowImageView"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:layout_gravity="center"
  26. android:src="@drawable/ic_pulltorefresh_arrow"
  27. android:contentDescription="@string/app_name"/>
  28. <!--进度条-->
  29. <ProgressBar
  30. android:id="@+id/head_progressBar"
  31. style="?android:attr/progressBarStyleSmall"
  32. android:layout_width="wrap_content"
  33. android:layout_height="wrap_content"
  34. android:layout_gravity="center"
  35. android:visibility="gone"/>
  36. </FrameLayout>
  37. <!--提示、最近更新-->
  38. <LinearLayout
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:layout_centerHorizontal="true"
  42. android:gravity="center_horizontal"
  43. android:orientation="vertical">
  44. <!--提示-->
  45. <TextView
  46. android:id="@+id/head_tipsTextView"
  47. android:layout_width="wrap_content"
  48. android:layout_height="wrap_content"
  49. android:text="@string/pulltorefresh"
  50. android:textColor="#ffffff"
  51. android:textSize="20sp"/>
  52. <!--最近更新-->
  53. <TextView
  54. android:id="@+id/head_lastUpdatedTextView"
  55. android:layout_width="wrap_content"
  56. android:layout_height="wrap_content"
  57. android:text="@string/lastupdate"
  58. android:textColor="#cc6600"
  59. android:textSize="10sp"/>
  60. </LinearLayout>
  61. </RelativeLayout>
  62. </LinearLayout>

殊途同归,扩展借鉴其他下拉刷新方式:

http://blog.csdn.net/nono_love_lilith/article/details/7100845
http://blog.csdn.net/nono_love_lilith/article/details/7202651

更多相关文章

  1. Android 广播监听应用APK卸载、覆盖、安装的状态
  2. Android设置沉浸式状态栏时改变状态栏的颜色(只对MIUI V6可用)
  3. Android找出弹状态栏广告的应用
  4. Android 刘海屏全屏适配(沉溺式状态栏,隐藏状态栏)
  5. Android 悬浮窗在状态栏之上不影响其他操作及悬浮窗自身控制
  6. Android 检查网络状态是否可用 (工具类总结三)
  7. 【Android】自定义ImageView高度自适应,宽度成比例缩放
  8. [Android]通知栏与操作栏的高度-State Bar & Navigation Bar

随机推荐

  1. ADK的安装
  2. Android下uptime获取系统启动和运行时间
  3. Android 代码风格指南
  4. 必须知道的Android屏幕自适应解决方案
  5. Android篮球计分器App
  6. android中layout区别
  7. 【Android】如何设置应用强制横屏或竖屏
  8. Android 工具类 Toast Log.d()
  9. Android学习路线[转]
  10. Android中Margin和Padding属性知识总结