android的OnScrollListener实现下拉刷新

摘要 OnScrollListener回调分析 如果adapter中的数据量很大的时候,在加载listview时会出现卡顿的现象。这是会让用户抓狂!最好的解决办法就是先加载一定数量的数据,然后在最下方提示正在加载! 动态加载就是把放入adapter中的数据分好几次加载。在用户拖动listv

OnScrollListener回调分析

如果adapter中的数据量很大的时候,在加载listview时会出现卡顿的现象。这是会让用户抓狂!最好的解决办法就是先加载一定数量的数据,然后在最下方提示正在加载!
动态加载就是把放入adapter中的数据分好几次加载。在用户拖动listview时再加载一定的数据,和sina微博的客户端类似。
给listview添加OnScrollListener监听事件默认会覆盖下面两个方法:

new OnScrollListener() {      
        boolean isLastRow = false;      
        @Override      
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {      
            //滚动时一直回调,直到停止滚动时才停止回调。单击时回调一次。      
            //firstVisibleItem:当前能看见的第一个列表项ID(从0开始)      
            //visibleItemCount:当前能看见的列表项个数(小半个也算)      
            //totalItemCount:列表项共数      
            //判断是否滚到最后一行      
            if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0) {      
                isLastRow = true;      
            }      
        }      
        @Override      
        public void onScrollStateChanged(AbsListView view, int scrollState) {      
            //正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调      
            //回调顺序如下      
            //第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动      
            //第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)      
            //第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动                
            //当屏幕停止滚动时为0;当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1;    
            //由于用户的操作,屏幕产生惯性滑动时为2    
            //当滚到最后一行且停止滚动时,执行加载      
            if (isLastRow && scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {      
                //加载元素      
                ......      
                isLastRow = false;      
            }      
        }      
    }

下面给一个常用的listview所用到的下拉效果demo代码:

1.首先创建一个头部xml文件

<?xml version="1.0" encoding="utf-8"?>  
<!-- ListView的头部 -->  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
 >  
  <!-- 内容 -->  
  <RelativeLayout  
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:id="@+id/head_contentLayout"
  android:paddingLeft="30dp"
  >  
  <!-- 箭头图像、进度条 -->  
  <FrameLayout  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_alignParentLeft="true"
  android:layout_centerVertical="true"
  >  
  <!-- 箭头 -->  
  <ImageView  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="center"
  android:src="@drawable/pull_down_arrow"
  android:id="@+id/head_arrowImageView"
  />  
  <!-- 进度条 -->  
  <ProgressBar  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  style="?android:attr/progressBarStyleSmall"
  android:layout_gravity="center"
  android:id="@+id/head_progressBar"
  android:visibility="gone"
  />  
  </FrameLayout>  
  <!-- 提示、最近更新 -->  
  <LinearLayout  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_centerHorizontal="true"
  android:orientation="vertical"
  android:gravity="center_horizontal"
  >  
  <!-- 提示 -->  
  <TextView  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="下拉刷新"
  android:textColor="@color/white"
  android:textSize="20sp"
  android:id="@+id/head_tipsTextView"
  />  
  <!-- 最近更新 -->  
  <TextView  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/head_lastUpdatedTextView"
  android:text="上次更新"
  android:textColor="@color/gold"
  android:textSize="10sp"
  />  
  </LinearLayout>  
  </RelativeLayout>  
</LinearLayout>

2.然后写一个class:

