android代码控制组件的移动,如何流畅移动view

本文介绍了如何利用Android Framework提供的scrollTo()、scrollBy()、Scroller类以及computeScroll()方法,实现流畅的控件移动效果。通过自定义View,结合Scroller的startScroll()和computeScrollOffset(),在3秒内平滑滚动到指定位置,解决了刷新延迟和画面重叠问题。
摘要由CSDN通过智能技术生成

前几天接到的任务中涉及到控件移动的问题,以前都是通过更新控件内容+补间动画来做的,但发觉应付连续多次请求时,有时刷新不及时导致延迟,画面出现重叠的情况。苦思许久,最后在同事的帮助下参考源码中Launcher应用的方法完美搞定。在这里必须分享一下……

在一切开始之前有必要先说说三个知识点:

1.scrollTo()和scrollBy()方法

如何移动控件,Android Framework中已经给我们留了接口。源码中控件基类View.java类的定义中有如下片段:

/**

* The offset, in pixels, by which the content of this view is scrolled

* horizontally.

* {@hide}

*/

@ViewDebug.ExportedProperty

protected int mScrollX;

/**

* The offset, in pixels, by which the content of this view is scrolled

* vertically.

* {@hide}

*/

@ViewDebug.ExportedProperty

protected int mScrollY;

/**

* Set the scrolled position of your view. This will cause a call to

* {@link #onScrollChanged(int, int, int, int)} and the view will be

* invalidated.

* @param x the x position to scroll to

* @param y the y position to scroll to

*/

public void scrollTo(int x, int y) {

if (mScrollX != x || mScrollY != y) {

int oldX = mScrollX;

int oldY = mScrollY;

mScrollX = x;

mScrollY = y;

onScrollChanged(mScrollX, mScrollY, oldX, oldY);

if (!awakenScrollBars()) {

invalidate();

}

}

}

/**

* Move the scrolled position of your view. This will cause a call to

* {@link #onScrollChanged(int, int, int, int)} and the view will be

* invalidated.

* @param x the amount of pixels to scroll by horizontally

* @param y the amount of pixels to scroll by vertically

*/

public void scrollBy(int x, int y) {

scrollTo(mScrollX + x, mScrollY + y);

}

“mScrollX”,“mScrollY”可以记录当前控件在父窗体中的偏移量,而调用scrollTo()和scrollBy()你就可以轻松的移动你的控件了。

2.Scroller类

尝试上面的方法,你很快就会发现它的用户体验非常差了,每次你的view会瞬间移动到指定的位置,给人一顿一顿的感觉。怎样才能控制view的偏移过程,从而使偏移更流畅,更完美呢?这一点并不需要我们做太多工作,因为Android Framework已经为我们提供了Scroller.java类,将一个迁移的动作变成了一个可控的过程,代码片段如下:

private int mCurrX;

private int mCurrY;

……

/**

* Call this when you want to know the new location.  If it returns true,

* the animation is not yet finished.  loc will be altered to provide the

* new location.

*/

public boolean computeScrollOffset() {

if (mFinished) {

return false;

}

int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

if (timePassed 

switch (mMode) {

case SCROLL_MODE:

float x = (float)timePassed * mDurationReciprocal;

if (mInterpolator == null)

x = viscousFluid(x);

else

x = mInterpolator.getInterpolation(x);

mCurrX = mStartX + Math.round(x * mDeltaX);

mCurrY = mStartY + Math.round(x * mDeltaY);

break;

case FLING_MODE:

float timePassedSeconds = timePassed / 1000.0f;

float distance = (mVelocity * timePassedSeconds)

- (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);

mCurrX = mStartX + Math.round(distance * mCoeffX);

// Pin to mMinX <= mCurrX <= mMaxX

mCurrX = Math.min(mCurrX, mMaxX);

mCurrX = Math.max(mCurrX, mMinX);

mCurrY = mStartY + Math.round(distance * mCoeffY);

// Pin to mMinY <= mCurrY <= mMaxY

mCurrY = Math.min(mCurrY, mMaxY);

mCurrY = Math.max(mCurrY, mMinY);

break;

}

}

else {

mCurrX = mFinalX;

mCurrY = mFinalY;

mFinished = true;

}

return true;

}

/**

* Start scrolling by providing a starting point and the distance to travel.

*

* @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;

// This controls the viscous fluid effect (how much of it)

mViscousFluidScale = 8.0f;

// must be set to 1.0 (used in viscousFluid())

mViscousFluidNormalize = 1.0f;

mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);

}

忽略细节,重点关注其中比较重要的两个方法的作用:

public boolean computeScrollOffset()

根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中

public void startScroll(int startX, int startY, int dx, int dy, int duration)

开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,到达坐标为(startX+dx , startY+dy)处。

3. computeScroll()方法

如何去控制这个流程,Android Framework提供了computeScroll()方法,方法位于ViewGroup.java类中。为了实现偏移控制,一般自定义控件都需要重载该方法。其调用过程位于View绘制流程draw()过程中,用于由父View调用用来请求子View根据偏移值mScrollX,mScrollY重新绘制

了解了以上的知识点,相信实现这个功能已经不是难事了,大概的思路如下;

首先,自定义一个控件继承于View,添加一个Scroller类型的成员变量mScroller;

其次,调用mScroller的startScroll()去产生一个偏移控制,手动调用invalid()方法去重新绘制控件;

最后,重写的computeScroll()方法,通过mScroller的computeScrollOffset()方法,根据当前已经逝去的时间,获取当前应该偏移的坐标;调用scrollBy()方法去逐步偏移,调用postInvalidate()方法刷新控件;

大致的代码如下,仅供参考:

public void nextMenuItem(){

……//一系列更新子View的逻辑操作

//更新结束后,使用动画控制偏移过程, 3s内到位

mScroller.startScroll(0, 0, menuItemWidth, 0,3000);

//重绘控件

invalidate();

}

@Override

public void computeScroll() {

if (mScroller.computeScrollOffset()) { //如果返回true,表示动画还没有结束

//产生平滑的动画效果,根据当前偏移量,每次滚动一点

scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

//此时同样也需要刷新View,否则效果可能有误差

postInvalidate();

} else { //如果返回false,表示startScroll完成

Log.i(tag, " scoller has finished -----");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值