我正在尝试使用RxJava为Android中的视图设置动画。我需要在一个间隔内更改视图的Y位置,但它无法正常工作。这是我开始的功能:
private void initiateCurrentWordFall(){
txtFalling.setY(fallingWordStartY);
Observable<Long> observable = Observable.interval(100, TimeUnit.MILLISECONDS);
fallingSubs = observable
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.computation())
.subscribe(new Observer<Long>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Long aLong) {
if(txtFalling.getY()!=screenHeight) {
txtFalling.setY(txtFalling.getY() + 1);
Log.d("Txt Y", String.valueOf(txtFalling.getY()));
}
else
wrongAnswerOrWordFeel();
}
});
}
我的执行中screenHeight等于1776。
这段代码应该每0.1秒将视图的Y减1个像素。我甚至试着记录正在发生的事情,这就是我得到的:
09-03 18:03:13.882 701-701/com.feliperrm.babbelproject D/Txt Y: -70.0
09-03 18:03:13.890 701-701/com.feliperrm.babbelproject D/Txt Y: -69.0
09-03 18:03:13.922 701-701/com.feliperrm.babbelproject D/Txt Y: -68.0
09-03 18:03:13.923 701-701/com.feliperrm.babbelproject D/Txt Y: -67.0
09-03 18:03:13.964 701-701/com.feliperrm.babbelproject D/Txt Y: -117.0
09-03 18:03:13.974 701-701/com.feliperrm.babbelproject D/Txt Y: -116.0
09-03 18:03:13.990 701-701/com.feliperrm.babbelproject D/Txt Y: -115.0
09-03 18:03:14.021 701-701/com.feliperrm.babbelproject D/Txt Y: -114.0
09-03 18:03:14.044 701-701/com.feliperrm.babbelproject D/Txt Y: -113.0
09-03 18:03:14.058 701-701/com.feliperrm.babbelproject D/Txt Y: -112.0
这种情况一直重复(文本视图到达某一点并重新开始)。我做错了什么?
编辑:
发现了问题,但我仍然对它为何会发生这种情况感到好奇。 我上面发布的这个函数被多次调用,因此当RxJava使textView掉线时,第一行
txtFalling.setY(fallingWordStartY);
让它再次恢复。但为什么函数被多次调用? 这就是我称之为的地方:
txtCurrentWord.animate().setDuration(1000).setStartDelay(150).alpha(1f).scaleX(1f).scaleY(1f).setInterpolator(new AccelerateInterpolator())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
txtCurrentWord.animate()
.y(topWordY - txtCurrentWord.getHeight()/5)
.scaleX(0.6f).scaleY(0.6f).setInterpolator(new AccelerateDecelerateInterpolator()).setStartDelay(250).setDuration(750).start();
bgNewWord.animate().alpha(0f).setStartDelay(250).setDuration(750)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
bgNewWord.setVisibility(View.GONE);
initiateCurrentWordFall();
}
}).start();
}
}).start();
很抱歉它非常难看,有很多嵌套动画,因为我只能在另一个结束后开始。奇怪的是onAnimationEnd被多次调用,即使我没有重新启动它们!
答案 0 :(得分:1)
我刚刚发现了自己的问题。对于那些感兴趣的人,我在一个有动画监听器设置的视图上开始动画,所以即使在这个动画设置期间没有设置这个监听器,它仍然被调用,并且它被设置为一遍又一遍地调用自己。我只需要在内部txtCurrentWord动画上添加.setListener(null)。
答案 1 :(得分:0)
当你最初考虑使用RxJava链接你的动画时,我认为你走在了正确的轨道上。作为一个示例,我编写了一个将动画转换为Observable
的包装器。 observable返回正在设置动画的View
,以便于清理。作为奖励,它不使用AnimationListener。这样做的副作用是,如果取消动画,您将无法收到通知,但如果需要,可以使用AnimationListener替换.withEndAction()
。这个基本习惯用法也可以用于其他动画类型,例如ObjectAnimators。
public class RxAnimation {
public static Observable<View> wrapViewPropertyAnimation(View view, Func1<View, ViewPropertyAnimator> animationCall) {
return Observable.<View>create(emitter -> {
ViewPropertyAnimator animation =
animationCall.call(view)
.withEndAction(() -> emitter.onNext(view));
animation.start();
emitter.setSubscription(new MainThreadSubscription() {
@Override
protected void onUnsubscribe() {
animation.cancel();
}
});
}, Emitter.BackpressureMode.BUFFER)
.subscribeOn(AndroidSchedulers.mainThread());
}
public static void initiateCurrentWordFall() {
// Whatever this does...
}
public static void runSampleCode(View txtCurrentWord, View bgNewWord, int topWordY) {
wrapViewPropertyAnimation(txtCurrentWord, v ->
v.animate().setDuration(1000).setStartDelay(150)
.alpha(1f).scaleX(1f).scaleY(1f)
.setInterpolator(new AccelerateInterpolator()))
.switchMap(lastView -> wrapViewPropertyAnimation(lastView, v ->
v.animate().y(topWordY - txtCurrentWord.getHeight()/5)
.scaleX(0.6f).scaleY(0.6f)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setStartDelay(250).setDuration(750)))
.switchMap(lastView -> wrapViewPropertyAnimation(bgNewWord, v ->
v.animate().alpha(0f).setStartDelay(250).setDuration(750))
)
.doOnNext(lastView -> {
lastView.setVisibility(View.GONE);
initiateCurrentWordFall();
})
.observeOn(AndroidSchedulers.mainThread())
// You should make sure your observable is bound to an Activity lifecycle
// to avoid leaks. I use RxLifecycle for this.
.subscribe();
}
}