Skip to content

Instantly share code, notes, and snippets.

@johnwatsondev
Last active August 29, 2015 14:24
Show Gist options
  • Save johnwatsondev/5922a3be8bc2555bff93 to your computer and use it in GitHub Desktop.
Save johnwatsondev/5922a3be8bc2555bff93 to your computer and use it in GitHub Desktop.
Pull to refresh ListView code. It's not good. Just a demo.
/**
* 短小精干的<strong>ListView</strong>下拉刷新和加载更多组件
*
* <p><strong>变更说明:</strong>
* <br>
* 默认如果设置了{@link OnRefreshListener}接口和{@link OnLoadMoreListener}接口,
* 则打开下拉刷新和加载更多功能。
* <br>
* 若设置监听器为null,抛出异常。
* <br>
* 一共六个Flag:
* <br>
* {@link #mCanRefresh}(是否可以下拉刷新)
* <br>
* {@link #mCanLoadMore}(是否可以加载更多)
* <br>
* {@link #mIsAutoLoadMore}(是否自动加载更多)
* <br>
* {@link #mIsDataLoadFinished}(所有数据是否加载完毕)
* <br>
* {@link #mMaxPullDistance}(HeaderView最大下拉距离 (px))
* <br>
* {@link #mIsMoveToFirstItemAfterRefresh}(下拉刷新后是否显示第一条Item)
*
* <p><strong>Note:</strong>
* <br>
* 1.新增设置所有数据加载完毕后提示
* <br>
* 2.优化所有Item高度小于ListView高度时,自动隐藏加载更多按钮。
* <br>
* 3.新增设置HeaderView最大下拉距离
*
* <p>大家可以根据实际项目需要自由更改,谢谢!
* <br>
* <strong>若有改进意见,请发送到俺的邮箱哈~ 多谢各位小伙伴!^_^</strong>
*
* @date May 26, 2015 11:34:37 PM
* @author JohnWatsonDev
* @email [email protected]
* @blog www.johnwatsondev.com
*/
public class PtrListView extends ListView implements OnScrollListener {
/** 显示格式化日期模板 */
private final static String DATE_FORMAT_STR = "yyyy年MM月dd日 HH:mm";
/** 实际的padding的距离与界面上偏移距离的比例 */
private final static int RATIO = 3;
/** 松开刷新 */
private final static int RELEASE_TO_REFRESH = 0;
/** 下拉刷新 */
private final static int PULL_TO_REFRESH = 1;
/** 正在刷新 */
private final static int REFRESHING = 2;
/** 刷新完成 */
private final static int DONE = 3;
/** 加载中 */
private final static int FOOTER_LOADING = 1;
/** 手动加载完成 */
private final static int FOOTER_MANUAL_LOAD_DONE = 2;
/** 自动加载完成 */
private final static int FOOTER_AUTO_LOAD_DONE = 3;
/** 0:RELEASE_TO_REFRESH;
* <p> 1:PULL_To_REFRESH;
* <p> 2:REFRESHING;
* <p> 3:DONE; */
private int mHeaderState;
/** 1:加载中 - FOOTER_LOADING
* <p> 2:手动加载完成 - FOOTER_MANUAL_LOAD_DONE
* <p> 3:自动加载完成 - FOOTER_AUTO_LOAD_DONE */
private int mFooterState;
// ================================= 功能设置Flag ================================
/** 可以下拉刷新? */
private boolean mCanRefresh = false;
/** 可以加载更多? */
private boolean mCanLoadMore = false;
/** 可以自动加载更多吗?(注意,先判断是否有加载更多,如果没有,这个flag也没有意义) */
private boolean mIsAutoLoadMore = true;
/** 所有数据是否加载完毕 */
private boolean mIsDataLoadFinished = false;
/** 下拉刷新后是否显示第一条Item */
private boolean mIsMoveToFirstItemAfterRefresh = true;
/** HeaderView最大下拉距离 (px) */
private int mMaxPullDistance = 0;
/**
* true if refreshing else false
*
* @return Is header refreshing?
*/
public boolean isRefreshing(){
if(mHeaderState == REFRESHING){
return true;
}
return false;
}
/**
* true if loading more else false
*
* @return Is footer loading more?
*/
public boolean isLoadingMore(){
if(mFooterState == FOOTER_LOADING){
return true;
}
return false;
}
/**
* default false
*
* @return Is can loadMore?
*/
public boolean isCanLoadMore() {
return mCanLoadMore;
}
public void setCanLoadMore(boolean pCanLoadMore) {
if(mLoadMoreListener == null){
throw new InvalidSettingException("Must set a valid OnLoadMoreListener...");
}
mCanLoadMore = pCanLoadMore;
if(mCanLoadMore && mIsDataLoadFinished){
mIsDataLoadFinished = false;
}
// Why mFooterState != FOOTER_LOADING ?
// 如果正在加载中,要等待加载完成后再onScroll()中移除FootView。
if(!mCanLoadMore && mFooterState != FOOTER_LOADING && getFooterViewsCount() > 0){
// 突然关闭加载更多功能之后,我们要移除FootView。
MyLogger.showLogWithLineNum(5,"调用set方法关闭加载更多 removeFooterView(endRootView);...");
removeFooterView();
}else if(mCanLoadMore && getFooterViewsCount() == 0 && mEnoughCount){
addFooterView();
}
}
/**
* default false
*
* @return Is can pull to refresh?
*/
public boolean isCanRefresh() {
return mCanRefresh;
}
public void setCanRefresh(boolean pCanRefresh) {
if(mRefreshListener == null){
throw new InvalidSettingException("Must set a valid OnRefreshListener...");
}
mCanRefresh = pCanRefresh;
if(mCanRefresh && mIsDataLoadFinished){
mIsDataLoadFinished = false;
}
}
/**
* default true
*
* @return Is auto loadMore?
*/
public boolean isAutoLoadMore() {
return mIsAutoLoadMore;
}
public void setAutoLoadMore(boolean pIsAutoLoadMore) {
mIsAutoLoadMore = pIsAutoLoadMore;
}
/**
* default false
*
* @return Is data load finished?
*/
public boolean isDataLoadFinished(){
return mIsDataLoadFinished;
}
public void setIsDataLoadFinished(boolean pIsDataLoadFinished){
mIsDataLoadFinished = pIsDataLoadFinished;
if(mIsDataLoadFinished){
setFooterViewLoadComplete();
}
}
/**
* 设置FooterView显示 “没有更多数据”
*/
private void setFooterViewLoadComplete(){
mFooterLoadTipsTextView.setText(R.string.p2refresh_no_more_data);
mFooterLoadTipsTextView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar.setVisibility(View.GONE);
mFooterView.setVisibility(View.VISIBLE);
}
/**
* default true
*
* @return Is move to first item after refresh finished?
*/
public boolean isMoveToFirstItemAfterRefresh() {
return mIsMoveToFirstItemAfterRefresh;
}
public void setMoveToFirstItemAfterRefresh(
boolean pIsMoveToFirstItemAfterRefresh) {
mIsMoveToFirstItemAfterRefresh = pIsMoveToFirstItemAfterRefresh;
}
/**
* Get max pull distance (This comment is garbage...)
*
* @return max pull distance
*/
public int getMaxPullDistance() {
return mMaxPullDistance;
}
public void setMaxPullDistance(int maxPullDistance) {
mMaxPullDistance = maxPullDistance;
}
// ============================================================================
private LayoutInflater mInflater;
private LinearLayout mHeaderView;
private TextView mTipsTextView;
private TextView mLastUpdatedTextView;
private ImageView mArrowImageView;
private ProgressBar mProgressBar;
private View mFooterView;
private ProgressBar mFooterLoadProgressBar;
private TextView mFooterLoadTipsTextView;
/** headerView动画 */
private RotateAnimation mArrowAnim;
/** headerView反转动画 */
private RotateAnimation mArrowReverseAnim;
/** 用于保证startY的值在一个完整的touch事件中只被记录一次 */
private boolean mIsRecored;
private int mHeaderViewWidth;
private int mHeaderViewHeight;
/** Touch事件开始Y坐标 */
private int mStartY;
private boolean mIsBack;
/** 纪录上一次Move事件的Y坐标,为了判断是下拉还是上滑 */
private int mLastMoveX;
private int mListViewHeight;
private int mFirstItemIndex;
private int mLastItemIndex;
private int mCount;
/** Item数量足够充满ListView高度? */
private boolean mEnoughCount;
private OnRefreshListener mRefreshListener;
private OnLoadMoreListener mLoadMoreListener;
public PtrListView(Context pContext) {
super(pContext);
init(pContext);
}
public PtrListView(Context pContext, AttributeSet pAttrs) {
super(pContext, pAttrs);
init(pContext);
}
public PtrListView(Context pContext, AttributeSet pAttrs, int pDefStyle) {
super(pContext, pAttrs, pDefStyle);
init(pContext);
}
/**
* 初始化操作
* @param pContext
*/
private void init(Context pContext) {
setCacheColorHint(pContext.getResources().getColor(R.color.transparent));
mInflater = LayoutInflater.from(pContext);
addHeadView();
setOnScrollListener(this);
initPullImageAnimation(0);
}
/**
* 为了获取ListView的高度
*/
private void addGlobalLayoutListener(){
if(this.getViewTreeObserver().isAlive()){
this.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// 此处获取高度的速度慢于getMeasureHight()获取的速度
// 二者值相同
// mListViewHeight = getHeight();
MyLogger.showLogWithLineNum(4, "onGlobalLayout");
}
});
}
// Need api 11, current min is 8.
// this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
//
// @Override
// public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
// int oldTop, int oldRight, int oldBottom) {
// MyLogger.showLogWithLineNum(4, "onLayoutChange");
// }
// });
}
/**
* 添加下拉刷新的HeadView
*/
private void addHeadView() {
mHeaderView = (LinearLayout) mInflater.inflate(R.layout.ptr_lv_head, null);
mArrowImageView = (ImageView) mHeaderView
.findViewById(R.id.head_arrowImageView);
mArrowImageView.setMinimumWidth(70);
mArrowImageView.setMinimumHeight(50);
mProgressBar = (ProgressBar) mHeaderView
.findViewById(R.id.head_progressBar);
mTipsTextView = (TextView) mHeaderView.findViewById(
R.id.head_tipsTextView);
mLastUpdatedTextView = (TextView) mHeaderView
.findViewById(R.id.head_lastUpdatedTextView);
measureView(mHeaderView);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderViewWidth = mHeaderView.getMeasuredWidth();
mHeaderView.setPadding(0, -1 * mHeaderViewHeight, 0, 0);
mHeaderView.invalidate();
Log.v("size", "width:" + mHeaderViewWidth + " height:"
+ mHeaderViewHeight);
addHeaderView(mHeaderView, null, false);
mHeaderState = DONE;
}
/**
* 添加加载更多FootView
*/
private void addFooterView() {
mFooterView = mInflater.inflate(R.layout.ptr_lv_footer_more, null);
mFooterView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar = (ProgressBar) mFooterView
.findViewById(R.id.pull_to_refresh_progress);
mFooterLoadTipsTextView = (TextView) mFooterView.findViewById(R.id.load_more);
mFooterView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 数据加载完毕 或 非“手动加载更多”状态不允许点击
if(mIsDataLoadFinished || mFooterState != FOOTER_MANUAL_LOAD_DONE)
return;
if(mCanLoadMore){
if(mCanRefresh){
// 当可以下拉刷新时,并且HeadView没有正在刷新,才可以点击加载更多。
if(mHeaderState != REFRESHING){
onLoadMore();
}
}else {
// 当不能下拉刷新时,可以点击加载更多。
onLoadMore();
}
}
}
});
addFooterView(mFooterView);
changeFooterViewStateByIsAutoLoadMore(true);
}
/**
* 根据是否自动加载更多Flag,更改FooterView状态。
*/
private void changeFooterViewStateByIsAutoLoadMore(boolean isFooterViewVisible){
if(mIsAutoLoadMore){
mFooterState = FOOTER_AUTO_LOAD_DONE;
}else{
mFooterState = FOOTER_MANUAL_LOAD_DONE;
}
changeFootViewByState(isFooterViewVisible);
}
/**
* 实例化下拉刷新的箭头的动画效果
* @param pAnimDuration 动画运行时长
*/
private void initPullImageAnimation(final int pAnimDuration) {
int _Duration;
if(pAnimDuration > 0){
_Duration = pAnimDuration;
}else{
_Duration = 250;
}
Interpolator _Interpolator = new LinearInterpolator();
mArrowAnim = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mArrowAnim.setInterpolator(_Interpolator);
mArrowAnim.setDuration(_Duration);
mArrowAnim.setFillAfter(true);
mArrowReverseAnim = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
mArrowReverseAnim.setInterpolator(_Interpolator);
mArrowReverseAnim.setDuration(_Duration);
mArrowReverseAnim.setFillAfter(true);
}
/**
* 测量HeadView宽高(注意:此方法仅适用于LinearLayout,请读者自己测试验证。)
* @param pChild
*/
private void measureView(View pChild) {
ViewGroup.LayoutParams p = pChild.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_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);
}
pChild.measure(childWidthSpec, childHeightSpec);
}
/**
* 获取view的高度
*
* @param view
* @return
*/
private int getMeasureHight(View view) {
int height = view.getHeight();
if (height == 0) {
ViewUtil.measureView(view);
height = view.getMeasuredHeight();
}
MyLogger.showLogWithLineNum(3, "getMeasureHight = "+height);
return height;
}
/**
* 获取所有Items的高度
*
* @return all items' total height
*/
private int getTotalItemsHeight(ListView listview){
int numberOfItems = listview.getCount();
// Get total height of all items.
int totalItemsHeight = 0;
for (int itemPos = 0; itemPos < numberOfItems; itemPos++) {
View item = listview.getAdapter().getView(itemPos, null, listview);
item.measure(0, 0);
totalItemsHeight += item.getMeasuredHeight();
}
// Get total height of all item dividers.
int totalDividersHeight = listview.getDividerHeight() * (numberOfItems - 1);
int totalHeight = totalItemsHeight + totalDividersHeight;
MyLogger.showLogWithLineNum(3, "getHeightByItems = "+totalHeight);
return totalHeight;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// MyLogger.showLogWithLineNum(4, "onAttachedToWindow");
}
/**
* <strong>FirstTime Run</strong><p>
* onAttachedToWindow ---> onLayout --->
* onLayoutChange ---> onGlobalLayout ---> onFocusChanged
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// MyLogger.showLogWithLineNum(4, "onLayout");
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
// if(gainFocus){
// MyLogger.showLogWithLineNum(4, "onFocusChanged");
// }
}
// /** mFitsOnScreenRunnable是否已经运行完一遍 */
// private boolean mIsFitsOnScreenRun = false;
/** 判断Item是否充满ListView的Runnable */
// private Runnable mFitsOnScreenRunnable = new Runnable() {
// @Override
// public void run() {
// isItemsFitOnScreen();
// }
// };
/**
* 判断Item是否充满ListView
*/
private void isItemsFitOnScreen(){
if(mHeaderView.getPaddingTop() > - mHeaderViewHeight){
// 说明下拉了,不进行判断,防止出现误判。
return;
}
int last = getLastVisiblePosition();
if(last == getCount() - 1
&& getChildAt(last) != null
&& getChildAt(last).getBottom() < getHeight()) {
// It fits!(所有Item高度小于ListView高度)
mEnoughCount = false;
if(mFooterView != null && mFooterView.getVisibility() == VISIBLE){
MyLogger.showLogWithLineNum(4, "fitonScreen mEnoughCount = "+mEnoughCount);
// 判断处当前TotalItemHeight高度不够时,我们要移除FootView。
MyLogger.showLogWithLineNum(5,"TotalItemHeight高度不够 removeFooterView(endRootView);...");
removeFooterView();
}
} else {
// It doesn't fit...(所有Item高度大于/等于ListView高度)
mEnoughCount = true;
MyLogger.showLogWithLineNum(4, "fitonScreen mEnoughCount = "+mEnoughCount);
}
// mIsFitsOnScreenRun = true;
}
/**
*为了在滑到底部前,提前更改FooterView的显示内容或移除FooterView。
*/
@Override
public void onScroll(AbsListView pView, int pFirstVisibleItem,
int pVisibleItemCount, int pTotalItemCount) {
if(mListViewHeight == 0){
mListViewHeight = getMeasureHight(this);
}
// http://stackoverflow.com/questions/13572667/
// android-listview-detect-if-listview-data-fits-on-screen-without-scrolling
// this.post(mFitsOnScreenRunnable);
isItemsFitOnScreen();// 无需post执行此段代码
// isFitOnScreen()方法可以替代下面的这段代码。
// 假如一直采用getHeightByItems()和getMeasureHight()方法比较高度,滑动会很卡,
// 原因为两个方法不停的去Measure高度,这个很耗时,也很消耗资源,而且在UI线程执行。
// 故只在mFitsOnScreenRunnable还没运行过的情况下使用该方法。
// 请自行测试卡顿情况,谢谢!
// if(!mIsFitsOnScreenRun){
// int totalItemHeight = getTotalItemsHeight(this);
// if(mListViewHeight == 0){
// mListViewHeight = getMeasureHight(this);
// }
//
// if(totalItemHeight != -1 && mListViewHeight != 0){
// if(totalItemHeight >= mListViewHeight){
// mEnoughCount = true;
// }else{
// mEnoughCount = false;
// }
// }
// }
// 若没有设置加载更多监听器,此功能自动屏蔽。
if(mCanLoadMore && mLoadMoreListener == null){
mCanLoadMore = false;
}
// 由于添加了HeaderView且HeaderView一直都存在。
// 所以pTotalItemCount总数要在你设置的Data.size基础上先加一,
// 若开启加载更多,则pTotalItemCount再加一,也就是加上了FooterView个数。
// 当滑到顶部时,FirstVisibleItem实际是HeaderView,FirstVisibleItem值为0,
// 当最后一个Item(如果FooterView存在,那么FooterView为最后一个Item)显示时,
// 恰好pFirstVisibleItem+pVisibleItemCount = pTotalItemCount。
// 无论FooterView是否存在,请读者根据下面三行日志验证。
mFirstItemIndex = pFirstVisibleItem;
mLastItemIndex = pFirstVisibleItem + pVisibleItemCount;
mCount = pTotalItemCount;
// MyLogger.showLogWithLineNum(4, "pFirstVisibleItem = "+pFirstVisibleItem);
// MyLogger.showLogWithLineNum(4, "pVisibleItemCount = "+pVisibleItemCount);
// MyLogger.showLogWithLineNum(4, "pTotalItemCount = "+pTotalItemCount);
if(mFooterState == FOOTER_LOADING || mIsDataLoadFinished)
return;
if(mCanLoadMore && mEnoughCount){// 存在加载更多功能并且Item数量足够
if(getFooterViewsCount() == 0){
addFooterView();
MyLogger.showLogWithLineNum(5, "getFooterViewsCount() == 0 addFooterView");
}
if(mIsAutoLoadMore){// 自动加载更多,FootView显示 “加载中...”
// 改变完状态之后,就无需重复改变。
if(mFooterState != FOOTER_AUTO_LOAD_DONE){
mFooterState = FOOTER_AUTO_LOAD_DONE;
changeFootViewByState(true);
MyLogger.showLogWithLineNum(5, "滚动的时候显示 加载中。。。");
}
}else{// 手动加载更多,FootView显示 “点击加载”
// if判断原理同上
if(mFooterState != FOOTER_MANUAL_LOAD_DONE){
mFooterState = FOOTER_MANUAL_LOAD_DONE;
changeFootViewByState(true);
MyLogger.showLogWithLineNum(5, "滚动的时候显示 点击加载");
}
}
}else if(mFooterView != null && mFooterView.getVisibility() == VISIBLE){
// 突然关闭加载更多功能之后,我们要移除FootView。
MyLogger.showLogWithLineNum(5,"滚动的时候 removeFooterView(endRootView);...");
removeFooterView();
}
}
/**
* 当关闭加载更多后,移除FooterView。
*/
private void removeFooterView(){
changeFooterViewStateByIsAutoLoadMore(false);
mFooterView.setVisibility(View.GONE);
this.post(new Runnable() {
@Override
public void run() {
PtrListView.this.removeFooterView(mFooterView);
PtrListView.this.requestLayout();
}
});
}
/**
*判断是否到达底部并且是否可以加载更多。
*/
@Override
public void onScrollStateChanged(AbsListView pView, int pScrollState) {
if(mIsDataLoadFinished)
return;
if(mCanLoadMore && mEnoughCount){// 存在加载更多功能并且Item数量足够
if (mLastItemIndex == mCount && pScrollState == SCROLL_STATE_IDLE) {
// 加载更多已经执行完毕
if (mFooterState != FOOTER_LOADING) {
if(mIsAutoLoadMore){// 自动加载更多,我们让FootView显示 “更 多”
if(mCanRefresh){
// 存在下拉刷新并且HeadView没有正在刷新时,FootView可以自动加载更多。
if(mHeaderState != REFRESHING){
onLoadMore();
}
}else{// 没有下拉刷新,直接进行加载更多。
onLoadMore();
}
}
}
}
}
}
/**
* 改变加载更多状态
*/
private void changeFootViewByState(boolean isFooterViewVisible) {
// 允许加载更多
switch (mFooterState) {
case FOOTER_LOADING:// 刷新中
// 加载中...
if (mFooterLoadTipsTextView.getText().equals(R.string.p2refresh_doing_end_refresh)) {
break;
}
mFooterLoadTipsTextView.setText(R.string.p2refresh_doing_end_refresh);
mFooterLoadTipsTextView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar.setVisibility(View.VISIBLE);
break;
case FOOTER_MANUAL_LOAD_DONE:// 手动刷新完成
// 点击加载
mFooterLoadTipsTextView.setText(R.string.p2refresh_end_click_load_more);
mFooterLoadTipsTextView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar.setVisibility(View.GONE);
if(isFooterViewVisible){
mFooterView.setVisibility(View.VISIBLE);
}else{
mFooterView.setVisibility(View.GONE);
}
break;
case FOOTER_AUTO_LOAD_DONE:// 自动刷新完成
// 加载中...
mFooterLoadTipsTextView.setText(R.string.p2refresh_doing_end_refresh);
mFooterLoadTipsTextView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar.setVisibility(View.VISIBLE);
if(isFooterViewVisible){
mFooterView.setVisibility(View.VISIBLE);
}else{
mFooterView.setVisibility(View.GONE);
}
break;
}
}
/**
*原作者的,我没改动,请读者自行优化。
*/
public boolean onTouchEvent(MotionEvent event) {
if (mCanRefresh) {
if(mCanLoadMore && mFooterState == FOOTER_LOADING){
// 如果存在加载更多功能,并且当前正在加载更多,默认不允许下拉刷新,必须加载完毕后才能使用。
return super.onTouchEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mFirstItemIndex == 0 && !mIsRecored) {
mIsRecored = true;
mStartY = (int) event.getY();
}
break;
case MotionEvent.ACTION_UP:
if (mHeaderState != REFRESHING) {
if (mHeaderState == DONE) {
}
if (mHeaderState == PULL_TO_REFRESH) {
mHeaderState = DONE;
changeHeaderViewByState();
}
if (mHeaderState == RELEASE_TO_REFRESH) {
mHeaderState = REFRESHING;
changeHeaderViewByState();
onRefresh();
}
}
mIsRecored = false;
mIsBack = false;
break;
case MotionEvent.ACTION_MOVE:
final int tempY = (int) event.getY();
// 当前可见的Item不是第一个,此时我们开始下拉,当第一个Item显示时,触发此代码段。
// 开始纪录下拉的Y坐标,Header即将显示!
if (!mIsRecored && mFirstItemIndex == 0) {
mIsRecored = true;
mStartY = tempY;
}
if (mHeaderState != REFRESHING && mIsRecored) {
// 保证在设置padding的过程中,当前的位置一直是在head,
// 否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动
// 可以松手去刷新了
if (mHeaderState == RELEASE_TO_REFRESH) {
setSelection(0);
// 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步
if (((tempY - mStartY) / RATIO < mHeaderViewHeight)
&& (tempY - mStartY) > 0) {
mHeaderState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
// 一下子推到顶了
else if (tempY - mStartY <= 0) {
mHeaderState = DONE;
changeHeaderViewByState();
}
// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步
}
// 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态
if (mHeaderState == PULL_TO_REFRESH) {
setSelection(0);
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - mStartY) / RATIO >= mHeaderViewHeight) {
mHeaderState = RELEASE_TO_REFRESH;
mIsBack = true;
changeHeaderViewByState();
} else if (tempY - mStartY <= 0) {
mHeaderState = DONE;
changeHeaderViewByState();
}
}
if (mHeaderState == DONE) {
if (tempY - mStartY > 0) {
mHeaderState = PULL_TO_REFRESH;
changeHeaderViewByState();
}
}
if (mHeaderState == PULL_TO_REFRESH) {
mHeaderView.setPadding(0, (tempY - mStartY) / RATIO - mHeaderViewHeight, 0, 0);
}
if (mHeaderState == RELEASE_TO_REFRESH) {
int tryPadding = (tempY - mStartY) / RATIO - mHeaderViewHeight;
int maxPullDistance = mListViewHeight / 4;
if(mMaxPullDistance > 0){
maxPullDistance = mMaxPullDistance;
}
// 若HeaderView底部和ListView顶部之间的距离 超过ListView高度的四分之一,我们不对HeaderView做Padding处理。
if(mListViewHeight > 0 && (mHeaderView.getPaddingTop() + mHeaderViewHeight) > maxPullDistance){
if((tempY - mLastMoveX) < 0 && mHeaderView.getPaddingTop() > 0){
mHeaderView.setPadding(0, tryPadding, 0, 0);
}
}else{
mHeaderView.setPadding(0, tryPadding, 0, 0);
mLastMoveX = tempY;
}
}
}
break;
}
}
return super.onTouchEvent(event);
}
/**
* 当HeadView状态改变时候,调用该方法,以更新界面
*/
private void changeHeaderViewByState() {
switch (mHeaderState) {
case RELEASE_TO_REFRESH:
mArrowImageView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
mTipsTextView.setVisibility(View.VISIBLE);
mLastUpdatedTextView.setVisibility(View.VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mArrowAnim);
// 松开刷新
mTipsTextView.setText(R.string.p2refresh_release_refresh);
break;
case PULL_TO_REFRESH:
mProgressBar.setVisibility(View.GONE);
mTipsTextView.setVisibility(View.VISIBLE);
mLastUpdatedTextView.setVisibility(View.VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(View.VISIBLE);
// 是由RELEASE_TO_REFRESH状态转变来的
if (mIsBack) {
mIsBack = false;
mArrowImageView.clearAnimation();
mArrowImageView.startAnimation(mArrowReverseAnim);
// 下拉刷新
mTipsTextView.setText(R.string.p2refresh_pull_to_refresh);
} else {
// 下拉刷新
mTipsTextView.setText(R.string.p2refresh_pull_to_refresh);
}
break;
case REFRESHING:
mHeaderView.setPadding(0, 0, 0, 0);
mProgressBar.setVisibility(View.VISIBLE);
mArrowImageView.clearAnimation();
mArrowImageView.setVisibility(View.GONE);
// 正在刷新...
mTipsTextView.setText(R.string.p2refresh_doing_head_refresh);
mLastUpdatedTextView.setVisibility(View.VISIBLE);
break;
case DONE:
mHeaderView.setPadding(0, -1 * mHeaderViewHeight, 0, 0);
mProgressBar.setVisibility(View.GONE);
mArrowImageView.clearAnimation();
mArrowImageView.setImageResource(R.drawable.arrow);
// 下拉刷新
mTipsTextView.setText(R.string.p2refresh_pull_to_refresh);
mLastUpdatedTextView.setVisibility(View.VISIBLE);
break;
}
}
/**
* 下拉刷新监听接口
*/
public interface OnRefreshListener {
public void onRefresh();
}
/**
* 加载更多监听接口
*/
public interface OnLoadMoreListener {
public void onLoadMore();
}
public void setOnRefreshListener(OnRefreshListener pRefreshListener) {
if(pRefreshListener != null){
mRefreshListener = pRefreshListener;
mCanRefresh = true;
}
}
public void setOnLoadListener(OnLoadMoreListener pLoadMoreListener) {
if(pLoadMoreListener != null){
mLoadMoreListener = pLoadMoreListener;
mCanLoadMore = true;
if(mCanLoadMore && getFooterViewsCount() == 0){
addFooterView();
}
}
}
/**
* 正在下拉刷新
*/
private void onRefresh() {
if (mRefreshListener != null) {
mRefreshListener.onRefresh();
}
}
/**
* 下拉刷新完成
*/
public void onRefreshComplete() {
// 下拉刷新后是否显示第一条Item
if(mIsMoveToFirstItemAfterRefresh)setSelection(0);
mHeaderState = DONE;
// 最近更新: Time
mLastUpdatedTextView.setText(
getResources().getString(R.string.p2refresh_refresh_lasttime) +
new SimpleDateFormat(DATE_FORMAT_STR, Locale.CHINA).format(new Date()));
changeHeaderViewByState();
// 如果数据加载完成了,那么下拉刷新之后,应该把标志位置为false,允许加载更多。
if(mIsDataLoadFinished){
mIsDataLoadFinished = false;
if(mCanLoadMore){
changeFooterViewStateByIsAutoLoadMore(true);
}
}
}
/**
* 正在加载更多,FootView显示 :加载中...
*/
private void onLoadMore() {
mFooterState = FOOTER_LOADING;
// 加载中...
mFooterLoadTipsTextView.setText(R.string.p2refresh_doing_end_refresh);
mFooterLoadTipsTextView.setVisibility(View.VISIBLE);
mFooterLoadProgressBar.setVisibility(View.VISIBLE);
if (mLoadMoreListener != null) {
mLoadMoreListener.onLoadMore();
}
}
/**
* 加载更多完成
*/
public void onLoadMoreComplete() {
if(mIsAutoLoadMore){
mFooterState = FOOTER_AUTO_LOAD_DONE;
}else{
mFooterState = FOOTER_MANUAL_LOAD_DONE;
}
changeFootViewByState(true);
}
/**
* 主要更新一下刷新时间啦!
* @param adapter
*/
public void setAdapter(BaseAdapter adapter) {
// 最近更新: Time
mLastUpdatedTextView.setText(
getResources().getString(R.string.p2refresh_refresh_lasttime) +
new SimpleDateFormat(DATE_FORMAT_STR, Locale.CHINA).format(new Date()));
super.setAdapter(adapter);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment