Android打造(ListView、GridView等)通用的下拉刷新、上拉自动加

原理都虽然简单,但是实现起来却也是会有很多小麻烦。这里没有采用通过设置onTouchListener的方法,因此使用这个方式在下拉的时候依然会出现ListView的最顶部的"HOLD"视图,,不太爽。这种实现方法也蛮简单的,具体看郭神的博客 Android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能。

下拉刷新基本原理

基本原理就是在用户滑动屏幕上的组件时,在onInterceptTouchEvent方法中判断是否到了ContentView (这里我们以ListView为例来说明)的最顶端,如果到了最顶端且用户还继续向下滑,那么会拦截触摸事件避免它分发到ListView,即在onInterceptTouchEvent中返回true ( 不太清楚的可以参考资料如下 :Android Touch事件分发过程。),这样就将触摸事件分发到了onTouchEvent函数中,我们对于用户触摸事件的处理逻辑主要都在这个函数中。如果该函数返回false,那么触摸事件则会分发给其Child View,这里的这个Child View就是ListView了,当返回false时用户滑动屏幕时就会滚动ListView。

/** 在适当的时候拦截触摸事件,这里指的适当的时候是当mContentView滑动到顶部,并且是下拉时拦截触摸事件,否则不拦截,交给其child* view 来处理。* @see* android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {/** This method JUST determines whether we want to intercept the motion.* If we return true, onTouchEvent will be called and we do the actual* scrolling there.*/final int action = MotionEventCompat.getActionMasked(ev);// Always handle the case of the touch gesture being complete.if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {// Do not intercept touch event, let the child handle itreturn false;}switch (action) {case MotionEvent.ACTION_DOWN:mYDown = (int) ev.getRawY();break;case MotionEvent.ACTION_MOVE:// int yDistance = (int) ev.getRawY() – mYDown;mYDistance = (int) ev.getRawY() – mYDown;showStatus(mCurrentStatus);Log.d(VIEW_LOG_TAG, "%%% isBottom : " + isBottom() + ", isTop : " + isTop()+ ", mYDistance : " + mYDistance);// 如果拉到了顶部, 并且是下拉,则拦截触摸事件,从而转到onTouchEvent来处理下拉刷新事件if ((isTop() && mYDistance > 0)|| (mYDistance > 0 && mCurrentStatus == STATUS_REFRESHING)) {return true;}break;}// Do not intercept touch event, let the child handle itreturn false;}

首先我们在ACTION_DOWN事件中记录用户按下的触摸点的Y轴坐标mYDown,然后在ACTION_MOVE中再次获取Y轴的坐标,计算出两者之间的差值。如果滑动的差值大于mTouchSlop则继续进行处理,mTouchSlop为判断一个触摸滑动事件是否有效的的最小阀值,如果小于这个阀值我们认为这个触摸滑动事件无效,例如手抖了一下,距离很短,因此我们忽略类似的事件。

在有效的滑动距离之内,我们判断当前组件的状态,如果不是正在刷新的状态,那么我们根据当前ListView的paddingTop的高度来设置不同的值,paddingTop如果高度大于ListView高度的70%,那么我们将当前状态设置为“释放可刷新”状态,即STATUS_RELEASE_TO_REFRESH状态;反之,我们设置状态为“继续下拉”状态,即“STATUS_PULL_TO_REFRESH”。通过这个paddingTop高度我们来规定当用户下拉距离到一定的距离后才出发刷新操作,否则视为无效下拉。然而不管这个时候是什么状态,我们都会修改Header的padding Top属性,从而达到拉伸header的效果。

当状态为“释放可刷新”时,我们抬起手指,会出发ACTION_UP事件,此时我们在该事件中进行下拉刷新操作。onTouchEvent代码如下 :

/** 在这里处理触摸事件以达到下拉刷新或者上拉自动加载的问题* @see android.view.View#onTouchEvent(android.view.MotionEvent)*/@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(VIEW_LOG_TAG, "@@@ onTouchEvent : action = " + event.getAction());switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mYDown = (int) event.getRawY();Log.d(VIEW_LOG_TAG, "#### ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.d(VIEW_LOG_TAG, "#### ACTION_MOVE");int currentY = (int) event.getRawY();mYDistance = currentY – mYDown;// 高度大于header view的高度才可以刷新if (mYDistance >= mTouchSlop) {if (mCurrentStatus != STATUS_REFRESHING) {//if (mHeaderView.getPaddingTop() > mHeaderViewHeight * 0.7f) {mCurrentStatus = STATUS_RELEASE_TO_REFRESH;mTipsTextView.setText(R.string.pull_to_refresh_release_label);} else {mCurrentStatus = STATUS_PULL_TO_REFRESH;mTipsTextView.setText(R.string.pull_to_refresh_pull_label);}}rotateHeaderArrow();int scaleHeight = (int) (mYDistance * 0.8f);// 去了滑动距离的80%,减小灵敏度而已// Y轴的滑动距离小于屏幕高度4分之一时才会拉伸header,反之保持不变if (scaleHeight <= mScrHeight / 4) {adjustHeaderPadding(scaleHeight);}}break;case MotionEvent.ACTION_UP:// 下拉刷新的具体操作doRefresh();break;default:break;}return true;}抬起手指时出发的刷新操作,代码如下:

/*** 执行刷新操作*/private final void doRefresh() {if (mCurrentStatus == STATUS_RELEASE_TO_REFRESH) {// 设置状态为正在刷新状态mCurrentStatus = STATUS_REFRESHING;mArrowImageView.clearAnimation();// 隐藏header中的箭头图标mArrowImageView.setVisibility(View.GONE);// 设置header中的进度条可见mHeaderProgressBar.setVisibility(View.VISIBLE);// 设置一些文本mTimeTextView.setText(R.string.pull_to_refresh_update_time_label);SimpleDateFormat sdf = new SimpleDateFormat();mTimeTextView.append(sdf.format(new Date()));//mTipsTextView.setText(R.string.pull_to_refresh_refreshing_label);// 执行回调if (mPullRefreshListener != null) {mPullRefreshListener.onRefresh();}// 使headview 正常显示, 直到调用了refreshComplete后再隐藏new HeaderViewHideTask().execute(0);} else {// 隐藏header viewadjustHeaderPadding(-mHeaderViewHeight);}} /** 滚动事件,实现滑动到底部时上拉加载更多* @see android.widget.AbsListView.OnScrollListener#onScroll(android.widget.* AbsListView, int, int, int)*/@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,int totalItemCount) {Log.d(VIEW_LOG_TAG, "&&& mYDistance = " + mYDistance);if (mFooterView == null || mYDistance >= 0 || mCurrentStatus == STATUS_LOADING|| mCurrentStatus == STATUS_REFRESHING) {return;}loadmore();}/*** 下拉到底部时加载更多*/private void loadmore() {if (isShowFooterView() && mLoadMoreListener != null) {mFooterTextView.setText(R.string.pull_to_refresh_refreshing_label);mFooterProgressBar.setVisibility(View.VISIBLE);adjustFooterPadding(0);mCurrentStatus = STATUS_LOADING;mLoadMoreListener.onLoadMore();}}其中loadmore函数中调用的isShowFooterView函数就是用来判断是否到了最底部的,代码如下 :

转动心中的期待,血在澎湃,吃苦流汗算什么。

Android打造(ListView、GridView等)通用的下拉刷新、上拉自动加

相关文章:

你感兴趣的文章:

标签云: