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