實現ListView的下拉刷新功能

1.實現原理

在ListView的頂部添加一個header佈局,在下拉過程中,然後重新onTouchEvent(),根據滑動過程中y軸的變化,改變header佈局的狀態,包括hender的提示音和箭頭方向。

2.實現步驟

1)創建header佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:id="@+id/layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_centerInParent="true"
            android:gravity="center">
            <TextView
                android:id="@+id/tv_tip"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉可以刷新"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/tv_lastupdatetime"/>

        </LinearLayout>
        <ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toLeftOf="@+id/layout"
            android:layout_marginRight="20dp"
            android:src="@drawable/pull_to_refresh_arrow"
            />
        <ProgressBar
            android:id="@+id/progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="?android:attr/progressBarStyleSmall"
            android:layout_toLeftOf="@+id/layout"
            android:layout_marginRight="20dp"
            android:visibility="gone"/>
    </RelativeLayout>
</LinearLayout>

2)自定義ListView
public class RefreshListView extends ListView implements AbsListView.OnScrollListener{
    private static final String TAG = "RefreshListView";

    private int headerHeight;//頂部佈局文件的高度
    //頂部的各種狀態
    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 final static int RATIO = 3;
    //實際的padding的距離與界面上偏移距離的比例
    //頭部佈局文件的控件
    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 firstItemIndex;
    //記錄初始的Y座標
    private int startY;
    //記錄當前的狀態
    private int state;
    private boolean isRefreshable;
    private boolean isBack;
    private onRefreshListener refreshListener;

    public RefreshListView(Context context) {
        super(context);
        initView(context);
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);

    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context){
        LayoutInflater inflater = LayoutInflater.from(context);
        headView = (LinearLayout) inflater.inflate(R.layout.header_layout, null);
        measureView(headView);
        headerHeight = headView.getMeasuredHeight();
        //隱藏頂部佈局文件
        headView.setPadding(headView.getPaddingLeft(), -headerHeight, headView.getPaddingRight(), headView.getPaddingBottom());
        headView.invalidate();
        this.addHeaderView(headView);
        arrowImageView = (ImageView) headView.findViewById(R.id.arrow);
        arrowImageView.setMinimumWidth(50);
        arrowImageView.setMinimumHeight(70);
        progressBar = (ProgressBar) headView.findViewById(R.id.progress);
        tipsTextview = (TextView) headView.findViewById(R.id.tv_tip);
        lastUpdatedTextView = (TextView) headView.findViewById(R.id.tv_lastupdatetime);
        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);
        animation.setDuration(200);
        animation.setFillAfter(true);
        state = DONE;
        isRefreshable = false;
    }

    /**
     * 通知父佈局,View佔用的寬高
     * @param view
     */
    private void measureView(View view) {
        ViewGroup.LayoutParams p = view.getLayoutParams();
        if (p == null){
            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        int width = ViewGroup.getChildMeasureSpec(0, 0, p.width);
        int height;
        int tempHeight = p.height;
        if (tempHeight > 0) {
            height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY);

        } else {
            height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        view.measure(width, height);
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            firstItemIndex = firstVisibleItem;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (isRefreshable) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    if (firstItemIndex == 0 && !isRecored) {
                        isRecored = true;
                        startY = (int) ev.getY();
                        Log.i(TAG, "在down時候記錄當前位置");
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG, "down時候記錄當前位置");
                    if (state != REFRESHING && state != LOADING) {
                        if (state == DONE) {

                        }
                        if (state == PULL_TO_REFRESH) {
                            state = DONE;
                            changeHeaderViewByState();
                            Log.i(TAG, "有下拉刷新狀態,到done狀態");
                        }
                        if (state == RELEASE_TO_REFRESH) {
                            state = REFRESHING;
                            changeHeaderViewByState();
                            onRefresh();
                            Log.i(TAG, "由鬆開刷新狀態,到done狀態");
                        }
                    }
                    isRecored = false;
                    isBack = false;
                    break;
                case MotionEvent.ACTION_MOVE:
                    int tempY = (int) ev.getY();
                    if (!isRecored && firstItemIndex == 0) {
                        Log.i(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 < headerHeight  && (tempY - startY) > 0) {
                                state = PULL_TO_REFRESH;
                                changeHeaderViewByState();
                                Log.i(TAG, "鬆開刷新轉爲下拉刷新");
                            } else if (tempY - startY <= 0) {
                                state = DONE;
                                changeHeaderViewByState();
                                Log.i(TAG, "鬆開刷新轉爲done");
                            }
                        }
                        if (state == DONE) {
                            if (tempY - startY > 0) {
                                state = PULL_TO_REFRESH;
                                changeHeaderViewByState();
                            }
                        }
                        if (state == PULL_TO_REFRESH) {
                            setSelection(0);
                            if ((tempY - startY) / RATIO >= headerHeight) {
                                state = RELEASE_TO_REFRESH;
                                isBack = true;
                                changeHeaderViewByState();
                                Log.i(TAG, "有下拉刷新轉爲鬆開刷新");
                            }
                        }
                        //更新headView的size
                        if (state == PULL_TO_REFRESH) {
                            headView.setPadding(0, -1 * headerHeight + (tempY - startY) / RATIO, 0, 0);
                        }
                        // 更新headView的paddingTop
                        if (state == RELEASE_TO_REFRESH) {
                            headView.setPadding(0, (tempY - startY) / RATIO
                                    - headerHeight, 0, 0);
                        }
                    }

                    break;

            }
        }
        return super.onTouchEvent(ev);
    }

    // 當狀態改變時候,調用該方法,以更新界面
    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 * headerHeight, 0, 0);
                progressBar.setVisibility(View.GONE);
                arrowImageView.clearAnimation();
                arrowImageView.setImageResource(R.drawable.pull_to_refresh_arrow);
                tipsTextview.setText("下拉刷新");
                lastUpdatedTextView.setVisibility(View.VISIBLE);
                Log.v(TAG, "當前狀態,done");
                break;
        }
    }

    public interface onRefreshListener {
        public void onRefresh();
    }

    public void setonRefreshListener(onRefreshListener onRefreshListener) {
        this.refreshListener = onRefreshListener;
        isRefreshable = true;
    }

    private void onRefresh() {
        if (refreshListener != null) {
            refreshListener.onRefresh();
        }
    }

    public void onRefreshComplete() {
        state = DONE;
        lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());
        changeHeaderViewByState();
    }

}
3)實現接口
@Override
    public void onRefresh() {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                setReflashData();
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                adapter.notifyDataSetChanged();
                listview.onRefreshComplete();
            }
        }.execute();
    }



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章