【安卓】从源码的角度深入分析Scroller
转载须注明出处,谢谢!http://blog.csdn.net/chdjj
这篇文章我将从源码的角度深入分析Scroller类。在阅读的时候,建议大家打开源码对照着看,否则可能看的云里雾里。
public class MyView extends LinearLayout { private Scroller mScroller; ... ... private void smoothScrollTo(int destX,int destY) { int scrollX = getScrollX(); int scrollY = getScrollY(); int deltaX = destX-scrollX; int deltaY = destY-scrollY; mScroller.startScroll(scrollX,scrollY,deltaX, deltaY, 1000); invalidate(); } @Override public void computeScroll() { if(mScroller != null) { if(mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); Log.d(TAG,"scrollX="+getScrollX()+",scrollY="+getScrollY()); postInvalidate(); } } } }
/** * Return the scrolled left position of this view. This is the left edge of * the displayed part of your view. You do not need to draw any pixels * farther left, since those are outside of the frame of your view on * screen. * * @return The left edge of the displayed part of your view, in pixels. */ public final int getScrollX() { return mScrollX; } /** * Return the scrolled top position of this view. This is the top edge of * the displayed part of your view. You do not need to draw any pixels above * it, since those are outside of the frame of your view on screen. * * @return The top edge of the displayed part of your view, in pixels. */ public final int getScrollY() { return mScrollY; }
public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } } public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
private int mMode;//模式,有SCROLL_MODE和FLING_MODE private int mStartX;//起始x方向偏移 private int mStartY;//起始y方向偏移 private int mFinalX;//终点x方向偏移 private int mFinalY;//终点y方向偏移 private int mCurrX;//当前x方向偏移 private int mCurrY;//当前y方向偏移 private long mStartTime;//起始时间 private int mDuration;//滚动持续时间 private float mDurationReciprocal;//持续时间的倒数 private float mDeltaX;//x方向应该滚动的距离,mDeltaX=mFinalX-mStartX private float mDeltaY;//y方向应该滚动的距离,mDeltaY=mFinalY-mStartY private boolean mFinished;//是否结束
对这些变量有个大致印象之后,我们就开始看startScroll源码了:
public void startScroll(int startX, int startY, int dx, int dy) { startScroll(startX, startY, dx, dy, DEFAULT_DURATION); } 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; }
public boolean computeScrollOffset() { if (mFinished) { return false; } int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); if (timePassed < mDuration) { switch (mMode) { case SCROLL_MODE: final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal); mCurrX = mStartX + Math.round(x * mDeltaX); mCurrY = mStartY + Math.round(x * mDeltaY); break; case FLING_MODE: ... ... break; } } else { mCurrX = mFinalX; mCurrY = mFinalY; mFinished = true; } return true; }
public void draw(Canvas canvas) { ... ... /* * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */ // Step 1, draw the background, if needed int saveCount; if (!dirtyOpaque) { drawBackground(canvas); } ... ... // Step 2, save the canvas' layers ... ... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers ... ... }draw方法将绘制分成了六步,其中第三步是调用onDraw去绘制内容,第四步是调用dispatchDraw去绘制子视图。我们看下dispatchDraw方法:
/** * Called by draw to draw the child views. This may be overridden * by derived classes to gain control just before its children are drawn * (but after its own view has been drawn). * @param canvas the canvas on which to draw the view */ protected void dispatchDraw(Canvas canvas) { }恩,没错,是个空方法,因为只有ViewGroup才有子视图,所以我们来到ViewGroup中,找到dispatchDraw:
@Override protected void dispatchDraw(Canvas canvas) { boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode); final int childrenCount = mChildrenCount; final View[] children = mChildren; ... ... for (int i = 0; i < childrenCount; i++) { int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } }代码也是极其长的,这里截取了一部分。我们看到这个方法中调用了drawChild方法,从名字上就可以看出这个方法是用来绘制子视图的,找到其实现:
protected boolean drawChild(Canvas canvas, View child, long drawingTime) { return child.draw(canvas, this, drawingTime); }这里调用了view的draw方法,当然,这个draw方法跟上面那个draw不一样,因为它有三个参数!那还等什么,我们回到View的代码中找到这个含有三个参数的draw方法:
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) { boolean more = false; final boolean childHasIdentityMatrix = hasIdentityMatrix(); final int flags = parent.mGroupFlags; ... ... int sx = 0; int sy = 0; if (!hasDisplayList) { computeScroll();//终于找到了computeScroll sx = mScrollX; sy = mScrollY; } ... ... return more; }
@Override public void computeScroll() { if (mScroller != null) { if (mScroller.computeScrollOffset()) { mScrollX = mScroller.getCurrX(); mScrollY = mScroller.getCurrY(); invalidateParentCaches(); postInvalidate(); // So we draw again } } }
package com.example.scrollerdemo; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.widget.LinearLayout; public class MyLinearLayout extends LinearLayout { private static final String TAG = "TEST"; public MyLinearLayout(Context context) { super(context); } public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void draw(Canvas canvas) { Log.i(TAG, "MyLinearLayout--->draw"); super.draw(canvas); } @Override protected void onDraw(Canvas canvas) { Log.i(TAG, "MyLinearLayout--->onDraw"); super.onDraw(canvas); } @Override public void computeScroll() { Log.i(TAG, "MyLinearLayout--->computeScroll"); super.computeScroll(); } }MyView(实现了平滑滚动效果):
package com.example.scrollerdemo; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.LinearLayout; import android.widget.Scroller; /** * @author Rowandjj * */ public class MyView extends LinearLayout { private static final String TAG = "TEST"; private Scroller mScroller; public MyView(Context context) { super(context); init(); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mScroller = new Scroller(getContext()); } public void testSmoothScroll() { //向右下方平滑滚动(向右偏移100,向下偏移100) smoothScrollTo(-100,-100); } private void smoothScrollTo(int destX,int destY) { int scrollX = getScrollX(); int scrollY = getScrollY(); Log.d(TAG,"scrollX="+scrollX+",scrollY="+scrollY); int deltaX = destX-scrollX; int deltaY = destY-scrollY; mScroller.startScroll(scrollX,scrollY,deltaX, deltaY, 1000); invalidate(); } @Override public void draw(Canvas canvas) { Log.i(TAG,"MyView------>draw run"); super.draw(canvas); } @Override protected void onDraw(Canvas canvas) { Log.i(TAG,"MyView------>onDraw run"); super.onDraw(canvas); } @Override protected void dispatchDraw(Canvas canvas) { Log.i(TAG,"MyView------>dispatchDraw run"); super.dispatchDraw(canvas); } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { Log.i(TAG,"MyView------>drawChild run"); return super.drawChild(canvas, child, drawingTime); } @Override public void computeScroll() { Log.i(TAG,"MyView------>computeScroll run"); if(mScroller != null) { if(mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(),mScroller.getCurrY()); Log.d(TAG,"scrollX="+getScrollX()+",scrollY="+getScrollY()); postInvalidate(); } } } }
MyImageView:
package com.example.scrollerdemo; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.util.Log; import android.widget.ImageView; public class MyImageView extends ImageView { private static final String TAG = "TEST"; public MyImageView(Context context) { super(context); } public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onDraw(Canvas canvas) { Log.i(TAG,"myImageView---->onDraw run"); super.onDraw(canvas); } @Override public void computeScroll() { Log.i(TAG,"myImageView---->computeScroll run"); super.computeScroll(); } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.scrollerdemo.MyLinearLayout xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffaaff" tools:context="com.example.scrollerdemo.MainActivity" > <com.example.scrollerdemo.MyView android:id="@+id/mv" android:layout_width="200dp" android:layout_height="200dp" android:layout_marginLeft="50dp" android:layout_marginTop="100dp" android:background="@android:color/darker_gray" android:orientation="vertical" > <com.example.scrollerdemo.MyImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> </com.example.scrollerdemo.MyView> </com.example.scrollerdemo.MyLinearLayout> </LinearLayout>下面是MainActivity的代码:
package com.example.scrollerdemo; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; public class MainActivity extends Activity implements OnClickListener { private MyView mv = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mv = (MyView) findViewById(R.id.mv); mv.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.mv://点击时触发滚动效果 mv.testSmoothScroll(); break; default: break; } } }代码很简单,不必过多介绍,下面看显示效果:
打开logcat查看日志:
中间日志太多,省略了,下面这段是最后打印的
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。