Android Listview下拉刷新数据

Android listview仿新浪微博下拉刷新例子,其实所有此功能最早是国外实现的,现在网上能看到的都是基于这个例子,只是有些细节不同。算法上确实比较巧妙,直得学习一下。

AndroidManifest.xml

01<?xml version="1.0" encoding="utf-8"?>
02<manifest xmlns:android="http://schemas.android.com/apk/res/android"
03    package="cn.com.karl.list"
04    android:versionCode="1"
05    android:versionName="1.0" >
06 
07    <uses-sdk android:minSdkVersion="4" />
08 
09    <application
10        android:icon="@drawable/ic_launcher"
11        android:label="@string/app_name" >
12        <activity
13            android:label="@string/app_name"
14            android:name=".MainActivity" >
15            <intent-filter >
16                <action android:name="android.intent.action.MAIN" />
17 
18                <category android:name="android.intent.category.LAUNCHER" />
19            </intent-filter>
20        </activity>
21    </application>
22 
23</manifest>

main..xml

01<?xml version="1.0" encoding="utf-8"?>
02<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:layout_width="fill_parent"
04    android:layout_height="fill_parent"
05    android:orientation="vertical" >
06   
07    <cn.com.karl.list.MyListView
08    android:layout_width="fill_parent"
09    android:layout_height="fill_parent"
10    android:id="@+id/listView"
11    />   
12 
13</LinearLayout>

head.xml

01<?xml version="1.0" encoding="utf-8"?>
02 
03<!-- ListView的头部 -->
04 
05<LinearLayout
06  xmlns:android="http://schemas.android.com/apk/res/android"
07  android:layout_width="fill_parent"
08  android:layout_height="wrap_content"
09  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.xml

01<?xml version="1.0" encoding="utf-8"?>
02<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:layout_width="match_parent"
04    android:layout_height="wrap_content"
05    android:gravity="center_vertical"
06    android:background="#ffffff"
07    android:orientation="horizontal" >
08 
09    <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>

MainActivity.java

001package cn.com.karl.list;
002 
003 
004import java.util.LinkedList;
005 
006import cn.com.karl.list.MyListView.OnRefreshListener;
007 
008import android.app.Activity;
009import android.content.res.Configuration;
010import android.os.AsyncTask;
011import android.os.Bundle;
012import android.util.DisplayMetrics;
013import android.util.Log;
014import android.view.LayoutInflater;
015import android.view.View;
016import android.view.ViewGroup;
017import android.view.WindowManager;
018import android.widget.BaseAdapter;
019import android.widget.TextView;
020 
021public class MainActivity extends Activity
022{
023 
024    private LinkedList<String> data;
025    private BaseAdapter adapter;
026    private MyListView listView;
027 
028    public void onCreate(Bundle savedInstanceState)
029    {
030        super.onCreate(savedInstanceState);
031        setContentView(R.layout.main);
032 
033        // 取得屏幕尺寸大小
034        displayScreenSize();
035       
036        data = new LinkedList<String>();
037        for(int i=0;i<10;i++)
038        {
039            data.add(String.valueOf(i));
040        }
041 
042        listView = (MyListView) findViewById(R.id.listView);
043       
044        adapter = new BaseAdapter()
045        {
046            public View getView(int position, View convertView, ViewGroup parent)
047            {
048                convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);
049                TextView textView = (TextView) convertView.findViewById(R.id.textView_item);
050                textView.setText(data.get(position));
051                return convertView;
052            }
053 
054            public long getItemId(int position)
055            {
056                return position;
057            }
058 
059            public Object getItem(int position)
060            {
061                return data.get(position);
062            }
063 
064            public int getCount()
065            {
066                return data.size();
067            }
068        };
069       
070        listView.setAdapter(adapter);
071 
072        listView.setOnRefreshListener(new OnRefreshListener()
073        {
074            public void onRefresh()
075            {
076                RefreshTask rTask = new RefreshTask();
077                rTask.execute(1000);
078            }
079        });
080    }
081   
082    // AsyncTask异步任务
083    class RefreshTask extends AsyncTask<Integer, Integer, String>
084    {   
085        @Override
086        protected String doInBackground(Integer... params)
087        {
088            try
089            {
090                Thread.sleep(params[0]);
091            }
092            catch (Exception e)
093            {
094                e.printStackTrace();
095            }
096            // 在data最前添加数据
097            data.addFirst("刷新后的内容");
098            return null;
099        }  
100 
101        @Override
102        protected void onPostExecute(String result)
103        {
104            super.onPostExecute(result);
105            adapter.notifyDataSetChanged();
106            listView.onRefreshComplete();
107        }      
108    }
109   
110    // 取得屏幕尺寸大小
111    public void displayScreenSize()
112    {
113        // 屏幕方面切换时获得方向
114        if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
115        {      
116            setTitle("landscape");
117        }
118       
119        if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
120        {
121            setTitle("portrait");
122        }
123       
124        // 获得屏幕大小1
125        WindowManager manager = getWindowManager();
126        int width = manager.getDefaultDisplay().getWidth();
127        int height = manager.getDefaultDisplay().getHeight();
128       
129        Log.v("am10", "width: " + width + " height: " + height);
130       
131        // 获得屏幕大小2
132        DisplayMetrics dMetrics = new DisplayMetrics();
133        getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
134        int screenWidth = dMetrics.widthPixels;
135        int screenHeight = dMetrics.heightPixels;
136       
137        Log.v("am10", "screenWidth: " + screenWidth + " screenHeight: " + screenHeight);
138    }
139}