package com.laohuai.appdemo.customui.ui;  
import java.util.Date;  
import com.laohuai.appdemo.customui.R;  
import android.content.Context;  
import android.util.AttributeSet;  
import android.util.Log;  
import android.view.LayoutInflater;  
import android.view.MotionEvent;  
import android.view.View;  
import android.view.ViewGroup;  
import android.view.animation.LinearInterpolator;  
import android.view.animation.RotateAnimation;  
import android.widget.AbsListView;  
import android.widget.BaseAdapter;  
import android.widget.ImageView;  
import android.widget.LinearLayout;  
import android.widget.ListView;  
import android.widget.AbsListView.OnScrollListener;  
import android.widget.ProgressBar;  
import android.widget.TextView;  
public class MyListView extends ListView implements OnScrollListener {  
    private static final String TAG = "listview";  
    private final static int RELEASE_To_REFRESH = 0;  
    private final static int PULL_To_REFRESH = 1;  
    private final static int REFRESHING = 2;  
    private final static int DONE = 3;  
    private final static int LOADING = 4;  
    // 实际的padding的距离与界面上偏移距离的比例  
    private final static int RATIO = 3;  
    private LayoutInflater inflater;  
    private LinearLayout headView;  
    private TextView tipsTextview;  
    private TextView lastUpdatedTextView;  
    private ImageView arrowImageView;  
    private ProgressBar progressBar;  
    private RotateAnimation animation;  
    private RotateAnimation reverseAnimation;  
    // 用于保证startY的值在一个完整的touch事件中只被记录一次  
    private boolean isRecored;  
    private int headContentWidth;  
    private int headContentHeight;  
    private int startY;  
    private int firstItemIndex;  
    private int state;  
    private boolean isBack;  
    private OnRefreshListener refreshListener;  
    private boolean isRefreshable;  
    public MyListView(Context context) {  
        super(context);  
        init(context);  
    }  
    public MyListView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        init(context);  
    }  
    private void init(Context context) {  
        setCacheColorHint(context.getResources().getColor(R.color.transparent));  
        inflater = LayoutInflater.from(context);  
        headView = (LinearLayout) inflater.inflate(R.layout.head, null);  
        arrowImageView = (ImageView) headView  
                .findViewById(R.id.head_arrowImageView);  
        arrowImageView.setMinimumWidth(70);  
        arrowImageView.setMinimumHeight(50);  
        progressBar = (ProgressBar) headView  
                .findViewById(R.id.head_progressBar);  
        tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);  
        lastUpdatedTextView = (TextView) headView  
                .findViewById(R.id.head_lastUpdatedTextView);  
        measureView(headView);  
        headContentHeight = headView.getMeasuredHeight();  
        headContentWidth = headView.getMeasuredWidth();  
        headView.setPadding(0, -1 * headContentHeight, 0, 0);  
        headView.invalidate();  
        Log.v("size", "width:" + headContentWidth + " height:"
                + headContentHeight);  
        addHeaderView(headView, null, false);  
        setOnScrollListener(this);  
        animation = new RotateAnimation(0, -180,  
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
        animation.setInterpolator(new LinearInterpolator());  
        animation.setDuration(250);  
        animation.setFillAfter(true);  
        reverseAnimation = new RotateAnimation(-180, 0,  
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
        reverseAnimation.setInterpolator(new LinearInterpolator());  
        reverseAnimation.setDuration(200);  
        reverseAnimation.setFillAfter(true);  
        state = DONE;  
        isRefreshable = false;  
    }  
    public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,  
            int arg3) {  
        firstItemIndex = firstVisiableItem;  
    }  
    public void onScrollStateChanged(AbsListView arg0, int arg1) {  
    }  
    public boolean onTouchEvent(MotionEvent event) {  
        if (isRefreshable) {  
            switch (event.getAction()) {  
            case MotionEvent.ACTION_DOWN:  
                if (firstItemIndex == 0 && !isRecored) {  
                    isRecored = true;  
                    startY = (int) event.getY();  
                    Log.v(TAG, "在down时候记录当前位置‘");  
                }  
                break;  
            case MotionEvent.ACTION_UP:  
                if (state != REFRESHING && state != LOADING) {  
                    if (state == DONE) {  
                        // 什么都不做  
                    }  
                    if (state == PULL_To_REFRESH) {  
                        state = DONE;  
                        changeHeaderViewByState();  
                        Log.v(TAG, "由下拉刷新状态,到done状态");  
                    }  
                    if (state == RELEASE_To_REFRESH) {  
                        state = REFRESHING;  
                        changeHeaderViewByState();  
                        onRefresh();  
                        Log.v(TAG, "由松开刷新状态,到done状态");  
                    }  
                }  
                isRecored = false;  
                isBack = false;  
                break;  
            case MotionEvent.ACTION_MOVE:  
                int tempY = (int) event.getY();  
                if (!isRecored && firstItemIndex == 0) {  
                    Log.v(TAG, "在move时候记录下位置");  
                    isRecored = true;  
                    startY = tempY;  
                }  
                if (state != REFRESHING && isRecored && state != LOADING) {  
                    // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动  
                    // 可以松手去刷新了  
                    if (state == RELEASE_To_REFRESH) {  
                        setSelection(0);  
                        // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步  
                        if (((tempY - startY) / RATIO < headContentHeight)  
                                && (tempY - startY) > 0) {  
                            state = PULL_To_REFRESH;  
                            changeHeaderViewByState();  
                            Log.v(TAG, "由松开刷新状态转变到下拉刷新状态");  
                        }  
                        // 一下子推到顶了  
                        else if (tempY - startY <= 0) {  
                            state = DONE;  
                            changeHeaderViewByState();  
                            Log.v(TAG, "由松开刷新状态转变到done状态");  
                        }  
                        // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步  
                        else {  
                            // 不用进行特别的操作,只用更新paddingTop的值就行了  
                        }  
                    }  
                    // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态  
                    if (state == PULL_To_REFRESH) {  
                        setSelection(0);  
                        // 下拉到可以进入RELEASE_TO_REFRESH的状态  
                        if ((tempY - startY) / RATIO >= headContentHeight) {  
                            state = RELEASE_To_REFRESH;  
                            isBack = true;  
                            changeHeaderViewByState();  
                            Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");  
                        }  
                        // 上推到顶了  
                        else if (tempY - startY <= 0) {  
                            state = DONE;  
                            changeHeaderViewByState();  
                            Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");  
                        }  
                    }  
                    // done状态下  
                    if (state == DONE) {  
                        if (tempY - startY > 0) {  
                            state = PULL_To_REFRESH;  
                            changeHeaderViewByState();  
                        }  
                    }  
                    // 更新headView的size  
                    if (state == PULL_To_REFRESH) {  
                        headView.setPadding(0, -1 * headContentHeight  
                                + (tempY - startY) / RATIO, 0, 0);  
                    }  
                    // 更新headView的paddingTop  
                    if (state == RELEASE_To_REFRESH) {  
                        headView.setPadding(0, (tempY - startY) / RATIO  
                                - headContentHeight, 0, 0);  
                    }  
                }  
                break;  
            }  
        }  
        return super.onTouchEvent(event);  
    }  
    // 当状态改变时候,调用该方法,以更新界面  
    private void changeHeaderViewByState() {  
        switch (state) {  
        case RELEASE_To_REFRESH:  
            arrowImageView.setVisibility(View.VISIBLE);  
            progressBar.setVisibility(View.GONE);  
            tipsTextview.setVisibility(View.VISIBLE);  
            lastUpdatedTextView.setVisibility(View.VISIBLE);  
            arrowImageView.clearAnimation();  
            arrowImageView.startAnimation(animation);  
            tipsTextview.setText("松开刷新");  
            Log.v(TAG, "当前状态,松开刷新");  
            break;  
        case PULL_To_REFRESH:  
            progressBar.setVisibility(View.GONE);  
            tipsTextview.setVisibility(View.VISIBLE);  
            lastUpdatedTextView.setVisibility(View.VISIBLE);  
            arrowImageView.clearAnimation();  
            arrowImageView.setVisibility(View.VISIBLE);  
            // 是由RELEASE_To_REFRESH状态转变来的  
            if (isBack) {  
                isBack = false;  
                arrowImageView.clearAnimation();  
                arrowImageView.startAnimation(reverseAnimation);  
                tipsTextview.setText("下拉刷新");  
            } else {  
                tipsTextview.setText("下拉刷新");  
            }  
            Log.v(TAG, "当前状态,下拉刷新");  
            break;  
        case REFRESHING:  
            headView.setPadding(0, 0, 0, 0);  
            progressBar.setVisibility(View.VISIBLE);  
            arrowImageView.clearAnimation();  
            arrowImageView.setVisibility(View.GONE);  
            tipsTextview.setText("正在刷新...");  
            lastUpdatedTextView.setVisibility(View.VISIBLE);  
            Log.v(TAG, "当前状态,正在刷新...");  
            break;  
        case DONE:  
            headView.setPadding(0, -1 * headContentHeight, 0, 0);  
            progressBar.setVisibility(View.GONE);  
            arrowImageView.clearAnimation();  
            arrowImageView.setImageResource(R.drawable.pull_down_arrow);  
            tipsTextview.setText("下拉刷新");  
            lastUpdatedTextView.setVisibility(View.VISIBLE);  
            Log.v(TAG, "当前状态,done");  
            break;  
        }  
    }  
    public void setonRefreshListener(OnRefreshListener refreshListener) {  
        this.refreshListener = refreshListener;  
        isRefreshable = true;  
    }  
    public interface OnRefreshListener {  
        public void onRefresh();  
    }  
    public void onRefreshComplete() {  
        state = DONE;  
        lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  
        changeHeaderViewByState();  
    }  
    private void onRefresh() {  
        if (refreshListener != null) {  
            refreshListener.onRefresh();  
        }  
    }  
    // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height  
    private void measureView(View child) {  
        ViewGroup.LayoutParams p = child.getLayoutParams();  
        if (p == null) {  
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
                    ViewGroup.LayoutParams.WRAP_CONTENT);  
        }  
        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);  
        int lpHeight = p.height;  
        int childHeightSpec;  
        if (lpHeight > 0) {  
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
                    MeasureSpec.EXACTLY);  
        } else {  
            childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
                    MeasureSpec.UNSPECIFIED);  
        }  
        child.measure(childWidthSpec, childHeightSpec);  
    }  
    public void setAdapter(BaseAdapter adapter) {  
        lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  
        super.setAdapter(adapter);  
    }  
}

3.在main.xml 中调用上面的这个class:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" 
    android:background="#FFFFFF">  
    <com.laohuai.appdemo.customui.ui.MyListView  
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:id="@+id/listView"
    android:listSelector="@android:color/transparent"
    />  
</LinearLayout>

4.实现Activity:

package com.laohuai.appdemo.customui;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import com.laohuai.appdemo.customui.ui.MyListView;  
import com.laohuai.appdemo.customui.ui.MyListView.OnRefreshListener;  
import android.app.Activity;  
import android.content.Context;  
import android.content.res.Resources;  
import android.os.AsyncTask;  
import android.os.Bundle;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.ViewGroup;  
import android.widget.BaseAdapter;  
import android.widget.TextView;  
public class MainActivity extends Activity {  
    HashMap<String, Object> maps;  
    MyListView listView;  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        listView = (MyListView) findViewById(R.id.listView);  
        getData();  
    }  
    private void getData() {  
        List<HashMap<String, Object>> lists = new ArrayList<HashMap<String, Object>>();  
        for (int i = 0; i < 40; i++) {  
            maps = new HashMap<String, Object>();  
            maps.put("test", "搞定了没有啊");  
            lists.add(maps);  
        }  
        final ListAdapter adapter = new ListAdapter(MainActivity.this, lists);  
        listView.setAdapter(adapter);  
        listView.setonRefreshListener(new OnRefreshListener() {  
            public void onRefresh() {  
                new AsyncTask<Void, Void, Void>() {  
                    protected Void doInBackground(Void... params) {  
                        try {  
                            Thread.sleep(1000);  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                        maps.get("刷新后添加的内容");  
                        return null;  
                    }  
                    protected void onPostExecute(Void result) {  
                        adapter.notifyDataSetChanged();  
                        listView.onRefreshComplete();  
                    }  
                }.execute(null);  
            }  
        });  
    }  
    class ListAdapter extends BaseAdapter {  
        private Context mContext;  
        private List<HashMap<String, Object>> data;  
        public ListAdapter(Context mContext, List<HashMap<String, Object>> data) {  
            super();  
            this.mContext = mContext;  
            this.data = data;  
        }  
        public int getCount() {  
            // TODO Auto-generated method stub  
            return data.size();  
        }  
        public Object getItem(int position) {  
            // TODO Auto-generated method stub  
            return position;  
        }  
        public long getItemId(int position) {  
            // TODO Auto-generated method stub  
            return position;  
        }  
        public View getView(int position, View convertView, ViewGroup parent) {  
            TextView tv = new TextView(getApplicationContext());  
            tv.setText((String)data.get(position).get("test"));  
            Resources rs = getResources();  
            tv.setTextColor(rs.getColor(R.color.white));  
            return tv;  
        }  
    }  
}