ListView的下拉刷新和上拉加载

一、先写头布局view文件:

<?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:orientation="horizontal" >
    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp" >
1、箭头:
   <ImageView
            android:id="@+id/iv_arr"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/common_listview_headview_red_arrow" />
2、进度条圆圈(隐藏):
        <ProgressBar
            android:id="@+id/pb_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:indeterminateDrawable="@drawable/custom_progress"
            android:visibility="invisible" />
    </FrameLayout>
3、下拉刷新的文字:
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical" >
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="下拉刷新"
            android:textColor="#f00"
            android:textSize="16sp" />
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="最后刷新时间:2015-03-10 17:07:07"
            android:textColor="@android:color/darker_gray"
            android:textSize="14sp" />
    </LinearLayout>
</LinearLayout>
二、自定义刷新时候的圆环

必须在progressbar里面设置android:indeterminateDrawable才能定义自己想要的环形
<ProgressBar
            android:id="@+id/pb_progress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:indeterminateDrawable="@drawable/custom_progress"
            android:visibility="invisible" />
custom_progress.xml外面是旋转动画,里面套一个自定义shape
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" >
    <shape
        android:innerRadius="12dp"
        android:shape="ring"
        android:thickness="3dp"
        android:useLevel="false" > 这个让环形变成圆圈,缺点是让原来的旋转动画消失了,所以要在外面套上一层自定义旋转动画
        <gradient
            android:centerColor="#3f00"
            android:endColor="#f00"
            android:startColor="#fff" />
    </shape>
</rotate>

三、自定义的ListView

public class RefreshListView extends ListView implements OnScrollListener,
		android.widget.AdapterView.OnItemClickListener {
//首先定义三种状态
	private static final int STATE_PULL_REFRESH = 0;// 下拉刷新
	private static final int STATE_RELEASE_REFRESH = 1;// 松开刷新
	private static final int STATE_REFRESHING = 2;// 正在刷新
	private View mHeaderView;
	private int startY = -1;// 滑动起点的y坐标
	private int mHeaderViewHeight;
	private int mCurrrentState = STATE_PULL_REFRESH;// 当前状态
	
    private TextView tvTitle;
	private TextView tvTime;
	private ImageView ivArrow;
	private ProgressBar pbProgress;
	private RotateAnimation animUp;
	private RotateAnimation animDown;
	public RefreshListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initHeaderView(); //初始化头部view
		initFooterView(); //初始化尾部view
	}
	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initHeaderView();
		initFooterView();
	}
	public RefreshListView(Context context) {
		super(context);
		initHeaderView();
		initFooterView();
	}
	/**
	 * 第一步!初始化头布局
	 */
	private void initHeaderView() {
    //1、找到头布局view
		mHeaderView = View.inflate(getContext(), R.layout.refresh_header, null); 
    //2、添加这个头布局到当前这个自定义ListView的头部
		this.addHeaderView(mHeaderView);
		tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title);
		tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time);
		ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arr);
		pbProgress = (ProgressBar) mHeaderView.findViewById(R.id.pb_progress);
   //3、测量这个头布局,得到头布局view的高度
		mHeaderView.measure(0, 0);
		mHeaderViewHeight = mHeaderView.getMeasuredHeight();
   //4、根据刚刚测量到的头布局高度,隐藏头布局
		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
   //5、先定义好动画,等到需要的时候再用
		initArrowAnim();
   //6、getCurrentTime:获取当前时间
		tvTime.setText("最后刷新时间:" + getCurrentTime());
	}
	/*
	 * 初始化脚布局
	 */
	private void initFooterView() {
		mFooterView = View.inflate(getContext(),
				R.layout.refresh_listview_footer, null);
		this.addFooterView(mFooterView);
		mFooterView.measure(0, 0);
		mFooterViewHeight = mFooterView.getMeasuredHeight();
		mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏
		this.setOnScrollListener(this);
	}
   //二、触摸事件
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
    //6、得到落地坐标
			startY = (int) ev.getRawY();
			break;
