1. 程式人生 > >Android UI 自定義ListView 實現下拉重新整理 載入更多

Android UI 自定義ListView 實現下拉重新整理 載入更多

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                Android UI--自定義ListView(實現下拉重新整理+載入更多)
關於實現ListView下拉重新整理和載入更多的實現,我想網上一搜就一堆。不過我就沒發現比較實用的,要不就是實現起來太複雜,要不就是不健全的。因為小巫近期要開發新浪微部落格戶端,需要實現ListView的下拉重新整理,所以就想把這個UI整合到專案當中去,這裡只是一個demo,可以根據專案的需要進行修改。

就不要太在乎介面了哈







知道你們想要原始碼了,去下吧:http://download.csdn.net/detail/wwj_748/6373183


自定義ListView:
package
com.markupartist.android.widget;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import com.markupartist.android.example.pulltorefresh.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.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.RelativeLayout;import android.widget.TextView;/** * 2013/8/13 * 自定義ListView,實現OnScrollListener介面 * 此ListView是為實現"下拉重新整理"和"上拉載入更多"而定製的,具體效果可參考新浪微博、騰訊微博 * @author wwj * */public class PullToRefreshListView extends ListView implements OnScrollListener {    private static final int TAP_TO_REFRESH = 1;   //(未重新整理)    private static final int PULL_TO_REFRESH = 2;   // 下拉重新整理    private static final int RELEASE_TO_REFRESH = 3;  // 釋放重新整理    private static final int REFRESHING = 4;    // 正在重新整理    private static final int TAP_TO_LOADMORE = 5;   // 未載入更多    private static final int LOADING = 6;     // 正在載入        private static final String TAG = "PullToRefreshListView";    private OnRefreshListener mOnRefreshListener;   // 重新整理監聽器    /**     * Listener that will receive notifications every time the list scrolls.     */    private OnScrollListener mOnScrollListener;    // 列表滾動監聽器    private LayoutInflater mInflater;         // 用於載入佈局檔案    private RelativeLayout mRefreshHeaderView;    // 重新整理檢視(也就是頭部那部分)     private TextView mRefreshViewText;      // 重新整理提示文字      private ImageView mRefreshViewImage;     // 重新整理向上向下的那個圖片    private ProgressBar mRefreshViewProgress;    // 這裡是圓形進度條    private TextView mRefreshViewLastUpdated;    // 最近更新的文字        private RelativeLayout mLoadMoreFooterView;    // 載入更多    private TextView mLoadMoreText;       // 提示文字    private ProgressBar mLoadMoreProgress;     // 載入更多進度條        private int mCurrentScrollState;      // 當前滾動位置       private int mRefreshState;        // 重新整理狀態     private int mLoadState;         // 載入狀態    private RotateAnimation mFlipAnimation;     // 下拉動畫    private RotateAnimation mReverseFlipAnimation;   // 恢復動畫    private int mRefreshViewHeight;       // 重新整理檢視高度         private int mRefreshOriginalTopPadding;     // 原始上部間隙    private int mLastMotionY;        // 記錄點選位置     public PullToRefreshListView(Context context) {        super(context);        init(context);    }    public PullToRefreshListView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init(context);    }    private void init(Context context) {        // Load all of the animations we need in code rather than through XML     /** 定義旋轉動畫**/     // 引數:1.旋轉開始的角度 2.旋轉結束的角度 3. X軸伸縮模式 4.X座標的伸縮值 5.Y軸的伸縮模式 6.Y座標的伸縮值        mFlipAnimation = new RotateAnimation(0, -180,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        mFlipAnimation.setInterpolator(new LinearInterpolator());        mFlipAnimation.setDuration(250);    // 設定持續時間        mFlipAnimation.setFillAfter(true);    // 動畫執行完是否停留在執行完的狀態        mReverseFlipAnimation = new RotateAnimation(-180, 0,                RotateAnimation.RELATIVE_TO_SELF, 0.5f,                RotateAnimation.RELATIVE_TO_SELF, 0.5f);        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());        mReverseFlipAnimation.setDuration(250);        mReverseFlipAnimation.setFillAfter(true);        // 獲取LayoutInflater例項物件        mInflater = (LayoutInflater) context.getSystemService(                Context.LAYOUT_INFLATER_SERVICE);        // 載入下拉重新整理的頭部檢視  mRefreshHeaderView = (RelativeLayout) mInflater.inflate(    R.layout.pull_to_refresh_header, this, false);        mRefreshViewText =            (TextView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_text);        mRefreshViewImage =            (ImageView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_image);        mRefreshViewProgress =            (ProgressBar) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_progress);        mRefreshViewLastUpdated =            (TextView) mRefreshHeaderView.findViewById(R.id.pull_to_refresh_updated_at);  mLoadMoreFooterView = (RelativeLayout) mInflater.inflate(    R.layout.loadmore_footer, this, false);  mLoadMoreText = (TextView) mLoadMoreFooterView.findViewById(R.id.loadmore_text);  mLoadMoreProgress = (ProgressBar) mLoadMoreFooterView.findViewById(R.id.loadmore_progress);          mRefreshViewImage.setMinimumHeight(50);  // 設定圖片最小高度        mRefreshHeaderView.setOnClickListener(new OnClickRefreshListener());        mRefreshOriginalTopPadding = mRefreshHeaderView.getPaddingTop();        mLoadMoreFooterView.setOnClickListener(new OnClickLoadMoreListener());        mRefreshState = TAP_TO_REFRESH;    // 初始重新整理狀態        mLoadState = TAP_TO_LOADMORE;        addHeaderView(mRefreshHeaderView);   // 增加頭部檢視        addFooterView(mLoadMoreFooterView);   // 增加尾部檢視        super.setOnScrollListener(this);          measureView(mRefreshHeaderView);    // 測量檢視        mRefreshViewHeight = mRefreshHeaderView.getMeasuredHeight(); // 得到檢視的高度    }    @Override    protected void onAttachedToWindow() {        setSelection(1);  // 設定當前選中的項    }    @Override    public void setAdapter(ListAdapter adapter) {        super.setAdapter(adapter);        setSelection(1);    }    /**     * Set the listener that will receive notifications every time the list     * scrolls.     *      * @param l The scroll listener.      */    @Override    public void setOnScrollListener(AbsListView.OnScrollListener l) {        mOnScrollListener = l;    }    /**     * Register a callback to be invoked when this list should be refreshed.     * 註冊監聽器     * @param onRefreshListener The callback to run.     */    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {        mOnRefreshListener = onRefreshListener;    }    /**     * Set a text to represent when the list was last updated.     * 設定一個文字來表示最近更新的列表,顯示的是最近更新列表的時間     * @param lastUpdated Last updated at.     */    public void setLastUpdated(CharSequence lastUpdated) {        if (lastUpdated != null) {            mRefreshViewLastUpdated.setVisibility(View.VISIBLE);            mRefreshViewLastUpdated.setText("更新於: " + lastUpdated);        } else {            mRefreshViewLastUpdated.setVisibility(View.GONE);        }    }    @Override    public boolean onTouchEvent(MotionEvent event) {        final int y = (int) event.getY();  // 獲取點選位置的Y座標        switch (event.getAction()) {            case MotionEvent.ACTION_UP:  // 手指擡起                if (!isVerticalScrollBarEnabled()) {                    setVerticalScrollBarEnabled(true);                }                if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {                    if ((mRefreshHeaderView.getBottom() > mRefreshViewHeight                            || mRefreshHeaderView.getTop() >= 0)                            && mRefreshState == RELEASE_TO_REFRESH) {                        // Initiate the refresh                        mRefreshState = REFRESHING;  // 重新整理狀態                        prepareForRefresh();                        onRefresh();                    } else if (mRefreshHeaderView.getBottom() < mRefreshViewHeight                            || mRefreshHeaderView.getTop() < 0) {                        // Abort refresh and scroll down below the refresh view                        resetHeader();                        setSelection(1);                    }                }                break;            case MotionEvent.ACTION_DOWN:                mLastMotionY = y;                break;            case MotionEvent.ACTION_MOVE:                applyHeaderPadding(event);                break;        }        return super.onTouchEvent(event);    }    private void applyHeaderPadding(MotionEvent ev) {        final int historySize = ev.getHistorySize();        // Workaround for getPointerCount() which is unavailable in 1.5        // (it's always 1 in 1.5)        int pointerCount = 1;        try {            Method method = MotionEvent.class.getMethod("getPointerCount");            pointerCount = (Integer)method.invoke(ev);        } catch (NoSuchMethodException e) {            pointerCount = 1;        } catch (IllegalArgumentException e) {            throw e;        } catch (IllegalAccessException e) {            System.err.println("unexpected " + e);        } catch (InvocationTargetException e) {            System.err.println("unexpected " + e);        }        for (int h = 0; h < historySize; h++) {            for (int p = 0; p < pointerCount; p++) {                if (mRefreshState == RELEASE_TO_REFRESH) {                    if (isVerticalFadingEdgeEnabled()) {                        setVerticalScrollBarEnabled(false);                    }                    int historicalY = 0;                    try {                        // For Android > 2.0                        Method method = MotionEvent.class.getMethod(                                "getHistoricalY", Integer.TYPE, Integer.TYPE);                        historicalY = ((Float) method.invoke(ev, p, h)).intValue();                    } catch (NoSuchMethodException e) {                        // For Android < 2.0                        historicalY = (int) (ev.getHistoricalY(h));                    } catch (IllegalArgumentException e) {                        throw e;                    } catch (IllegalAccessException e) {                        System.err.println("unexpected " + e);                    } catch (InvocationTargetException e) {                        System.err.println("unexpected " + e);                    }                    // Calculate the padding to apply, we divide by 1.7 to                    // simulate a more resistant effect during pull.                    int topPadding = (int) (((historicalY - mLastMotionY)                            - mRefreshViewHeight) / 1.7);                    // 設定上、下、左、右四個位置的間隙間隙                    mRefreshHeaderView.setPadding(                            mRefreshHeaderView.getPaddingLeft(),                            topPadding,                            mRefreshHeaderView.getPaddingRight(),                            mRefreshHeaderView.getPaddingBottom());                }            }        }    }    /**     * Sets the header padding back to original size.     * 設定頭部填充會原始大小     */    private void resetHeaderPadding() {        mRefreshHeaderView.setPadding(                mRefreshHeaderView.getPaddingLeft(),                mRefreshOriginalTopPadding,                mRefreshHeaderView.getPaddingRight(),                mRefreshHeaderView.getPaddingBottom());    }    /**     * Resets the header to the original state.     * 重新設定頭部為原始狀態     */    private void resetHeader() {        if (mRefreshState != TAP_TO_REFRESH) {            mRefreshState = TAP_TO_REFRESH;            resetHeaderPadding();            // Set refresh view text to the pull label            mRefreshViewText.setText(R.string.pull_to_refresh_tap_label);            // Replace refresh drawable with arrow drawable            mRefreshViewImage.setImageResource(R.drawable.ic_pulltorefresh_arrow);            // Clear the full rotation animation            mRefreshViewImage.clearAnimation();            // Hide progress bar and arrow.            mRefreshViewImage.setVisibility(View.GONE);            mRefreshViewProgress.setVisibility(View.GONE);        }    }        /**     * 重設ListView尾部檢視為初始狀態     */    private void resetFooter() {     if(mLoadState != TAP_TO_LOADMORE) {      mLoadState = TAP_TO_LOADMORE;            // 進度條設定為不可見      mLoadMoreProgress.setVisibility(View.GONE);      // 按鈕的文字替換為“載入更多”      mLoadMoreText.setText(R.string.loadmore_label);     }         }        /**     * 測量檢視的大小     * @param child     */    private void measureView(View child) {        ViewGroup.LayoutParams p = child.getLayoutParams();        if (p == null) {            p = new ViewGroup.LayoutParams(                    ViewGroup.LayoutParams.MATCH_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);    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        // When the refresh view is completely visible, change the text to say        // "Release to refresh..." and flip the arrow drawable.        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL                && mRefreshState != REFRESHING) {            if (firstVisibleItem == 0) {  // 如果第一個可見條目為0                mRefreshViewImage.setVisibility(View.VISIBLE); // 讓指示箭頭變得可見                /**如果頭部檢視相對與父容器的位置大於其自身高度+20或者頭部檢視的頂部位置>0,並且要在重新整理狀態不等於"釋放以重新整理"**/                if ((mRefreshHeaderView.getBottom() > mRefreshViewHeight + 20                        || mRefreshHeaderView.getTop() >= 0)                        && mRefreshState != RELEASE_TO_REFRESH) {                    mRefreshViewText.setText(R.string.pull_to_refresh_release_label);// 設定重新整理文字為"Release to refresh..."                    mRefreshViewImage.clearAnimation();     // 清除動畫                     mRefreshViewImage.startAnimation(mFlipAnimation); // 啟動動畫                    mRefreshState = RELEASE_TO_REFRESH;     // 更改重新整理狀態為“釋放以重新整理"                } else if (mRefreshHeaderView.getBottom() < mRefreshViewHeight + 20                        && mRefreshState != PULL_TO_REFRESH) {                    mRefreshViewText.setText(R.string.pull_to_refresh_pull_label);// 設定重新整理文字為"Pull to refresh..."                    if (mRefreshState != TAP_TO_REFRESH) {                        mRefreshViewImage.clearAnimation();                        mRefreshViewImage.startAnimation(mReverseFlipAnimation);                    }                    mRefreshState = PULL_TO_REFRESH;                }            } else {                mRefreshViewImage.setVisibility(View.GONE);   // 讓重新整理箭頭不可見                resetHeader(); // 重新設定頭部為原始狀態            }        } else if (mCurrentScrollState == SCROLL_STATE_FLING                && firstVisibleItem == 0                && mRefreshState != REFRESHING) {            setSelection(1);        }        if (mOnScrollListener != null) {            mOnScrollListener.onScroll(view, firstVisibleItem,                    visibleItemCount, totalItemCount);        }    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        mCurrentScrollState = scrollState;        if (mOnScrollListener != null) {            mOnScrollListener.onScrollStateChanged(view, scrollState);        }    }        /**為重新整理做準備**/    public void prepareForRefresh() {        resetHeaderPadding();          mRefreshViewImage.setVisibility(View.GONE);   // 去掉重新整理的箭頭        // We need this hack, otherwise it will keep the previous drawable.        mRefreshViewImage.setImageDrawable(null);        mRefreshViewProgress.setVisibility(View.VISIBLE); // 圓形進度條變為可見        // Set refresh view text to the refreshing label        mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label);        mRefreshState = REFRESHING;    }        /**為載入更多做準備**/    public void prepareForLoadMore() {     mLoadMoreProgress.setVisibility(View.VISIBLE);       mLoadMoreText.setText(R.string.loading_label);     mLoadState = LOADING;    }    public void onRefresh() {        Log.d(TAG, "onRefresh");        if (mOnRefreshListener != null) {            mOnRefreshListener.onRefresh();        }    }        public void OnLoadMore() {     Log.d(TAG, "onLoadMore");     if(mOnRefreshListener != null) {      mOnRefreshListener.onLoadMore();     }    }    /**     * Resets the list to a normal state after a refresh.     * @param lastUpdated Last updated at.     */    public void onRefreshComplete(CharSequence lastUpdated) {        setLastUpdated(lastUpdated); // 顯示更新時間        onRefreshComplete();    }    /**     * Resets the list to a normal state after a refresh.     */    public void onRefreshComplete() {                Log.d(TAG, "onRefreshComplete");        resetHeader();        // If refresh view is visible when loading completes, scroll down to        // the next item.        if (mRefreshHeaderView.getBottom() > 0) {            invalidateViews();            setSelection(1);        }    }        public void onLoadMoreComplete() {     Log.d(TAG, "onLoadMoreComplete");     resetFooter();    }    /**     * Invoked when the refresh view is clicked on. This is mainly used when     * there's only a few items in the list and it's not possible to drag the     * list.     * 點選重新整理     */    private class OnClickRefreshListener implements OnClickListener {        @Override        public void onClick(View v) {            if (mRefreshState != REFRESHING) {                prepareForRefresh();                onRefresh();            }        }    }        /**     *      * @author wwj     * 載入更多     */    private class OnClickLoadMoreListener implements OnClickListener {  @Override  public void onClick(View v) {   if(mLoadState != LOADING) {    prepareForLoadMore();    OnLoadMore();   }  }    }    /**     * Interface definition for a callback to be invoked when list should be     * refreshed.     * 介面定義一個回撥方法當列表應當被重新整理     */    public interface OnRefreshListener {        /**         * Called when the list should be refreshed.         * 當列表應當被重新整理是呼叫這個方法         * <p>         * A call to {@link PullToRefreshListView #onRefreshComplete()} is         * expected to indicate that the refresh has completed.         */        public void onRefresh();                public void onLoadMore();    }}

使用方法:
package com.markupartist.android.example.pulltorefresh;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Date;import java.util.LinkedList;import android.app.Activity;import android.content.Context;import android.os.AsyncTask;import android.os.Bundle;import android.widget.ArrayAdapter;import com.markupartist.android.widget.PullToRefreshListView;import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;public class PullToRefreshActivity extends Activity private LinkedList<String> mListItems; public static PullToRefreshListView weiboListView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.pull_to_refresh);  weiboListView = (PullToRefreshListView) findViewById(R.id.weibolist);  // Set a listener to be invoked when the list should be refreshed.  weiboListView.setOnRefreshListener(new OnRefreshListener() {   @Override   public void onRefresh() {    // Do work to refresh the list here.    new GetDataTask(PullToRefreshActivity.this, 0).execute();   }   @Override   public void onLoadMore() {    new GetDataTask(PullToRefreshActivity.this, 1).execute();   }  });  mListItems = new LinkedList<String>();  mListItems.addAll(Arrays.asList(mStrings));  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,    android.R.layout.simple_list_item_1, mListItems);  weiboListView.setAdapter(adapter); } private class GetDataTask extends AsyncTask<Void, Void, String[]> {  private Context context;  private int index;  public GetDataTask(Context context, int index) {   this.context = context;   this.index = index;  }  @Override  protected String[] doInBackground(Void... params) {   // Simulates a background job.   try {    Thread.sleep(2000);   } catch (InterruptedException e) {    ;   }   return mStrings;  }  @Override  protected void onPostExecute(String[] result) {   if (index == 0) {    // 將字串“Added after refresh”新增到頂部    mListItems.addFirst("Added after refresh...");    SimpleDateFormat format = new SimpleDateFormat(      "yyyy年MM月dd日  HH:mm");    String date = format.format(new Date());    // Call onRefreshComplete when the list has been refreshed.    weiboListView.onRefreshComplete(date);   } else if (index == 1) {    mListIte