listView 模仿ios的上拉刷新下拉加載更多

重新編輯了下,之前得有點bug,界面也不美觀。新的下載地址 http://download.csdn.net/detail/xizhao88/7638623,歡迎指教。




網上看了幾個都不是太滿意,要嘛只是上拉刷新,要嘛就是加載更多,沒有一個完整的。自己抽時間寫了個,還是有好些地方需要優化。寫的不好還請指教,謝謝。

PullListView頂部放刷新View(PullRefreshView),底部放加載更多View(PullLoadMoreView).

例子下載地址http://download.csdn.net/detail/xizhao88/7168167

package com.zhangliucheng.pulllistview;

@SuppressLint("NewApi")
public class PullListView extends RelativeLayout implements OnTouchListener, OnScrollListener, HeaderTriggerRefresh, FooterTriggerLoadMore {
	
	public static final int triggerHeight = 100;
	
	private int defaultColor = Color.LTGRAY;
	
	private Handler mHandler;
	
	private boolean listViewIsRefreshing = false;
	private boolean listViewIsLoadMore = false;
	
	private PullRefreshView refreshView;
	private PullLoadMoreView loadMoreView;
	private ListView mListView;
	
	private ScrollState scrollState = ScrollState.NORMAL;
	private ListViewPullDelegate listViewDelegate;
	
	
	private enum ScrollState {
		NORMAL,
		TOP,
		DWON
	}
	
	public interface ListViewPullDelegate {
		public void pullListViewTriggerRefresh(ListView listView);
		public void pullListViewTriggerLoadMore(ListView listView);
	}
	
	public void setListViewDelegate(ListViewPullDelegate listViewDelegate) {
		this.listViewDelegate = listViewDelegate;
	}

	public PullListView(Context context) {
		super(context);
		initView();
	}
	
