android下拉加载更多

简介: <p style="margin-top:0px; margin-bottom:0px; padding-top:0px; padding-bottom:0px; font-family:Arial; font-size:14px; line-height:26px"> 新浪微博,和QQ空间里面,都有那个下拉刷新的效果,另很多人眼前一亮,细细分析,原理原来如此。</p> <p styl

新浪微博,和QQ空间里面,都有那个下拉刷新的效果,另很多人眼前一亮,细细分析,原理原来如此。

在原作者的基础上,写了一些注释,和帮助大家更好的阅读理解,(可能其中有些地方注释不准,欢迎指正,谢谢)

源代码下载地址:http://download.csdn.net/detail/weidi1989/4588246

先来看一下效果图:

 

下面,就亮出关键代码,自定义的一个MyListView:

[java]  view plain copy
  1. /** 
  2.  * 重写一个ListView,主要是添加一个下拉事件 
  3.  *  
  4.  * @author way 
  5.  *  
  6.  */  
  7. public class MyListView extends ListView implements OnScrollListener {  
  8.   
  9.     private static final String TAG = "listview";  
  10.   
  11.     private final static int RELEASE_To_REFRESH = 0// 松开刷新状态  
  12.     private final static int PULL_To_REFRESH = 1;// 拉动刷新状态  
  13.     private final static int REFRESHING = 2;// 正在刷新状态  
  14.     private final static int DONE = 3;// 已经加载完毕状态  
  15.     private final static int LOADING = 4;// 正在加载数据状态  
  16.     private final static int RATIO = 3;// 实际的padding的距离与界面上偏移距离的比例  
  17.   
  18.     private LayoutInflater inflater;  
  19.     private LinearLayout headView;// ListView头部的View  
  20.   
  21.     private TextView tipsTextview;// 提示信息“下拉刷新”的TextView  
  22.     private TextView lastUpdatedTextView;// 上次更新时间的TextView  
  23.     private ImageView arrowImageView;// 箭头的图片  
  24.     private ProgressBar progressBar;// 刷新进度  
  25.   
  26.     private RotateAnimation animation;// 箭头向下动画  
  27.     private RotateAnimation reverseAnimation;// 逆向箭头动画  
  28.   
  29.     private boolean isRecored;// 用于保证startY的值在一个完整的touch事件中只被记录一次  
  30.   
  31.     private int headContentWidth;// 头部View内容的宽度  
  32.     private int headContentHeight;// 头部view内容的高度  
  33.   
  34.     private int startY;// 向下触屏事件时的手指起始y轴位置  
  35.   
  36.     private int firstItemIndex;// ListView第一项的索引  
  37.   
  38.     private int state;// 刷新状态  
  39.   
  40.     private boolean isBack;// 是否反弹  
  41.   
  42.     private OnRefreshListener refreshListener;// 给外面预留的刷新的接口  
  43.   
  44.     private boolean isRefreshable;// 是否刷新的标志位  
  45.   
  46.     /** 
  47.      * 第一个构造器 
  48.      *  
  49.      * @param context 
  50.      *            上下文对象 
  51.      */  
  52.     public MyListView(Context context) {  
  53.         super(context);  
  54.         init(context);  
  55.     }  
  56.   
  57.     /** 
  58.      * 第二个构造器 
  59.      *  
  60.      * @param context 
  61.      *            上下文对象 
  62.      * @param attrs 
  63.      *            属性 
  64.      */  
  65.     public MyListView(Context context, AttributeSet attrs) {  
  66.         super(context, attrs);  
  67.         init(context);  
  68.     }  
  69.   
  70.     /** 
  71.      * 初始化数据 
  72.      *  
  73.      * @param context 
  74.      *            上下文对象 
  75.      */  
  76.     private void init(Context context) {  
  77.         // setCacheColorHint(context.getResources().getColor(R.color.transparent));  
  78.         inflater = LayoutInflater.from(context);  
  79.         headView = (LinearLayout) inflater.inflate(R.layout.head, null);// 获取ListView头部的view  
  80.   
  81.         arrowImageView = (ImageView) headView  
  82.                 .findViewById(R.id.head_arrowImageView);// 从头部的View获取箭头图片  
  83.         arrowImageView.setMinimumWidth(70);  
  84.         arrowImageView.setMinimumHeight(50);  
  85.         progressBar = (ProgressBar) headView  
  86.                 .findViewById(R.id.head_progressBar);// 获取刷新进度条  
  87.         tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);// 提示信息的TextView  
  88.         lastUpdatedTextView = (TextView) headView  
  89.                 .findViewById(R.id.head_lastUpdatedTextView);// 最后刷新时间的TextView  
  90.   
  91.         measureView(headView);// 自己写的一个方法,没有很理解  
  92.         headContentHeight = headView.getMeasuredHeight();// 得到headView的原始高度  
  93.         headContentWidth = headView.getMeasuredWidth();  
  94.   
  95.         headView.setPadding(0, -1 * headContentHeight, 00);// 设置内容的内部偏移量  
  96.         headView.invalidate();  
  97.   
  98.         Log.v("size""width:" + headContentWidth + " height:"  
  99.                 + headContentHeight);  
  100.   
  101.         addHeaderView(headView, nullfalse);// 加到ListView的头部view,ListView组件提供了两个很实用的功能,那就是可以在顶部和底部添加自定义的视图  
  102.         setOnScrollListener(this);  
  103.   
  104.         // 箭头向下动画  
  105.         animation = new RotateAnimation(0, -180,  
  106.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  107.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  108.         animation.setInterpolator(new LinearInterpolator());  
  109.         animation.setDuration(250);  
  110.         animation.setFillAfter(true);  
  111.   
  112.         // 逆向箭头动画  
  113.         reverseAnimation = new RotateAnimation(-1800,  
  114.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  115.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  116.         reverseAnimation.setInterpolator(new LinearInterpolator());  
  117.         reverseAnimation.setDuration(200);  
  118.         reverseAnimation.setFillAfter(true);  
  119.   
  120.         state = DONE;// 第一次加载默认完成的状态  
  121.         isRefreshable = false;// 刷新标志位默认为false  
  122.     }  
  123.   
  124.     /** 
  125.      * 滑动时被调用 
  126.      */  
  127.     public void onScroll(AbsListView view, int firstVisiableItem,  
  128.             int visibleItemCount, int totalItemCount) {  
  129.         firstItemIndex = firstVisiableItem;// ListView第一个索引值为ListView数据中第一个可见项  
  130.     }  
  131.   
  132.     /** 
  133.      * 滑动状态改变时被调用 
  134.      */  
  135.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  136.     }  
  137.   
  138.     /** 
  139.      * 触摸事件 
  140.      */  
  141.     public boolean onTouchEvent(MotionEvent event) {  
  142.         if (isRefreshable) {// 如果刷新标志为true  
  143.             switch (event.getAction()) {  
  144.             case MotionEvent.ACTION_DOWN:// 向下  
  145.                 if (firstItemIndex == 0 && !isRecored) {  
  146.                     isRecored = true;  
  147.                     startY = (int) event.getY();  
  148.                     Log.v(TAG, "在down时候记录当前位置");  
  149.                 }  
  150.                 break;  
  151.   
  152.             case MotionEvent.ACTION_UP:// 向上  
  153.                 if (state != REFRESHING && state != LOADING) {  
  154.                     if (state == DONE) {  
  155.                         // 什么都不做  
  156.                     }  
  157.                     if (state == PULL_To_REFRESH) {// 下拉刷新  
  158.                         state = DONE;  
  159.                         changeHeaderViewByState();// 更新头部view  
  160.                         Log.v(TAG, "由下拉刷新状态,到done状态");  
  161.                     }  
  162.                     if (state == RELEASE_To_REFRESH) {// 释放刷新  
  163.                         state = REFRESHING;  
  164.                         changeHeaderViewByState();  
  165.                         onRefresh();// 调用接口的方法,通知外面  
  166.   
  167.                         Log.v(TAG, "由松开刷新状态,到done状态");  
  168.                     }  
  169.                 }  
  170.                 isRecored = false;  
  171.                 isBack = false;  
  172.                 break;  
  173.   
  174.             case MotionEvent.ACTION_MOVE:// 手指移动  
  175.                 int tempY = (int) event.getY();  
  176.                 if (!isRecored && firstItemIndex == 0) {  
  177.                     Log.v(TAG, "在move时候记录下位置");  
  178.                     isRecored = true;  
  179.                     startY = tempY;  
  180.                 }  
  181.                 if (state != REFRESHING && isRecored && state != LOADING) {  
  182.   
  183.                     // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动  
  184.   
  185.                     // 可以松手去刷新了  
  186.                     if (state == RELEASE_To_REFRESH) {  
  187.                         setSelection(0);  
  188.                         // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步  
  189.                         if (((tempY - startY) / RATIO < headContentHeight)  
  190.                                 && (tempY - startY) > 0) {  
  191.                             state = PULL_To_REFRESH;  
  192.                             changeHeaderViewByState();  
  193.                             Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");  
  194.                         }  
  195.                         // 一下子推到顶了  
  196.                         else if (tempY - startY <= 0) {  
  197.                             state = DONE;  
  198.                             changeHeaderViewByState();  
  199.                             Log.v(TAG, "由松开刷新状态转变到done状态");  
  200.                         }  
  201.                         // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步  
  202.                         else {  
  203.                             // 不用进行特别的操作,只用更新paddingTop的值就行了  
  204.                         }  
  205.                     }  
  206.                     // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态  
  207.                     if (state == PULL_To_REFRESH) {  
  208.                         setSelection(0);  
  209.                         // 下拉到可以进入RELEASE_TO_REFRESH的状态  
  210.                         if ((tempY - startY) / RATIO >= headContentHeight) {  
  211.                             state = RELEASE_To_REFRESH;  
  212.                             isBack = true;  
  213.                             changeHeaderViewByState();  
  214.                             Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");  
  215.                         }  
  216.                         // 上推到顶了  
  217.                         else if (tempY - startY <= 0) {  
  218.                             state = DONE;  
  219.                             changeHeaderViewByState();  
  220.                             Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");  
  221.                         }  
  222.                     }  
  223.                     // done状态下  
  224.                     if (state == DONE) {  
  225.                         if (tempY - startY > 0) {  
  226.                             state = PULL_To_REFRESH;  
  227.                             changeHeaderViewByState();  
  228.                         }  
  229.                     }  
  230.                     // 更新headView的size  
  231.                     if (state == PULL_To_REFRESH) {  
  232.                         headView.setPadding(0, -1 * headContentHeight  
  233.                                 + (tempY - startY) / RATIO, 00);  
  234.                     }  
  235.                     // 更新headView的paddingTop  
  236.                     if (state == RELEASE_To_REFRESH) {  
  237.                         headView.setPadding(0, (tempY - startY) / RATIO  
  238.                                 - headContentHeight, 00);  
  239.                     }  
  240.                 }  
  241.                 break;  
  242.             }  
  243.         }  
  244.         return super.onTouchEvent(event);  
  245.     }  
  246.   
  247.     // 当状态改变时候,调用该方法,以更新界面  
  248.     private void changeHeaderViewByState() {  
  249.         switch (state) {  
  250.         case RELEASE_To_REFRESH:// 松开刷新状态  
  251.             arrowImageView.setVisibility(View.VISIBLE);// 显示箭头  
  252.             progressBar.setVisibility(View.GONE);// 移除进度条  
  253.             tipsTextview.setVisibility(View.VISIBLE);// 显示提示信息  
  254.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 显示最后刷新时间  
  255.             arrowImageView.clearAnimation();// 先移除所有动画  
  256.             arrowImageView.startAnimation(animation);// 加载箭头向下动画  
  257.   
  258.             tipsTextview.setText("松开刷新");  
  259.   
  260.             Log.v(TAG, "当前状态,松开刷新");  
  261.             break;  
  262.         case PULL_To_REFRESH:// 下拉刷新状态  
  263.             progressBar.setVisibility(View.GONE);// 移除进度条  
  264.             tipsTextview.setVisibility(View.VISIBLE);// 显示提示信息  
  265.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 显示最后刷新时间  
  266.             arrowImageView.clearAnimation();// 先移除所有动画  
  267.             arrowImageView.setVisibility(View.VISIBLE);// 箭头图片可见  
  268.             // 如果是由RELEASE_To_REFRESH状态转变来的,就加载动画  
  269.             if (isBack) {  
  270.                 isBack = false;  
  271.                 arrowImageView.clearAnimation();  
  272.                 arrowImageView.startAnimation(reverseAnimation);  
  273.                 tipsTextview.setText("下拉刷新");  
  274.             } else {  
  275.                 tipsTextview.setText("下拉刷新");  
  276.             }  
  277.             Log.v(TAG, "当前状态,下拉刷新");  
  278.             break;  
  279.   
  280.         case REFRESHING:// 正在刷新状态  
  281.             headView.setPadding(0000);// 无内部偏移  
  282.             progressBar.setVisibility(View.VISIBLE);// 进度条可见  
  283.             arrowImageView.clearAnimation();// 先清除动画  
  284.             arrowImageView.setVisibility(View.GONE);// 再移除箭头动画  
  285.             tipsTextview.setText("正在刷新...");// 提示信息变成正在刷新...  
  286.             lastUpdatedTextView.setVisibility(View.VISIBLE);// 最后刷新时间可见  
  287.             Log.v(TAG, "当前状态,正在刷新...");  
  288.             break;  
  289.         case DONE:// 完成状态  
  290.             headView.setPadding(0, -1 * headContentHeight, 00);  
  291.             progressBar.setVisibility(View.GONE);  
  292.             arrowImageView.clearAnimation();  
  293.             arrowImageView.setImageResource(R.drawable.arrow_down);  
  294.             tipsTextview.setText("下拉刷新");  
  295.             lastUpdatedTextView.setVisibility(View.VISIBLE);  
  296.   
  297.             Log.v(TAG, "当前状态,done");  
  298.             break;  
  299.         }  
  300.     }  
  301.   
  302.     // 刷新完成  
  303.     public void onRefreshComplete() {  
  304.         state = DONE;  
  305.         SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm");  
  306.         String date = format.format(new Date());  
  307.         lastUpdatedTextView.setText("最近更新:" + date);  
  308.         changeHeaderViewByState();  
  309.     }  
  310.   
  311.     /** 
  312.      * 一个下拉刷新的监听接口 
  313.      *  
  314.      */  
  315.     public interface OnRefreshListener {  
  316.         public void onRefresh();  
  317.     }  
  318.   
  319.     // 监听方法  
  320.     public void setonRefreshListener(OnRefreshListener refreshListener) {  
  321.         this.refreshListener = refreshListener;  
  322.         isRefreshable = true;  
  323.     }  
  324.   
  325.     // 正在刷新  
  326.     private void onRefresh() {  
  327.         if (refreshListener != null) {  
  328.             refreshListener.onRefresh();  
  329.         }  
  330.     }  
  331.   
  332.     /** 
  333.      * 此方法是“估计”headView的width以及height 
  334.      *  
  335.      * @param child 
  336.      *            传入进来的headView 
  337.      */  
  338.     private void measureView(View child) {  
  339.         ViewGroup.LayoutParams p = child.getLayoutParams();  
  340.         if (p == null) {  
  341.             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  342.                     ViewGroup.LayoutParams.WRAP_CONTENT);  
  343.         }  
  344.         int childWidthSpec = ViewGroup.getChildMeasureSpec(00 + 0, p.width);  
  345.         int lpHeight = p.height;  
  346.         int childHeightSpec;  
  347.         if (lpHeight > 0) {  
  348.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
  349.                     MeasureSpec.EXACTLY);  
  350.         } else {  
  351.             childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
  352.                     MeasureSpec.UNSPECIFIED);  
  353.         }  
  354.         child.measure(childWidthSpec, childHeightSpec);  
  355.     }  
  356.   
  357.     // ListView添加Adapter方法  
  358.     public void setAdapter(BaseAdapter adapter) {  
  359.         SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm");  
  360.         String date = format.format(new Date());  
  361.         lastUpdatedTextView.setText("最近更新:" + date);  
  362.         super.setAdapter(adapter);  
  363.     }  
  364. }  
