Android自定義控件下拉刷新

關於ListView,在開發中經常會用到,但是一個原生的ListView並不能滿足我們的開發需求,我們很多時候都會利用下拉刷新和上拉加載更多進行獲取數據,這也算ListView的優化,這時候我們可以用第三方框架pullToRefreshListView,這是一個強大的第三方庫,然而,今天我們要說的是自定義下拉刷新。
首先我們應該繼承ListView,進行擴充,複寫構造方法,進行初始化;

<span style="font-size:18px;">private void init() {
	upAnimation = new RotateAnimation(0, -180,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f);
	upAnimation.setDuration(200);
	upAnimation.setFillAfter(true);
	downAnimation = new RotateAnimation(-180, -360,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f,
			RotateAnimation.RELATIVE_TO_SELF, 0.5f);
	downAnimation.setDuration(200);
	downAnimation.setFillAfter(true);
	addHeadView();
}</span>

在初始化方法中定義了兩個動畫,這兩個動畫是在下拉刷新時,箭頭的展示動畫,同時在初始化中進行頭部的添加;

<span style="font-size:18px;">private void addHeadView() {
	headView = View.inflate(getContext(), R.layout.layout_header, null);
	iv_arrow = (ImageView) headView.findViewById(R.id.iv_arrow);


	pb_rotate = (ProgressBar) headView.findViewById(R.id.pb_rotate);
	tv_state = (TextView) headView.findViewById(R.id.tv_state);
	tv_time = (TextView) headView.findViewById(R.id.tv_time);
	headView.measure(0, 0);
	headHeight = headView.getMeasuredHeight();
	headView.setPadding(0, -headHeight, 0, 0);
	addHeaderView(headView);
}</span>

整個ListView下拉刷新佈局的顯示和隱藏都是利用頭部局的Padding來進行設置的,獲取到頭部的高,進行隱藏設置。
然後就是核心的功能OnTouchEvent()方法,

<span style="font-size:18px;">public boolean onTouchEvent(MotionEvent ev) {
	if (state == XIALA) {
		tv_state.setText(XIALA_STATE);
	}else if(state == SONGKAI){
		tv_state.setText(SONGKAI_STATE);
	}else if(state == ZHENGZAI){
		tv_state.setText(ZHENGZAI_STATE);
	}
	switch (ev.getAction()) {
	case MotionEvent.ACTION_DOWN:
		height = (int) ev.getY();
		break;
	case MotionEvent.ACTION_MOVE:
		if (state == ZHENGZAI) {
			break;
		}
		int y = (int) (ev.getY() - height);
		int offerY = y - headHeight;
		// 不要禁掉ListView的滑動事件
		if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
			headView.setPadding(0, offerY, 0, 0);
			if (offerY > 0 && state == XIALA) {
				state = SONGKAI;
				setHeadView();
			} else if (offerY < 0 && state == SONGKAI) {
				state = XIALA;
				setHeadView();
			}
			return true;
		}
		break;
	case MotionEvent.ACTION_UP:
		if (state == XIALA) {
			headView.setPadding(0, -headHeight, 0, 0);
		} else if (state == SONGKAI) {
			state = ZHENGZAI;
			headView.setPadding(0, 0, 0, 0);
			setHeadView();


			if (onRefreshListener != null) {
				onRefreshListener.refresh();
			}


		}
		break;


	}
	return super.onTouchEvent(ev);
}</span>

在OnTouchEvent方法中通過ACTION_DOWN,ACTION_MOVE,ACTION_UP,三種狀態進行設置,
來看ACTION_MOVE狀態的處理:
顯示判斷是否在刷新狀態,在的話直接終止,然後通過getY,得到滑動的時候的Y的座標,和按下時得到的Y的座標進行相減,得到移動的座標,

<span style="font-size:18px;">if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
			headView.setPadding(0, offerY, 0, 0);
			if (offerY > 0 && state == XIALA) {
				state = SONGKAI;
				setHeadView();
			} else if (offerY < 0 && state == SONGKAI) {
				state = XIALA;
				setHeadView();
			}
			return true;
		}</span>