MyListView.java

001package cn.com.karl.list;
002 
003import java.text.SimpleDateFormat;
004import java.util.Date;
005 
006import android.content.Context;
007import android.util.AttributeSet;
008import android.util.Log;
009import android.view.LayoutInflater;
010import android.view.MotionEvent;
011import android.view.View;
012import android.view.ViewGroup;
013import android.view.animation.LinearInterpolator;
014import android.view.animation.RotateAnimation;
015import android.widget.AbsListView;
016import android.widget.BaseAdapter;
017import android.widget.ImageView;
018import android.widget.LinearLayout;
019import android.widget.ListView;
020import android.widget.AbsListView.OnScrollListener;
021//import android.widget.ProgressBar;
022import android.widget.TextView;
023 
024public class MyListView extends ListView implements OnScrollListener
025{
026    private static final String TAG = "am10";
027    private final static int RELEASE_To_REFRESH = 0;
028    private final static int PULL_To_REFRESH = 1;
029    private final static int REFRESHING = 2;
030    private final static int DONE = 3;
031    private final static int LOADING = 4;
032 
033    // 实际的padding的距离与界面上偏移距离的比例
034    private final static int RATIO = 3;
035   
036    private LayoutInflater inflater;
037    private LinearLayout headView;
038    private TextView tipsTextview;
039    private TextView lastUpdatedTextView;
040    private ImageView arrowImageView;
041    //private ProgressBar progressBar;
042 
043    private RotateAnimation animation;
044    private RotateAnimation reverseAnimation;
045 
046    // 用于保证startY的值在一个完整的touch事件中只被记录一次
047    private boolean isRecored;
048   
049    private int startY;
050    private int firstItemIndex;
051   
052    private int headContentWidth;
053    private int headContentHeight;
054 
055    private int state;
056 
057    private boolean isBack;
058 
059    private OnRefreshListener refreshListener;
060 
061    private boolean isRefreshable;
062 
063    public MyListView(Context context)
064    {
065        super(context);
066        init(context);
067    }
068 
069    public MyListView(Context context, AttributeSet attrs)
070    {
071        super(context, attrs);
072        init(context);
073    }
074 
075    private void init(Context context)
076    {
077        //setCacheColorHint(context.getResources().getColor(R.color.transparent));
078        inflater = LayoutInflater.from(context);
079 
080        headView = (LinearLayout) inflater.inflate(R.layout.head, null);
081 
082        arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);
083        arrowImageView.setMinimumWidth(70);
084        arrowImageView.setMinimumHeight(50);
085       
086        //progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);    
087        tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);
088        lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);
089 
090        // 估算headView的宽和高
091        measureView(headView);
092        headContentHeight = headView.getMeasuredHeight();
093        headContentWidth = headView.getMeasuredWidth();
094       
095        headView.setPadding(0, -1 * headContentHeight, 0, 0);
096        headView.invalidate();
097 
098        Log.v(TAG, "width:" + headContentWidth + " height:" + headContentHeight);
099 
100        addHeaderView(headView, null, false);
101        setOnScrollListener(this);
102 
103        animation = new RotateAnimation(0, -180,
104                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
105                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
106        animation.setInterpolator(new LinearInterpolator());
107        animation.setDuration(250);
108        animation.setFillAfter(true);
109 
110        reverseAnimation = new RotateAnimation(-180, 0,
111                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
112                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
113        reverseAnimation.setInterpolator(new LinearInterpolator());
114        reverseAnimation.setDuration(200);
115        reverseAnimation.setFillAfter(true);
116 
117        state = DONE;
118        isRefreshable = false;
119    }
120 
121    public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2, int arg3)
122    {
123        firstItemIndex = firstVisiableItem;
124        //Log.v(TAG, "firstItemIndex: "+firstItemIndex);
125    }
126 
127    public void onScrollStateChanged(AbsListView arg0, int arg1)
128    {
129    }
130 
131    public boolean onTouchEvent(MotionEvent event)
132    {
133        // Log.v(TAG, "isRefreshable: "+isRefreshable);
134        if (isRefreshable)
135        {
136            switch (event.getAction())
137            {
138            case MotionEvent.ACTION_DOWN:
139                if (firstItemIndex == 0 && !isRecored)
140                {
141                    isRecored = true;
142                   
143                    // 触摸屏幕的位置
144                    startY = (int) event.getY();   
145                    Log.v(TAG, "在down时候记录当前位置" + " startY:"+startY);
146                }
147                break;
148 
149            case MotionEvent.ACTION_UP:
150                if (state != REFRESHING && state != LOADING)
151                {
152                    if (state == DONE)
153                    {
154                        // 什么都不做
155                    }
156                   
157                    if (state == PULL_To_REFRESH)
158                    {
159                        state = DONE;
160                        changeHeaderViewByState();
161                        Log.v(TAG, "由下拉刷新状态,到done状态");
162                    }
163                   
164                    if (state == RELEASE_To_REFRESH)
165                    {
166                        state = REFRESHING;
167                        changeHeaderViewByState();
168                        onRefresh();
169                        Log.v(TAG, "由松开刷新状态,到done状态");
170                    }
171                }
172 
173                isRecored = false;
174                isBack = false;
175                break;
176 
177            case MotionEvent.ACTION_MOVE:
178                int tempY = (int) event.getY();
179                //Log.v(TAG, "tempY: " + tempY);
180               
181                /**                             
182                 * 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶部,
183                 * 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用
184                 *
185                 * 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分
186                 *
187                 * 此时需要更改状态:state = PULL_To_REFRESH
188                 */
189                if (!isRecored && firstItemIndex == 0)
190                {
191                    isRecored = true;
192                    startY = tempY;
193                    Log.v(TAG, "在move时候记录下位置" + " startY:"+startY);
194                }
195 
196                if (state != REFRESHING && isRecored && state != LOADING)
197                {
198                    /**
199                     * 保证在设置padding的过程中,当前的位置一直是在head,
200                     * 否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
201                     */               
202 
203                    // 可以松手去刷新了
204                    if (state == RELEASE_To_REFRESH)
205                    {
206                        setSelection(0);
207 
208                        // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
209                        if (((tempY - startY) / RATIO < headContentHeight) && (tempY - startY) > 0)
210                        {
211                            state = PULL_To_REFRESH;
212                            changeHeaderViewByState();
213                            Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");
214                        }
215                       
216                        // 一下子推到顶了,没有显示header部分时,应该恢复DONE状态,这里机率很小
217                        else if (tempY - startY <= 0)
218                        {
219                            state = DONE;
220                            changeHeaderViewByState();
221                            Log.v(TAG, "---由松开刷新状态转变到done状态");
222                        }                      
223                        else
224                        {
225                            // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
226                            // 不用进行特别的操作,只用更新paddingTop的值就行了
227                        }
228                    }
229                   
230                    // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
231                    if (state == PULL_To_REFRESH)
232                    {
233                        setSelection(0);
234 
235                        /**
236                         * 下拉到可以进入RELEASE_TO_REFRESH的状态
237                         *
238                         * 等于headContentHeight时,即是正好完全显示header部分
239                         * 大于headContentHeight时,即是超出header部分更多
240                         *
241                         * 当header部分能够完全显示或者超出显示,
242                         * 需要更改状态: state = RELEASE_To_REFRESH
243                         */
244                        if ((tempY - startY) / RATIO >= headContentHeight)
245                        {
246                            state = RELEASE_To_REFRESH;
247                            isBack = true;
248                            changeHeaderViewByState();
249                            Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");
250                        }
251                       
252                        // 上推到顶了,没有显示header部分时,应该恢复DONE状态
253                        else if (tempY - startY <= 0)
254                        {
255                            state = DONE;
256                            changeHeaderViewByState();
257                            Log.v(TAG, "由done或者下拉刷新状态转变到done状态");
258                        }
259                    }
260 
261                    // done状态下
262                    if (state == DONE)
263                    {
264                        if (tempY - startY > 0)
265                        {
266                            /**                             
267                             * 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶部,
268                             * 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用
269                             *
270                             * 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分
271                             *
272                             * 此时需要更改状态:state = PULL_To_REFRESH
273                             */
274                            //Log.v(TAG, "----------------PULL_To_REFRESH " + (tempY - startY));                           
275                            state = PULL_To_REFRESH;
276                            changeHeaderViewByState();
277                        }
278                    }
279 
280                    // 更新headView的paddingTop
281                    if (state == PULL_To_REFRESH)
282                    {
283                        //Log.v(TAG, "----------------PULL_To_REFRESH2 " + (tempY - startY));
284                        headView.setPadding(0, -1 * headContentHeight + (tempY - startY) / RATIO, 0, 0);
285                    }
286 
287                    // 继续更新headView的paddingTop
288                    if (state == RELEASE_To_REFRESH)
289                    {
290                        headView.setPadding(0, (tempY - startY) / RATIO - headContentHeight, 0, 0);
291                    }
292                }
293                break;
294            }
295        }
296        return super.onTouchEvent(event);
297    }
298 
299    // 当状态改变时候,调用该方法,以更新界面
300    private void changeHeaderViewByState()
301    {
302        switch (state)
303        {
304            case RELEASE_To_REFRESH:
305                arrowImageView.setVisibility(View.VISIBLE);
306                //progressBar.setVisibility(View.GONE);
307                tipsTextview.setVisibility(View.VISIBLE);
308                lastUpdatedTextView.setVisibility(View.VISIBLE);
309   
310                arrowImageView.clearAnimation();
311                arrowImageView.startAnimation(animation);
312   
313                tipsTextview.setText("松开刷新");
314   
315                Log.v(TAG, "当前状态,松开刷新");
316                break;
317               
318            case PULL_To_REFRESH:
319                //progressBar.setVisibility(View.GONE);
320                tipsTextview.setVisibility(View.VISIBLE);
321                lastUpdatedTextView.setVisibility(View.VISIBLE);
322                arrowImageView.clearAnimation();
323                arrowImageView.setVisibility(View.VISIBLE);
324               
325                /**
326                 *  是否向下滑回,是由RELEASE_To_REFRESH状态转变来的
327                 */
328                if (isBack)
329                {                  
330                    isBack = false;
331                    arrowImageView.clearAnimation();
332                    arrowImageView.startAnimation(reverseAnimation);   
333                    tipsTextview.setText("下拉刷新");
334                    //Log.v(TAG, "isBack: " + isBack);
335                }
336                else
337                {                  
338                    tipsTextview.setText("下拉刷新");
339                    //Log.v(TAG, "isBack: " + isBack);
340                }
341               
342                Log.v(TAG, "当前状态,下拉刷新");
343                break;
344   
345            case REFRESHING:
346                Log.v(TAG, "REFRESHING...");
347                headView.setPadding(0, 0, 0, 0);
348   
349                //progressBar.setVisibility(View.VISIBLE);
350                arrowImageView.clearAnimation();
351                arrowImageView.setVisibility(View.GONE);
352                tipsTextview.setText("正在刷新...");
353                lastUpdatedTextView.setVisibility(View.VISIBLE);
354   
355                Log.v(TAG, "当前状态,正在刷新...");
356                break;
357               
358            case DONE:
359                headView.setPadding(0, -1 * headContentHeight, 0, 0);
360   
361                //progressBar.setVisibility(View.GONE);
362                arrowImageView.clearAnimation();
363                arrowImageView.setImageResource(R.drawable.arrow_down);
364                tipsTextview.setText("下拉刷新");
365                lastUpdatedTextView.setVisibility(View.VISIBLE);
366   
367                Log.v(TAG, "当前状态,done");
368                break;
369        }
370    }
371 
372    public void setOnRefreshListener(OnRefreshListener refreshListener)
373    {
374        this.refreshListener = refreshListener;
375        isRefreshable = true;
376    }
377 
378    public interface OnRefreshListener
379    {
380        public void onRefresh();
381    }
382 
383    public void onRefreshComplete()
384    {
385        state = DONE;
386        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm");
387        String date = format.format(new Date());
388        lastUpdatedTextView.setText("最近更新:" + date);
389        changeHeaderViewByState();
390    }
391 
392    private void onRefresh()
393    {
394        if (refreshListener != null)
395        {
396            refreshListener.onRefresh();
397        }
398    }
399 
400    // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height
401    private void measureView(View child)
402    {
403        ViewGroup.LayoutParams p = child.getLayoutParams();
404        if (p == null)
405        {
406            //Log.v(TAG, "LayoutParams is null.");
407            p = new ViewGroup.LayoutParams(
408                    ViewGroup.LayoutParams.FILL_PARENT,
409                    ViewGroup.LayoutParams.WRAP_CONTENT);
410        }
411 
412        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
413        int lpHeight = p.height;
414        int childHeightSpec;
415       
416        if (lpHeight > 0)
417        {
418            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
419        }
420        else
421        {
422            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
423        }
424 
425        child.measure(childWidthSpec, childHeightSpec);
426    }
427 
428    public void setAdapter(BaseAdapter adapter)
429    {
430        SimpleDateFormat format=new SimpleDateFormat("yyyy年MM月dd日  HH:mm");
431        String date=format.format(new Date());
432        lastUpdatedTextView.setText("最近更新:" + date);
433        super.setAdapter(adapter);
434    }
435}

这个例子忘记那里下载的,这里也尊重作者,注明并不是我本人写的。如果谁知道原作者资料请告知,在此添加。

我修改了一下代码,使用AsyncTask异步任务更新数据,还有添加和修改了一些注释。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值