ListView的下拉刷新,點擊加載更多

大部分應用裏面都有下拉刷新和點擊加載更多這個功能,直接貼代碼,可以直接用

public class DragListView extends ListView implements OnScrollListener,
		OnClickListener {
	// 拖拉ListView枚舉所有狀態
	private enum DListViewState {
		LV_NORMAL, // 普通狀態
		LV_PULL_REFRESH, // 下拉狀態(未超過mHeadViewHeight)
		LV_RELEASE_REFRESH, // 鬆開可刷新狀態(超過mHeadViewHeight)
		LV_LOADING;// 加載狀態
	}

	// 點擊加載更多枚舉所有狀態
	private enum DListViewLoadingMore {
		LV_NORMAL, // 普通狀態
		LV_LOADING, // 加載狀態
		LV_OVER; // 結束狀態
	}

	private View mHeadView;// 頭部headView
	private TextView mRefreshTextview; // 刷新msg(mHeadView)
	private TextView mLastUpdateTextView;// 更新事件(mHeadView)
	private ImageView mArrowImageView;// 下拉圖標(mHeadView)
	private ProgressBar mHeadProgressBar;// 刷新進度體(mHeadView)

	private int mHeadViewWidth; // headView的寬(mHeadView)
	private int mHeadViewHeight;// headView的高(mHeadView)

	private View mFootView;// 尾部mFootView
	private View mLoadMoreView;// mFootView 的view(mFootView)
	private TextView mLoadMoreTextView;// 加載更多.(mFootView)
	private View mLoadingView;// 加載中...View(mFootView)

	private Animation animation, reverseAnimation;// 旋轉動畫,旋轉動畫之後旋轉動畫.

	private int mFirstItemIndex = -1;// 當前視圖能看到的第一個項的索引

	// 用於保證startY的值在一個完整的touch事件中只被記錄一次
	private boolean mIsRecord = false;

	private int mStartY, mMoveY;// 按下是的y座標,move時的y座標

	private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉狀態.(自定義枚舉)

	private DListViewLoadingMore loadingMoreState = DListViewLoadingMore.LV_NORMAL;// 加載更多默認狀態.

	private final static int RATIO = 2;// 手勢下拉距離比.

	private boolean mBack = false;// headView是否返回.

	private OnRefreshLoadingMoreListener onRefreshLoadingMoreListener;// 下拉刷新接口(自定義)

	private boolean isScroller = true;// 是否屏蔽ListView滑動。
//-------------------------------------------------------------------
	private  boolean startPull_bool=false;
	public DragListView(Context context) {
		super(context, null);
		initDragListView(context);
	}

	public DragListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initDragListView(context);
	}

	// 注入下拉刷新接口
	public void setOnRefreshListener(
			OnRefreshLoadingMoreListener onRefreshLoadingMoreListener) {
		this.onRefreshLoadingMoreListener = onRefreshLoadingMoreListener;
	}

	/***
	 * 初始化ListView
	 */
	public void initDragListView(Context context) {

		String time = "1994.12.05";// 更新時間

		initHeadView(context, time);// 初始化該head.

		initLoadMoreView(context);// 初始化footer

		setOnScrollListener(this);// ListView滾動監聽
	}

	/***
	 * 初始話頭部HeadView
	 * 
	 * @param context
	 *            上下文
	 * @param time
	 *            上次更新時間
	 */
	public void initHeadView(Context context, String time) {
		mHeadView = LayoutInflater.from(context).inflate(R.layout.head, null);
		mArrowImageView = (ImageView) mHeadView
				.findViewById(R.id.head_arrowImageView);
		mArrowImageView.setMinimumWidth(60);

		mHeadProgressBar = (ProgressBar) mHeadView
				.findViewById(R.id.head_progressBar);

		mRefreshTextview = (TextView) mHeadView
				.findViewById(R.id.head_tipsTextView);

		/*
		 * mLastUpdateTextView = (TextView) mHeadView
		 * .findViewById(R.id.head_lastUpdatedTextView); // 顯示更新事件
		 * mLastUpdateTextView.setText("最近更新:" + time);
		 */

		measureView(mHeadView);
		// 獲取寬和高
		mHeadViewWidth = mHeadView.getMeasuredWidth();
		mHeadViewHeight = mHeadView.getMeasuredHeight();

		addHeaderView(mHeadView, null, false);// 將初始好的ListView add進拖拽ListView
		// 在這裏我們要將此headView設置到頂部不顯示位置.
		mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0);

		initAnimation();// 初始化動畫
	}

	/***
	 * 初始化底部加載更多控件
	 */
	private void initLoadMoreView(Context context) {
		mFootView = LayoutInflater.from(context).inflate(R.layout.footer, null);

		mLoadMoreView = mFootView.findViewById(R.id.load_more_view);

		mLoadMoreTextView = (TextView) mFootView
				.findViewById(R.id.load_more_tv);

		mLoadingView = (LinearLayout) mFootView
				.findViewById(R.id.loading_layout);

		mLoadMoreView.setOnClickListener(this);

		addFooterView(mFootView);
	}

	/***
	 * 初始化動畫
	 */
	private void initAnimation() {
		// 旋轉動畫
		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(250);
		reverseAnimation.setFillAfter(true);
	}

	/***
	 * 作用:測量 headView的寬和高.
	 * 
	 * @param child
	 */
	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);
	}

	/***
	 * touch 事件監聽
	 */
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		// 按下
		case MotionEvent.ACTION_DOWN:
			doActionDown(ev);
			break;
		// 移動
		case MotionEvent.ACTION_MOVE:
			doActionMove(ev);
			break;
		// 擡起
		case MotionEvent.ACTION_UP:
			doActionUp(ev);
			break;
		default:
			break;
		}
		/***
		 * 如果是ListView本身的拉動,那麼返回true,這樣ListView不可以拖動.
		 * 如果不是ListView的拉動,那麼調用父類方法,這樣就可以上拉執行.
		 */
		if (isScroller) {
			return super.onTouchEvent(ev);
		} else {
			return true;
		}

	}

	/***
	 * 摁下操作
	 * 
	 * 作用:獲取摁下是的y座標
	 * 
	 * @param event
	 */
	void doActionDown(MotionEvent event) {
//		if (mIsRecord == false && mFirstItemIndex == 0) {// 沒有記錄並且headview沒有出現
//			mStartY = (int) event.getY();
//			mIsRecord = true;
//		}
	}

	/***
	 * 拖拽移動操作
	 * 
	 * @param event
	 */
	void doActionMove(MotionEvent event) {
		mMoveY = (int) event.getY();// 獲取實時滑動y座標
		// 檢測是否是一次touch事件.
		Log.d("-----mFirstItemIndex-----", getFirstVisiblePosition()+"");
		//if (mIsRecord == false && mFirstItemIndex == 1) {
		if(mIsRecord==false&&getFirstVisiblePosition()==0){
			Log.d("-----mFirstItemIndex-----","************************");
			startPull_bool=true;  
			mStartY = (int) event.getY();
			mIsRecord = true;
		}
		/***
		 * 如果touch關閉或者正處於Loading狀態的話 return.
		 */
		if (mIsRecord == false || mlistViewState == DListViewState.LV_LOADING) {
			return;
		}
		// 向下啦headview移動距離爲y移動的一半.(比較友好)

		/*
		 * 這裏是有問題的,看代碼表面意思是:只要往下滑動,就設置狀態爲下拉,然後定位到第一行 並設置headView的padding
		 * 我感覺正確的邏輯應該是當第一行出現時,下拉纔會這樣,你在中間滑動時,就應該是正常的
		 * 滑動,不做任何處理,所以應該加個判斷滑到頂部時,才計算offset;
		 */
		// 檢測是否滑動頂部(headView是0,第一行是1)
		int offset = 0;
		if (startPull_bool) {
			// offset = (mMoveY - mStartY) / RATIO;
			offset = (mMoveY - mStartY);// 你想判斷headView下拉了一半不應該在這,這裏表示開始下拉了
		}
		Log.d("------offset------------", offset + "-----"+mMoveY+"----"+mStartY+"-----------"+startPull_bool);
		switch (mlistViewState) {
		// 普通狀態
		case LV_NORMAL: {
			// 如果<0,則意味着上滑動.
			if (offset > 0) {
				// 設置headView的padding屬性.
				mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0);// 和下面重複了(300行)
				switchViewState(DListViewState.LV_PULL_REFRESH);// 下拉狀態
			}

		}
			break;
		// 下拉狀態
		case LV_PULL_REFRESH: {
			// setSelection(0);// 選中第一項,可選.
			// 設置headView的padding屬性.
			mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0);
			// switchViewState(DListViewState.LV_RELEASE_REFRESH);
			if (offset < 0) {// 這裏應該沒必要了,offset是>=0的;
				/***
				 * 要明白爲什麼isScroller = false;
				 */
				isScroller = false;
				switchViewState(DListViewState.LV_NORMAL);// 普通狀態
				Log.e("jj", "isScroller=" + isScroller);
			} else if (offset > mHeadViewHeight / 2) {// 如果下拉的offset超過headView的高度則要執行刷新.
				switchViewState(DListViewState.LV_RELEASE_REFRESH);// 更新爲可刷新的下拉狀態.
			}
		}
			break;
		// 可刷新狀態
		case LV_RELEASE_REFRESH: {
			setSelection(0);
			// 設置headView的padding屬性.
			mHeadView.setPadding(0, offset - mHeadViewHeight, 0, 0);
			// 下拉offset>0,但是沒有超過headView的高度.那麼要goback 原裝.
			if (offset >= 0 && offset <= mHeadViewHeight) {
				mBack = true;
				switchViewState(DListViewState.LV_PULL_REFRESH);
			} else if (offset < 0) {
				switchViewState(DListViewState.LV_NORMAL);
			} else {

			}
		}
			break;
		default:
			return;
		}
		;
	}

	/***
	 * 手勢擡起操作
	 * 
	 * @param event
	 */
	public void doActionUp(MotionEvent event) {
		startPull_bool=false;
		mIsRecord = false;// 此時的touch事件完畢,要關閉。
		isScroller = true;// ListView可以Scrooler滑動.
		mBack = false;
		// 如果下拉狀態處於loading狀態.
		if (mlistViewState == DListViewState.LV_LOADING) {
			return;
		}
		// 處理相應狀態.
		switch (mlistViewState) {
		// 普通狀態
		case LV_NORMAL:

			break;
		// 下拉狀態
		case LV_PULL_REFRESH:
			mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0);
			switchViewState(mlistViewState.LV_NORMAL);
			break;
		// 刷新狀態
		case LV_RELEASE_REFRESH:
			mHeadView.setPadding(0, 0, 0, 0);
			switchViewState(mlistViewState.LV_LOADING);
			onRefresh();// 下拉刷新
			break;
		}

	}

	// 切換headview視圖
	private void switchViewState(DListViewState state) {

		switch (state) {
		// 普通狀態
		case LV_NORMAL: {
			mArrowImageView.clearAnimation();// 清除動畫
			mArrowImageView.setImageResource(R.drawable.arrow);
		}
			break;
		// 下拉狀態
		case LV_PULL_REFRESH: {
			mHeadProgressBar.setVisibility(View.GONE);// 隱藏進度條
			mArrowImageView.setVisibility(View.VISIBLE);// 下拉圖標
			mRefreshTextview.setText("下拉刷新");
			mArrowImageView.clearAnimation();// 清除動畫

			// 是有可刷新狀態(LV_RELEASE_REFRESH)轉爲這個狀態才執行,其實就是你下拉後在上拉會執行.
			if (mBack) {
				mBack = false;
				mArrowImageView.clearAnimation();// 清除動畫
				mArrowImageView.startAnimation(reverseAnimation);// 啓動反轉動畫
			}
		}
			break;
		// 鬆開刷新狀態
		case LV_RELEASE_REFRESH: {
			mHeadProgressBar.setVisibility(View.GONE);// 隱藏進度條
			mArrowImageView.setVisibility(View.VISIBLE);// 顯示下拉圖標
			mRefreshTextview.setText("鬆開獲取新動態");
			mArrowImageView.clearAnimation();// 清除動畫
			mArrowImageView.startAnimation(animation);// 啓動動畫
		}
			break;
		// 加載狀態
		case LV_LOADING: {
			Log.e("!!!!!!!!!!!", "convert to IListViewState.LVS_LOADING");
			mHeadProgressBar.setVisibility(View.VISIBLE);
			mArrowImageView.clearAnimation();
			mArrowImageView.setVisibility(View.GONE);
			mRefreshTextview.setText("載入中...");
		}
			break;
		default:
			return;
		}
		// 切記不要忘記時時更新狀態。
		mlistViewState = state;

	}

	/***
	 * 下拉刷新
	 */
	private void onRefresh() {
		if (onRefreshLoadingMoreListener != null) {
			onRefreshLoadingMoreListener.onRefresh();
		}
	}

	/***
	 * 下拉刷新完畢
	 */
	public void onRefreshComplete() {
		mHeadView.setPadding(0, -1 * mHeadViewHeight, 0, 0);// 迴歸.
		switchViewState(mlistViewState.LV_NORMAL);//
	}

	/***
	 * 點擊加載更多
	 * 
	 * @param flag
	 *            數據是否已全部加載完畢
	 */
	public void onLoadMoreComplete(boolean flag) {
		if (flag) {
			updateLoadMoreViewState(DListViewLoadingMore.LV_OVER);
		} else {
			updateLoadMoreViewState(DListViewLoadingMore.LV_NORMAL);
		}

	}

	// 更新Footview視圖
	private void updateLoadMoreViewState(DListViewLoadingMore state) {
		switch (state) {
		// 普通狀態
		case LV_NORMAL:
			mLoadingView.setVisibility(View.GONE);
			mLoadMoreTextView.setVisibility(View.VISIBLE);
			mLoadMoreTextView.setText("查看更多");
			break;
		// 加載中狀態
		case LV_LOADING:
			mLoadingView.setVisibility(View.VISIBLE);
			mLoadMoreTextView.setVisibility(View.GONE);
			break;
		// 加載完畢狀態
		case LV_OVER:
			mLoadingView.setVisibility(View.GONE);
			mLoadMoreTextView.setVisibility(View.VISIBLE);
			mLoadMoreTextView.setText("加載完畢");
			break;
		default:
			break;
		}
		loadingMoreState = state;
	}

	/***
	 * ListView 滑動監聽
	 */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {

	}

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

	/***
	 * 底部點擊事件
	 */
	@Override
	public void onClick(View v) {
		// 防止重複點擊
		if (onRefreshLoadingMoreListener != null
				&& loadingMoreState == DListViewLoadingMore.LV_NORMAL) {
			updateLoadMoreViewState(DListViewLoadingMore.LV_LOADING);
			onRefreshLoadingMoreListener.onLoadMore();// 對外提供方法加載更多.
		}

	}

	/***
	 * 自定義接口
	 */
	public interface OnRefreshLoadingMoreListener {
		/***
		 * // 下拉刷新執行
		 */
		void onRefresh();

		/***
		 * 點擊加載更多
		 */
		void onLoadMore();
	}

}