	public PullListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}
	
	public ListView getListView() {
		return mListView;
	}

	public void setAdapter(ListAdapter adapter) {
		mListView.setAdapter(adapter);
	}
	
	/**
	 * 設置刷新和加載更多View的背景色
	 * @param colorId
	 */
	public void setRefreshAndLoadMoreViewBackground(int colorId) {
		refreshView.setBackgroundColor(colorId);
		loadMoreView.setBackgroundColor(colorId);
	}
	
	/**
	 * 設置尖頭圖片
	 * @param imageId
	 */
	public void setArrowImage(int imageId) {
		refreshView.setArrowImage(imageId);
		loadMoreView.setArrowImage(imageId);
	}
	
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		//正在刷新或者加載,直接返回
		if(listViewIsRefreshing || listViewIsLoadMore) {
			return false;

		//listView滑動到頂部,並且繼續向下滑動
		} else if(scrollState == ScrollState.TOP) {
			return refreshView.headerOnTouch(v, event);

		//向上滑動
		} else if(scrollState == ScrollState.DWON) {
			return loadMoreView.footerOnTouch(v, event);
		}
		return false;
	}
	
	@Override
	public void refresh() {
		setListViewIsRefreshing(true);
		
		if(null != listViewDelegate){
			listViewDelegate.pullListViewTriggerRefresh(this.mListView);
		}
	}
	
	@Override
	public void refreshFinish() {
		mHandler.sendEmptyMessage(0);
	}
	
	@Override
	public void loadMore() {
		setListViewIsLoadMore(true);
		if(null != listViewDelegate){
			listViewDelegate.pullListViewTriggerLoadMore(this.mListView);
		}
	}

	@Override
	public void loadMoreFinish() {
		mHandler.sendEmptyMessage(1);
	}
	
	/**
	 * 設置RefreshView狀態
	 * @param isRefreshing true 進入刷新狀態,否則進入正常狀態
	 */
	private void setListViewIsRefreshing(boolean isRefreshing) {
		if(!listViewIsRefreshing && isRefreshing) {
			refreshView.setRefresh();
			listViewIsRefreshing = true;
		} else if(!isRefreshing) {
			refreshView.setNormal();
			listViewIsRefreshing = false;
		}
	}
	
	/**
	 * 設置LoadMoreView狀態
	 * @param isLoadMore true 進入加載更多,否則進入正常狀態
	 */
	private void setListViewIsLoadMore(boolean isLoadMore) {
		if(!listViewIsLoadMore && isLoadMore) {
			loadMoreView.setRefresh();
			listViewIsLoadMore = true;
		} else if(!isLoadMore) {
			loadMoreView.setNormal();
			listViewIsLoadMore = false;
		}
	}
	
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		if(firstVisibleItem == 0) {
			setPullDirection(true);
			scrollState = ScrollState.TOP;
		} else if((firstVisibleItem + visibleItemCount) == totalItemCount) {
			setPullDirection(false);
			scrollState = ScrollState.DWON;
		} else {
			scrollState = ScrollState.NORMAL;
		}
	}
	
	private void initView() {
		mHandler = new MyHandler();
		
		//添加刷新view
		refreshView = new PullRefreshView(getContext(), this);
		refreshView.setId(1);
		addView(refreshView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		
		//添加listView
		mListView = new ListView(getContext());
		mListView.setId(2);
		addView(mListView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		
		//添加加載更多
		loadMoreView = new PullLoadMoreView(getContext(), this);
		loadMoreView.setId(3);
		addView(loadMoreView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		
		//設置監聽事件
		mListView.setOnTouchListener(this);
		mListView.setOnScrollListener(this);
		
		setRefreshAndLoadMoreViewBackground(defaultColor);
	}
	
	/**
	 * 但listView滑動到頂部或者頂部會掉用該方法,重新設置佈局方式
	 * @param downward 爲true的時候(listView下拉到頂部), loadMoreView  -below- listView,listView  -blow -refreshView.
	 * 					爲false的時候(listView上拉到底部),listView  -above -refreshView, loadMoreView  -above- listView
	 */
	private void setPullDirection(boolean downward) {
		if(downward && scrollState == ScrollState.NORMAL) {
			LayoutParams refreshParams = new LayoutParams(refreshView.getLayoutParams().width, 
					refreshView.getLayoutParams().height);
			refreshParams.topMargin = -triggerHeight;
			updateViewLayout(refreshView, refreshParams);
			
			LayoutParams listViewParams = new LayoutParams(mListView.getLayoutParams().width, 
					mListView.getLayoutParams().height);
			listViewParams.addRule(BELOW, refreshView.getId());
			updateViewLayout(mListView, listViewParams);
			
			LayoutParams loadMoreParams = new LayoutParams(loadMoreView.getLayoutParams().width, 
					loadMoreView.getLayoutParams().height);
			loadMoreParams.addRule(BELOW, mListView.getId());
			loadMoreParams.bottomMargin = -triggerHeight;
			updateViewLayout(loadMoreView, loadMoreParams);
			
		} else if(!downward && scrollState == ScrollState.NORMAL){
			LayoutParams loadMoreParams = new LayoutParams(loadMoreView.getLayoutParams().width, 
					loadMoreView.getLayoutParams().height);
			loadMoreParams.bottomMargin = -triggerHeight;
			loadMoreParams.addRule(ALIGN_PARENT_BOTTOM, TRUE);
			updateViewLayout(loadMoreView, loadMoreParams);
			
			LayoutParams listViewParams = new LayoutParams(mListView.getLayoutParams().width, 
					mListView.getLayoutParams().height);
			listViewParams.addRule(ABOVE, loadMoreView.getId());
			updateViewLayout(mListView, listViewParams);
			
			LayoutParams refreshParams = new LayoutParams(refreshView.getLayoutParams().width, 
					refreshView.getLayoutParams().height);
			refreshParams.topMargin = -triggerHeight;
			refreshParams.addRule(ABOVE, mListView.getId());
			updateViewLayout(refreshView, refreshParams);
		}
	}
	
	@SuppressLint("HandlerLeak")
	private class MyHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 0:
				setListViewIsRefreshing(false);
				break;
			case 1:
				setListViewIsLoadMore(false);
			default:
				break;
			}
		}
	}
	
	public static class AnimatorListenerNullImpl implements AnimatorListener {
		@Override
		public void onAnimationCancel(Animator animation) {
		}
		@Override
		public void onAnimationEnd(Animator animation) {
		}
		@Override
		public void onAnimationRepeat(Animator animation) {
		}
		@Override
		public void onAnimationStart(Animator animation) {
		}
	}
}

