自定义ListView下拉刷新上拉加载
ONE Goal,ONE Passion!
---czfy
公司让分析项目,发现一个效果确实不错,分析了半天,用了一种感觉很low的方法实现了,感觉那个效果肯定是属性动画做出来,等明天开始学习怎样去写那个效果吧.属性动画确实不错,建议可以参考”宏洋”大神的博客吧,写的确实不错!好了,开始今天的正题.
—自定义ListView下拉刷新上拉加载
第一步:PullRefreshListView完整代码
public class PullRefreshListView extends ListView {
Context context;
private View refresh_header; //刷新头
private View refresh_footer; //刷新脚布局
private int refresh_headerMeasuredHeight; //刷新头的高度
private int refresh_footerMeasuredHeight; // 刷新脚的高度
private ImageView refresh_img; //做动画的图
private ProgressBar refresh_header_progressbar;
private int currentState = 0; //刷新头的当前状态
private int DOWNPULL_REFRESH = 1; // 下拉刷新
private int RELEASE_REFRESH = 2; // 释放刷新
private int DOING_REFRESH = 3; // 正在刷新
private boolean isMoreLoding = true; // 防止上拉时多次请求加载更多.只有当第一次加载完成后才能进行第二次加载
public PullRefreshListView(Context context) {
this(context, null);
}
public PullRefreshListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PullRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initView();
}
/**
* 初始化 头布局,以及脚布局
*/
private void initView() {
//自己的刷新头布局
refresh_header = View.inflate(context, R.layout.refresh_header, null);
//自己想要的刷新脚布局
refresh_footer = View.inflate(context, R.layout.refresh_footer, null);
//添加头,脚布局
addHeaderView(refresh_header);
addFooterView(refresh_footer);
refresh_img = (ImageView) refresh_header.findViewById(R.id.refresh_header_imageview);
refresh_header_progressbar = (ProgressBar) findViewById(R.id.refresh_header_progressbar);
//得到刷新头的高度
refresh_header.measure(0, 0);
refresh_headerMeasuredHeight = refresh_header.getMeasuredHeight();
//得到刷新脚的高度
refresh_footer.measure(0, 0);
refresh_footerMeasuredHeight = refresh_footer.getMeasuredHeight();
//隐藏刷新头,脚
refresh_header.setPadding(0, -refresh_headerMeasuredHeight, 0, 0);
refresh_footer.setPadding(0,-refresh_footerMeasuredHeight,0,0);
//为listview设置滑动监听
this.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && getLastVisiblePosition() == getCount() - 1) {
if (mOnRefreshListener != null && isMoreLoding) {
isMoreLoding = false;
//显示脚布局
refresh_footer.setPadding(0,0,0,0);
mOnRefreshListener.refreshFooter();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
private int downY; //downY的位置
private int moveY; //moveY的位置
private int disY; //disY的大小
private int moveTop; //刷新头移动的后应该隐藏的高度
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
// getFirstVisiblePosition() 返回屏幕上显示第一条item的position
if (getFirstVisiblePosition() == 0) {
currentState = DOWNPULL_REFRESH;
downY = (int) ev.getY();
}
break;
case MotionEvent.ACTION_MOVE:
if ((int) ev.getY() - downY > 300) { // 如果滑动的距离大于300就不允许滑动了
return true;
}
//只有当第一个item显示的在屏幕上的时候才允许出现刷新头
if (getFirstVisiblePosition() == 0) {
moveY = (int) ev.getY();
disY = moveY - downY;
if (disY > 0) {
moveTop = -refresh_headerMeasuredHeight + disY;
refresh_header.setPadding(0, moveTop, 0, 0);
/**
* 1, 如果是下拉刷新状态(下拉过程)且刷新头已经全部显示
*/
if (currentState == DOWNPULL_REFRESH && moveTop > 0) {
currentState = RELEASE_REFRESH;
refreshState(currentState);
}
/**
* 2,如果是释放刷新状态,但是又不刷新,而是将刷新头送回
*/
if (currentState == RELEASE_REFRESH && moveTop < 0) {
currentState = DOWNPULL_REFRESH;
refreshState(currentState);
}
}
}
break;
case MotionEvent.ACTION_UP:
if (currentState == DOWNPULL_REFRESH) {
//隐藏刷新头
refresh_header.setPadding(0, -refresh_headerMeasuredHeight, 0, 0);
}
if (currentState == RELEASE_REFRESH) { //释放刷新
currentState = DOING_REFRESH;
refreshState(currentState);
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 刷新头布局的状态
*/
private void refreshState(int state) {
switch (state) {
case 1: // 下拉刷新状态.
up2down();
break;
case 2: //释放刷新
down2up();
break;
case 3: // 正在刷新
refresh_img.clearAnimation(); //把动画清空
refresh_header_progressbar.setVisibility(View.VISIBLE);
refresh_img.setVisibility(View.GONE);
if (mOnRefreshListener != null) {
mOnRefreshListener.refreshHeader();
}
break;
}
}
/**
* 刷新完成
*
* @param flag 0:刷新完成调用时传递的参数
* 1:加载完成时传达的参数
*/
public void refreshComplete(int flag) {
switch (flag) {
case 0: // 刷新完成
// 数据刷新是直接对集合添加了,速度太快,可能看不到圆形进度条.正常开发是是可以看到的 refresh_header_progressbar.setVisibility(View.INVISIBLE);
refresh_img.setVisibility(View.VISIBLE);
refresh_header.setPadding(0, -refresh_headerMeasuredHeight, 0, 0);
break;
case 1: //加载完成
isMoreLoding = true;
// refresh_footer.setPadding(0,-refresh_footerMeasuredHeight,0,0); 把这句注释掉是因为数据加载是直接对集合添加了,速度太快,可能看不到脚布局.正在项目中使用时应该加上这句代码
break;
}
}
/**
* 1,让刷新头的箭头至下向上旋转
* 2,改变文字提示
*/
public void down2up() {
RotateAnimation down2up = new RotateAnimation(0, -180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
down2up.setDuration(200);
down2up.setFillAfter(true);
refresh_img.startAnimation(down2up);
}
/**
* 1,让刷新头至上向下旋转
* 2,改变文字提示
*/
public void up2down() {
RotateAnimation up2down = new RotateAnimation(-180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
up2down.setDuration(200);
up2down.setFillAfter(true);
refresh_img.startAnimation(up2down);
}
/**
* 暴露给外部使用的回调方法
*
* @param mOnRefreshListener
*/
public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
this.mOnRefreshListener = mOnRefreshListener;
}
public OnRefreshListener mOnRefreshListener;
public interface OnRefreshListener {
// 刷新数据的回调
void refreshHeader();
//加载更多数据的回调
void refreshFooter();
}
}
第二步: 一些用到的布局
头布局文件 R.layout.refresh_header:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/refresh_header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_margin="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!--这里ProgressBar隐藏用的是invisible 因为这个属性值会将ProgressBar不显示,但是还是会占位置 -->
<ProgressBar
android:id="@+id/refresh_header_progressbar"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/refresh_header_imageview"
android:layout_gravity="center"
android:src="@drawable/red_arrow" //你自己的图片,就是一个箭头
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</FrameLayout>
<LinearLayout
android:layout_gravity="center"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_gravity="center"
android:id="@+id/refresh_header_text"
android:text="下拉刷新"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_gravity="center"
android:id="@+id/refresh_header_time"
android:text="1999-10-10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
脚布局文件 R.layout.refresh_footer:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/refresh_footer_root"
android:orientation="horizontal"
android:gravity="center">
<ProgressBar
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="正在加载..."
android:layout_margin="20dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
第三步:如何使用
像正常使用系统listview一样:
//设置监听
lv.setOnRefreshListener(new PullRefreshListView.OnRefreshListener() {
@Override
public void refreshHeader() {
mList.add(0,"刷新出来的数据");
lv.refreshComplete(0); adapter.notifyDataSetChanged();
}
@Override
public void refreshFooter() {
mList.add(mList.size(), "加载出来的数据");
lv.refreshComplete(1);
adapter.notifyDataSetChanged();
}
});
第四步:箭头图片资源
箭头图片
注意:刷新过程中,文字和时间这里我没有做处理,使用时处理一下就可以了.我们自定义的listview已经实现了.