转载须注明出处,谢谢!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;//是否结束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();
}
}
}
}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;
}
}
}代码很简单,不必过多介绍,下面看显示效果:原文地址:http://blog.csdn.net/chdjj/article/details/41678897