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
每个框架或者应用都是为了解决某些问题才出现旦生的,没有一个事物是可以解决所有问题的.如果觉得某个框架或者应用使用很不方便,那么很有可能就是你没有将其使用到正确的地方,没有按开发者的设计初衷来使用它,当 ...
随机推荐
- 【转】Intel RealSense(实感技术)概览
Intel RealSense(实感技术)概览 1 Reply 版权声明:本文系本站作者自己翻译整理,欢迎转载,但转载请以超链接形式注明文章来源(planckscale.info).作者信息和本声明, ...
- vSphere、Hyper-V与XenServer 你选哪个?
vSphere.Hyper-V与XenServer 你选哪个? 当在VMware vSphere.Microsoft Hyper-V与Citrix Systems XenServer之间做出选择时,v ...
- PingUtil in Android
Ping a host in Android:“ping -c 1 127.0.0.1”-c 1: The ping times. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 ...
- 如何给zencart安装image handler插件?
以下内容均为个人的工作总结,有错误的理解都很正常,所以提醒您,可以参考,但是由此造成的一切后果,本人概不负责. 1 去zencart官网下载一个插件包(注意版本是否匹配相应的zencart版本,我的是 ...
- IDEA 新建文件默认加入CVS
是要先add,不过可以设置创建的文件都默认 add的.修改默认值看下图:打开系统设置,找到 Version Control 设置选项: 在 When files are created 选项中选择第二 ...
- java前后台之间传值的几种方式
自己写的代码太少,有时候前后台传值还写的不是很熟练,现在总结一下,加深下印象. 1.jquery的Ajax传值 ---->前台到后台 期望功能:把前台用户输入的信息保存在数据库里. 前台jsp代 ...
- Android SDK 在线更新镜像服务器
大连东软信息学院镜像服务器地址:- http://mirrors.neusoft.edu.cn 端口:80 北京化工大学镜像服务器地址:- IPv4: http://ubuntu.buct.edu.c ...
- HTTP请求报文和HTTP响应报文
HTTP报文是面向文本的,报文中的每一个字段都是一些ASCII码串,各个字段的长度是不确定的.HTTP有两类报文:请求报文和响应报文. HTTP请求报文 一个HTTP请求报文由请求行(request ...
- Java计时器Timer和TimerTask用法
package com.sy.game.test; import java.util.Timer; import java.util.TimerTask; public class TimeTask ...
- selenium使用actions.moveToElement处理菜单
//should set firefox path //FirefoxBinary binary=new FirefoxBinary(new File("C:\\Program Files ...