我是Android开发的新手,我想了解一下Scroller小部件(android.widget.Scroller)。它如何使视图动画化?如果存在Animation对象,是否可以访问该对象?如果可以,如何访问?我已经阅读了源代码,但没有找到任何线索,或者可能是因为我太新了?
我只是想在Scroller完成滚动后执行一些操作,类似于
m_scroller.getAnimation().setAnimationListener(...);
Scroller小部件实际上并不为您完成太多工作。 它不会触发任何回调,也不会执行任何动画,它只会响应各种方法调用。
那么它有什么用呢?好吧,它为您执行例如抛掷的所有计算,这很方便。 因此,通常您要做的是创建一个Runnable,反复询问Scroller:“现在我的滚动位置应该是什么?我们完成抛掷了吗?” 然后将该Runnable重新发布到Handler上(通常在View上),直到完成抛掷为止。
这是我正在处理的Fragment中的示例:
private class Flinger implements Runnable {
private final Scroller scroller;
private int lastX = 0;
Flinger() {
scroller = new Scroller(getActivity());
}
void start(int initialVelocity) {
int initialX = scrollingView.getScrollX();
int maxX = Integer.MAX_VALUE; // or some appropriate max value in your code
scroller.fling(initialX, 0, initialVelocity, 0, 0, maxX, 0, 10);
Log.i(TAG, "starting fling at " + initialX + ", velocity is " + initialVelocity + "");
lastX = initialX;
getView().post(this);
}
public void run() {
if (scroller.isFinished()) {
Log.i(TAG, "scroller is finished, done with fling");
return;
}
boolean more = scroller.computeScrollOffset();
int x = scroller.getCurrX();
int diff = lastX - x;
if (diff != 0) {
scrollingView.scrollBy(diff, 0);
lastX = x;
}
if (more) {
getView().post(this);
}
}
boolean isFlinging() {
return !scroller.isFinished();
}
void forceFinished() {
if (!scroller.isFinished()) {
scroller.forceFinished(true);
}
}
}
使用Scroller.startScroll的细节应该是类似的。
就像Bill Phillips所说,Scroller只是一个Android SDK类,帮助计算滚动位置。我在这里有一个完整的工作示例:
public class SimpleScrollableView extends TextView {
private Scroller mScrollEventChecker;
private int mLastFlingY;
private float mLastY;
private float mDeltaY;
public SimpleScrollableView(Context context) {
this(context, null, 0);
}
public SimpleScrollableView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SimpleScrollableView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mScrollEventChecker != null && !mScrollEventChecker.isFinished()) {
return super.onTouchEvent(event);
}
final int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
int movingDelta = (int) (event.getY() - mLastY);
mDeltaY += movingDelta;
offsetTopAndBottom(movingDelta);
invalidate();
return true;
case MotionEvent.ACTION_UP:
mScrollEventChecker = new Scroller(getContext());
mScrollEventChecker.startScroll(0, 0, 0, (int) -mDeltaY, 1000);
post(new Runnable() {
@Override
public void run() {
if (mScrollEventChecker.computeScrollOffset()) {
int curY = mScrollEventChecker.getCurrY();
int delta = curY - mLastFlingY;
offsetTopAndBottom(delta); // this is the method make this view move
invalidate();
mLastFlingY = curY;
post(this);
} else {
mLastFlingY = 0;
mDeltaY = 0;
}
}
});
return super.onTouchEvent(event);
}
return super.onTouchEvent(event);
}
}
Scroller
类,然后拦截相应的动画开始方法来标记已启动,在computeScrollOffset()返回false,也就是动画完成的值后,我们通过监听器通知调用者:public class ScrollerImpl extends Scroller {
...Constructor...
private boolean mIsStarted;
private OnFinishListener mOnFinishListener;
@Override
public boolean computeScrollOffset() {
boolean result = super.computeScrollOffset();
if (!result && mIsStarted) {
try { // Don't let any exception impact the scroll animation.
mOnFinishListener.onFinish();
} catch (Exception e) {}
mIsStarted = false;
}
return result;
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy);
mIsStarted = true;
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, duration);
mIsStarted = true;
}
@Override
public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
super.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
mIsStarted = true;
}
public void setOnFinishListener(OnFinishListener onFinishListener) {
mOnFinishListener = onFinishListener;
}
public static interface OnFinishListener {
void onFinish();
}
}
ScrollerImpl
吗? - VinceStyling上面的答案很好。Scroller#startScroll(...)的确是以相同的方式工作。
例如,自定义滚动TextView的源码在此处: http://bear-polka.blogspot.com/2009/01/scrolltextview-scrolling-textview-for.html
使用TextView#setScroller(Scroller)将Scroller设置到TextView上。
SDK的TextView源代码在此处: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/widget/TextView.java#TextView.0mScroller
显示TextView#setScroller(Scroller)设置了一个类字段,该字段在像bringPointIntoView(int)这样的情况下被使用,其中会调用Scroller#scrollTo(int, int, int, int)。
bringPointIntoView()调整mScrollX和mScrollY(带有一些SDK碎片化代码),然后调用invalidate()。所有这些的目的是mScrollX和mScrollY在onPreDraw(...)等方法中被使用,从而影响视图中绘制内容的位置。
isFlinging()
方法不应该返回!scroller.isFinished()
吗?要么这样,要么它应该被重命名为isFinished()
。不过这是一个很好的例子 :) - ashughes