目录
相关文章
|
3月前
|
前端开发 Android开发 Windows
27. 【Android教程】下拉选择框 Spinner
27. 【Android教程】下拉选择框 Spinner
136 2
|
Android开发
Android官方下拉刷新控件SwipeRefreshLayout
Android官方下拉刷新控件SwipeRefreshLayout
684 0
Android官方下拉刷新控件SwipeRefreshLayout
|
Android开发
Android中下拉通知栏,Activity会走哪些生命周期?
我们就可以做一个总结:当前Activity中,下拉通知栏,是不走任何生命周期的。
209 0
|
Java Shell API
Android源码(6.0和8.1) 屏蔽状态栏下拉和屏蔽导航栏显示
Android源码(6.0和8.1) 屏蔽状态栏下拉和屏蔽导航栏显示
477 0
|
Android开发
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
804 0
Android 11 SystemUI(状态/导航栏)-状态栏下拉时图标的隐藏与通知面板的半透黑色背景
|
Android开发
Android官方下拉选择控件Spinner
Android官方下拉选择控件Spinner
392 0
Android官方下拉选择控件Spinner
|
存储 缓存 Android开发
Android EditText输入框实现下拉且保存最近5个历史记录
Android EditText输入框实现下拉且保存最近5个历史记录
426 0
Android EditText输入框实现下拉且保存最近5个历史记录
|
XML Android开发 数据格式
android 个人中心下拉弹回效果-PullscrollView
android 个人中心下拉弹回效果-PullscrollView
android 个人中心下拉弹回效果-PullscrollView
|
Android开发
Android开发禁用通知栏下拉
Android开发禁用通知栏下拉
264 0
|
算法 Android开发 数据格式
Android CoordinatorLayout(六) 加入下拉功能
上章讲了CoordinatorLayout的卡顿BUG,既然有BUG又没解决,说实话没必要讲下去,但是做事总要有始有终,既然写了就把它写完吧,顶着BUG去写。
1831 0