case MotionEvent.ACTION_MOVE:
	//7、 确保startY有效,其实没什么用		
         if (startY == -1) {
				startY = (int) ev.getRawY();
			}
    //8、 正在刷新时不做处理
			if (mCurrrentState == STATE_REFRESHING) {
				break;
			}
    //9、得到移动的时候的即时坐标
			int endY = (int) ev.getRawY();
    //10、得到移动偏移量
			int dy = endY - startY;
    //11、只有是下拉并且当前是第一个item,才允许下拉
			if (dy > 0 && getFirstVisiblePosition() == 0) {
    //12、计算padding
				int padding = dy - mHeaderViewHeight;
    //13、设置当前头部view的padding
				mHeaderView.setPadding(0, padding, 0, 0);
    //14、如果padding=0,表示整个头布局已经显示出来了,而且当前的状态不是松开刷新状态,此时要改变状态为松开刷新
		if (padding > 0 && mCurrrentState != STATE_RELEASE_REFRESH) {
    //15、状态改为松开刷新
					mCurrrentState = STATE_RELEASE_REFRESH;
   //16、根据当前状态改变view布局
					refreshState();
   //17、elseif padding小于0,代表没完全拉出来,而且不等于下拉刷新状态
				} else if (padding < 0 && mCurrrentState != STATE_PULL_REFRESH) {
   //18、改为下拉刷新状态
					mCurrrentState = STATE_PULL_REFRESH;
   //19、根据当前状态改变view布局
					refreshState();
				}
   //只有是下拉并且当前是第一个item,才允许自己处理
				return true;
			}
			break;
case MotionEvent.ACTION_UP:
			startY = -1;// 重置
   //20、若松开手的时候是松开刷新状态,将状态变成正在刷新状态
			if (mCurrrentState == STATE_RELEASE_REFRESH) {
				mCurrrentState = STATE_REFRESHING;// 正在刷新
   //21、把padding设置成0,完全显示头部view
				mHeaderView.setPadding(0, 0, 0, 0);// 显示
   //22、根据当前状态更改头布局view
				refreshState();
   //23、如果松开手时还是下拉刷新状态,恢复隐藏的位置
			} else if (mCurrrentState == STATE_PULL_REFRESH) {
				mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
			}
			break;
		default:
			break;
		}
		return super.onTouchEvent(ev);
	}
	/**
	 * 根据状态修改头布局view
	 */
	private void refreshState() {
		switch (mCurrrentState) {
		case STATE_PULL_REFRESH:
			tvTitle.setText("下拉刷新");
			ivArrow.setVisibility(View.VISIBLE);
			pbProgress.setVisibility(View.INVISIBLE);
            //设置箭头转向动画
			ivArrow.startAnimation(animDown);
			break;
		case STATE_RELEASE_REFRESH:
			tvTitle.setText("松开刷新");
			ivArrow.setVisibility(View.VISIBLE);
			pbProgress.setVisibility(View.INVISIBLE);
			ivArrow.startAnimation(animUp);
			break;
		case STATE_REFRESHING:
			tvTitle.setText("正在刷新...");
            // 必须先清除动画,才能隐藏
			ivArrow.clearAnimation();
			ivArrow.setVisibility(View.INVISIBLE);
			pbProgress.setVisibility(View.VISIBLE);
  //24、当状态为正在刷新时,检测是否实现了监听回调,若有,调用接口方法,实现回调
			if (mListener != null) {
				mListener.onRefresh();//告诉调用者,正在刷新状态呢,快点请求网络吧
			}
			break;
		default:
			break;
		}
	}
	/**
	 * 初始化箭头动画
	 */
	private void initArrowAnim() {
		// 箭头向上动画
		animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
				Animation.RELATIVE_TO_SELF, 0.5f);
		animUp.setDuration(200);
		animUp.setFillAfter(true);
		// 箭头向下动画
		animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,
				0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
		animDown.setDuration(200);
		animDown.setFillAfter(true);
	}
	
	private View mFooterView;
	private int mFooterViewHeight;
     //25、写一个接口传递消息给外部
    OnRefreshListener mListener;
	public void setOnRefreshListener(OnRefreshListener listener) {
		mListener = listener;
	}
	public interface OnRefreshListener {
		public void onRefresh();
		public void onLoadMore();// 加载下一页数据
	}
	/*
	 *26、 当外部数据成功获取或失败之后,调用此方法隐藏头/尾布局
	 */
	public void onRefreshComplete(boolean success) {
		if (isLoadingMore) {// 正在加载更多...
			mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏脚布局
			isLoadingMore = false;
		} else {
			mCurrrentState = STATE_PULL_REFRESH;
			tvTitle.setText("下拉刷新");
			ivArrow.setVisibility(View.VISIBLE);
			pbProgress.setVisibility(View.INVISIBLE);
			mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
            //如果成功了才更新时间
			if (success) {
				tvTime.setText("最后刷新时间:" + getCurrentTime());
			}
		}
	}
	/**
	 * 获取当前时间
	 */
	public String getCurrentTime() {
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return format.format(new Date());
	}
	private boolean isLoadingMore;
  !!! 上拉刷新就implements OnScrollListener,实现未实现的方法:
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (scrollState == SCROLL_STATE_IDLE
				|| scrollState == SCROLL_STATE_FLING) {
   //当滑动的状态是 fling或者空闲状态时,而且getLastVisiblePosition() == getCount() - 1,并且又不是LoadingMore状态,显示尾部布局,改变listview显示位置,设置isLoadingMore = true,最后实现接口回调,请求网络
			if (getLastVisiblePosition() == getCount() - 1 && !isLoadingMore) {           // 滑动到最后
				System.out.println("到底了.....");
				mFooterView.setPadding(0, 0, 0, 0);// 显示
				setSelection(getCount() - 1);// 改变listview显示位置
				isLoadingMore = true;
				if (mListener != null) {
					mListener.onLoadMore();
				}
			}
		}
	}
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
	}
	OnItemClickListener mItemClickListener;
	@Override
	public void setOnItemClickListener(
			android.widget.AdapterView.OnItemClickListener listener) {
		super.setOnItemClickListener(this);
		mItemClickListener = listener;
	}
	@Override
	public void onItemClick(AdapterView<?> parent, View view, int position,
			long id) {
		if (mItemClickListener != null) {
			mItemClickListener.onItemClick(parent, view, position
					- getHeaderViewsCount(), id);
		}
	}
}

  • 上拉加载更多:
一、在initData时, 得到缓存数据,
1、假如有缓存数据则调用parseData(cache, false)解析result,得到“more”,保存下一页地址,然后判断是否加载更多,如果不是的话就把获取到的数据展示在ListView上,若是加载更多,则从原来的集合上追加数据,再notifyDataSetChanged();
  2、假如没有缓存数据,则通过请求地址 getDataFromServer () 网络获取数据,获取成功再解析(parseData)数据,隐藏下拉刷新框,保存result;获取失败则提示吐司,并隐藏下拉刷新框:
public void initData() {
String cache = CacheUtils.getCache(mUrl, mActivity);
		if (!TextUtils.isEmpty(cache)) {
			parseData(cache, false);
		}
		getDataFromServer();

二、给ListView设置监听事件
    
    
//在initView()时,ListView设置的下拉刷新监听回调
lvList.setOnRefreshListener(new OnRefreshListener() {
//当下拉刷新时,会自动回调此函数加载网络数据  
@Override
public void onRefresh() {
getDataFromServer();
}
//当上拉加载时,若mMoreUrl不为空,调用加载更多数据方法  
@Override
public void onLoadMore() {
if (mMoreUrl != null) {
getMoreDataFromServer();
} else {
//若 mMoreUrl为空,那就是没有下一页了,则应该收起加载更多的布局
Toast.makeText(mActivity, "最后一页了", Toast.LENGTH_SHORT)
.show();
lvList.onRefreshComplete(false);// 收起加载更多的布局
}
}
});
三、网络获取数据
    
    
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpMethod.GET, mUrl, new RequestCallBack<String>() {
 
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = (String) responseInfo.result;
System.out.println("页签详情页返回结果:" + result);
 
parseData(result, false);
//调用ListView的方法,通知ListView隐藏刷新头view  
lvList.onRefreshComplete(true);
 
// 设置缓存
CacheUtils.setCache(mUrl, result, mActivity);
}
 
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
 
lvList.onRefreshComplete(false);
}
});
}
四、网络获取更多(下一页)数据
    
    
private void getMoreDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpMethod.GET, mMoreUrl, new RequestCallBack<String>() {
 
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = (String) responseInfo.result;
 
parseData(result, true);
 
lvList.onRefreshComplete(true);
}
 
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();
error.printStackTrace();
lvList.onRefreshComplete(false);
}
});
}

五、解析result数据:
   
   
protected void parseData(String result, boolean isMore) {
Gson gson = new Gson();
mTabDetailData = gson.fromJson(result, TabData.class);
System.out.println("页签详情解析:" + mTabDetailData);
 
// 1、在得到数据之后,解析数据,得到“more”中的地址
String more = mTabDetailData.data.more;
// 2、若“more”不为空,则拼接下一页的链接地址,赋值给 mMoreUrl
if (!TextUtils.isEmpty(more)) {
mMoreUrl = GlobalContants.SERVER_URL + more;
} else {
//3、否则没有下一页
mMoreUrl = null;
}
//4、如果不是加载更多,是刷新页面,则ArrayList<TabNewsData> mNewsList(新闻数据集合)= 新获取到的数据  
if (!isMore)
{
//5、给集合重新赋值,刷新界面
mNewsList = mTabDetailData . data . news ;
 
if ( mNewsList != null ) {
mNewsAdapter = new NewsAdapter();
lvList.setAdapter(mNewsAdapter);
}
} else {  
//6、如果是加载下一页,需要将数据追加给原来的集合
ArrayList<TabNewsData> news = mTabDetailData.data.news;
mNewsList.addAll(news);
mNewsAdapter.notifyDataSetChanged();
}
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值