當移動的長度大於負的頭部的高度,同時ListView的第一個的Item是0的時候,才進行事件的攔截,這是爲了防止禁掉ListView的滑動事件,當offerY>0的時候,這時候頭部佈局的狀態是鬆開刷新狀態,反之,下拉刷新狀態。在setHeadView()方法中進行狀態的設置,
來看ACTION_UP時候做了哪些事:

<span style="font-size:18px;">case MotionEvent.ACTION_UP:
		if (state == XIALA) {
			headView.setPadding(0, -headHeight, 0, 0);
		} else if (state == SONGKAI) {
			state = ZHENGZAI;
			headView.setPadding(0, 0, 0, 0);
			setHeadView();


			if (onRefreshListener != null) {
				onRefreshListener.refresh();
			}


		}
		break;

	}</span>

當狀態是下拉刷新狀態時,這時候鬆開應該隱藏頭部佈局,當時鬆開刷新狀態時,顯示頭部佈局,並通過回調進行數據的獲取,
對於setHeadView()方法:

<span style="font-size:18px;">private void setHeadView() {
	switch (state) {
	case SONGKAI:
		tv_state.setText(SONGKAI_STATE);
		iv_arrow.startAnimation(upAnimation);
		break;
	case XIALA:
		tv_state.setText(XIALA_STATE);
			iv_arrow.startAnimation(downAnimation);
		break;


	case ZHENGZAI:
		tv_state.setText(ZHENGZAI_STATE);
		iv_arrow.clearAnimation();
		tv_time.setText("最後刷新:" + getDate());
		iv_arrow.setVisibility(View.INVISIBLE);
		pb_rotate.setVisibility(View.VISIBLE);
		break;


	default:
		break;
	}
}</span>

在這個方法中設置狀態的顯示和時間的顯示,同時圖標執行動畫,其中getDate()方法是獲取當前的時間進行最後刷新的顯示。
當數據添加完後執行// 關閉刷新refreshCpmplete()方法進行頭部佈局的隱藏,狀態的設置。

<span style="font-size:18px;">public void refreshCpmplete() {
	state = SONGKAI;
	headView.setPadding(0, -headHeight, 0, 0);
	pb_rotate.setVisibility(View.INVISIBLE);
	tv_state.setText(XIALA_STATE);
	iv_arrow.setVisibility(View.VISIBLE);
}</span>

使用下拉刷新時,只需要調用回調函數進行異步數據的獲取

<span style="font-size:18px;">public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
	this.onRefreshListener = onRefreshListener;
}</span>

同時你可以通過調用setFirstDate()方法進行第一次時間的設置。通過setPullState()setUnlinkState()方法進行下拉,上拉刷新顯示的是什麼字,

源代碼:

package com.example.androidtest;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.R.string;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Switch;
import android.widget.TextView;

public class MyRefreshListView extends ListView {
	// 設置三種狀態
	private final int XIALA = 0;
	private final int SONGKAI = 1;
	private final int ZHENGZAI = 2;
	// 狀態的標示
	private int state = XIALA;
	// 三種狀態顯示的字體
	private static String XIALA_STATE = "下拉刷新";
	private static String SONGKAI_STATE = "鬆開刷新";
	private static String ZHENGZAI_STATE = "正在刷新";

	// 頭部的高度
	private int headHeight;
	private View headView;
	private ImageView iv_arrow;
	private ProgressBar pb_rotate;
	private TextView tv_state;
	private TextView tv_time;

	public MyRefreshListView(Context context, AttributeSet attrs,
			int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		init();
	}

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

	public MyRefreshListView(Context context) {
		super(context);
		init();
	}

	private RotateAnimation upAnimation, downAnimation;

