Android中ListView下拉刷新的实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中ListView下拉刷新的实现相关的知识,希望对你有一定的参考价值。

技术分享

ListView中的下拉刷新是非常常见的,也是经常使用的,看到有很多同学想要,那我就整理一下,供大家参考。那我就不解释,直接上代码了。

这里需要自己重写一下ListView,重写代码如下:

 

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

重写完ListView之后,在布局文件中是这么使用的,头部下拉刷新的布局文件lv_header.xml代码如下:

 

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

在Main.xml中进行设置,代码如下:

 

[html] view plain copy
 
 技术分享技术分享
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:background="#000000" &nbs

    以上是关于Android中ListView下拉刷新的实现的主要内容,如果未能解决你的问题,请参考以下文章

    android开发:列表listview的实现 | 下拉刷新

    android 下拉刷新怎么实现

    Android自定义控件--下拉刷新的实现

    android 安卓自定义listview实现下拉刷新

    Listview嵌套Viewpager实现仿淘宝搜狐广告主页,并实现listview的下拉刷新

    android ListView 做下拉刷新 下拉跳到第一项怎么解决