您的位置:首页 > 其它

针对自定义组件上拉刷新下拉加载更多PullToRefreshView的分析(二)

2013-08-09 14:40 579 查看
第二篇要从触摸事件讲起,前面已经可以看到各个子view已经加载进来,并且做了一些初始化,于是接下来,就是对用户输入做一些判断操作,让view可以跟着用户手势进行相应的改变。

转载请注明出处:/article/9484999.html

当你看见手指拖着列表往下拉的时候,头部的view会慢慢往下滑而不是一下子就跳出来,这个也是根据子view距离父view的距离来做相应的判断然后让view一点点的出来的,可以说这里面的操作大部分都跟距离位置相关,这也是值得学习的地方,因为细节的一些不同才能更加做出体验自然的应用。

这里需要对触摸手势做出反应,需要重写两个方法,分别是onInterceptTouchEvent(MotionEvent e)和onTouchEvent(MotionEvent event),onInterceptTouchEvent()用于处理事件并改变事件的传递方向,系统会根据它的返回值决定是把接下来的触摸事件让给自己的onTouchEvent()还是子view的onTouchEvent()来处理,而onTouchEvent()
用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。对这一块有疑问的,可以参考这个链接所进行的解释:/article/5556616.html

搞懂了上面的内容,接下来的事情就好办了,我们只需要对MotionEvent是上下还是移动还是取消进行相应的判断并进行一些view的改动就能达到我们的目的。

首先我们来看下onInterceptTouchEvent()这个方法:

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int y = (int) e.getRawY();
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// 首先拦截down事件,记录y坐标
mLastMotionY = y;
break;

case MotionEvent.ACTION_MOVE:
// deltaY > 0 是向下运动,< 0是向上运动
int deltaY = y - mLastMotionY;
if (isRefreshViewScroll(deltaY)) {
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return false;
}
可以看到,第一句用int y = (int) e.getRawY();得到了触摸事件触发时第一个点的Y坐标值,并且用e.getAction()得到了MotionEvent事件的值,很显然,第一个事件必然就是MotionEvent.ACTION_DOWN,记录了起始Y坐标之后,返回false,到第二次事件为MotionEvent.ACTION_MOVE的时候,调用了isRefreshViewScroll()来判断列表是滑到了顶部还是底部,这样就可以根据它的返回值来确定接下来是不是该让父view也就是PullToRefreshView中的两个refreshView进行滑动了。如果是isRefreshViewScroll()返回真,则onInterceptTouchEvent()
也返回真,那就该由父view的onTouchEvent() 处理接下来触摸手势事件。

看下isRefreshViewScroll()的部分关键代码

//返回屏幕上可见的第一个view在adapter中的位置&&返回adapterview中第一个view的上边界与父view之间的像素距离
if (mAdapterView.getFirstVisiblePosition() == 0
&& child.getTop() == 0) {
mPullState = PULL_DOWN_STATE;//设置下拉状态为正在下拉
return true;//返回true,这个时候父view的onTouchEvent开始接受触摸事件
}
注释里面已经讲得比较清楚了,接下来它如果返回true的话就到了父view的onTouchEvent() 来处理下拉或者上拉事件了。

来看下onTouchEvent() 中的这部分代码

case MotionEvent.ACTION_MOVE:
int deltaY = y - mLastMotionY;
if (mPullState == PULL_DOWN_STATE) {
// PullToRefreshView执行下拉
Log.i(TAG, " pull down!parent view move!");
headerPrepareToRefresh(deltaY);
// setHeaderPadding(-mHeaderViewHeight);
} else if (mPullState == PULL_UP_STATE) {
// PullToRefreshView执行上拉
Log.i(TAG, "pull up!parent view move!");
footerPrepareToRefresh(deltaY);
}
mLastMotionY = y;
break;
这里的mPullState是之前在isRefreshViewScroll()中已经被赋值了,这里进行判断是上拉还是下拉,从而进行相应的刷新操作。

来看下headerPrepareToRefresh()做了些什么,可以看到其实是对header 的refreshView进行一些变动,改隐藏的隐藏该显示的显示,比如从“下拉刷新”的文字变成“松开手刷新”之类的,以及对那个箭头的动画启动。

private void headerPrepareToRefresh(int deltaY) {
int newTopMargin = changingHeaderViewTopMargin(deltaY);
// 当header view的topMargin>=0时,说明已经完全显示出来了,修改header view 的提示状态
if (newTopMargin >= 0 && mHeaderState != RELEASE_TO_REFRESH) {
mHeaderTextView.setText(R.string.pull_to_refresh_release_label);
mHeaderUpdateTextView.setVisibility(View.VISIBLE);
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
mHeaderState = RELEASE_TO_REFRESH;
} else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {// 拖动时没有释放
mHeaderImageView.clearAnimation();
mHeaderImageView.startAnimation(mFlipAnimation);
// mHeaderImageView.
mHeaderTextView.setText(R.string.pull_to_refresh_pull_label);
mHeaderState = PULL_TO_REFRESH;
}
}
上面代买中changingHeaderViewTopMargin(int deltaY) 起到了下拉的弹簧效果,实际上市根据你下拉的距离来乘以小于1的正数来让你感觉拉着有感觉,这里面还做了限制上拉下拉同时触发的情况,这里可以自己注释掉那个判断去实验一下。

当我们的newTopMargin大于等于0也就是表示那个refreshView已经完全显示出来了,就可以放开手进行刷新操作了,于是下面的事件就是判断是上拉刷新还是下拉刷新,或者是refreshView还没有完全显示出来就进行了释放操作是不会进行刷新的,而是隐藏refreshView,然后用invalidate();告知UI线程进行界面刷新。

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
int topMargin = getHeaderTopMargin();
if (mPullState == PULL_DOWN_STATE) {
if (topMargin >= 0) {
// 开始刷新
headerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
} else if (mPullState == PULL_UP_STATE) {
if (Math.abs(topMargin) >= mHeaderViewHeight
+ mFooterViewHeight) {
// 开始执行footer 刷新
footerRefreshing();
} else {
// 还没有执行刷新,重新隐藏
setHeaderTopMargin(-mHeaderViewHeight);
}
}
break;
我们来看下刷新操作

private void headerRefreshing() {
mHeaderState = REFRESHING;
setHeaderTopMargin(0);
mHeaderImageView.setVisibility(View.GONE);
mHeaderImageView.clearAnimation();
mHeaderImageView.setImageDrawable(null);
mHeaderProgressBar.setVisibility(View.VISIBLE);
mHeaderTextView.setText(R.string.pull_to_refresh_refreshing_label);
if (mOnHeaderRefreshListener != null) {
mOnHeaderRefreshListener.onHeaderRefresh(this);
}
}

这里就直接设置了这个refreshView的各种组件的状态,并用mOnHeaderRefreshListener.onHeaderRefresh(this);(在TestListView中进行了重写的回调方法),去调用onHeaderRefreshComplete()进行相应的刷新完成之后的恢复操作。

至此,整个PullToRefreshView的自定义组件的概念就出来了,相信以后遇到此种类似组件的时候,也能够达到举一反三的效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