Прервите анимацию перевода и начните другую - PullRequest
3 голосов
/ 01 декабря 2011

У меня есть приложение, в котором мне нужно настроить положение просмотра в соответствии с текущим фокусированным подпредставлением (это список с фокусируемыми элементами, текущий фокусированный элемент должен находиться в центре экрана - используется для телевизионного приложения, управляемого с пульта дистанционного управления телевизором).
Положение должно быть скорректировано с анимацией.
У меня получилось работать только с одной проблемой: если пользователь меняет фокус до завершения анимации (быстро нажимает кнопку «вверх»), следующая анимация начинается с «прыжка» - она ​​начинается с той же позиции, что и первая.

Итак, я пытаюсь отменить предыдущую анимацию и запустить другую, но затем новая анимация начинается с того места, где была запущена первая анимация, поэтому пользователь может видеть очень очевидные скачки анимации, которые выглядят действительно ужасно.

Вот код:

@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);
}

Поэтому должен возникнуть вопрос: как я могу начать переводить анимацию с того места, где осталась предыдущая анимация перевода (при условии, что она была отменена)?
Или, проще говоря, как я могу определить текущий видимый прямоугольник вида?

Ответы [ 2 ]

11 голосов
/ 02 декабря 2011

Очевидно, что вы не можете получить текущую позицию просмотра, но вы можете получить текущее состояние анимации.
Таким образом, вы можете получить текущее смещение по 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 голос
/ 06 августа 2013

Для всех, кто ищет помощь в создании часов или спидометра, как я (ищите RotationAnimation

        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;  
        }
...