下拉刷新的界面  header.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="70dp" >

        <!-- 箭頭圖像、進度條 -->

        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="30dp" >

            <!-- 箭頭 -->

            <ImageView
                android:id="@+id/header_arrowImageView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:src="@drawable/arrow" />

            <!-- 進度條 -->

            <ProgressBar
                android:id="@+id/header_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_centerInParent="true"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <!-- 提示 -->

            <TextView
                android:id="@+id/header_tipsTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="下拉可以刷新"
                android:textColor="@color/grey21"
                android:textSize="20sp" />

            <!-- 最近更新 -->

            
        </LinearLayout>

        <View
            android:layout_width="fill_parent"
            android:layout_height="1px"
            android:layout_alignParentBottom="true"
            android:background="@color/gray" >
        </View>
    </RelativeLayout>

</LinearLayout>

點擊加載更多 footer.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/load_more_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true"
    android:gravity="center"
    android:minHeight="80dp" >

    <TextView
        android:id="@+id/load_more_tv"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="獲取更多"
        android:textColor="@color/grey21"
        android:textSize="20sp" />

    <LinearLayout
        android:id="@+id/loading_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:visibility="gone" >

        <ProgressBar
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp" />

        <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="加載中..."
            android:textColor="@color/grey21"
            android:textSize="18sp" />
    </LinearLayout>

</RelativeLayout>


發佈了34 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章