zoukankan      html  css  js  c++  java
  • Android UI之下拉刷新上拉刷新实现

    在实际开发中我们经常要用到上拉刷新和下拉刷新,因此今天我写了一个上拉和下拉刷新的demo,有一个自定义的下拉刷新控件

    只需要在布局文件中直接引用就可以使用,非常方便,非常使用,以下是源代码;

    自定义的ListView RTPullListView

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


    以上就是有下拉和上拉刷新功能的自定义的listview,下拉刷新头和上拉刷新的底部样式都可以自定义

    这里给出我的下拉头布局和上拉底部布局,你们可以在此基础上进行修改自定义;

    pulllist_head.xml 头布局

     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:paddingTop="5dp"
     7     android:paddingBottom="5dp"
     8     android:background="#FFFFFFFF" >
     9 
    10     <!-- 内容 -->
    11 
    12     <RelativeLayout
    13         android:id="@+id/head_contentLayout"
    14         android:layout_width="fill_parent"
    15         android:layout_height="wrap_content"
    16         android:layout_marginTop="10dp"
    17         android:layout_marginBottom="10dp"
    18         android:paddingLeft="10dp" >
    19 
    20         <!-- 箭头图像、进度条 -->
    21 
    22         <FrameLayout
    23             android:layout_width="wrap_content"
    24             android:layout_height="wrap_content"
    25             android:layout_alignParentLeft="true"
    26             android:layout_centerVertical="true" >
    27 
    28             <!-- 箭头 -->
    29 
    30             <ImageView
    31                 android:id="@+id/head_arrowImageView"
    32                 android:layout_width="wrap_content"
    33                 android:layout_height="wrap_content"
    34                 android:layout_gravity="center"
    35                 android:src="@drawable/pulltorefresh" />
    36 
    37             <!-- 进度条 -->
    38 
    39             <ProgressBar
    40                 android:id="@+id/head_progressBar"
    41                 style="@android:style/Widget.ProgressBar.Small"
    42                 android:layout_width="wrap_content"
    43                 android:layout_height="wrap_content"
    44                 android:layout_gravity="center"
    45                 android:visibility="gone" />
    46         </FrameLayout>
    47 
    48         <!-- 提示、最近更新 -->
    49 
    50         <LinearLayout
    51             android:layout_width="wrap_content"
    52             android:layout_height="wrap_content"
    53             android:layout_centerHorizontal="true"
    54             android:gravity="center_horizontal"
    55             android:orientation="vertical" >
    56 
    57             <!-- 提示 -->
    58 
    59             <TextView
    60                 android:id="@+id/head_tipsTextView"
    61                 android:layout_width="wrap_content"
    62                 android:layout_height="wrap_content"
    63                 android:textSize="16sp" />
    64 
    65             <!-- 最近更新 -->
    66 
    67             <TextView
    68                 android:id="@+id/head_lastUpdatedTextView"
    69                 android:layout_width="wrap_content"
    70                 android:layout_height="wrap_content"
    71                 android:textSize="13sp" />
    72         </LinearLayout>
    73     </RelativeLayout>
    74 
    75 </LinearLayout>

    底部布局:list_footview.xml

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:id="@+id/list_footview"
     4     android:layout_width="fill_parent"
     5     android:layout_height="fill_parent"
     6     android:background="@android:color/white"
     7     android:orientation="vertical" >
     8 
     9     <LinearLayout
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:layout_centerInParent="true"
    13         android:layout_centerVertical="true"
    14         android:layout_marginBottom="25dp"
    15         android:layout_marginTop="25dp"
    16         android:gravity="center"
    17         android:orientation="horizontal" >
    18 
    19         <TextView
    20             android:id="@+id/text_view"
    21             android:layout_width="wrap_content"
    22             android:layout_height="wrap_content"
    23             android:gravity="center"
    24             android:text="获取更多"
    25             android:textColor="@android:color/black"
    26             android:textSize="16sp" />
    27 
    28         <ProgressBar
    29             android:id="@+id/footer_progress"
    30             style="?android:attr/progressBarStyleSmall"
    31             android:layout_width="wrap_content"
    32             android:layout_height="wrap_content"
    33             android:layout_marginLeft="3dp"
    34             android:visibility="gone" />
    35     </LinearLayout>
    36 
    37 </RelativeLayout>

    ok这样一个自定义的下拉上拉刷新的listview就定义好了,可以在activity的布局文件中直接引用,并且像操纵listview那样使用

    非常方便,这里也给出我使用的源码;

    首先是主界面布局:main.xml

     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:orientation="vertical" >
     6 
     7     <com.ryantang.pulllistview.RTPullListView
     8         android:id="@+id/pullListView"
     9         android:layout_width="fill_parent"
    10         android:layout_height="wrap_content" />
    11 
    12 </LinearLayout>

    主界面activity中使用源码:

      1 package com.ryantang.pulllistview;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 import android.app.Activity;
      7 import android.os.Bundle;
      8 import android.os.Handler;
      9 import android.os.Message;
     10 import android.view.LayoutInflater;
     11 import android.view.View;
     12 import android.view.View.OnClickListener;
     13 import android.widget.ArrayAdapter;
     14 import android.widget.ProgressBar;
     15 import android.widget.RelativeLayout;
     16 
     17 import com.ryantang.pulllistview.RTPullListView.OnRefreshListener;
     18 
     19 /**
     20  * PullListView
     21  * @author Ryan
     22  *
     23  */
     24 public class RTPullListViewActivity extends Activity {
     25     private static final int INTERNET_FAILURE = -1;
     26     private static final int LOAD_SUCCESS = 1;
     27     private static final int LOAD_MORE_SUCCESS = 3;
     28     private static final int NO_MORE_INFO = 4;
     29     private static final int LOAD_NEW_INFO = 5;
     30     private RTPullListView pullListView;
     31     private ProgressBar moreProgressBar;
     32     private List<String> dataList;
     33     private ArrayAdapter<String> adapter;
     34     
     35     @Override
     36     public void onCreate(Bundle savedInstanceState) {
     37         super.onCreate(savedInstanceState);
     38         setContentView(R.layout.main);
     39         pullListView = (RTPullListView) this.findViewById(R.id.pullListView);
     40         
     41         dataList = new ArrayList<String>();
     42         for (int i = 0; i < 5; i++) {
     43             dataList.add("Item data "+i);
     44         }
     45         adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, dataList);
     46         pullListView.setAdapter(adapter);
     47         
     48         //添加listview底部获取更多按钮(可自定义)
     49         LayoutInflater inflater = LayoutInflater.from(this);
     50         View view = inflater.inflate(R.layout.list_footview, null);
     51         RelativeLayout footerView =(RelativeLayout) view.findViewById(R.id.list_footview);
     52         moreProgressBar = (ProgressBar) view.findViewById(R.id.footer_progress);
     53         pullListView.addFooterView(footerView);
     54         
     55         //下拉刷新监听器
     56         pullListView.setonRefreshListener(new OnRefreshListener() {
     57             
     58             @Override
     59             public void onRefresh() {
     60                 new Thread(new Runnable() {
     61                     
     62                     @Override
     63                     public void run() {
     64                         try {
     65                             Thread.sleep(2000);
     66                             dataList.clear();
     67                             for (int i = 0; i < 5; i++) {
     68                                 dataList.add("Item data "+i);
     69                             }
     70                             myHandler.sendEmptyMessage(LOAD_NEW_INFO);
     71                         } catch (InterruptedException e) {
     72                             e.printStackTrace();
     73                         }
     74                     }
     75                 }).start();
     76             }
     77         });
     78         
     79         //获取跟多监听器
     80         footerView.setOnClickListener(new OnClickListener() {//
     81             
     82             @Override
     83             public void onClick(View v) {
     84 
     85                 moreProgressBar.setVisibility(View.VISIBLE);
     86                 
     87                 new Thread(new Runnable() {
     88                     
     89                     @Override
     90                     public void run() {
     91                         try {
     92                             Thread.sleep(2000);
     93                             for (int i = 0; i < 5; i++) {
     94                                 dataList.add("New item data "+i);
     95                             }
     96                             myHandler.sendEmptyMessage(LOAD_MORE_SUCCESS);
     97                         } catch (InterruptedException e) {
     98                             e.printStackTrace();
     99                         }
    100                     }
    101                 }).start();
    102             }
    103         });
    104     }
    105     
    106     //结果处理
    107     private Handler myHandler = new Handler(){
    108 
    109         @Override
    110         public void handleMessage(Message msg) {
    111             super.handleMessage(msg);
    112             switch (msg.what) {
    113             case LOAD_MORE_SUCCESS:
    114                 moreProgressBar.setVisibility(View.GONE);
    115                 adapter.notifyDataSetChanged();
    116                 pullListView.setSelectionfoot();
    117                 break;
    118 
    119             case LOAD_NEW_INFO:
    120                 adapter.notifyDataSetChanged();
    121                 pullListView.onRefreshComplete();
    122                 break;
    123             default:
    124                 break;
    125             }
    126         }
    127         
    128     };
    129 }

     效果图如下:

  • 相关阅读:
    二、制作BOM表格--物料表格--Bill of Materials
    一、生成网络表--create Netlist
    Python使用OpenCV实现简单的人脸检测
    Spring编程式和声明式事务实例讲解
    可能是最漂亮的Spring事务管理详解
    关于Java IO与NIO知识都在这里
    Java IO,硬骨头也能变软
    Java NIO之拥抱Path和Files
    Java NIO之Selector(选择器)
    Java NIO 之 Channel(通道)
  • 原文地址:https://www.cnblogs.com/all88/p/4172321.html
Copyright © 2011-2022 走看看