带有下拉刷新功能的Listview控件

来源:互联网 发布:a链接调用js方法 编辑:程序博客网 时间:2024/05/17 03:18


1.原理

其实就是继承了Listview控件,为其默认加一个header,这个header就是带箭头的那些东西。然后override onTouchEvent函数,根据滑动过程中y坐标的变化来动态修改header的状态(包括箭头的方向和文字提示),其他用法和普通的Listview一样。

[java] view plaincopy
  1. public class PullToRefreshListView extends ListView implements OnScrollListener   
  2. {  
  3.   
  4.     private static final String TAG = "PullToRefreshListView";  
  5.   
  6.     private final static int RELEASE_TO_REFRESH = 0;  
  7.     private final static int PULL_TO_REFRESH = 1;  
  8.     private final static int REFRESHING = 2;  
  9.     private final static int DONE = 3;  
  10.     private final static int LOADING = 4;  
  11.   
  12.     // 实际的padding的距离与界面上偏移距离的比例  
  13.     private final static int RATIO = 3;  
  14.     private LayoutInflater inflater;  
  15.   
  16.     //listview的头部 用于显示刷新的箭头等  
  17.     private LinearLayout headView;  
  18.     private TextView tipsTextview;  
  19.     private TextView lastUpdatedTextView;  
  20.     private ImageView arrowImageView;  
  21.     private ProgressBar progressBar;  
  22.   
  23.     //箭头旋转的动画  
  24.     private RotateAnimation animation;  
  25.     private RotateAnimation reverseAnimation;  
  26.   
  27.     // 用于保证startY的值在一个完整的touch事件中只被记录一次  
  28.     private boolean isRecored;  
  29.     private int headContentWidth;  
  30.     private int headContentHeight;  
  31.     private int startY;  
  32.     private int firstItemIndex;  
  33.     private int state;  
  34.     private boolean isBack;  
  35.   
  36.     private OnRefreshListener refreshListener;  
  37.   
  38.     private boolean isRefreshable;  
  39.   
  40.     public PullToRefreshListView(Context context)   
  41.     {  
  42.         super(context);  
  43.         init(context);  
  44.     }  
  45.   
  46.     public PullToRefreshListView(Context context, AttributeSet attrs)   
  47.     {  
  48.         super(context, attrs);  
  49.         init(context);  
  50.     }  
  51.   
  52.     private void init(Context context)  
  53.     {  
  54.         setCacheColorHint(context.getResources().getColor(R.color.transparent));  
  55.         inflater = LayoutInflater.from(context);  
  56.         headView = (LinearLayout) inflater.inflate(R.layout.head, null);  
  57.   
  58.         arrowImageView = (ImageView) headView  
  59.                 .findViewById(R.id.head_arrowImageView);  
  60.         arrowImageView.setMinimumWidth(70);  
  61.         arrowImageView.setMinimumHeight(50);  
  62.         progressBar = (ProgressBar) headView  
  63.                 .findViewById(R.id.head_progressBar);  
  64.         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);  
  65.         lastUpdatedTextView = (TextView) headView  
  66.                 .findViewById(R.id.head_lastUpdatedTextView);  
  67.   
  68.         measureView(headView);  
  69.         headContentHeight = headView.getMeasuredHeight();  
  70.         headContentWidth = headView.getMeasuredWidth();  
  71.   
  72.         headView.setPadding(0, -1 * headContentHeight, 00);  
  73.         headView.invalidate();  
  74.   
  75.         Log.v("size""width:" + headContentWidth + " height:"  
  76.                 + headContentHeight);  
  77.   
  78.         addHeaderView(headView, nullfalse);  
  79.         setOnScrollListener(this);  
  80.   
  81.         animation = new RotateAnimation(0, -180,  
  82.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  83.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  84.         animation.setInterpolator(new LinearInterpolator());  
  85.         animation.setDuration(250);  
  86.         animation.setFillAfter(true);  
  87.   
  88.         reverseAnimation = new RotateAnimation(-1800,  
  89.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  90.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  91.         reverseAnimation.setInterpolator(new LinearInterpolator());  
  92.         reverseAnimation.setDuration(200);  
  93.         reverseAnimation.setFillAfter(true);  
  94.   
  95.         state = DONE;  
  96.         isRefreshable = false;  
  97.     }  
  98.   
  99.     @Override  
  100.     public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2, int arg3)   
  101.     {  
  102.         firstItemIndex = firstVisiableItem;  
  103.     }  
  104.   
  105.     @Override  
  106.     public void onScrollStateChanged(AbsListView arg0, int arg1)  
  107.     {  
  108.     }  
  109.   
  110.     @Override  
  111.     public boolean onTouchEvent(MotionEvent event)   
  112.     {  
  113.         if (isRefreshable)   
  114.         {  
  115.             switch (event.getAction())   
  116.             {  
  117.             case MotionEvent.ACTION_DOWN:  
  118.                 if (firstItemIndex == 0 && !isRecored)   
  119.                 {  
  120.                     isRecored = true;  
  121.                     startY = (int) event.getY();  
  122.                     Log.v(TAG, "在down时候记录当前位置");  
  123.                 }  
  124.                 break;  
  125.             case MotionEvent.ACTION_UP:  
  126.                 if (state != REFRESHING && state != LOADING) {  
  127.                     if (state == DONE) {  
  128.                         // 什么都不做  
  129.                     }  
  130.                     if (state == PULL_TO_REFRESH) {  
  131.                         state = DONE;  
  132.                         changeHeaderViewByState();  
  133.   
  134.                         Log.v(TAG, "由下拉刷新状态,到done状态");  
  135.                     }  
  136.                     if (state == RELEASE_TO_REFRESH) {  
  137.                         state = REFRESHING;  
  138.                         changeHeaderViewByState();  
  139.                         onRefresh();  
  140.   
  141.                         Log.v(TAG, "由松开刷新状态,到done状态");  
  142.                     }  
  143.                 }  
  144.                 isRecored = false;  
  145.                 isBack = false;  
  146.                 break;  
  147.             case MotionEvent.ACTION_MOVE:  
  148.                 int tempY = (int) event.getY();  
  149.                 if (!isRecored && firstItemIndex == 0) {  
  150.                     Log.v(TAG, "在move时候记录下位置");  
  151.                     isRecored = true;  
  152.                     startY = tempY;  
  153.                 }  
  154.                 if (state != REFRESHING && isRecored && state != LOADING) {  
  155.                     // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动  
  156.                     // 可以松手去刷新了  
  157.                     if (state == RELEASE_TO_REFRESH) {  
  158.                         setSelection(0);  
  159.                         // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步  
  160.                         if (((tempY - startY) / RATIO < headContentHeight)  
  161.                                 && (tempY - startY) > 0) {  
  162.                             state = PULL_TO_REFRESH;  
  163.                             changeHeaderViewByState();  
  164.                             Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");  
  165.                         }  
  166.                         // 一下子推到顶了  
  167.                         else if (tempY - startY <= 0) {  
  168.                             state = DONE;  
  169.                             changeHeaderViewByState();  
  170.                             Log.v(TAG, "由松开刷新状态转变到done状态");  
  171.                         }  
  172.                         // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步  
  173.                         else {  
  174.                             // 不用进行特别的操作,只用更新paddingTop的值就行了  
  175.                         }  
  176.                     }  
  177.                     // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态  
  178.                     if (state == PULL_TO_REFRESH) {  
  179.                         setSelection(0);  
  180.                         // 下拉到可以进入RELEASE_TO_REFRESH的状态  
  181.                         if ((tempY - startY) / RATIO >= headContentHeight) {  
  182.                             state = RELEASE_TO_REFRESH;  
  183.                             isBack = true;  
  184.                             changeHeaderViewByState();  
  185.                             Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");  
  186.                         }  
  187.                         // 上推到顶了  
  188.                         else if (tempY - startY <= 0) {  
  189.                             state = DONE;  
  190.                             changeHeaderViewByState();  
  191.                             Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");  
  192.                         }  
  193.                     }  
  194.                     // done状态下  
  195.                     if (state == DONE) {  
  196.                         if (tempY - startY > 0) {  
  197.                             state = PULL_TO_REFRESH;  
  198.                             changeHeaderViewByState();  
  199.                         }  
  200.                     }  
  201.                     // 更新headView的size  
  202.                     if (state == PULL_TO_REFRESH) {  
  203.                         headView.setPadding(0, -1 * headContentHeight + (tempY - startY) / RATIO, 00);  
  204.                     }  
  205.                     // 更新headView的paddingTop  
  206.                     if (state == RELEASE_TO_REFRESH) {  
  207.                         headView.setPadding(0, (tempY - startY) / RATIO  
  208.                                 - headContentHeight, 00);  
  209.                     }  
  210.                 }  
  211.                 break;  
  212.             }  
  213.         }  
  214.   
  215.         return super.onTouchEvent(event);  
  216.     }  
  217.   
  218.     // 当状态改变时候,调用该方法,以更新界面  
  219.     private void changeHeaderViewByState()  
  220.     {  
  221.         switch (state)   
  222.         {  
  223.         case RELEASE_TO_REFRESH:  
  224.             arrowImageView.setVisibility(View.VISIBLE);  
  225.             progressBar.setVisibility(View.GONE);  
  226.             tipsTextview.setVisibility(View.VISIBLE);  
  227.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  228.   
  229.             arrowImageView.clearAnimation();  
  230.             arrowImageView.startAnimation(animation);  
  231.             tipsTextview.setText("放开以刷新");  
  232.             Log.v(TAG, "当前状态,松开刷新");  
  233.             break;  
  234.         case PULL_TO_REFRESH:  
  235.             progressBar.setVisibility(View.GONE);  
  236.             tipsTextview.setVisibility(View.VISIBLE);  
  237.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  238.             arrowImageView.clearAnimation();  
  239.             arrowImageView.setVisibility(View.VISIBLE);  
  240.             // 是由RELEASE_To_REFRESH状态转变来的  
  241.             if (isBack) {  
  242.                 isBack = false;  
  243.                 arrowImageView.clearAnimation();  
  244.                 arrowImageView.startAnimation(reverseAnimation);  
  245.                 tipsTextview.setText("下拉刷新");  
  246.             } else {  
  247.                 tipsTextview.setText("下拉刷新");  
  248.             }  
  249.             Log.v(TAG, "当前状态,下拉刷新");  
  250.             break;  
  251.   
  252.         case REFRESHING:  
  253.             headView.setPadding(0000);  
  254.             progressBar.setVisibility(View.VISIBLE);  
  255.             arrowImageView.clearAnimation();  
  256.             arrowImageView.setVisibility(View.GONE);  
  257.             tipsTextview.setText("正在刷新...");  
  258.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  259.             Log.v(TAG, "当前状态,正在刷新...");  
  260.             break;  
  261.         case DONE:  
  262.             headView.setPadding(0, -1 * headContentHeight, 00);  
  263.             progressBar.setVisibility(View.GONE);  
  264.             arrowImageView.clearAnimation();  
  265.             arrowImageView.setImageResource(R.drawable.arrow);  
  266.             tipsTextview.setText("下拉刷新");  
  267.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  268.             Log.v(TAG, "当前状态,done");  
  269.             break;  
  270.         }  
  271.     }  
  272.   
  273.     public void setonRefreshListener(OnRefreshListener refreshListener)  
  274.     {  
  275.         this.refreshListener = refreshListener;  
  276.         isRefreshable = true;  
  277.     }  
  278.   
  279.     public interface OnRefreshListener   
  280.     {  
  281.         public void onRefresh();  
  282.     }  
  283.   
  284.     public void onRefreshComplete()  
  285.     {  
  286.         state = DONE;  
  287.         lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  
  288.         changeHeaderViewByState();  
  289.     }  
  290.   
  291.     private void onRefresh()   
  292.     {  
  293.         if (refreshListener != null)  
  294.         {  
  295.             refreshListener.onRefresh();  
  296.         }  
  297.     }  
  298.   
  299.     // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height  
  300.     private void measureView(View child) {  
  301.         ViewGroup.LayoutParams p = child.getLayoutParams();  
  302.         if (p == null) {  
  303.             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  304.                     ViewGroup.LayoutParams.WRAP_CONTENT);  
  305.         }  
  306.         int childWidthSpec = ViewGroup.getChildMeasureSpec(00 + 0, p.width);  
  307.         int lpHeight = p.height;  
  308.         int childHeightSpec;  
  309.         if (lpHeight > 0) {  
  310.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
  311.                     MeasureSpec.EXACTLY);  
  312.         } else {  
  313.             childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
  314.                     MeasureSpec.UNSPECIFIED);  
  315.         }  
  316.         child.measure(childWidthSpec, childHeightSpec);  
  317.     }  
  318.   
  319.     public void setAdapter(BaseAdapter adapter)  
  320.     {  
  321.         lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  
  322.         super.setAdapter(adapter);  
  323.     }  
  324.   
  325. }  


header的layout xml

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

2.如何使用

使用起来很简单,只有一点不同,要实现OnRefreshListener接口,标志当下拉刷新的时候你所要做的事情。

[java] view plaincopy
  1. listView.setonRefreshListener(new OnRefreshListener() {  
  2.     @Override  
  3.     public void onRefresh()   
  4.     {  
  5.         new AsyncTask<Void, Void, Void>() {  
  6.                         //刷新过程中需要做的操作在这里  
  7.             protected Void doInBackground(Void... params)  
  8.             {  
  9.                 try   
  10.                 {  
  11.                     Thread.sleep(1000);  
  12.                 }   
  13.                 catch (Exception e)   
  14.                 {  
  15.                     e.printStackTrace();  
  16.                 }  
  17.                 data.add("new item");  
  18.                 return null;  
  19.             }  
  20.                         //刷新完成后要通知listview进行界面调整  
  21.             @Override  
  22.             protected void onPostExecute(Void result)   
  23.             {  
  24.                 adapter.notifyDataSetChanged();  
  25.                 listView.onRefreshComplete();  
  26.             }  
  27.   
  28.         }.execute(null);  
  29.     }  
  30. });  
0 0
原创粉丝点击