Статьи

Android: контроллер жестов Multi-Touch

Немногие из наших проектов (наших или для клиентов) требовали реализации мультитач-функций для манипулирования изображениями стандартными жестами (перетаскивание, поворот, масштабирование). Хотя реализация мультитач-контроллера сложна и интересна, она отнимает много времени, и если что-то готово и просто работает, зачем изобретать велосипед? Поэтому мы решили провести небольшое исследование, чтобы увидеть, какой из проектов с открытым исходным кодом лучше всего подходит для наших нужд. Не упоминая контроллеры и проекты, которые нам не нравились и не находили привлекательными, я упомяну, что наш опыт работы с несколькими демонстрационными проектами, которые мы создали в то время, показал, что лучший мультитач-контроллер с простой в реализации логикой и простым в использовании понять синтаксис был Android Multitouch контроллер Люка Хатчисона . Источники доступны в соответствии с условиями лицензии MIT.

Что такое мультитач-контроллер?

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

Уже упомянутый мультитач-контроллер — это Android Multitouch Controller . Сначала нужно подготовить пустой проект Android и загрузить файл MultiTouchController.java. 90% того, что нам нужно для этого урока, здесь.

Необходимость

До сих пор примеры для контроллера Multitouch для Android приведены с использованием пакета ресурсов (Drawables), который впоследствии можно изменить через Canvas. Наша идея состоит в том, чтобы упростить этот процесс и создать на холсте только один подвижный объект. Итак, мы пойдем с простым растровым изображением, которое будет нарисовано поверх холста, которое впоследствии можно будет перемещать / масштабировать / поворачивать из его исходного положения. Поэтому сначала нам понадобятся две вещи: представление, реализующее интерфейс MultiTouchObjectCanvas, а затем пользовательский объект / сущность / виджет, который будет содержать логику для его рисования. Этот объект является «манипулятивным» и содержит растровое изображение, которое он представляет.

Поэтому, когда мы говорим C anvas, мы думаем о пользовательском представлении, которое вам нужно создать, которое будет реализовывать интерфейс MultiTouchObjectCanvas (где T — это объект Pinch, который необходимо изменить). Этот интерфейс сообщает нам, есть ли у нас объект (перетаскиваемый объект) или виджет в точке, где пользователь коснулся экрана. Если это так, реализация вернет соответствующий объект, иначе ноль. Таким образом, чтобы использовать этот интерфейс, нам нужно реализовать несколько методов, без которых мы не сможем достичь результатов. В любом случае, IDE скажет вам, что вы должны реализовать, это методы (среди конструкторов из родительского класса View и т. Д.):

1
2
3
4
@Override
public T getDraggableObjectAtPoint(PointInfo touchPoint) {
        return null;
}

Этот метод, как вы можете видеть, возвращает интересующий объект из места, где пользователь сделал сенсорный ввод. Это означает, что если пользователь коснулся x: 120; y: 100, этот метод должен проверить, находится ли эта точка в области, занятой виджетом. Когда вы увидите полную реализацию, вы узнаете, как это делается.

1
2
3
4
@Override
public void getPositionAndScale(T obj,
        PositionAndScale objPosAndScaleOut) {
}

Этот метод работает с объектом PositionAndScale для виджета, к которому прикасаются. Виджет передается в качестве первого аргумента, а PositionAndScale — в качестве второго, и это довольно информативно: при касании объекта ‘obj’ примените атрибуты ‘objPosAndScaleOut’ для положения, масштаба и угла. Но это только для начальной позиции экрана, что делает движение следующим обязательным методом.

1
2
3
4
5
@Override
public boolean setPositionAndScale(T obj,
 PositionAndScale newObjPosAndScale, PointInfo touchPoint) {
 return false;
}

Опять же, у нас есть виджет в качестве первого аргумента, свойства позиции / масштаба / угла нового объекта и вспомогательный объект из PointInfo, который сообщает, является ли это жестом с несколькими касаниями или одиночным (перетаскиванием).

1
2
3
@Override
public void selectObject(T obj, PointInfo touchPoint) {
}

Этот метод обеспечивает информирование контроллера о том, какой объект выбирается. Объект obj имеет выделенный объект. Поэтому, когда мы реализуем эти методы и установили объект контроллера мультитач, мы можем реализовать логику виджета, которая содержит данные за элементом (ами), которые нарисованы на холсте. Да, в этом случае нам нужен объект MultiTouchController, который должен быть определен следующим образом:

1
2
3
4
5
private MultiTouchController
         
          mMultiTouchController = new MultiTouchController
          
          (this);

И что с этим делать? Хорошо, когда происходит событие касания, нам нужно передать «все» этому контроллеру. И это делается путем переопределения onTouchEvent для пользовательского View / Canvas:

1
2
3
4
@Override
public boolean onTouchEvent(MotionEvent ev) {
 return mMultiTouchController.onTouchEvent(ev);
}

