仿新浪微博的ListView下拉更新功能

来源:互联网 发布:linux高级程序设计图灵 编辑:程序博客网 时间:2024/05/22 04:31

哈!先看看新浪的下拉更新是什么样的吧!

        

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

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

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

  14.     <TextView
  15.         android:id="@+id/textView_item"
  16.         android:layout_width="wrap_content"
  17.         android:layout_height="wrap_content"
  18.         android:layout_marginLeft="10dp"
  19.         android:text="TextView" />

  20. </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. </LinearLayout>
复制代码

这里的MyListView是我们自定义的ListView,因为顶部的下拉刷新系统的ListView不能够实现,所以我们需要自定义ListView。   布局文件搞定,下面看下自定义的MyListView:
  1. package cn.com.karl.list;

  2. import java.text.SimpleDateFormat;
  3. import java.util.Date;


  4. import android.content.Context;
  5. import android.util.AttributeSet;
  6. import android.util.Log;
  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.BaseAdapter;
  15. import android.widget.ImageView;
  16. import android.widget.LinearLayout;
  17. import android.widget.ListView;
  18. import android.widget.AbsListView.OnScrollListener;
  19. import android.widget.ProgressBar;
  20. import android.widget.TextView;

  21. public class MyListView extends ListView implements OnScrollListener {

  22.         private static final String TAG = "listview";

  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.         // 实际的padding的距离与界面上偏移距离的比例
  29.         private final static int RATIO = 3;

  30.         private LayoutInflater inflater;

  31.         private LinearLayout headView;

  32.         private TextView tipsTextview;
  33.         private TextView lastUpdatedTextView;
  34.         private ImageView arrowImageView;
  35.         private ProgressBar progressBar;


  36.         private RotateAnimation animation;
  37.         private RotateAnimation reverseAnimation;

  38.         // 用于保证startY的值在一个完整的touch事件中只被记录一次
  39.         private boolean isRecored;

  40.         private int headContentWidth;
  41.         private int headContentHeight;

  42.         private int startY;
  43.         private int firstItemIndex;

  44.         private int state;

  45.         private boolean isBack;

  46.         private OnRefreshListener refreshListener;

  47.         private boolean isRefreshable;

  48.         public MyListView(Context context) {
  49.                 super(context);
  50.                 init(context);
  51.         }

  52.         public MyListView(Context context, AttributeSet attrs) {
  53.                 super(context, attrs);
  54.                 init(context);
  55.         }

  56.         private void init(Context context) {
  57.                 //setCacheColorHint(context.getResources().getColor(R.color.transparent));
  58.                 inflater = LayoutInflater.from(context);

  59.                 headView = (LinearLayout) inflater.inflate(R.layout.head, null);

  60.                 arrowImageView = (ImageView) headView
  61.                                 .findViewById(R.id.head_arrowImageView);
  62.                 arrowImageView.setMinimumWidth(70);
  63.                 arrowImageView.setMinimumHeight(50);
  64.                 progressBar = (ProgressBar) headView
  65.                                 .findViewById(R.id.head_progressBar);
  66.                 tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
  67.                 lastUpdatedTextView = (TextView) headView
  68.                                 .findViewById(R.id.head_lastUpdatedTextView);

  69.                 measureView(headView);
  70.                 headContentHeight = headView.getMeasuredHeight();
  71.                 headContentWidth = headView.getMeasuredWidth();

  72.                 headView.setPadding(0, -1 * headContentHeight, 0, 0);
  73.                 headView.invalidate();

  74.                 Log.v("size", "width:" + headContentWidth + " height:"
  75.                                 + headContentHeight);

  76.                 addHeaderView(headView, null, false);
  77.                 setOnScrollListener(this);

  78.                 animation = new RotateAnimation(0, -180,
  79.                                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
  80.                                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  81.                 animation.setInterpolator(new LinearInterpolator());
  82.                 animation.setDuration(250);
  83.                 animation.setFillAfter(true);

  84.                 reverseAnimation = new RotateAnimation(-180, 0,
  85.                                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,
  86.                                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  87.                 reverseAnimation.setInterpolator(new LinearInterpolator());
  88.                 reverseAnimation.setDuration(200);
  89.                 reverseAnimation.setFillAfter(true);

  90.                 state = DONE;
  91.                 isRefreshable = false;
  92.         }

  93.         public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,
  94.                         int arg3) {
  95.                 firstItemIndex = firstVisiableItem;
  96.         }

  97.         public void onScrollStateChanged(AbsListView arg0, int arg1) {
  98.         }

  99.         public boolean onTouchEvent(MotionEvent event) {

  100.                 if (isRefreshable) {
  101.                         switch (event.getAction()) {
  102.                         case MotionEvent.ACTION_DOWN:
  103.                                 if (firstItemIndex == 0 && !isRecored) {
  104.                                         isRecored = true;
  105.                                         startY = (int) event.getY();
  106.                                         Log.v(TAG, "在down时候记录当前位置‘");
  107.                                 }
  108.                                 break;

  109.                         case MotionEvent.ACTION_UP:

  110.                                 if (state != REFRESHING && state != LOADING) {
  111.                                         if (state == DONE) {
  112.                                                 // 什么都不做
  113.                                         }
  114.                                         if (state == PULL_To_REFRESH) {
  115.                                                 state = DONE;
  116.                                                 changeHeaderViewByState();

  117.                                                 Log.v(TAG, "由下拉刷新状态,到done状态");
  118.                                         }
  119.                                         if (state == RELEASE_To_REFRESH) {
  120.                                                 state = REFRESHING;
  121.                                                 changeHeaderViewByState();
  122.                                                 onRefresh();

  123.                                                 Log.v(TAG, "由松开刷新状态,到done状态");
  124.                                         }
  125.                                 }

  126.                                 isRecored = false;
  127.                                 isBack = false;

  128.                                 break;

  129.                         case MotionEvent.ACTION_MOVE:
  130.                                 int tempY = (int) event.getY();

  131.                                 if (!isRecored && firstItemIndex == 0) {
  132.                                         Log.v(TAG, "在move时候记录下位置");
  133.                                         isRecored = true;
  134.                                         startY = tempY;
  135.                                 }

  136.                                 if (state != REFRESHING && isRecored && state != LOADING) {

  137.                                         // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动

  138.                                         // 可以松手去刷新了
  139.                                         if (state == RELEASE_To_REFRESH) {

  140.                                                 setSelection(0);

  141.                                                 // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
  142.                                                 if (((tempY - startY) / RATIO < headContentHeight)
  143.                                                                 && (tempY - startY) > 0) {
  144.                                                         state = PULL_To_REFRESH;
  145.                                                         changeHeaderViewByState();

  146.                                                         Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
  147.                                                 }
  148.                                                 // 一下子推到顶了
  149.                                                 else if (tempY - startY <= 0) {
  150.                                                         state = DONE;
  151.                                                         changeHeaderViewByState();

  152.                                                         Log.v(TAG, "由松开刷新状态转变到done状态");
  153.                                                 }
  154.                                                 // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
  155.                                                 else {
  156.                                                         // 不用进行特别的操作,只用更新paddingTop的值就行了
  157.                                                 }
  158.                                         }
  159.                                         // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
  160.                                         if (state == PULL_To_REFRESH) {

  161.                                                 setSelection(0);

  162.                                                 // 下拉到可以进入RELEASE_TO_REFRESH的状态
  163.                                                 if ((tempY - startY) / RATIO >= headContentHeight) {
  164.                                                         state = RELEASE_To_REFRESH;
  165.                                                         isBack = true;
  166.                                                         changeHeaderViewByState();

  167.                                                         Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
  168.                                                 }
  169.                                                 // 上推到顶了
  170.                                                 else if (tempY - startY <= 0) {
  171.                                                         state = DONE;
  172.                                                         changeHeaderViewByState();

  173.                                                         Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");
  174.                                                 }
  175.                                         }

  176.                                         // done状态下
  177.                                         if (state == DONE) {
  178.                                                 if (tempY - startY > 0) {
  179.                                                         state = PULL_To_REFRESH;
  180.                                                         changeHeaderViewByState();
  181.                                                 }
  182.                                         }

  183.                                         // 更新headView的size
  184.                                         if (state == PULL_To_REFRESH) {
  185.                                                 headView.setPadding(0, -1 * headContentHeight
  186.                                                                 + (tempY - startY) / RATIO, 0, 0);

  187.                                         }

  188.                                         // 更新headView的paddingTop
  189.                                         if (state == RELEASE_To_REFRESH) {
  190.                                                 headView.setPadding(0, (tempY - startY) / RATIO
  191.                                                                 - headContentHeight, 0, 0);
  192.                                         }

  193.                                 }

  194.                                 break;
  195.                         }
  196.                 }

  197.                 return super.onTouchEvent(event);
  198.         }

  199.         // 当状态改变时候,调用该方法,以更新界面
  200.         private void changeHeaderViewByState() {
  201.                 switch (state) {
  202.                 case RELEASE_To_REFRESH:
  203.                         arrowImageView.setVisibility(View.VISIBLE);
  204.                         progressBar.setVisibility(View.GONE);
  205.                         tipsTextview.setVisibility(View.VISIBLE);
  206.                         lastUpdatedTextView.setVisibility(View.VISIBLE);

  207.                         arrowImageView.clearAnimation();
  208.                         arrowImageView.startAnimation(animation);

  209.                         tipsTextview.setText("松开刷新");

  210.                         Log.v(TAG, "当前状态,松开刷新");
  211.                         break;
  212.                 case PULL_To_REFRESH:
  213.                         progressBar.setVisibility(View.GONE);
  214.                         tipsTextview.setVisibility(View.VISIBLE);
  215.                         lastUpdatedTextView.setVisibility(View.VISIBLE);
  216.                         arrowImageView.clearAnimation();
  217.                         arrowImageView.setVisibility(View.VISIBLE);
  218.                         // 是由RELEASE_To_REFRESH状态转变来的
  219.                         if (isBack) {
  220.                                 isBack = false;
  221.                                 arrowImageView.clearAnimation();
  222.                                 arrowImageView.startAnimation(reverseAnimation);

  223.                                 tipsTextview.setText("下拉刷新");
  224.                         } else {
  225.                                 tipsTextview.setText("下拉刷新");
  226.                         }
  227.                         Log.v(TAG, "当前状态,下拉刷新");
  228.                         break;

  229.                 case REFRESHING:

  230.                         headView.setPadding(0, 0, 0, 0);

  231.                         progressBar.setVisibility(View.VISIBLE);
  232.                         arrowImageView.clearAnimation();
  233.                         arrowImageView.setVisibility(View.GONE);
  234.                         tipsTextview.setText("正在刷新...");
  235.                         lastUpdatedTextView.setVisibility(View.VISIBLE);

  236.                         Log.v(TAG, "当前状态,正在刷新...");
  237.                         break;
  238.                 case DONE:
  239.                         headView.setPadding(0, -1 * headContentHeight, 0, 0);

  240.                         progressBar.setVisibility(View.GONE);
  241.                         arrowImageView.clearAnimation();
  242.                         arrowImageView.setImageResource(R.drawable.arrow_down);
  243.                         tipsTextview.setText("下拉刷新");
  244.                         lastUpdatedTextView.setVisibility(View.VISIBLE);

  245.                         Log.v(TAG, "当前状态,done");
  246.                         break;
  247.                 }
  248.         }

  249.         public void setonRefreshListener(OnRefreshListener refreshListener) {
  250.                 this.refreshListener = refreshListener;
  251.                 isRefreshable = true;
  252.         }

  253.         public interface OnRefreshListener {
  254.                 public void onRefresh();
  255.         }

  256.         public void onRefreshComplete() {
  257.                 state = DONE;
  258.                 SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日  HH:mm");
  259.                 String date=format.format(new Date());
  260.                 lastUpdatedTextView.setText("最近更新:" + date);
  261.                 changeHeaderViewByState();
  262.         }

  263.         private void onRefresh() {
  264.                 if (refreshListener != null) {
  265.                         refreshListener.onRefresh();
  266.                 }
  267.         }

  268.         // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
  269.         private void measureView(View child) {
  270.                  ViewGroup.LayoutParams p = child.getLayoutParams();
  271.                 if (p == null) {
  272.                     p = new ViewGroup.LayoutParams(
  273.                             ViewGroup.LayoutParams.FILL_PARENT,
  274.                             ViewGroup.LayoutParams.WRAP_CONTENT);
  275.                 }

  276.                 int childWidthSpec = ViewGroup.getChildMeasureSpec(0,
  277.                         0 + 0, p.width);
  278.                 int lpHeight = p.height;
  279.                 int childHeightSpec;
  280.                 if (lpHeight > 0) {
  281.                     childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
  282.                 } else {
  283.                     childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
  284.                 }
  285.                 child.measure(childWidthSpec, childHeightSpec);
  286.         }

  287.         public void setAdapter(BaseAdapter adapter) {
  288.                 SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日  HH:mm");
  289.                 String date=format.format(new Date());
  290.                 lastUpdatedTextView.setText("最近更新:" + date);
  291.                 super.setAdapter(adapter);
  292.         }
  293.         

  294. }
复制代码
最后在MainActivity中添加内容:
  1. public class MainActivity extends Activity {

  2.         private LinkedList data;
  3.         private BaseAdapter adapter;

  4.         public void onCreate(Bundle savedInstanceState) {
  5.                 super.onCreate(savedInstanceState);
  6.                 setContentView(R.layout.main);

  7.                 data = new LinkedList();
  8.             for(int i=0;i<10;i++){
  9.                     data.add(String.valueOf(i));
  10.             }

  11.                 final MyListView listView = (MyListView) findViewById(R.id.listView);
  12.                 adapter = new BaseAdapter() {
  13.                         public View getView(int position, View convertView, ViewGroup parent) {
  14.                                  convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);
  15.                                 TextView textView = (TextView) convertView.findViewById(R.id.textView_item);
  16.                                 textView.setText(data.get(position));
  17.                                 return convertView;
  18.                         }

  19.                         public long getItemId(int position) {
  20.                                 return position;
  21.                         }

  22.                         public Object getItem(int position) {
  23.                                 return data.get(position);
  24.                         }

  25.                         public int getCount() {
  26.                                 return data.size();
  27.                         }
  28.                 };
  29.                 listView.setAdapter(adapter);

  30.                 listView.setonRefreshListener(new OnRefreshListener() {
  31.                         public void onRefresh() {
  32.                                 new AsyncTask() {
  33.                                         protected Void doInBackground(Void... params) {
  34.                                                 try {
  35.                                                         Thread.sleep(1000);
  36.                                                 } catch (Exception e) {
  37.                                                         e.printStackTrace();
  38.                                                 }
  39.                                                 data.addFirst("刷新后的内容");
  40.                                                 return null;
  41.                                         }

  42.                                         @Override
  43.                                         protected void onPostExecute(Void result) {
  44.                                                 adapter.notifyDataSetChanged();
  45.                                                 listView.onRefreshComplete();
  46.                                         }

  47.                                 }.execute(null);
  48.                         }
  49.                 });
  50.         }
  51. }
复制代码
  运行效果:



   
    源码下载地址:仿新浪微博的ListView下拉更新功能

 CustomListView.rar (78.74 KB, 下载次数: 1265) 
原创粉丝点击