package com.zhangliucheng.pulllistview;

import com.zhangliucheng.pulllistview.PullListView.AnimatorListenerNullImpl;

@SuppressLint({ "NewApi", "ViewConstructor" })
public class PullLoadMoreView extends LinearLayout {
	
	private final int trigger = PullListView.triggerHeight;
	private final String normalInfo = "釋放加載更多...";
	private final String refreshInfo = "正在加載...";
	
	private FooterTriggerLoadMore mFooterTriggerLoadmore;
	
	private ImageView mArrow;
	private ProgressBar mProgress;
	private TextView mLabel;
	
	public interface FooterTriggerLoadMore {
		public void loadMore();
		public void loadMoreFinish();
	}
	
	public PullLoadMoreView(Context context, FooterTriggerLoadMore footerTriggerLoadmore) {
		super(context);
		this.mFooterTriggerLoadmore = footerTriggerLoadmore;
		initView();
	}
	
	private float downY = 0;
	public boolean footerOnTouch(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = event.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			float distance = event.getRawY() - downY;
			if(distance > 0) {
				scrollDistance(0);
				return false;
			} 
			scrollDistance(Math.abs(distance));;
			break;
		case MotionEvent.ACTION_UP:
			endScroll();
			break;
		}
		return true;
	}
	
	public void setArrowImage(int imageId) {
		mArrow.setBackgroundResource(imageId);
	}
	
	public void scrollDistance(float distance) {
		//isPulling爲ture,說明當前在向上pull,反之
		boolean isPulling = getPaddingBottom() - distance < 0;
		
		setPaddingBottom((int)distance);

		if(distance > trigger && isPulling) {
			setTriggerAnim(true);
		} else if(distance < trigger && !isPulling){
			setTriggerAnim(false);
		}
	}
	
	public void endScroll() {
		if(getPaddingBottom() > trigger) {
			mFooterTriggerLoadmore.loadMore();
		} else if(getPaddingBottom() < trigger){
			mFooterTriggerLoadmore.loadMoreFinish();
		}
	}
	
	/**
	 * 恢復正常狀態,將尖頭圖片顯示,等待框隱藏,並設置label的信息
	 */
	public void setNormal() {
		ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), 0);
		oa.setInterpolator(new LinearInterpolator());
		oa.setDuration(200);
		oa.start();
		oa.addListener(new AnimatorListenerNullImpl() {
			@Override
			public void onAnimationEnd(Animator arg0) {
				mArrow.setVisibility(View.VISIBLE);
				mArrow.setRotation(-180);
				mProgress.setVisibility(View.GONE);
				mLabel.setText(normalInfo);
			}
		});
	}
	
	/**
	 * 設置正在刷新,將尖頭圖片隱藏,等待框顯示出來,並設置label的信息
	 */
	public void setRefresh() {
		ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), trigger);
		oa.setInterpolator(new LinearInterpolator());
		oa.setDuration(200);
		oa.start();
		oa.addListener(new AnimatorListenerNullImpl() {
			@Override
			public void onAnimationEnd(Animator arg0) {
				mArrow.setVisibility(View.GONE);
				mProgress.setVisibility(View.VISIBLE);
				mLabel.setText(refreshInfo);
			}
		});
	}
	
	/**
	 * 這個方法供 ObjectAnimator.ofInt(this, "paddingBottom", getPaddingBottom(), trigger);使用
	 * @param f
	 */
	public void setPaddingBottom(int f) {
		setPadding(0, 0, 0, f);
		//這句話很關鍵
		((View)getParent()).setPadding(0, -f, 0, 0);
	}
	
	/**
	 * 下拉或上拉的時候,尖頭產生動畫
	 * @param isOpen
	 */
	public void setTriggerAnim(boolean isOpen) {
		if(isOpen) {
			if (mArrow.getRotation() == -180) {
				mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(0).start();
			}
		} else {
			if (mArrow.getRotation() == 0) {
				mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(-180).start();
			}
		}
	}
	
	private void initView() {
		LinearLayout layout = new LinearLayout(getContext());
		layout.setOrientation(HORIZONTAL);
		layout.setGravity(Gravity.CENTER_VERTICAL);
		addView(layout, LayoutParams.MATCH_PARENT, trigger);
		
		RelativeLayout relLayout = new RelativeLayout(getContext());
		LayoutParams relParams = new LayoutParams(80, 80);
		relParams.leftMargin = 100;
		layout.addView(relLayout, relParams);
		
		//尖頭圖片
		mArrow = new ImageView(getContext());
		mArrow.setRotation(-180);
		RelativeLayout.LayoutParams arrowParams = new RelativeLayout.LayoutParams(30, 80);
		arrowParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
		relLayout.addView(mArrow, arrowParams);
		
		//進度條
		mProgress = new ProgressBar(getContext());
		mProgress.setVisibility(View.GONE);
		RelativeLayout.LayoutParams progressPrams = new RelativeLayout.LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		progressPrams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
		relLayout.addView(mProgress, progressPrams);
		
		//文字說明
		mLabel = new TextView(getContext());
		mLabel.setTextColor(Color.BLACK);
		mLabel.setText(normalInfo);
		mLabel.setTextSize(20);
		LinearLayout.LayoutParams namePrams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		namePrams.leftMargin = 50;
		layout.addView(mLabel, namePrams);
	}
}

