[size=medium]之前在用ListView做下拉重新整理和上拉加載的時候一直都是在網上找的執行個體,
但效果都不是太好,然後自己根據執行個體的思路,自己梳理了一下,自己也寫了一把,感覺也不是太好,在此做下記錄吧,希望對初入ANDROID開發的新手們有所幫助,直接上代碼吧[/size]
package cn.zan.control.view;
import java.util.Date;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import cn.zan.zan_society.R;
/**
* Gening 2013-10-31
**/
public class CustomListView extends ListView {
private static final int DONE = 0;
private static final int PULL_TO_REFRESH = 1;
private static final int RELEASE_TO_REFRESH = 2;
private static final int REFRESHING = 3;
private static final float RATIO = 3;
private int state; // 目前下拉重新整理的狀态
private int firstVisibleIndex; // ListView 中第一個可以看見的Item下标
/***************************** ListView 頭部 View 控件 ***************************/
private View headView; // ListView 頭部 View
private ImageView headArrow; // ListView 頭部 View的箭頭
private ProgressBar progressBar; // ListView 頭部 View的讀取轉圈
private TextView headTitle; // ListView 頭部 View裡的文字
private TextView headLastUpdate; // ListView 頭部 View裡的文字,最後更新時間
private int headContentHeight; // ListView 頭部 View的高度
private Animation animation;
private Animation reverseAnimation;
private boolean isRefreshable;
private boolean isRecored = false; // 用來記錄第一次按下坐标點,在整個滑動的過程中 隻記錄一次
private float startY;
private boolean isBack = false;
private boolean isFootLoading = false; // 正在加載底部資料辨別
private boolean hasFoot = false; // 是否有了底部 View(FootView)
private int lastPos; // 最後一個可見的item的位置
private int count; // ListView Item總數,注意不是目前可見的item總數
public CustomListView(Context context, AttributeSet attrs) {
super(context, attrs);
if (isInEditMode()) { return; }
init(context);
}
private void init(Context context) {
// listview 設定滑動時緩沖背景色
setCacheColorHint(0x00000000);
headView = View.inflate(context, R.layout.head, null);
headArrow = (ImageView) headView.findViewById(R.id.head_arrow);
progressBar = (ProgressBar) headView.findViewById(R.id.progressbar);
headTitle = (TextView) headView.findViewById(R.id.head_title);
headLastUpdate = (TextView) headView.findViewById(R.id.head_last_update);
headArrow.setMinimumWidth(50);
headArrow.setMinimumHeight(70);
MeasureView(headView);
headContentHeight = headView.getMeasuredHeight();
headView.setPadding(0, -1 * headContentHeight, 0, 0);
addHeaderView(headView); // 為 ListView加入頂部 View
this.setOnScrollListener(custom_listview_onscroll_lis);
animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(250);
animation.setFillAfter(true); // 設定動畫結束時,停留在動畫結束位置 (保留動畫效果)
animation.setInterpolator(new LinearInterpolator()); // 勻速變化
reverseAnimation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);// 設定動畫結束時,停留在動畫結束位置 (保留動畫效果)
reverseAnimation.setInterpolator(new LinearInterpolator());// 勻速變化
// 設定目前headView的狀态
state = DONE;
// 設定目前下拉重新整理是否可用
isRefreshable = false;
}
/** 測量headView的 寬高 **/
private void MeasureView(View child) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
if (null == lp) {
lp = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
}
int measureChildWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int measureChildHeight;
if (lp.height > 0) {
measureChildHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
} else {
measureChildHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(measureChildWidth, measureChildHeight);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isRefreshable) {
if (firstVisibleIndex == 0 && !isRecored) {
startY = event.getY();
isRecored = true;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (isRefreshable) {
float tempY = event.getY();
if (firstVisibleIndex == 0 && !isRecored) {
startY = tempY;
isRecored = true;
}
if (isRecored) {
if (tempY - startY < 0) {
break;
}
if (state != REFRESHING) {
if (state == PULL_TO_REFRESH) {
if ((tempY - startY) / RATIO >= headContentHeight && (tempY - startY) > 0) {
// 向下拉了 從下拉重新整理的狀态 來到 松開重新整理的狀态
state = RELEASE_TO_REFRESH;
changeHeadViewOfState();
} else if ((tempY - startY) <= 0) {
// 向上推了 從下拉重新整理的狀态 來到 重新整理完成的狀态
state = DONE;
changeHeadViewOfState();
}
} else if (state == RELEASE_TO_REFRESH) {
// 向上推了 還沒有完全将HEADVIEW 隐藏掉(可以看到一部分)
if ((tempY - startY) / RATIO < headContentHeight && (tempY - startY) > 0) {
// 從松開重新整理的狀态 來到 下拉重新整理的狀态
state = PULL_TO_REFRESH;
changeHeadViewOfState();
isBack = true;
} else if ((tempY - startY) <= 0) {
// 向上推了 一下子推到了最上面 從松開重新整理的狀态 來到 重新整理完成的狀态 (資料不重新整理的)
state = DONE;
changeHeadViewOfState();
}
} else if (state == DONE) {
// 重新整理完成的狀态 來到 下拉重新整理的狀态
if ((tempY - startY) > 0) {
state = PULL_TO_REFRESH;
changeHeadViewOfState();
}
}
if (state == PULL_TO_REFRESH)
headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0);
if (state == RELEASE_TO_REFRESH)
headView.setPadding(0, (int) ((tempY - startY) / RATIO - headContentHeight), 0, 0);
}
}
}
break;
case MotionEvent.ACTION_UP:
if (isRefreshable) {
if (state != REFRESHING) {
if (state == PULL_TO_REFRESH) {
// 松手
state = DONE;
changeHeadViewOfState();
} else if (state == RELEASE_TO_REFRESH) {
// 松手
state = REFRESHING;
changeHeadViewOfState();
// 執行資料重新整理方法
onRefresh();
}
}
isRecored = false;
isBack = false;
}
break;
}
return super.onTouchEvent(event);
}
/** 改變下拉重新整理時,頭部控件顯示樣式 **/
private void changeHeadViewOfState() {
switch (state) {
case PULL_TO_REFRESH:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("下拉可以重新整理");
// 由 松開重新整理 到 下拉重新整理
if (isBack) {
headArrow.startAnimation(animation);
isBack = false;
}
break;
case RELEASE_TO_REFRESH:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headArrow.startAnimation(reverseAnimation);
headTitle.setText("松開可以重新整理");
break;
case REFRESHING:
headArrow.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("正在重新整理...");
headView.setPadding(0, 0, 0, 0);
break;
case DONE:
headArrow.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
headTitle.setVisibility(View.VISIBLE);
headLastUpdate.setVisibility(View.VISIBLE);
headArrow.clearAnimation();
headTitle.setText("下拉可以重新整理");
headView.setPadding(0, -1 * headContentHeight, 0, 0);
break;
}
}
/** ListView 監聽事件 **/
private OnScrollListener custom_listview_onscroll_lis = new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (!isFootLoading) {
// 沒有滾動
if (hasFoot && scrollState == SCROLL_STATE_IDLE) {
isFootLoading = true;
onFootLoading();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
firstVisibleIndex = firstVisibleItem;
lastPos = getLastVisiblePosition();
count = totalItemCount;
// 因為剛進入的時候,lastPos=-1,count=0,這個時候不能讓它執行onAddFoot方法
if (lastPos == count - 1 && !hasFoot && lastPos != -1) {
hasFoot = true;
onAddFoot();
}
}
};
/** 下拉重新整理監聽 **/
private OnRefreshListner refreshListner; // 重新整理監聽器
/** 設定下拉重新整理監聽器 **/
public void setOnRefreshListner(OnRefreshListner listener) {
isRefreshable = true;
refreshListner = listener;
}
/** 執行下拉重新整理操作 **/
private void onRefresh() {
if (refreshListner != null) {
refreshListner.onRefresh();
}
}
/** 添加底部View(FootView)監聽器 **/
public OnAddFootListener onAddFootListener;
/** 設定添加Foot監聽器 **/
public void setOnAddFootListener(OnAddFootListener addFootListener) {
onAddFootListener = addFootListener;
}
/** 執行添加Foot **/
public void onAddFoot() {
if (onAddFootListener != null && hasFoot) {
onAddFootListener.addFoot();
}
}
/** 上拉加載,監聽器 **/
public OnFootLoadingListener footLoadingListener;
public void setOnFootLoadingListener(OnFootLoadingListener footLoading) {
footLoadingListener = footLoading;
}
/** 執行底部加載 **/
public void onFootLoading() {
if (footLoadingListener != null && isFootLoading) {
footLoadingListener.onFootLoading();
}
}
/********************************************************************** 監聽器 **************************************************************************/
/** 下拉重新整理監聽器 **/
public interface OnRefreshListner {
// 下拉重新整理的時候,在這裡執行擷取資料的過程
void onRefresh();
}
/** 添加Foot的監聽器 **/
public interface OnAddFootListener {
// 這裡是使用者addFootView的操作
void addFoot();
}
/** 上拉加載監聽器 **/
public interface OnFootLoadingListener {
// 上拉加載的時候,在這裡行背景擷取資料的過程
void onFootLoading();
}
/******************************************************************** 對外調用方法 *****************************************************************/
/** 底部資料加載完成,使用者需要加入一個removeFootView的操作 **/
public void onFootLoadingComplete() {
hasFoot = false;
isFootLoading = false;
}
/** 上拉重新整理完成時 所執行的操作,更改狀态,隐藏head **/
public void onRefreshComplete() {
state = DONE;
changeHeadViewOfState();
headLastUpdate.setText("最後重新整理時間: " + new Date().toLocaleString());
}
@Override
public void setAdapter(ListAdapter adapter) {
headLastUpdate.setText("最後重新整理時間: " + new Date().toLocaleString());
super.setAdapter(adapter);
}
}
[size=large]在外部使用的時候,
需使用下拉重新整理,直接實作CustomListView的OnRefreshListenner接口
需使用上拉加載,先實作Custom的setOnAddFootListener接口為ListView賦上想要顯示的布局,然後再實作OnFootLoadingListener接口執行加載時需要做的請求、資料解析及ListView資料源重新整理。最後需調用CustomListView.OnFootLoadingComplete()回複初始狀态 和 CustomListView.removeFooterView()移除FootViewW布局。
大概實作就是這樣,在此因為時間的原因沒有寫上實作的思路,有時間補上。但是此執行個體有明顯的不足之處。
1. 下拉重新整理與上拉加載的效果太突兀,還有待優化。
2. 下拉重新整理與上拉加載的touch事件實作上區分的還不是很清晰明确,還有待優化。[/size]