И с этим, и с несколькими другими сотрудниками у нас есть необходимый холст, который заботится о объектах, которые он рисует. Итак, нам нужна еще одна вещь, логика для виджета Pinch с его растровым изображением, координатами и т. Д. Поэтому мы создаем объект PinchWidget, который является модификацией некоторых примеров, которые идут с контроллером Multitouch. Суть этого объекта в этих двух методах:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public boolean setPos(PositionAndScale newImgPosAndScale, int uiMode, int uiModeAnisotropic, boolean isMultitouch) {
 boolean ret = false;
 float x = newImgPosAndScale.getXOff();
 float y = newImgPosAndScale.getYOff();
 if(isMultitouch) {
  x = mCenterX;
  y = mCenterY;
 }
 
 ret = setPos(x, y,
  (uiMode & uiModeAnisotropic) != 0 ? newImgPosAndScale.getScaleX() : newImgPosAndScale.getScale(),
   (uiMode & uiModeAnisotropic) != 0 ? newImgPosAndScale.getScaleY() : newImgPosAndScale.getScale(),
    newImgPosAndScale.getAngle());
 
 return ret;
}

и

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private boolean setPos(float centerX, float centerY, float scaleX, float scaleY, float angle) {
 float ws = (mImage.getWidth() / 2) * scaleX, hs = (mImage.getHeight() / 2) * scaleY;
 float newMinX = centerX - ws, newMinY = centerY - hs, newMaxX = centerX + ws, newMaxY = centerY + hs;
 mCenterX = centerX;
 mCenterY = centerY;
 mScaleFactor = scaleX;
 mAngle = angle;
 
 mMinX = newMinX;
 mMinY = newMinY;
 mMaxX = newMaxX;
 mMaxY = newMaxY;
 
 return true;
}

Эти два метода делают возможным перемещение, поскольку они дают информацию о координатах, масштабном коэффициенте и угле PinchWidget. Они каким-то образом связаны, то есть первый метод выполняет вычисления относительно данных, которые он получает от второго. Итак, теперь у нас есть координаты объекта, его масштаб и угол. Нам просто нужно нарисовать его, используя метод draw (Canvas):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public void draw(Canvas canvas) {
 Paint itemPaint = new Paint();
 itemPaint.setAntiAlias(true);
 itemPaint.setFilterBitmap(true);
 
 float dx = (mMaxX + mMinX) / 2;
 float dy = (mMaxY + mMinY) / 2;
 
 canvas.save();
 
 canvas.translate(dx, dy);
 canvas.rotate(mAngle * 180.0f / (float) Math.PI);
 canvas.translate(-dx, -dy);
 
 Rect srcRect = new Rect(0, 0, mImage.getWidth(), mImage.getHeight());
 Rect dstRect = new Rect((int) mMinX, (int) mMinY, (int) mMaxX, (int) mMaxY);
 
 canvas.drawBitmap(mImage, srcRect, dstRect, null);
 
 canvas.restore();
}

mImage — это растровое изображение элемента / виджета, который мы рисуем. Это должно быть нарисовано, чтобы увидеть что-то. И чтобы нарисовать его нам нужны его исходные и конечные пункты (в этом виде реализации). Источником является размер изображения (в нашем случае Rect [0,0,300,300]) и место назначения (где должно быть нарисовано), которое вычисляется из методов init и setPos PinchWidget. Затем изображение рисуется так же, как и любое другое растровое изображение, используя метод drawBitmap (…). Теперь вернемся к реализации MultiTouchView. Как упоминалось ранее, имея в виду, что мы объявили это представление в XML, мы будем использовать:

1
public MultiTouchView(Context context, AttributeSet attrs)

конструктор. Там мы инициализируем контекст. Также у нас есть этот метод:

1
2
3
4
public void setPinchWidget(Bitmap bitmap) {
 mPinchWidget = new PinchWidget(bitmap);
 mPinchWidget.init(mContext.getResources());
}

Этот метод сообщает MultiTouchView, что такое наш PinchWidget. Именно там он и создается, и, вызывая init () (ресурсы здесь передаются только для вычисления ширины и высоты отображения), мы вызываем весь механизм, который будет рисовать виджет. И из этого представления это происходит в его методе onDraw ():

1
2
3
4
5
6
7
@Override
public void onDraw(Canvas canvas) {
 super.onDraw(canvas);
   
 canvas.drawColor(Color.WHITE);
 mPinchWidget.draw(canvas);
}

Довольно просто, не правда ли? Так что, если все идет так, как описано, и у вас есть идея такого рода реализации, вы увидите что-то вроде этого на экране:


Вывод

Операции MultiTouch в Android — это та же математика, что и на других платформах. Но когда вам нужно время для такого рода проектов, Android Multitouch Contoroller — это инструмент, экономящий время, и когда вы загружаете его, читайте документированные методы, смотрите элегантный и красивый код и не забывайте благодарить разработчика за то, что он сделал.

Возможно, стоит упомянуть, что наше исследование заняло почти 1 год (9 месяцев, если быть точным) о том, какой контроллер Multitouch нам нужно использовать в наших настоящих и будущих приложениях.

Источники примера приложения доступны в нашем репозитории GitHub . Далее мы попытаемся осветить, что нужно сделать, чтобы показать много растровых изображений поверх представления Canvas. Или, если вы сами разберетесь, не стесняйтесь написать учебник, и мы будем рады опубликовать его здесь.

Приятного кодирования и не забудьте поделиться!

Ссылка: Android: контроллер жестов Multi-Touch от нашего партнера JCG Александра Балаловского в блоге 2dwarfs .