Android自定義ListView實現下拉刷新列表

jopen 10年前發布 | 59K 次閱讀 ListView Android開發 移動開發

效果圖:

Android自定義ListView實現下拉刷新列表

下面看一下實現過程:

主Activity:

    package com.example.pulltorefreshlistview;  

    import java.util.ArrayList;  
    import java.util.List;  

    import android.support.v7.app.ActionBarActivity;  
    import android.os.AsyncTask;  
    import android.os.Bundle;  
    import android.view.Menu;  
    import android.view.MenuItem;  
    import android.widget.ArrayAdapter;  
    import android.widget.ListView;  
    import android.widget.Toast;  

    public class Main extends ActionBarActivity {  
        private int m = 1;  

        @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  

             final AlexListView listView = (AlexListView) findViewById(R.id.mylist);  
             final List<String> adapterData = new ArrayList<String>();     
                for (int i = 0; i < 20; i++) {     
                   adapterData.add("ListItem" + i);     
                }     
                 final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,     
                android.R.layout.simple_list_item_1, adapterData);     
                listView.setAdapter(adapter);    

            listView.setonRefreshListener(new AlexListView.OnRefreshListener() {  
                @Override  
                public void onRefresh() {  

                    new AsyncTask<Void, Void, Void>() { //刷新過程中需要做的操作在這里  
                     protected Void doInBackground(Void... params) { try {  
                      Thread.sleep(1000); } catch (Exception e) {  
                      e.printStackTrace(); }   
                     adapterData.add("New ListItem"+m);   
                     m++;  
                     return null; }  
                     //刷新完成后要通知listview進行界面調整  
                     @Override   
                     protected void onPostExecute(Void result) {  
                      adapter.notifyDataSetChanged();   
                      listView.onRefreshComplete();  
                     }  
                }.execute();  

                }  
            });  
        }  

        @Override  
        public boolean onCreateOptionsMenu(Menu menu) {  
            getMenuInflater().inflate(R.menu.main, menu);  
            return true;  
        }  

        @Override  
        public boolean onOptionsItemSelected(MenuItem item) {  
            int id = item.getItemId();  
            if (id == R.id.action_settings) {  
                return true;  
            }  
            return super.onOptionsItemSelected(item);  
        }  
    }  

自定義的ListView:
    package com.example.pulltorefreshlistview;  

    import java.util.Date;  

    import android.content.Context;  
    import android.graphics.Color;  
    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 AlexListView extends ListView implements OnScrollListener {  

        private static final String TAG = "PullToRefreshListView";  

        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;  

        // listview的頭部 用于顯示刷新的箭頭等  
        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 AlexListView(Context context) {  
            super(context);  
            init(context);  
        }  

        public AlexListView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
            init(context);  
        }  

        private void init(Context context) {  
            // setCacheColorHint(context.getResources().getColor(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.e("size", "width:" + headContentWidth + " height:"  
                    + headContentHeight);  

            addHeaderView(headView, null, false);  
            setOnScrollListener(this);  
            // {@ TChip ZJ:逆時針旋轉180度 @}  
            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);  
            // {@ TChip ZJ:順時針旋轉180度 @}  
            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;  
        }  

        @Override  
        public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,  
                int arg3) {  
            firstItemIndex = firstVisiableItem;  
        }  

        @Override  
        public void onScrollStateChanged(AbsListView arg0, int arg1) {  
        }  

        @Override  
        public boolean onTouchEvent(MotionEvent event) {  
            if (isRefreshable) {  
                switch (event.getAction()) {  
                case MotionEvent.ACTION_DOWN:  
                    if (firstItemIndex == 0 && !isRecored) {  
                        isRecored = true;  
                        // {@ TChip ZJ:記錄下down事件的y位置 @}  
                        startY = (int) event.getY();  
                        Log.e(TAG, "MotionEvent.ACTION_DOWN");  
                    }  
                    break;  
                case MotionEvent.ACTION_UP:  
                    if (state != REFRESHING && state != LOADING) {  
                        if (state == DONE) {  
                            // 什么都不做  
                        }  
                        if (state == PULL_TO_REFRESH) {  
                            state = DONE;  
                            changeHeaderViewByState();  
                            Log.e(TAG, "state == PULL_TO_REFRESH");  
                        }  
                        if (state == RELEASE_TO_REFRESH) {  
                            state = REFRESHING;  
                            changeHeaderViewByState();  
                            onRefresh();  
                            Log.e(TAG, "state == RELEASE_TO_REFRESH");  
                        }  
                    }  
                    isRecored = false;  
                    isBack = false;  
                    break;  
                case MotionEvent.ACTION_MOVE:  
                    int tempY = (int) event.getY();  
                    if (!isRecored && firstItemIndex == 0) {  
                        Log.e(TAG, "MotionEvent.ACTION_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.e(TAG, "由松開刷新狀態轉變到下拉刷新狀態");  
                            }  
                            // 一下子推到頂了  
                            else if (tempY - startY <= 0) {  
                                state = DONE;  
                                changeHeaderViewByState();  
                                Log.e(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.e(TAG, "由done或者下拉刷新狀態轉變到松開刷新");  
                            }  
                            // 上推到頂了  
                            else if (tempY - startY <= 0) {  
                                state = DONE;  
                                changeHeaderViewByState();  
                                Log.e(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.e(TAG, "State-->RELEASE_TO_REFRESH");  
                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.e(TAG, "State-->PULL_TO_REFRESH");  
                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.e(TAG, "State-->REFRESHING");  
                break;  
            case DONE:  
                headView.setPadding(0, -1 * headContentHeight, 0, 0);  
                progressBar.setVisibility(View.GONE);  
                arrowImageView.clearAnimation();  
                arrowImageView.setImageResource(R.drawable.ic_launcher);  
                tipsTextview.setText("下拉刷新");  
                lastUpdatedTextView.setVisibility(View.VISIBLE);  
                Log.e(TAG, "State-->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) {  
            // {@ TChip ZJ:設置Header中最近更新時間 @}  
            lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());  
            super.setAdapter(adapter);  
        }  
    }  


代碼下載

 

 

 

轉載請注明出處:周木水的CSDN博客 http://blog.csdn.net/zhoumushui

我的GitHub:周木水的GitHub https://github.com/zhoumushui

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!