ListView 下拉刷新 加载更多

1、首先继承ListView,利用addHeaderView和addFooterView对其添加头布局和脚布局,在根据View的setPadding(left, top, right, bottom)方法设置它的top属性,当top为负数时就在慢慢的隐藏布局。

2、获取头布局和脚布局的高度:由于view在onMeasure之后才能获取到它的高度,因此如果想在其之前获取高度可以调用measure(0, 0)进行测量,之后调用getMeasuredHeight就可以获取它的高度。

3、设置动画:这里使用旋转动画RotateAnimation,由于是逆时针在旋转,因此它的角度小于0。分别是0到-180和-180到-360。在下一次调用动画之前,可能上一次的动画没有结束,所以需要调用clearAnimation清除动画。

4、计算手指滑动的距离:需要重写onTouchEvent事件,在MotionEvent.ACTION_DOWN时记录下按下的坐标,然后与在移动时MotionEvent.ACTION_MOVE的坐标相减就得到移动的距离。

5、判断到达顶部和底部:需要实现OnScrollListener接口,需要重写下面两个方法

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if(scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {
			// 判断当前是否已经到了底部
			if(isScrollToBottom && !isLoadingMore) {
				isLoadingMore = true;
			}
		}
		
	}

	/**
	 * 当滚动时调用
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		firstVisibleItemPosition = firstVisibleItem; //firstVisibleItem等于0表示到达顶部
 
		if(getLastVisiblePosition() == (totalItemCount - 1)) {//最后一个可见的位置和总共的条目减一相等表示到达底部
			isScrollToBottom = true;//是否到达底部
		} else {
			isScrollToBottom = false;
		}
	}

6、设置回调接口:定义一个接口 ,引入改接口的对象,在需要调用的地方进行调用接口里面的方法就可以。

public class RefreshListView extends ListView implements OnScrollListener {

	private static final String TAG = "RefreshListView";
	private int firstVisibleItemPosition;	// 屏幕显示在第一个的item的索引
	private int downY;	// 按下时y轴的偏移量
	private int headerViewHeight;	// 头布局的高度
	private View headerView;	// 头布局的对象
	
	private final int DOWN_PULL_REFRESH = 0;	// 下拉刷新状态
	private final int RELEASE_REFRESH = 1;	// 松开刷新
	private final int REFRESHING = 2;	// 正在刷新中
	
	private int currentState = DOWN_PULL_REFRESH;		// 头布局的状态: 默认为下拉刷新状态
	private Animation upAnimation;	// 向上旋转的动画
	private Animation downAnimation; // 向下旋转的动画
	private ImageView ivArrow;	// 头布局的剪头
	private ProgressBar mProgressBar; // 头布局的进度条
	private TextView tvState; // 头布局的状态
	private TextView tvLastUpdateTime; // 头布局的最后更新时间
	
	private OnRefreshListener mOnRefershListener;
	private boolean isScrollToBottom;		// 是否滑动到底部
	private View footerView;	// 脚布局的对象
	private int footerViewHeight; // 脚布局的高度
	private boolean isLoadingMore = false;	// 是否正在加载更多中

	public RefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		initHeaderView();
		
		initFooterView();
		
		this.setOnScrollListener(this);
	}

	/**
	 * 初始化脚布局
	 */
	private void initFooterView() {
		footerView = View.inflate(getContext(), R.layout.listview_footer, null);
		
		footerView.measure(0, 0);
		
		footerViewHeight = footerView.getMeasuredHeight();
		Log.i(TAG, "脚布局的高度: " + footerViewHeight);
		
		footerView.setPadding(0, -footerViewHeight, 0, 0);
		
		this.addFooterView(footerView);
	}

	/**
	 * 初始化头布局
	 */
	private void initHeaderView() {
		headerView = View.inflate(getContext(), R.layout.listview_header, null);
		ivArrow = (ImageView) headerView.findViewById(R.id.iv_listview_header_arrow);
		mProgressBar = (ProgressBar) headerView.findViewById(R.id.pb_listview_header);
		tvState = (TextView) headerView.findViewById(R.id.tv_listview_header_state);
		tvLastUpdateTime = (TextView) headerView.findViewById(R.id.tv_listview_header_last_update_time);
		
		// 设置最后刷新时间
		tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());
		
		headerView.measure(0, 0);		// 系统会帮我们测量出headerView的高度
		
		headerViewHeight = headerView.getMeasuredHeight();
		Log.i(TAG, "测量后的高度: " + headerViewHeight);
		
		headerView.setPadding(0, -headerViewHeight, 0, 0);
		 
		this.addHeaderView(headerView);		// 向ListView的顶部添加一个view对象
		
		initAnimation();
	}
	
	/**
	 * 获得系统的最新时间
	 * @return
	 */
	private String getLastUpdateTime() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(System.currentTimeMillis());
	}

	/**
	 * 初始化动画
	 */
	private void initAnimation() {
		
		upAnimation = new RotateAnimation(
				0f, -180f, 
				Animation.RELATIVE_TO_SELF, 0.5f, 
				Animation.RELATIVE_TO_SELF, 0.5f);
		upAnimation.setDuration(500);
		upAnimation.setFillAfter(true);	// 动画结束后, 停留在结束的位置上
		
		downAnimation = new RotateAnimation(
				-180f, -360f, 
				Animation.RELATIVE_TO_SELF, 0.5f, 
				Animation.RELATIVE_TO_SELF, 0.5f);
		downAnimation.setDuration(500);
		downAnimation.setFillAfter(true);	// 动画结束后, 停留在结束的位置上
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = (int) ev.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			int moveY = (int) ev.getY();
			
			// 移动中的y - 按下的y = 间距.
			int diff = (moveY - downY) / 2;
			
			// -头布局的高度 + 间距 = paddingTop
			int paddingTop = -headerViewHeight + diff;
			
			// 如果: -头布局的高度  > paddingTop的值    执行super.onTouchEvent(ev);
			if(firstVisibleItemPosition == 0
					&& -headerViewHeight < paddingTop) {
//				Log.i(TAG, "当前在顶部滑动");
				
				if(paddingTop > 0 && currentState == DOWN_PULL_REFRESH) {		// 完全显示了. 并且当前的状态是下拉刷新状态
					Log.i(TAG, "松开刷新");
					currentState = RELEASE_REFRESH;
					refreshHeaderView();
				} else if(paddingTop < 0 && currentState == RELEASE_REFRESH) {		// 没有显示完全
					Log.i(TAG, "下拉刷新");
					currentState = DOWN_PULL_REFRESH;
					refreshHeaderView();
				}
				
				// 下拉头布局
				headerView.setPadding(0, paddingTop, 0, 0);
				return true;
			}
			
			break;
		case MotionEvent.ACTION_UP:
			
			// 判断当前的状态是松开刷新还是下拉刷新
			if(currentState == RELEASE_REFRESH) {	
				Log.i(TAG, "刷新数据.");
				
				// 把头布局设置为完全显示状态
				headerView.setPadding(0, 0, 0, 0);
				
				// 进入到正在刷新中状态
				currentState = REFRESHING;
				refreshHeaderView();
				
				if(mOnRefershListener != null) {
					mOnRefershListener.onDownPullRefresh();		// 调用使用者的监听方法
				}
			} else if(currentState == DOWN_PULL_REFRESH) {
				// 隐藏头布局
				headerView.setPadding(0, -headerViewHeight, 0, 0);
			}
			break;
		default:
			break;
		}
		return super.onTouchEvent(ev);
	}
	
	/**
	 * 根据currentState刷新头布局的状态
	 */
	private void refreshHeaderView() {
		switch (currentState) {
		case DOWN_PULL_REFRESH:	// 下拉刷新状态
			tvState.setText("下拉刷新");
			ivArrow.startAnimation(downAnimation);	// 执行向下旋转
			break;
		case RELEASE_REFRESH:	// 松开刷新状态
			tvState.setText("松开刷新");
			ivArrow.startAnimation(upAnimation);	// 执行向上旋转
			break;
		case REFRESHING:	// 正在刷新中状态		ctrl + shift + X 大写  Y小写
			ivArrow.clearAnimation();
			ivArrow.setVisibility(View.GONE);
			mProgressBar.setVisibility(View.VISIBLE);
			tvState.setText("正在刷新中...");
			break;
		default:
			break;
		}
	}

	/**
	 * 当滚动状态改变时回调
	 * 
	 * SCROLL_STATE_IDLE  停滞状态
	 * SCROLL_STATE_TOUCH_SCROLL 按住时滚动的状态
	 * SCROLL_STATE_FLING 猛地一滑
	 */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {

		if(scrollState == SCROLL_STATE_IDLE 
				|| scrollState == SCROLL_STATE_FLING) {
			// 判断当前是否已经到了底部
			
			if(isScrollToBottom && !isLoadingMore) {
				isLoadingMore = true;
				
				// 当前到底部
				Log.i(TAG, "加载更多数据");
				
				footerView.setPadding(0, 0, 0, 0);
				this.setSelection(this.getCount());
				
				if(mOnRefershListener != null) {
					mOnRefershListener.onLoadingMore();
				}
			}
		}
		
	}

	/**
	 * 当滚动时调用
	 * firstVisibleItem 当前屏幕显示在顶部的item的position
	 * visibleItemCount 当前屏幕显示了多少个条目的总数.
	 * totalItemCount	ListView的总条目的总数
	 * 
	 * 20 + 10 = 30;
	 * 
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		firstVisibleItemPosition = firstVisibleItem;
		
//		Log.i(TAG, "lastPosition: " + getLastVisiblePosition() + ", count: " + totalItemCount);
		if(getLastVisiblePosition() == (totalItemCount - 1)) {
			isScrollToBottom = true;
		} else {
			isScrollToBottom = false;
		}
	}
	
	/**
	 * 设置刷新监听事件
	 * @param listener
	 */
	public void setOnRefreshListener(OnRefreshListener listener) {
		mOnRefershListener = listener;
	}
	
	/**
	 * 隐藏头布局
	 */
	public void hideHeaderView() {
		headerView.setPadding(0, -headerViewHeight, 0, 0);
		ivArrow.setVisibility(View.VISIBLE);
		mProgressBar.setVisibility(View.GONE);
		tvState.setText("下拉刷新");
		tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());
		currentState = DOWN_PULL_REFRESH;
	}
	
	public void hideFooterView() {
		footerView.setPadding(0, -footerViewHeight, 0, 0);
		isLoadingMore = false;
	}
}