package com.zhangliucheng.pulllistview;
import com.zhangliucheng.pulllistview.PullListView.AnimatorListenerNullImpl;

/**
 *
 * 在listView的上面,默認是看不見的,下拉listView的時候纔會出現
 * 
 * @author zhangliucheng
 *
 */
@SuppressLint({ "NewApi", "ViewConstructor" })
public class PullRefreshView extends LinearLayout {
	
	private final int trigger = PullListView.triggerHeight;
	private final String normalInfo = "釋放刷新...";
	private final String refreshInfo = "正在刷新...";
	
	private HeaderTriggerRefresh mHeaderTriggerRefresh;
	
	private ImageView mArrow;
	private ProgressBar mProgress;
	private TextView mLabel;
	
	public interface HeaderTriggerRefresh {
		public void refresh();
		public void refreshFinish();
	}
	
	public PullRefreshView(Context context, HeaderTriggerRefresh headerTriggerRefresh) {
		super(context);
		this.mHeaderTriggerRefresh = headerTriggerRefresh;
		initView();
	}
	
	private float downY = 0;
	public boolean headerOnTouch(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downY = event.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			float distance = event.getRawY() - downY;
			//如果是向上pull,直接滾動到0,並且返回
			if(distance < 0) {
				scrollDistance(0);
				return false;
			} 
			scrollDistance(distance);
			break;
		case MotionEvent.ACTION_UP:
			endScroll();
			break;
		}
		return true;
	}
	
	public void setArrowImage(int imageId) {
		mArrow.setBackgroundResource(imageId);
	}
	
	public void scrollDistance(float distance) {
		//isPulling爲ture,說明當前在向下pull,反之
		boolean isPulling = getPaddingTop() - distance < 0;
		
		setPaddingTop((int)distance);
		
		//paddingTop大於trigger,並且當前在向下pull,觸發動畫
		if(distance > trigger && isPulling) {
			setTriggerAnim(true);
		
		} else if(distance < trigger && !isPulling){
			setTriggerAnim(false);
		}
	}
	
	public void endScroll() {
		if(getPaddingTop() > trigger) {
			mHeaderTriggerRefresh.refresh();
		} else if(getPaddingTop() < trigger){
			mHeaderTriggerRefresh.refreshFinish();
		}
	}
	
	/**
	 * 恢復正常狀態,將尖頭圖片顯示,等待框隱藏,並設置label的信息
	 */
	public void setNormal() {
		ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), 0);
		oa.setInterpolator(new LinearInterpolator());
		oa.setDuration(200);
		oa.start();
		oa.addListener(new AnimatorListenerNullImpl() {
			@Override
			public void onAnimationEnd(Animator arg0) {
				mArrow.setVisibility(View.VISIBLE);
				mArrow.setRotation(0);
				mProgress.setVisibility(View.GONE);
				mLabel.setText(normalInfo);
			}
		});
	}
	
	/**
	 * 設置正在刷新,將尖頭圖片隱藏,等待框顯示出來,並設置label的信息
	 */
	public void setRefresh() {
		ObjectAnimator oa = ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), trigger);
		oa.setInterpolator(new LinearInterpolator());
		oa.setDuration(200);
		oa.start();
		oa.addListener(new AnimatorListenerNullImpl() {
			@Override
			public void onAnimationEnd(Animator arg0) {
				mArrow.setVisibility(View.GONE);
				mProgress.setVisibility(View.VISIBLE);
				mLabel.setText(refreshInfo);
			}
		});
	}
	
	/**
	 * 這個方法供 ObjectAnimator.ofInt(this, "paddingTop", getPaddingTop(), trigger);使用
	 * @param f
	 */
	public void setPaddingTop(int f) {
		setPadding(0, f, 0, 0);
		((View)getParent()).setPadding(0, 0, 0,-f);
	}
	
	/**
	 * 下拉或上拉的時候,尖頭產生動畫
	 * @param isOpen
	 */
	public void setTriggerAnim(boolean isOpen) {
		if(isOpen) {
			if (mArrow.getRotation() == 0) {
				mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(-180).start();
			}
		} else {
			if (mArrow.getRotation() == -180) {
				mArrow.animate().setDuration(100).setInterpolator(new AccelerateDecelerateInterpolator()).rotation(0).start();
			}
		}
	}
	
	private void initView() {
		LinearLayout layout = new LinearLayout(getContext());
		layout.setOrientation(HORIZONTAL);
		layout.setGravity(Gravity.CENTER_VERTICAL);
		addView(layout, LayoutParams.MATCH_PARENT, trigger);
		
		RelativeLayout relLayout = new RelativeLayout(getContext());
		LayoutParams relParams = new LayoutParams(80, 80);
		relParams.leftMargin = 100;
		layout.addView(relLayout, relParams);
		
		//尖頭圖片
		mArrow = new ImageView(getContext());
		RelativeLayout.LayoutParams arrowParams = new RelativeLayout.LayoutParams(30, 80);
		arrowParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
		relLayout.addView(mArrow, arrowParams);
		
		//進度條
		mProgress = new ProgressBar(getContext());
		mProgress.setVisibility(View.GONE);
		RelativeLayout.LayoutParams progressPrams = new RelativeLayout.LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		progressPrams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
		relLayout.addView(mProgress, progressPrams);
		
		//文字說明
		mLabel = new TextView(getContext());
		mLabel.setTextColor(Color.BLACK);
		mLabel.setText(normalInfo);
		mLabel.setTextSize(20);
		LinearLayout.LayoutParams namePrams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		namePrams.leftMargin = 50;
		layout.addView(mLabel, namePrams);
		
	}
}


xml佈局裏面

<com.zhangliucheng.pulllistview.PullListView

        android:id="@+id/pullListView"

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>


Actvity裏面

mPullListView = (PullListView) findViewById(R.id.pullListView);
		mPullListView.setAdapter(arrayAdapter);
		mPullListView.setArrowImage(R.drawable.black_arrow);
		mPullListView.setListViewDelegate(new ListViewPullDelegate() {
			@Override
			public void pullListViewTriggerRefresh(ListView listView) {
				new Handler().postDelayed(new Runnable() {
					@Override
					public void run() {
						/** 這邊添加刷新 **/
						mPullListView.refreshFinish();
					}
				}, 1000);
			}
			
			@Override
			public void pullListViewTriggerLoadMore(ListView listView) {
				new Thread(new Runnable() {
					@Override
					public void run() {
						try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						MainActivity.this.runOnUiThread(new Runnable() {
							@Override
							public void run() {
								arrayAdapter.notifyDataSetChanged();
							}
						});
						//該方法和mPullListView.refreshFinish() 都會在主線程裏面執行,所以可以在任何線程調用這兩方法
						mPullListView.loadMoreFinish();
					}
				}).start();
			}
		});


簡單的效果圖


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章