大部分应用里面都有下拉刷新和点击加载更多这个功能,直接贴代码,可以直接用
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>