public class MainActivity extends Activity implements OnRefreshListener {

    protected static final String TAG = "MainActivity";
	private List<String> textList;
	private MyAdapter adapter;
	private RefreshListView mListView;

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mListView = (RefreshListView) findViewById(R.id.refreshlistview);
        
        textList = new ArrayList<String>();
        
        for (int i = 0; i < 30; i++) {
			textList.add("ListView数据项 "+ i);
		}
        
        adapter = new MyAdapter();
		mListView.setAdapter(adapter);
        
        mListView.setOnRefreshListener(this);
    }

    class MyAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return textList.size();
		}

		@Override
		public Object getItem(int arg0) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		public long getItemId(int position) {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			TextView tv = new TextView(MainActivity.this);
			tv.setText(textList.get(position));
			tv.setTextSize(18);
			tv.setTextColor(Color.BLACK);
			return tv;
		}
    	
    }

	@Override
	public void onDownPullRefresh() {
		
		new AsyncTask<String, Integer, Void>() {

			@Override
			protected Void doInBackground(String... params) {
				Log.i(TAG, "doInBackground");
				SystemClock.sleep(2000);
				
				textList.add(0, "这是下拉刷新出来的数据");
				
				return null;
			}

			@Override
			protected void onPostExecute(Void result) {
				Log.i(TAG, "onPostExecute");
				adapter.notifyDataSetChanged();		// 锟斤拷锟铰斤拷锟斤拷
				
				mListView.hideHeaderView();
			}
		}.execute(new String[]{});		// 锟斤拷始执锟斤拷锟届步锟斤拷锟斤拷
		
	}

	@Override
	public void onLoadingMore() {
		
		new AsyncTask<Void, Void, Void>() {

			@Override
			protected Void doInBackground(Void... params) {
				SystemClock.sleep(5000);
				
				textList.add("这是加载更多的数据。。。");
				textList.add("这是加载更多的数据。。。");
				textList.add("这是加载更多的数据。。。");
				textList.add("这是加载更多的数据。。。");
				textList.add("这是加载更多的数据。。。");
				textList.add("这是加载更多的数据。。。");
				return null;
			}

			@Override
			protected void onPostExecute(Void result) {
				adapter.notifyDataSetChanged();
				
				// 锟斤拷锟狡脚诧拷锟斤拷锟斤拷锟斤拷
				mListView.hideFooterView();
			}
		}.execute(new Void[]{});
		
	}
}

/**
 * @author andong
 * 自动刷新ListView的刷新监听事件
 */
public interface OnRefreshListener {

	/**
	 * 下拉刷新.加载完成后需要把头布局隐藏
	 */
	void onDownPullRefresh();
	
	/**
	 * 加载更多, 加载完成后需要把脚布局隐藏
	 */
	void onLoadingMore();
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值