Страницы

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

пятница, 5 июля 2019 г.

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

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

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

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