zoukankan      html  css  js  c++  java
  • 仿新浪微博的ListView下拉更新功能

    由于最近做的项目中也用到了这个功能,今天刚好实现了下,就趁现在有时间写篇博客分享下。在做的时候也参考了下别人的代码,毕竟站在巨人的肩膀上才会是自己更加强大。哈哈!先看看新浪的下拉更新是什么样的吧!

           

          OK,今天我们要实现的就是上面的下拉刷新功能。

          首先实现下拉刷新的布局文件 layout/head.xml

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

            然后设置item的布局模板item.xml:

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="wrap_content"  
    5.     android:gravity="center_vertical"  
    6.     android:background="#ffffff"  
    7.     android:orientation="horizontal" >  
    8.   
    9.     <ImageView  
    10.         android:id="@+id/imageView_item"  
    11.         android:layout_width="wrap_content"  
    12.         android:layout_height="wrap_content"  
    13.         android:layout_marginLeft="10dp"  
    14.         android:src="@drawable/ic_launcher" />  
    15.   
    16.     <TextView  
    17.         android:id="@+id/textView_item"  
    18.         android:layout_width="wrap_content"  
    19.         android:layout_height="wrap_content"  
    20.         android:layout_marginLeft="10dp"  
    21.         android:text="TextView" />  
    22.   
    23. </LinearLayout>  

         接下来是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.     <cn.com.karl.list.MyListView  
    8.     android:layout_width="fill_parent"  
    9.     android:layout_height="fill_parent"  
    10.     android:id="@+id/listView"  
    11.     />  
    12.       
    13.   
    14. </LinearLayout>  


       这里的MyListView是我们自定义的ListView,因为顶部的下拉刷新系统的ListView不能够实现,所以我们需要自定义ListView。

       布局文件搞定,下面看下自定义的MyListView:

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


    最后在MainActivity中添加内容:

     
    1. public class MainActivity extends Activity {  
    2.   
    3.     private LinkedList<String> data;  
    4.     private BaseAdapter adapter;  
    5.   
    6.     public void onCreate(Bundle savedInstanceState) {  
    7.         super.onCreate(savedInstanceState);  
    8.         setContentView(R.layout.main);  
    9.   
    10.         data = new LinkedList<String>();  
    11.         for(int i=0;i<10;i++){  
    12.             data.add(String.valueOf(i));  
    13.         }  
    14.   
    15.         final MyListView listView = (MyListView) findViewById(R.id.listView);  
    16.         adapter = new BaseAdapter() {  
    17.             public View getView(int position, View convertView, ViewGroup parent) {  
    18.                  convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);  
    19.                 TextView textView = (TextView) convertView.findViewById(R.id.textView_item);  
    20.                 textView.setText(data.get(position));  
    21.                 return convertView;  
    22.             }  
    23.   
    24.             public long getItemId(int position) {  
    25.                 return position;  
    26.             }  
    27.   
    28.             public Object getItem(int position) {  
    29.                 return data.get(position);  
    30.             }  
    31.   
    32.             public int getCount() {  
    33.                 return data.size();  
    34.             }  
    35.         };  
    36.         listView.setAdapter(adapter);  
    37.   
    38.         listView.setonRefreshListener(new OnRefreshListener() {  
    39.             public void onRefresh() {  
    40.                 new AsyncTask<Void, Void, Void>() {  
    41.                     protected Void doInBackground(Void... params) {  
    42.                         try {  
    43.                             Thread.sleep(1000);  
    44.                         } catch (Exception e) {  
    45.                             e.printStackTrace();  
    46.                         }  
    47.                         data.addFirst("刷新后的内容");  
    48.                         return null;  
    49.                     }  
    50.   
    51.                     @Override  
    52.                     protected void onPostExecute(Void result) {  
    53.                         adapter.notifyDataSetChanged();  
    54.                         listView.onRefreshComplete();  
    55.                     }  
    56.   
    57.                 }.execute(null);  
    58.             }  
    59.         });  
    60.     }  
    61. }  

    下载地址:http://download.csdn.net/detail/wangkuifeng0118/4228476

    http://blog.csdn.net/wangkuifeng0118/article/details/7463594

    http://www.cnblogs.com/notice520/archive/2012/01/20/2328035.html

  • 相关阅读:
    马化腾:办公用QQ休闲用微信[Dream Catchers论坛]
    wordpress无法安装这个包。: PCLZIP_ERR_MISSING_FILE (-4) : Missing archive file 'C:WINDOWSTEMP/wordpress-4.tmp'
    如何精准化的做微信公众号运营?
    图像处理(卷积)作者太棒了
    What is an eigenvector of a covariance matrix?
    再谈协方差矩阵之主成分分析
    [转]浅谈协方差矩阵
    Deep Learning and the Triumph of Empiricism
    UML要点总结(一)
    Android TextView中有图片有文字混合排列
  • 原文地址:https://www.cnblogs.com/jiezzy/p/2542112.html
Copyright © 2011-2022 走看看