Страницы

Поиск по вопросам

суббота, 11 апреля 2020 г.

Анимация в Android Canvas для minSdkVersion=“8”

#java #android #анимация #animate

                    
В моей словесной игре типа "Эрудита" для Android отсутствует анимация - 

Например, при выборе в меню "Вернуть буквы" фишки просто удаляются с игрового поля
и потом (вдруг!) показываются внизу экрана. 

Тоже самое с "Помешать буквы" - фишки просто рисуются на новых позициях внизу экрана:



Конечно, это выглядит не очень наглядно и мне хотелось бы добавить простые анимации
для буквенных фишек - но при этом не терять совместимость с Android 2.2 - поэтому нельзя
например использовать ValueAnimator.

Кроме того, мне кажется, что можно обойтись без Runnable и создания дополнительных
thread-ов - потому что я уже успешно анимирую flinging игрового поля (когда пользователь
скроллит его энергичным движением пальца) в главном thread при помощи ScrollerCompat.

Вот класс буквенных фишек:

public class Tile {
    public static int sWidth;
    public static int sHeight;

    // этот прямоугольник содержит координаты и размер фишки
    private Rect mRect = new Rect();


Для анимации я решил добавить в этот класс конечные координаты и флаг, что движение
фишки еще не закончилось:

    private Point mTarget = new Point();
    public boolean moving = false;


И два метода для перемещения фишки - без и с анимацией:

    public void move(int x, int y) {
        mRect.left = x;
        mRect.top = y;
        mRect.right = x + sWidth;
        mRect.bottom = y + sHeight;
    }

    public void moveAnimated(int x, int y) {
        moving = true;
        mTarget.x = x;
        mTarget.y = y;
    }


Потом, в методе отрисовки моего главного custom View я вызываю:

public class GameView extends View {
    private static final int DELAY = 30;
    private ArrayList mBarTiles = new ArrayList();

    @Override
    protected void onDraw(Canvas canvas) {
        if (mGameBoard.isFlinging()) {
            postInvalidateDelayed(DELAY);
        } else {
            for (SmallTile tile: mBoardTiles) {
                if (tile.moving) {
                    postInvalidateDelayed(DELAY);
                    break;
                }
            }
        }

        // нарисовать (с учетом scroll-а) игровое поле с фишками
        mGameBoard.draw(canvas, mBoardTiles);

        // нарисовать до 7 фишек внизу экрана - с анимацией
        for (SmallTile tile: mBarTiles) 
            tile.draw(canvas);

        // нарисовать 1 фишку которую тащит пользователь
        mDraggedTile.draw(canvas);
    }


Как видите наверху, если одна из фишек еще в процессе движения (и ее флаг moving=true),
то вызывается onDraw еще раз через 30 миллисекунд. 

То же самое для анимации игрового поля - если оно еще движется (isFlinging() возвращает
true) - но это уже работает без проблем.

Теперь собственно мой вопрос:

В процедуре отрисовке фишки нужно учесть ее движение и выставить ее актуальные координаты:

public void draw(Canvas canvas) {
    if (moving) {
        int dx = mTarget.x - mRect.left;
        int dy = mTarget.y - mRect.top;

        // ВОТ ЗДЕСЬ Я ЗАСТРЯЛ - КАКИЕ КООРДИНАТЫ ВЫСТАВИТЬ?
        move(XXX, YYY); 

        // ЕСЛИ ФИШКА ДОЛЕТЕЛА - НУЖНО ВЫСТАВИТЬ moving=false
    }

    // нарисовать background фишки - желтый прямоугольник
    canvas.drawRect(mRect, mPaint);

    // нарисовать Bitmap с буквой и ее числовым значением
    canvas.save();
    canvas.translate(mRect.left, mRect.top);
    Drawable d = sLetters.get(mLetter);
    d.draw(canvas);
    canvas.restore();
}


К сожалению, я не знаю как правильно подсчитать координаты!

У меня есть актуальные координаты фишки: mRect.left и mRect.top

У меня есть координаты куда должна прилететь фишка: mTarget.x и mTarget.y

Пока moving==true, каждые 30 миллисекунд вызывается код отрисовки фишки, которому
нужно дать новые координаты - но как их подсчитать?
    


Ответы

Ответ 1



Тогда вам нужно выбрать время анимации, допустим 1 секунда = 1000 миллисек. Тогда поделив 1000 на 30 (fps по сути) получаем 33 - количество раз, которое мы вызовем метод draw для достижения цели. Получается, что наш путь dx и dy тоже нужно поделить на 33, тогда мы получим шаг, на который у нас будет сдвигаться фишка, при каждом вызове draw. Ну а дальше остается прибавлять этот шаг к текущим координатам и с ними вызывать метод move: move(mRect.left + (int) (dx / 33), mRect.top + (int) (dy / 33));

Комментариев нет:

Отправить комментарий