ViewPager部分源码分析三:scroll
手指在屏幕上滑动,触发到onTouchEvent(),执行case MotionEvent.ACTION_MOVE: 。。。
public boolean onTouchEvent(MotionEvent ev) {
if (mFakeDragging) {
// A fake drag is in progress already, ignore this real one
// but still eat the touch events.
// (It is likely that the user is multi-touching the screen.)
return true;
} if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
return false;
} if (mAdapter == null || mAdapter.getCount() == 0) {
// Nothing to present or scroll; nothing to touch.
return false;
} if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev); final int action = ev.getAction();
boolean needsInvalidate = false; switch (action & MotionEventCompat.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mScroller.abortAnimation();
mPopulatePending = false;
populate(); // Remember where the motion event started
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
if (pointerIndex == -1) {
// A child has consumed some touch events and put us into an inconsistent state.
needsInvalidate = resetTouch();
break;
}
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true); // Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = MotionEventCompat.findPointerIndex(
ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
needsInvalidate |= performDrag(x);
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
velocityTracker, mActivePointerId);
mPopulatePending = true;
final int width = getClientWidth();
final int scrollX = getScrollX();
final ItemInfo ii = infoForCurrentScrollPosition();
final float marginOffset = (float) mPageMargin / width;
final int currentPage = ii.position;
final float pageOffset = (((float) scrollX / width) - ii.offset)
/ (ii.widthFactor + marginOffset);
final int activePointerIndex =
MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float x = MotionEventCompat.getX(ev, activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity); needsInvalidate = resetTouch();
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
scrollToItem(mCurItem, true, 0, false);
needsInvalidate = resetTouch();
}
break;
case MotionEventCompat.ACTION_POINTER_DOWN: {
final int index = MotionEventCompat.getActionIndex(ev);
final float x = MotionEventCompat.getX(ev, index);
mLastMotionX = x;
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
}
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionX = MotionEventCompat.getX(ev,
MotionEventCompat.findPointerIndex(ev, mActivePointerId));
break;
}
if (needsInvalidate) {
ViewCompat.postInvalidateOnAnimation(this);
}
return true;
}
onTouchEvent(MotionEvent ev)
--> performDrag()
private boolean performDrag(float x) {
boolean needsInvalidate = false; final float deltaX = mLastMotionX - x;
mLastMotionX = x; float oldScrollX = getScrollX();
float scrollX = oldScrollX + deltaX;
final int width = getClientWidth(); float leftBound = width * mFirstOffset;
float rightBound = width * mLastOffset;
boolean leftAbsolute = true;
boolean rightAbsolute = true; final ItemInfo firstItem = mItems.get(0);
final ItemInfo lastItem = mItems.get(mItems.size() - 1);
if (firstItem.position != 0) {
leftAbsolute = false;
leftBound = firstItem.offset * width;
}
if (lastItem.position != mAdapter.getCount() - 1) {
rightAbsolute = false;
rightBound = lastItem.offset * width;
} if (scrollX < leftBound) {
if (leftAbsolute) {
float over = leftBound - scrollX;
needsInvalidate = mLeftEdge.onPull(Math.abs(over) / width);
}
scrollX = leftBound;
} else if (scrollX > rightBound) {
if (rightAbsolute) {
float over = scrollX - rightBound;
needsInvalidate = mRightEdge.onPull(Math.abs(over) / width);
}
scrollX = rightBound;
}
// Don't lose the rounded component
mLastMotionX += scrollX - (int) scrollX;
scrollTo((int) scrollX, getScrollY());
pageScrolled((int) scrollX); return needsInvalidate;
}
performDrag(float x)
--> pageScrolled()
private boolean pageScrolled(int xpos) {
if (mItems.size() == 0) {
mCalledSuper = false;
onPageScrolled(0, 0, 0);
if (!mCalledSuper) {
throw new IllegalStateException(
"onPageScrolled did not call superclass implementation");
}
return false;
}
final ItemInfo ii = infoForCurrentScrollPosition();
final int width = getClientWidth();
final int widthWithMargin = width + mPageMargin;
final float marginOffset = (float) mPageMargin / width;
final int currentPage = ii.position;
final float pageOffset = (((float) xpos / width) - ii.offset) /
(ii.widthFactor + marginOffset);
final int offsetPixels = (int) (pageOffset * widthWithMargin); mCalledSuper = false;
onPageScrolled(currentPage, pageOffset, offsetPixels);
if (!mCalledSuper) {
throw new IllegalStateException(
"onPageScrolled did not call superclass implementation");
}
return true;
}
pageScrolled(int xpos)
--> onPageScrolled()
protected void onPageScrolled(int position, float offset, int offsetPixels) {
// Offset any decor views if needed - keep them on-screen at all times.
if (mDecorChildCount > 0) {
final int scrollX = getScrollX();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
final int width = getWidth();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) continue; final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int childLeft = 0;
switch (hgrav) {
default:
childLeft = paddingLeft;
break;
case Gravity.LEFT:
childLeft = paddingLeft;
paddingLeft += child.getWidth();
break;
case Gravity.CENTER_HORIZONTAL:
childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
paddingLeft);
break;
case Gravity.RIGHT:
childLeft = width - paddingRight - child.getMeasuredWidth();
paddingRight += child.getMeasuredWidth();
break;
}
childLeft += scrollX; final int childOffset = childLeft - child.getLeft();
if (childOffset != 0) {
child.offsetLeftAndRight(childOffset);
}
}
} dispatchOnPageScrolled(position, offset, offsetPixels); if (mPageTransformer != null) {
final int scrollX = getScrollX();
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.isDecor) continue;
final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
mPageTransformer.transformPage(child, transformPos);
}
} mCalledSuper = true;
}
onPageScrolled(int position, float offset, int offsetPixels)
--> dispatchOnPageScrolled() 处理OnPageChangeListener
private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {
if (mOnPageChangeListener != null) {
mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
}
if (mOnPageChangeListeners != null) {
for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
OnPageChangeListener listener = mOnPageChangeListeners.get(i);
if (listener != null) {
listener.onPageScrolled(position, offset, offsetPixels);
}
}
}
if (mInternalPageChangeListener != null) {
mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
}
}
dispatchOnPageScrolled(int position, float offset, int offsetPixels)
--> onPageScrolled()中,执行完dispatchOnPageScrolled()之后,对mPageTransformer处理的代码是进行动画处理,因此自定义的动画类(继承PageTransformer,并实现transformPage()方法)在这里得到执行。可以看到,通过for循环对每一个child都进行动画处理。
在分析这个过程是,对scrollX不太明白,就对原来的一个例子(Android Tab -- 使用ViewPager、Fragment、FragmentPagerAdapter来实现)作了下调整,进行跟踪分析。
-->
根据跟踪到的数据对onPageScrolled(int position, float positionOffset, int positionOffsetPixels)进行分析:
先看看scrollX:
scrollX = 已经滑过去的标签宽度之和(old scrollX,其实就是对应图中标签1的宽度) + 本次滑动的宽度
本次滑动的宽度 = 当前左边显示的标签的宽度(child的width) - 当前左边显示的标签剩下的宽度(fragment2滑动后剩下的宽度)
再来说三个参数:
position: 当前左边显示的标签位置(从0开始),图片对应的是1。
positionOffset: 本次滑动的宽度 / 当前左边显示的标签的宽度(child的width)
positionOffsetSetPixels: 本次滑动的宽度
如果没有重写PagerAdapter.getPageWidth()方法,就可以简单按以上对三个参数参数的理解。
然而,但重写了PagerAdapter.getPageWidth()方法后发现不太一样了!!
public float getPageWidth(int position) {
if(position == 2)
return 1.f;
return 0.4f;
}
除了Fragment3是1倍宽度,其他的都是0.4倍宽度。
-->
在这种情况下得到的:
scrollX:同上一种情况,依旧是每次滑动距离之和。
position:同上一种情况,依旧是当前最左边标签的序号。
positionOffset:依旧可以简单的理解为:本次滑动的宽度 / 当前左边显示的标签的宽度(child的width),对应图片中:Fragment1滑动的距离 / Fragment1的宽度
但是,
positionOffsetSetPixels: 并不是 本次滑动的宽度, 而是positionOffset反映到ViewPager的宽度上,按源码的意思是应该就是 positionOffset * getClientWidth() 。
当然,源码还有对mPageMargin即page之间的间距的处理,本示例为0。
因此positionOffsetSetPixels并不是327(494 - 167),而是817(跟踪到的数据,嘿嘿。。。)
ViewPager部分源码分析三:scroll的更多相关文章
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
- tomcat源码分析(三)一次http请求的旅行-从Socket说起
p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...
- ABP源码分析三:ABP Module
Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...
- ABP源码分析三十一:ABP.AutoMapper
这个模块封装了Automapper,使其更易于使用. 下图描述了改模块涉及的所有类之间的关系. AutoMapAttribute,AutoMapFromAttribute和AutoMapToAttri ...
- ABP源码分析三十三:ABP.Web
ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在A ...
- ABP源码分析三十四:ABP.Web.Mvc
ABP.Web.Mvc模块主要完成两个任务: 第一,通过自定义的AbpController抽象基类封装ABP核心模块中的功能,以便利的方式提供给我们创建controller使用. 第二,一些常见的基础 ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
- Duilib源码分析(三)XML解析器—CMarkup
上一节介绍了控件构造器CDialogBuilder,接下来将分析其XML解析器CMarkup: CMarkup:xml解析器,目前内置支持三种编码格式:UTF8.UNICODE.ASNI,默认为UTF ...
- Django搭建及源码分析(三)---+uWSGI+nginx
每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...
随机推荐
- Android开发学习笔记--计时器的应用实例
为了了解安卓计时器的用法,写了一个秒表的应用,正是这个秒表,让我对Android应用的速度大跌眼镜,我设置了一个计时器,10ms更新一次显示的时间,然后更标准的时间一比较发现,跑10s就有一秒的时间误 ...
- sqlserver2008清日志
use [DB Name] Select NAME,size From sys.database_files GO ALTER DATABASE [DB Name] SET RECOVERY SIMP ...
- 2016移动端web5分钟速成(适合新手)
http://www.w3cfuns.com/notes/20813/fecbb840a2574cf712a8625f88a7ab3a.html
- 2016年11月6日--form表单验证和事件、正则表达式
1.表单验证<form></form> (1).非空验证(去空格) (2).对比验证(跟一个值对比) (3).范围验证(根据一个范围进行判断) (4).固定格式验证:电话号码, ...
- 把ZenCart在线商店搭建到本地
前些日子,要给在线的zencart商店做一些小改动,需要安装一个插件.大家都知道,zencart有很多插件选用,兼容性也好坏不一,直接在正在运营的网站程序上修改,难免会影响到客户体验,出什么差错了代价 ...
- Bloom Filter 概念和原理
Bloom filter 是由 Howard Bloom 在 1970 年提出的二进制向量数据结构,它具有很好的空间和时间效率,被用来检测一个元素是不是集合中的一个成员.如果检测结果为是,该元素不一定 ...
- tornado RequestHandler request.body & request.arguments
request.body , 请求的原始内容,post方式放在body中的. request.arguments, body参数和url参数的统一体, 同时也是经过“加工”,解码的. eg.在对接其他 ...
- git 忽略文件权限
git config --add core.filemode false 发现.git/config 中新增了一行: [core] ... filemode = false ref: http://b ...
- HTML页面的布局
1.纵向切分页面:CSS POSITION的默认值为:STATIC 1) <html> <head> <title>Hello</title> < ...
- PHP-redis中文文档
phpredis是php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系 很有用;以下是redis官方提供的命令使用技巧: 下载地址如下: https://github.com/wi ...