首页 > 代码库 > Android自定义ListView下拉刷新

Android自定义ListView下拉刷新

实现的目标是本地有数据并没有刷新。下拉数据及时刷新数据。

我在网上找了某位写的MyListView,这个东西的下拉核心部分还是没有弄明白。非常感谢这位作者。

 

XML布局文件源代码:

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

 

自定义MyListView源代码:

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.ImageView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.AbsListView.OnScrollListener;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;//加载中        private LayoutInflater inflater = null;//对于一个没有被载入或者想要动态载入的界面    private LinearLayout headView;//线性布局    private ImageView arrowImageView = null;//箭头图片        private TextView tipsTextview;    private TextView lastUpdatedTextView;    private ProgressBar progressBar;        private int headContentWidth;//head的宽度    private int headContentHeight;//head的高度        private RotateAnimation animation;    private RotateAnimation reverseAnimation;        private int firstItemIndex;        /**     *  记录ListView的状态信息,起始状态为DONE     */    private int state;    /**     *  控制是否能够刷新,默认为false     */    private boolean isRefreshable;        // 实际的padding的距离与界面上偏移距离的比例    private final static int RATIO = 3;    // 用于保证startY的值在一个完整的touch事件中只被记录一次    private boolean isRecored;    private int startY;    private boolean isBack;        private OnRefreshListener refreshListener;        public interface OnRefreshListener {        public void onRefresh();    }        private void onRefresh() {        if (refreshListener != null) {            refreshListener.onRefresh();        }    }        public MyListView(Context context) {        super(context);        // TODO Auto-generated constructor stub    }        public MyListView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }        private void init(Context context) {        /**         * ListView是常用的显示控件,默认背景是和系统窗口一样的透明色,如果给ListView加上背景图片,或者背景颜色时,滚动时listView会黑掉,         * 原因是,滚动时,列表里面的view重绘时,用的依旧是系统默认的透明色,颜色值为#FF191919         * 要改变这种情况,listView的属性 Android:cacheColorHint="#00000000"即可         */        setCacheColorHint(context.getResources().getColor(R.color.transparent));        inflater = LayoutInflater.from(context);        headView = (LinearLayout) inflater.inflate(R.layout.utils_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);        /**         * 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height         */        measureView(headView);        headContentHeight = headView.getMeasuredHeight();        headContentWidth = headView.getMeasuredWidth();        headView.setPadding(0, -1 * headContentHeight, 0, 0);        headView.invalidate();        /**         * Header添加到ListView头部,然后设置一个Adapter         */        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;            }        /**     * 重载 AbsListView中的onTouchEvent事件     *      * 参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息,例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。     * 返回值:该方法的返回值机理与键盘响应事件的相同,同样是当已经完整地处理了该事件且不希望其他回调方法再次处理时返回true,否则返回false。     *      */    @Override    public boolean onTouchEvent(MotionEvent event) {        if (isRefreshable) {            //得到触摸的类型            switch (event.getAction()) {                case MotionEvent.ACTION_DOWN:                    /**                     * firstVisibleItem:当前能看见的第一个列表项ID(从0开始)                     * 意思就是往下拉的时候没有isRecored而且能看到一个的列表项时记录startY                     */                    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();                    /**                     * 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶部,                     * 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用                     * 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分                     * 此时需要更改状态:state = PULL_To_REFRESH                      */                    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的状态                             * 等于headContentHeight时,即是正好完全显示header部分                             * 大于headContentHeight时,即是超出header部分更多                             * 当header部分能够完全显示或者超出显示,                             * 需要更改状态: state = 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) {                            /**                             * 手指移动过程中tempY数据会不断变化,当滑动到firstItemIndex,即到达顶                             * 需要记录手指所在屏幕的位置: startY = tempY ,后面作位置比较使用                             * 如果手指继续向下推,tempY继续变化,当tempY-startY>0,即是需要显示header部分                             * 此时需要更改状态:state = PULL_To_REFRESH                             */                            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);    }        //滚动时一直回调,直到停止滚动时才停止回调。单击时回调一次。           public void onScroll(AbsListView view, int     firstVisibleItem, int visibleItemCount, int totalItemCount) {         firstItemIndex = firstVisibleItem;    }    //正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调        //回调顺序如下        //第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动        //第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)        //第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动      public void onScrollStateChanged(AbsListView view, int scrollState) {        }          /**     * 当状态改变时候,调用该方法,以更新界面     */    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.arrow);            tipsTextview.setText("下拉刷新");            lastUpdatedTextView.setVisibility(View.VISIBLE);            Log.v(TAG, "当前状态,done");            break;        }    }        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);    }}

 

使用MyListView:

private MyListView myListView = null;myListView=(MyListView)((Activity) context).findViewById(R.id.listview);private void RefreshListener(){    myListView.setonRefreshListener(new OnRefreshListener() {        public void onRefresh() {             new AsyncTask<Void, Void, Void>() {                protected Void doInBackground(Void... params) {                    try {                        Thread.sleep(8000);                    } catch (Exception e) {                        e.printStackTrace();                    }                       return null;                }                 @Override                protected void onPostExecute(Void result) {                    myAdapter.notifyDataSetChanged();                    myListView.onRefreshComplete();                }             }.execute((Void)null);            Log.e("Informacation","下拉刷新");            //doSomeThings        }    });}