Android - как сделать RotateAnimation более плавной и «физической»?

Я использую своего рода «стрелку компаса», которая следует за пунктом назначения в зависимости от физической ориентации устройства с помощью датчика магнитного поля. Внезапно я столкнулся с небольшой проблемой.

С азимутом и азимутом все в порядке, но реалистичная анимация превратилась в действительно сложную задачу. Я пытался использовать разные интерполяторы, чтобы сделать анимацию более «физической» (т.е. как в реальном компасе, стрелка которого колеблется после вращения шпильки, ускоряется и замедляется во время движения и т. Д.).

Сейчас я пользуюсьinterpolator.accelerate_decelerate и все довольно хорошо, пока обновления не начинают приходить быстро. Это заставляет анимации перекрывать друг друга, и стрела становится нервной и нервной. Я хочу избежать этого. Я попытался создать очередь, чтобы каждая следующая анимация ожидала до конца или отбрасывала обновления, которые приходят очень быстро. Это делало анимацию гладкой, но поведение стрелы превратилось в абсолютно нелогичное.

Итак, у меня есть 2 вопроса:

1) есть ли способсделать анимированные переходы более плавными в тех случаях, когда анимации перекрывают друг друга?

2) есть ли способостановить анимацию, которая в данный момент обрабатывается и получить промежуточную позицию объекта?

Мой код ниже.UpdateRotation() метод обрабатывает обновления ориентации и направления и выполняет анимацию внешнегоviewArrow Посмотреть.

public class DirectionArrow {

// View that represents the arrow
final View viewArrow;

// speed of rotation of the arrow, degrees/sec
final double rotationSpeed;

// current values of bearing and azimuth
float bearingCurrent = 0;
float azimuthCurrent = 0;


/*******************************************************************************/

/**
 * Basic constructor
 * 
 * @param   view            View representing an arrow that should be rotated
 * @param   rotationSpeed   Speed of rotation in deg/sec. Recommended from 50 (slow) to 500 (fast)
 */
public DirectionArrow(View view, double rotationSpeed) {
    this.viewArrow = view;
    this.rotationSpeed = rotationSpeed;
}

/**
 * Extended constructor
 * 
 * @param   viewArrow       View representing an arrow that should be rotated
 * @param   rotationSpeed   Speed of rotation in deg/sec. Recommended from 50 (slow) to 500 (fast)
 * @param   bearing         Initial bearing 
 * @param   azimuth         Initial azimuth
 */
public DirectionArrow(View viewArrow, double rotationSpeed, float bearing, float azimuth){
    this.viewArrow = viewArrow;
    this.rotationSpeed = rotationSpeed;
    UpdateRotation(bearing, azimuth);
}

/**
 * Invoke this to update orientation and animate the arrow
 * 
 * @param   bearingNew  New bearing value, set >180 or <-180 if you don't need to update it 
 * @param   azimuthNew  New azimuth value, set >360 or <0 if you don't need to update it
 */
public void UpdateRotation(float bearingNew, float azimuthNew){

    // look if any parameter shouldn't be updated
    if (bearingNew < -180 || bearingNew > 180){
        bearingNew = bearingCurrent;
    }
    if (azimuthNew < 0 || azimuthNew > 360){
        azimuthNew = azimuthCurrent;
    }

    // log
    Log.println(Log.DEBUG, "compass", "Setting rotation: B=" + bearingNew + " A=" + azimuthNew);

    // calculate rotation value
    float rotationFrom = bearingCurrent - azimuthCurrent;
    float rotationTo = bearingNew - azimuthNew;

    // correct rotation angles
    if (rotationFrom < -180) {
        rotationFrom += 360;
    }
    while (rotationTo - rotationFrom < -180) {
        rotationTo += 360;
    }
    while (rotationTo - rotationFrom > 180) {
        rotationTo -= 360;
    }

    // log again
    Log.println(Log.DEBUG, "compass", "Start Rotation to " + rotationTo);

    // create an animation object
    RotateAnimation rotateAnimation = new RotateAnimation(rotationFrom, rotationTo, 
            Animation.RELATIVE_TO_SELF, (float) 0.5, Animation.RELATIVE_TO_SELF, (float) 0.5);

    // set up an interpolator
    rotateAnimation.setInterpolator(viewArrow.getContext(), interpolator.accelerate_decelerate);

    // force view to remember its position after animation
    rotateAnimation.setFillAfter(true);

    // set duration depending on speed
    rotateAnimation.setDuration((long) (Math.abs(rotationFrom - rotationTo) / rotationSpeed * 1000));

    // start animation
    viewArrow.startAnimation(rotateAnimation);

    // update cureent rotation
    bearingCurrent = bearingNew;
    azimuthCurrent = azimuthNew;
}
}

Ответы на вопрос(4)

Ваш ответ на вопрос