赞
踩
Scroller类其实就是对View的scrollTo()以及ScrollBy()方法封装以及优化,比如我们熟悉的网易客户端主界面的侧滑菜单,如果要实现当滑动超过一半的时候回弹的效果,而不是一下子就回到了最终点,view的scrollTo(),scrollBy()方法是没有做这个功能的,而Scroller类就做了这些优化,使用起来更方便,如果对View的那个二个方法很真的懂了的话,讲Scroller就很容易懂,不懂的话可以去看下我另外一篇关于View的scrollTo()和scrollBy()方法,自认为讲的比较详细,讲Scroller类首先从它的构造花函数讲起,在API 11之前就2个构造函数,API 11后添加了一个新的构造函数,
/** * Create a Scroller with the default duration and interpolator. */ public Scroller(Context context) { this(context, null); } /** * Create a Scroller with the specified interpolator. If the interpolator is * null, the default (viscous) interpolator will be used. "Flywheel" behavior will * be in effect for apps targeting Honeycomb or newer. */ public Scroller(Context context, Interpolator interpolator) { this(context, interpolator, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB); } /** * Create a Scroller with the specified interpolator. If the interpolator is * null, the default (viscous) interpolator will be used. Specify whether or * not to support progressive "flywheel" behavior in flinging. */ public Scroller(Context context, Interpolator interpolator, boolean flywheel) { mFinished = true; if (interpolator == null) { mInterpolator = new ViscousFluidInterpolator(); } else { mInterpolator = interpolator; } mPpi = context.getResources().getDisplayMetrics().density * 160.0f; mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); mFlywheel = flywheel; mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning }第一个构造函数很简单,就一个Context形参,无需做过多的介绍
第二个构造函数,第一个形参还是Context,第二个形参是插补器,里面封装了很多动画效果,
第三个构造函数有三个形参,最后一个形参是boolean值,是API 11后有的,什么意思先留在这里
今天就不讲Interpolator 这个类的使用,因为它会涉及很多子类,每个子类对应的动画效果不一样,到时候会讲动画系列会好好讲下,
现在看下Scroller类都给我们提供了哪些方法可以给我们调用,
public final boolean isFinished() {} 判断滑动是否结束
public final int getDuration() {} 返回滑动所需的时间(单位为毫秒)
public final int getCurrX() {} 返回当前x轴偏移量
public final int getCurrY() {} 返回当前y轴偏移量
public float getCurrVelocity(){}返回当前滚动的速度
public final void forceFinished(boolean finished) {}强制终止滚动
public final int getStartX() {} 返回滚动起始点x轴偏移量
public final int getStartY() {} 返回滚动起始点y轴偏移量
public final int getFinalX() {}返回滚动结束x轴的偏移量
public final int getFinalY() {}返回滚动结束y轴的偏移量
public boolean computeScrollOffset() {} 判断是否滑动结束,
public void setFinalX(int newX) {} 设置x轴方向终止偏移量
public void setFinalY(int newY) {} 设置y轴方向终止偏移量
public int timePassed() {}返回开始滑动经过的时间
public void extendDuration(int extend) {}延迟extend 秒再滑动
public void abortAnimation() {}终止动画执行
public void fling(int startX, int startY, int velocityX, int velocityY,int minX, int maxX, int minY, int maxY) {} 快速滑动松开手势惯性滑动
使用Scroller类实现view的滑动一般就三个步骤
第一步:创建Scroller类的对象
第二步:调用Scroller类的startScroll()方法,先看下它的源码:
/** * Start scrolling by providing a starting point, the distance to travel, * and the duration of the scroll. * * @param startX Starting horizontal scroll offset in pixels. Positive * numbers will scroll the content to the left. * @param startY Starting vertical scroll offset in pixels. Positive numbers * will scroll the content up. * @param dx Horizontal distance to travel. Positive numbers will scroll the * content to the left. * @param dy Vertical distance to travel. Positive numbers will scroll the * content up. * @param duration Duration of the scroll in milliseconds. */ public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }你会发现这个方法其实一堆赋值操作,并没有帮你实现滑动的逻辑,滑动的逻辑其实还是调用View的scrollTo()方法,startScroll()方法就是初始化了一些滑动的数据
第三步:调用Scroller类的computeScrollOffset()方法判断是否滑动到了终点,如果没有滑动到终点的话就重写View的computeScroll()方法,这个方法在View中是空实现,所以重写这个方法实现你继续滑动的逻辑,为什么会调用这个方法呢?结合源码分析:
首先会调用ViewGroup中的invalidate()方法,因为ViewGroup是继承自View的,而ViewGroup并没有重写invalidate()方法,所以调用的是View中的invalidate()方法:
/** * Invalidate the whole view. If the view is visible, * { @link #onDraw(android.graphics.Canvas)} will be called at some point in * the future. This must be called from a UI thread. To call from a non-UI thread, * call { @link #postInvalidate()}. */ public void invalidate() { invalidate(true); }调用如下方法:
/** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be called with * invalidateCache set to false to skip that invalidation step for cases that do not * need it (for example, a component that remains at the same dimensions with the same * content). * * @param invalidateCache Whether the drawing cache for this view should be invalidated as * well. This is usually true for a full invalidate, but may be set to false if the * View's contents or dimensions have not changed. */ void invalidate(boolean invalidateCache) { if (skipInvalidate()) { return; } if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~PFLAG_DRAWN; mPrivateFlags |= PFLAG_DIRTY; if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; //noinspection PointlessBooleanExpression,ConstantConditions if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { if (p != null && ai != null && ai.mHardwareAccelerated) { // fast-track for GL-enabled applications; just invalidate the whole hierarchy // with a null dirty rect, which tells the ViewAncestor to redraw everything p.invalidateChild(this, null); return; } } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect;//把ai定义的矩形赋值给r r.set(0, 0, mRight - mLeft, mBottom - mTop);//设置矩形的区域 也就是2个坐标点 // Don't call invalidate -- we don't want to internally scroll // our own bounds p.invalidateChild(this, r);//调用ViewGroup中的重新绘制子view的方法,因为ViewGroup是实现了ViewParent()接口 } } }ViewGroup中的invalidateChild(View view,Rect rect)
/** * Don't call or override this method. It is used for the implementation of * the view hierarchy. */ public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // If the child is drawing an animation, we want to copy this flag onto // ourselves and the parent to make sure the invalidate request goes // through final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) == PFLAG_DRAW_ANIMATION; // Check whether the child that requests the invalidate is fully opaque // Views being animated or transformed are not considered opaque because we may // be invalidating their old position and need the parent to paint behind them. Matrix childMatrix = child.getMatrix(); final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() == null && childMatrix.isIdentity(); // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY; if (child.mLayerType != LAYER_TYPE_NONE) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; child.mLocalDirtyRect.union(dirty); } final int[] location = attachInfo.mInvalidateChildLocation; location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; if (!childMatrix.isIdentity() || (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); Matrix transformMatrix; if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { Transformation t = attachInfo.mTmpTransformation; boolean transformed = getChildStaticTransformation(child, t); if (transformed) { transformMatrix = attachInfo.mTmpMatrix; transformMatrix.set(t.getMatrix()); if (!childMatrix.isIdentity()) { transformMatrix.preConcat(childMatrix); } } else { transformMatrix = childMatrix; } } else { transformMatrix = childMatrix; } transformMatrix.mapRect(boundingRect); dirty.set((int) (boundingRect.left - 0.5f), (int) (boundingRect.top - 0.5f), (int) (boundingRect.right + 0.5f), (int) (boundingRect.bottom + 0.5f)); } do { View view = null; if (parent instanceof View) { view = (View) parent; } if (drawAnimation) { if (view != null) { view.mPrivateFlags |= PFLAG_DRAW_ANIMATION; } else if (parent instanceof ViewRootImpl) { ((ViewRootImpl) parent).mIsAnimating = true; } } // If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate if (view != null) { if ((view.mViewFlags & FADING_EDGE_MASK) != 0 && view.getSolidColor() == 0) { opaqueFlag = PFLAG_DIRTY; } if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag; } } parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent Matrix m = view.getMatrix(); if (!m.isIdentity()) { RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int) (boundingRect.left - 0.5f), (int) (boundingRect.top - 0.5f),
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。