	private void init() {
		// 圖標執行的動畫的設置
		upAnimation = new RotateAnimation(0, -180,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		upAnimation.setDuration(200);
		upAnimation.setFillAfter(true);
		downAnimation = new RotateAnimation(-180, -360,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		downAnimation.setDuration(200);
		downAnimation.setFillAfter(true);
		addHeadView();
	}

	// 添加頭部佈局
	private void addHeadView() {
		headView = View.inflate(getContext(), R.layout.layout_header, null);
		iv_arrow = (ImageView) headView.findViewById(R.id.iv_arrow);

		pb_rotate = (ProgressBar) headView.findViewById(R.id.pb_rotate);
		tv_state = (TextView) headView.findViewById(R.id.tv_state);
		tv_time = (TextView) headView.findViewById(R.id.tv_time);
		headView.measure(0, 0);
		headHeight = headView.getMeasuredHeight();
		headView.setPadding(0, -headHeight, 0, 0);
		addHeaderView(headView);
	}

	// 手指down的時候記錄下Y的座標
	private int height;

	/**
	 * 設置下拉刷新時,顯示的狀態。
	 * 
	 * @param state
	 */
	public void setPullState(String state) {
		XIALA_STATE = state;
	}

	/**
	 * 設置鬆開刷新顯示的狀態。
	 * 
	 * @param state
	 */
	public void setUnlinkState(String state) {
		SONGKAI_STATE = state;
	}

	/**
	 * 設置正在刷新顯示的狀態
	 * 
	 * @param state
	 */
	public void setNowState(String state) {
		ZHENGZAI_STATE = state;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (state == XIALA) {
			tv_state.setText(XIALA_STATE);
		} else if (state == SONGKAI) {
			tv_state.setText(SONGKAI_STATE);
		} else if (state == ZHENGZAI) {
			tv_state.setText(ZHENGZAI_STATE);
		}
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			height = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			if (state == ZHENGZAI) {
				break;
			}
			int y = (int) (ev.getY() - height);
			int offerY = y - headHeight;
			// 不要禁掉ListView的滑動事件
			if (offerY > -headHeight && getFirstVisiblePosition() == 0) {
				headView.setPadding(0, offerY, 0, 0);
				if (offerY > 0 && state == XIALA) {
					state = SONGKAI;
					setHeadView();
				} else if (offerY < 0 && state == SONGKAI) {
					state = XIALA;
					setHeadView();
				}
				return true;
			}
			break;
		case MotionEvent.ACTION_UP:
			if (state == XIALA) {
				headView.setPadding(0, -headHeight, 0, 0);
			} else if (state == SONGKAI) {
				state = ZHENGZAI;
				headView.setPadding(0, 0, 0, 0);
				setHeadView();

				if (onRefreshListener != null) {
					onRefreshListener.refresh();
				}

			}
			break;

		}
		return super.onTouchEvent(ev);
	}

	private void setHeadView() {
		switch (state) {
		case SONGKAI:
			tv_state.setText(SONGKAI_STATE);
			iv_arrow.startAnimation(upAnimation);
			break;
		case XIALA:
			tv_state.setText(XIALA_STATE);
			iv_arrow.startAnimation(downAnimation);
			break;

		case ZHENGZAI:
			tv_state.setText(ZHENGZAI_STATE);
			iv_arrow.clearAnimation();
			tv_time.setText("最後刷新:" + getDate());
			iv_arrow.setVisibility(View.INVISIBLE);
			pb_rotate.setVisibility(View.VISIBLE);
			break;

		default:
			break;
		}
	}

	/**
	 * 設置第一次時間的顯示
	 * @param name
	 */
	public void setFirstDate(String name) {
		tv_time.setText(name);
	}

	// 定義下拉刷新的回調
	private OnRefreshListener onRefreshListener;

	public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
		this.onRefreshListener = onRefreshListener;
	}

	public interface OnRefreshListener {
		public void refresh();
	}

	private String getDate() {
		SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
		return format.format(new Date());
	}

	// 關閉刷新
	public void refreshCpmplete() {
		state = SONGKAI;
		headView.setPadding(0, -headHeight, 0, 0);
		pb_rotate.setVisibility(View.INVISIBLE);
		tv_state.setText(XIALA_STATE);
		iv_arrow.setVisibility(View.VISIBLE);
	}

}


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