我有一个应用程序,我需要根据当前聚焦的子视图调整视图位置(它是一个具有可聚焦项目的列表,当前聚焦的项目必须位于屏幕的中心 - 用于从电视遥控器控制的电视应用程序)。
必须使用动画调整位置
我只使用了一个问题:如果用户在动画完成之前更改焦点(快速点击“向上”按钮两次),下一个动画以“跳转”开始 - 它从与第一个相同的位置开始。
所以我尝试做的是取消之前的动画并启动另一个动画,然后从第一个动画开始的点开始新动画,这样用户就可以在动画中看到一个非常明显的跳跃,看起来非常糟糕。 / p>
以下是代码:
@Override
public void requestChildFocus(final View child, final View focused) {
super.requestChildFocus(child, focused);
//this test code included for explanation
Rect r = new Rect();
child.getDrawingRect(r); //this will return view's position ignoring animation state
Rect r2 = new Rect();
child.getGlobalVisibleRect(r2); //as will this one too
Log.d("Top: " + child.getTop() + "; Drawing rect: " + r.toString() + "; global visible rect: " + r2.toString());
//all of this methods will ignore changes that were made
//by animation object - they'll return numbers from LayoutParam
//calculate current position inside view and position to move to
//cursorOffset - is the "center" of the screen
final int currentPosition = child.getTop();
final int requaredPosition = cursorOffset - focused.getTop();
//cancel current running animation - layout params will not change
//if i do change layout params when cancelling animation, it looks even worse
//because of jumping back list jumps forward
if (currentAnimation != null) {
Animation animation = currentAnimation;
currentAnimation = null;
animation.cancel();
}
//just a regular translate animation
TranslateAnimation animation = new TranslateAnimation(0, 0, 0, requaredPosition - currentPosition);
animation.setDuration(300);
animation.setFillEnabled(true);
animation.setFillBefore(true);
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
currentAnimation = animation;
}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
if (animation == currentAnimation) {
//change layout params if animation finished running (wasn't cancelled)
RelativeLayout.LayoutParams params = (LayoutParams) child.getLayoutParams();
params.setMargins(0, requaredPosition, 0, 0);
child.setLayoutParams(params);
}
}
});
child.startAnimation(animation);
}
所以必须提出的问题是:如何从以前的翻译动画留下的位置(假设它被取消)开始翻译动画? 或者,用简单的话说,我如何确定视图的当前可见矩形?
答案 0 :(得分:11)
显然你无法获得当前的观看位置,但你可以获得当前的动画状态 所以你可以通过这样做得到当前的y偏移:
Transformation transformation = new Transformation();
float[] matrix = new float[9];
currentAnimation.getTransformation(AnimationUtils.currentAnimationTimeMillis(), transformation);
transformation.getMatrix().getValues(matrix);
float y = matrix[Matrix.MTRANS_Y];
这就是我如何能够取消一个动画并从我离开的位置开始另一个动画。如果有人关心,那就是完整的代码:
private Animation currentAnimation;
private float[] matrix = new float[9];
private Transformation transformation = new Transformation();
@Override
public void requestChildFocus(final View child, final View focused) {
super.requestChildFocus(child, focused);
final int currentPosition;
if (currentAnimation != null) {
currentAnimation.getTransformation(AnimationUtils.currentAnimationTimeMillis(), transformation);
transformation.getMatrix().getValues(matrix);
float y = matrix[Matrix.MTRANS_Y];
RelativeLayout.LayoutParams params = (LayoutParams) child.getLayoutParams();
params.topMargin += y;
//child.getTop() will return wrong position until layout actually happens,
//so I use params.topMargin as a current position in case I need to cancel
currentPosition = params.topMargin;
child.requestLayout();
currentAnimation.setAnimationListener(null);
currentAnimation.cancel();
currentAnimation = null;
} else {
currentPosition = child.getTop();
}
final int requaredPosition = cursorOffset - focused.getTop();
TranslateAnimation animation = new TranslateAnimation(0, 0, 0, requaredPosition - currentPosition);
animation.setDuration(300);
animation.setFillEnabled(true);
animation.setFillBefore(true);
animation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
currentAnimation = animation;
}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
if (animation == currentAnimation) {
RelativeLayout.LayoutParams params = (LayoutParams) child.getLayoutParams();
params.setMargins(0, requaredPosition, 0, 0);
child.requestLayout();
}
currentAnimation = null;
}
});
child.startAnimation(animation);
}
希望有人觉得这很有用。
答案 1 :(得分:1)
为每个寻求帮助的人创造一个像我这样的时钟或速度表 (寻找RotationAnimation< API lvl 9), 我发现,设置AnimationListener并保存是个好主意 布尔变量中的动画状态。 在开始新动画之前,请检查状态。 因此,您的动画将一直运行到最后并且看起来非常流畅。 我很抱歉我的英语。
if(isAnimationActive){
//[...]
rotate = new RotateAnimation(oldRotateDegree, rotateDegree, Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(rotateDuration);
rotate.setFillAfter(true);
rotate.setAnimationListener(this);
}
//[...]
@Override
public void onAnimationEnd(Animation animation)
{
this.isAnimationActive = false;
}
@Override
public void onAnimationStart(Animation animation)
{
this.isAnimationActive = true;
}