Лекция__ . Интерфейс приложений: ресурсы и особенности touch-интерфейса




 

План лекции:

1. Ресурсы приложений

2. Особенности touch-interface

 

1. Ресурсы

При разработке мобильных приложений необходимо выработать привычку отделять ресурсы приложения от кода. К ресурсам приложения могут относиться: изображения, строки, цвета, компоновки элементов пользовательского интерфейса (layout) и т. д. Отделение ресурсов от кода позволяет использовать альтернативные ресурсы для различных конфигураций устройств: язык, разрешение экрана и т. д. Для обеспечения совместимости с различными конфигурациями, ресурсы необходимо сгруппировать в директории по типу ресурсов и конфигурации устройства, полученные директории поместить в папку res/.

Для любого типа ресурсов можно определить две группы. Первая определяет ресурсы, которые будут использоваться независимо от конфигурации устройства или в том случае, когда под конфигурацию нет подходящих альтернативных ресурсов. Эта группа называется ресурсы по умолчанию (default). Вторая группа определяет ресурсы, подходящие для определенной конфигурации устройства, размещается в директории с названием, обозначающим данную конфигурацию. Такие ресурсы называются альтернативными.

Каждый тип ресурсов необходимо размещать в специальной поддиректории папки res/. Рассмотрим основные из этих поддиректорий:

animator/ - содержит XML файлы, которые определяют свойства анимации;

anim/ - содержит XML файлы, которые определяют анимацию преобразований;

color/ - содержит XML файлы, которые определяют списки цветов;

drawable/ - содержит графические файлы или XML файлы, которые компилируются в графические ресурсы;

layout/ - содержит XML файлы, которые определяют компоновку элементов пользовательского интерфейса;

menu/ - содержит XML файлы, которые определяют все меню приложения;

values/ - содержит XML файлы, которые определяют простые значения, таких ресурсов как, строки, числа, цвета.

Следует отметить, что файлы ресурсов нельзя размещать в папку

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

Все ресурсы, которые содержатся в рассмотренных поддиректориях являются ресурсами по умолчанию. Понятно, что различные типы устройств могут требовать различных типов ресурсов. Например, для устройств с разными размерами экрана компоновки элементов пользовательского интерфейса должны отличаться.

Чтобы определить зависимые от конфигурации альтернативы для множества ресурсов:

1. необходимо создать директорию в каталоге res/, присвоить этой директории имя в следующей форме:

имя_ресурса-спецификатор_конфигурации, где

• имя_ресурса - имя директории, соответствующего ресурса по умолчанию (см. выше);

• спецификатор_конфигурации - имя, определяющее конфигурацию, для которой используются данные ресурсы.

Полный список доступных спецификаторов: здесь (https://developer.android.com/guide/topics/resources/providing-resources.html);

2. необходимо сохранить ресурсы в новой директории, файл ресурсов должен называться в точности так же, как соответствующий файл ресурсов по умолчанию.

Например, если компоновка элементов пользовательского интерфейса сохранена, как ресурс по умолчанию, в папке res/layout/, можно (скорее даже нужно) определить альтернативную компоновку элементов пользовательского интерфейса, соответствующую горизонтальной (альбомной) ориентации экрана смартфона и сохранить ее в папке res/layout-land/. Android автоматически определит подходящую компоновку, сверяя текущее состояние устройства с именами папок в каталоге /res.

Все ресурсы после определения могут быть доступны по ссылке на их ID, которые определены в автоматически генерируемом классе R.

Для каждого типа ресурсов в R классе существует подкласс, например, R.drawable для всех графических ресурсов. ID ресурса всегда имеет две составляющие:

• тип ресурса - все ресурсы группируются по типам, например, string, drawable, layout;

• имя ресурса - либо имя файла без расширения, либо значение атрибута android:name в XML файле для простого значения.

Получить доступ к ресурсу можно двумя способами:

• в коде: можно использовать выражения вида R.тип_ресурса.имя_ресурса, например, R.string.hello;

• в XML: используется специальный XML синтаксис, который соответствует ID определенному в R классе, например, @string/hello.

 

 

2. Особенности touch-interface

 

Особенностью большинства мобильных устройств является наличие сенсорного экрана и возможность управления пальцем (touch-interface), очевидно, что это необходимо учитывать и использовать при разработке приложений. Смартфон сегодня является спутником человека всегда и везде, в связи с этим, довольно часто используется, как фотоаппарат или проигрыватель музыки, а также смартфоны все чаще становятся инструментами ориентирования на местности.

Для начала обратим внимание на экран смартфона. В современных смартфонах экран занимает практически всю площадь передней панели устройства, имеет высокое разрешение и является чувствительным к прикосновениям. Благодаря такой чувствительности, для взаимодействия с устройством и его приложениями можно использовать виртуальные элементы управления, чаще всего кнопки, отображаемые на экране.

В связи с чем отпадает необходимость в физических кнопках. В смартфонах реализуется, так называемый, touch-интерфейс - интерфейс, основанный на виртуальных элементах управления, выбор которых выполняется простым касанием, а также на использовании жестов (gestures).

Если точек касания несколько (т. е. используется несколько пальцев), такой интерфейс, уже называется multi-touch.

Рассмотрение особенностей смартфонов будет неполным, если оставить без внимания датчики и сенсоры, которыми оснащены большинство устройств. Эти микроустройства обеспечивают связь смартфона с окружающей средой и добавляют новые удивительные функции. С помощью датчика приближения, например, можно отключать подсветку экрана при приближении телефона к уху пользователя во время разговора, блокировать экран, чтобы не было возможности случайно нажать на отбой. Акселерометр может использоваться для смены ориентации экрана, для управления в играх, особенно симуляторах, а также в качестве шагомера. Датчик освещенности позволяет регулировать яркость экрана. Гироскоп может применяться для определения более точного позиционирования устройства в пространстве. Все рассмотренные особенности в совокупности увеличивают привлекательность смартфонов, позволяют разработчикам создавать приложения с разнообразными, полезными, интересными и иногда неожиданными функциями. Далее в лекции рассмотрим перечисленные возможности смартфонов более подробно и узнаем, как можно их использовать при разработке приложений.

 

Сенсорное (touch) управление

 

Сенсорное управление подразумевает использование сенсорных жестов для взаимодействия с приложением. Ниже представлен набор жестов, поддерживаемый системой Android.

 

Касание (touch). Использование: Запуск действия по умолчанию для выбранного элемента. Выполнение: нажать, отпустить.

 

Длинное касание (long touch). Использование: Выбор элемента. Не стоит использовать этот жест для вызова контекстного меню. Выполнение: нажать, ждать, отпустить.

 

Скольжение или перетаскивание (swipe or drag). Использование: Прокрутка содержимого или навигация между элементами интерфейса одного уровня иерархии. Выполнение: нажать, переместить, отпустить.

 

Скольжение после длинного касания (long press drag). Использование: Перегруппировка данных или перемещение в контейнер. Выполнение: длительное касание, переместить, отпустить.

 

Двойное касание (double touch). Использование: Увеличение масштаба, выделение текста. Выполнение: быстрая последовательность двух касаний.

 

Перетаскивание с двойным касанием (double touch drag). Использование: Изменение размеров: расширение или сужение по отношению к центру жеста. Выполнение: касание, следующее за двойным касанием со смещением вверх или вниз при этом:

• смещение вверх уменьшает размер содержимого;

• смещение вниз увеличивает размер содержимого.

 

Сведение пальцев (pinch close). Использование: уменьшение содержимого, сворачивание. Выполнение: касание экрана двумя пальцами, свести, отпустить.

 

Разведение пальцев (pinch open). Использование: увеличение содержимого, разворачивание. Выполнение: касание экрана двумя пальцами, развести, отпустить.

 

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

 

Сбор данных о сенсорных событиях

 

Основные действия, которые может произвести пользователь при взаимодействии с сенсорным экраном: коснуться экрана пальцем, переместить палец по экрану и отпустить. Эти действия распознаются системой Android, как сенсорные события (touch-события). Каждый раз при появлении сенсорного события инициируется вызов метода onTouchEvent(). Обработка события станет возможной, если этот метод реализован в классе активности или некоторого компонента, иначе событие просто игнорируется.

Жест начинается, при первом касании экрана, продолжается пока система отслеживает положение пальцев пользователя и заканчивается получением финального события, состоящего в том, что ни один палец не касается экрана. Объект MotionEvent, передаваемый в метод onTouchEvent(), предоставляет детали каждого взаимодействия. Рассмотрим основные константы класса MotionEvent, определяющие сенсорные события:

• MotionEvent.ACTION_DOWN - касание экрана пальцем, является начальной точкой для любого сенсорного события или жеста;

• MotionEvent.ACTION_MOVE - перемещение пальца по экрану;

• MotionEvent.ACTION_UP - поднятие пальца от экрана.

Приложение может использовать предоставленные данные для распознавания жеста. Можно реализовать свою собственную обработку событий для распознавания жеста, таким образом можно работать с произвольными жестами в приложении.

Если же в приложении необходимо использовать стандартные жесты, описанные выше, можно воспользоваться классом GestureDetector. Этот класс позволяет распознать стандартные жесты без обработки отдельных сенсорных событий.

Распознавание жестов Android предоставляет класс GestureDetector для распознавания стандартных жестов. Некоторые жесты, которые он поддерживает включают: onDown(), onLongPress(), onFling() и т. д. Можно использовать класс GestureDetector в связке с методом onTouchEvent().

Начиная с версии 1.6, Android предоставляет API для работы с жестами, который располагается в пакете android.gesture и позволяет сохранять, загружать, создавать и распознавать жесты. Виртуальное устройство Android (AVD), начиная все с той же версии 1.6, содержит предустановленное приложение, которое называется Gesture Builder и позволяет создавать жесты.

После создания жесты сохраняются на SD карте виртуального устройства и могут быть добавлены в приложение в виде бинарного ресурса. Для распознавания жестов необходимо добавить компонент GestureOverlayView, в XML файл активности. Этот компонент может быть добавлен как обычный элемент графического интерфейса пользователя и встроен в компоновку, например RelativeLayout. C другой стороны он может быть использован, как прозрачный слой поверх других компонентов, в этом случае в XML файле активности он должен быть записан, как корневой элемент.

Кроме всего вышеперечисленного, для использования собственных жестов в приложении необходимо реализовать интерфейс OnGesturePerformedListener и его метод onGesturePerformed().

 

Рассмотрим реализацию методов touch-интерфейса на примере приложения с перелистыванием картинок, их скроллингом и масштабированием.

 

Главным элементом нашего приложения является класс ImageViewer. Компонент представляет собой activity, который вызывается при старте из главного activity. После выбора файла, мы его загружаем и показываем на экране с помощью класса ImageViewer. Рассмотрим класс более подробно.

Класс является наследником класса View и переопределяет только один его метод onDraw. Также класс содержит конструктор и метод загрузки изображения:

 

public class ImageViewer extends View

{

private Bitmap image = null;

public ImageViewer(Context context)

{

super(context);

}

 

@Override

public void onDraw(Canvas canvas)

{

if (image!= null) canvas.drawBitmap(image, 0, 0, null);

}

 

public void loadImage(String fileName)

{

image = BitmapFactory.decodeFile(fileName);

}

}

 

Если мы загрузим картинку по размерам больше чем экран смартфона, то отобразится только часть ее и у нас не будет способа ее подвинуть или уменьшить.

 

Добавим теперь возможность скроллинга. Скроллинг по своей сути представляет собой жест, при котором пользователь дотрагивается пальцем к экрану, передвигает его не отрывая, и отпускает. Для того чтоб иметь возможность обрабатывать события связанные с тач-скрином, нужно переопределить метод onTouchEvent. Метод принимает один параметр типа MotionEvent и должен возвратить true в случае обработки события. Через этот метод можно реализовать поддержку любого жеста, включая скроллинг.

Для распознавания скроллинга нам нужно зафиксировать момент дотрагивания, перемещения и отпускания. К счастью нету необходимости делать это вручную так как в Android SDK есть класс делающий всю работу за нас. Таким образом для того чтоб распознать жест скроллинга, нужно добавить в наш класс поле типа GestureDetector которое инициализируется объектом реализующим интерфейс OnGestureListener (именно этот объект будет получать события скроллинга). Также нужно переопределить метод onTouchEvent в классе ImageViewer и передавать обработку событий из него в наш объект типа OnGestureListener. Измененный класс ImageViewer (без неизмененных методов) представлен ниже:

 

public class ImageViewer extends View

{

private Bitmap image = null;

 

private final GestureDetector gestureDetector;

 

public ImageViewer(Context context)

{

super(context);

gestureDetector = new GestureDetector(context, new MyGestureListener());

}

 

@Override

public boolean onTouchEvent(MotionEvent event)

{

if (gestureDetector.onTouchEvent(event)) return true;

return true;

}

 

private class MyGestureListener extends SimpleOnGestureListener

{

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)

{

scrollBy((int)distanceX, (int)distanceY);

return true;

}

}

}

 

Как видно на самом деле ми наследуем MyGestureListener не от OnGestureListener, а от SimpleOnGestureListener. Последний класс просто реализует интерфейс OnGestureListener с помощью пустых методов. Этим мы избавляем себя от реализации всех методов, выбирая только те, что нужно.

 

Теперь если загрузить большую картинку, мы, по крайней мере, сможем скролить ее. Но: во первых мы можем проскроллить за рамки картинки, во вторых нету скроллбаров, которые бы подсказали нам где мы находимся и сколько еще осталось до краев.

 

Решим для начала вторую проблему. Поиск в Интернет приводит нас к тому, что нужно переопределить методы computeHorizontalScrollRange и computeVerticalScrollRange. Эти методы должны возвратить реальные размеры картинки (на самом деле есть еще методы которые имеют отношение к скроллбарам – это методы computeHorizontalScrollExtent, computeHorizontalScrollOffset и такая же пара для вертикального скроллбара. Если переопределить и их, то тогда возвращать можно более произвольные значения). Но этого оказывается недостаточно – скроллбары в первых нужно включить, во вторых проинициализировать. Включаются они методами setHorizontalScrollBarEnabled и setVerticalScrollBarEnabled, инициализируются методом initializeScrollbars. Но вот незадача – последний метод принимает немного непонятный параметр типа TypedArray. Этот параметр должен содержать в себе набор стандартных для View атрибутов. Список можно увидеть здесь в таблице «XML Attributes». Если бы мы создавали наш view из XML, Android runtime бы автоматически составил такой список. Но так как мы создаем класс программно, нужно также создать этот список программно. Для этого нужно создать файл attrs.xml в каталоге res\values с таким содержимым:

 

<?xml version="1.0" encoding="utf-8"?>

<resources>

<declare-styleable name="View">

<attr name="android:background"/>

<attr name="android:clickable"/>

<attr name="android:contentDescription"/>

<attr name="android:drawingCacheQuality"/>

<attr name="android:duplicateParentState"/>

<attr name="android:fadeScrollbars"/>

<attr name="android:fadingEdge"/>

<attr name="android:fadingEdgeLength"/>

<attr name="android:fitsSystemWindows"/>

<attr name="android:focusable"/>

<attr name="android:focusableInTouchMode"/>

<attr name="android:hapticFeedbackEnabled"/>

<attr name="android:id"/>

<attr name="android:isScrollContainer"/>

<attr name="android:keepScreenOn"/>

<attr name="android:longClickable"/>

<attr name="android:minHeight"/>

<attr name="android:minWidth"/>

<attr name="android:nextFocusDown"/>

<attr name="android:nextFocusLeft"/>

<attr name="android:nextFocusRight"/>

<attr name="android:nextFocusUp"/>

<attr name="android:onClick"/>

<attr name="android:padding"/>

<attr name="android:paddingBottom"/>

<attr name="android:paddingLeft"/>

<attr name="android:paddingRight"/>

<attr name="android:paddingTop"/>

<attr name="android:saveEnabled"/>

<attr name="android:scrollX"/>

<attr name="android:scrollY"/>

<attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>

<attr name="android:scrollbarAlwaysDrawVerticalTrack"/>

<attr name="android:scrollbarDefaultDelayBeforeFade"/>

<attr name="android:scrollbarFadeDuration"/>

<attr name="android:scrollbarSize"/>

<attr name="android:scrollbarStyle"/>

<attr name="android:scrollbarThumbHorizontal"/>

<attr name="android:scrollbarThumbVertical"/>

<attr name="android:scrollbarTrackHorizontal"/>

<attr name="android:scrollbarTrackVertical"/>

<attr name="android:scrollbars"/>

<attr name="android:soundEffectsEnabled"/>

<attr name="android:tag"/>

<attr name="android:visibility"/>

</declare-styleable>

</resources>

 

В файле просто перечислены все атрибуты, кроме некоторых на которые указывает компилятор как на ошибку – видимо в документации список приведен самый последний. Измененный класс ImageViewer (кроме неизменных методов):

 

public class ImageViewer extends View

{

private Bitmap image = null;

private final GestureDetector gestureDetector;

 

public ImageViewer(Context context)

{

super(context);

gestureDetector = new GestureDetector(context, new MyGestureListener());

// init scrollbars

setHorizontalScrollBarEnabled(true);

setVerticalScrollBarEnabled(true);

TypedArray a = context.obtainStyledAttributes(R.styleable.View);

initializeScrollbars(a);

a.recycle();

}

 

@Override

protected int computeHorizontalScrollRange()

{

return image.getWidth();

}

 

@Override

protected int computeVerticalScrollRange()

{

return image.getHeight();

}

}

 

Не хотелось бы на этом останавливаться, поэтому давайте добавим поддержку жеста «бросок» (fling). Этот жест есть просто дополнение к жесту скроллинга, но учитывается скорость перемещения пальца в последние моменты (перед отпусканием), и если она не нулевая, скроллинг продолжается с постепенным затуханием. Поддержка этого жеста уже заложена в GestureDetector – поэтому нам нужно всего лишь переопределить метод onFling в классе MyGestureListener. Отловив это событие нам нужно еще некоторое время изменять положение скроллинга. Конечно, это можно сделать «вручную» с помощью таймеров или еще как, но опять же в Android SDK уже есть класс, реализующий нужный функционал. Поэтому нужно добавить в класс ImageViewer еще одно поле типа Scroller, которое и будет заниматься «остаточным» скроллингом – для старта скроллинга нужно вызвать его метод fling. Также нужно показать скроллбары (они ведь прячутся, когда не нужны) вызовом метода awakenScrollBars. И последнее что нужно сделать – это переопределить метод computeScroll, который должен непосредственно делать скроллинг с помощью метода scrollTo (класс Scroller сам не занимается скроллингом – он просто работает с координатами). Код измененного класса ImageViewer представлен ниже:

 

public class ImageViewer extends View

{

private Bitmap image = null;

private final GestureDetector gestureDetector;

private final Scroller scroller;

 

public ImageViewer(Context context)

{

super(context);

gestureDetector = new GestureDetector(context, new MyGestureListener());

scroller = new Scroller(context);

// init scrollbars

setHorizontalScrollBarEnabled(true);

setVerticalScrollBarEnabled(true);

TypedArray a = context.obtainStyledAttributes(R.styleable.View);

initializeScrollbars(a);

a.recycle();

}

 

@Override

public void computeScroll()

{

if (scroller.computeScrollOffset())

{

int oldX = getScrollX();

int oldY = getScrollY();

int x = scroller.getCurrX();

int y = scroller.getCurrY();

scrollTo(x, y);

if (oldX!= getScrollX() || oldY!= getScrollY())

{

onScrollChanged(getScrollX(), getScrollY(), oldX, oldY);

}

postInvalidate();

}

}

 

private class MyGestureListener extends SimpleOnGestureListener

{

@Override

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

{

scroller.fling(getScrollX(), getScrollY(), -(int)velocityX, -(int)velocityY, 0, image.getWidth() - getWidth(), 0, image.getHeight() - getHeight());

awakenScrollBars(scroller.getDuration());

return true;

}

}

}

 

В завершения разговора о жесте fling надо сделать одну мелочь – при прикосновении пальцем во время скроллинга от броска, нужно остановить скроллинг. На этот раз мы это сделаем «вручную» в методе onTouchEvent. Измененный метод представлен ниже:

 

@Override

public boolean onTouchEvent(MotionEvent event)

{

// check for tap and cancel fling

if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN)

{

if (!scroller.isFinished()) scroller.abortAnimation();

}

if (gestureDetector.onTouchEvent(event)) return true;

return true;

}

 

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

 

@Override

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

{

boolean scrollBeyondImage = ((getScrollX() < 0) || (getScrollX() > image.getWidth()) || (getScrollY() < 0) || (getScrollY() > image.getHeight()));

if (scrollBeyondImage) return false;

scroller.fling(getScrollX(), getScrollY(), -(int)velocityX, -(int)velocityY, 0, image.getWidth() - getWidth(), 0, image.getHeight() - getHeight());

awakenScrollBars(scroller.getDuration());

return true;

}

 

Теперь мы опять можем беспрепятственно скролить за рамки картинки. Кажется, эту проблему мы уже вспоминали… У нее есть элегантное решение, лежащее в том, что при отпускании пальца (при завершении скроллинга за рамками картинки) нужно картинку плавно вернуть в «положенное» место. И опять мы это сделаем «вручную» в методе onTouchEvent:

 

@Override

public boolean onTouchEvent(MotionEvent event)

{ // check for tap and cancel fling

if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN)

{

if (!scroller.isFinished()) scroller.abortAnimation();

}

if (gestureDetector.onTouchEvent(event)) return true;

// check for pointer release

if ((event.getPointerCount() == 1) && ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP))

{

int newScrollX = getScrollX();

if (getScrollX() < 0) newScrollX = 0;

else if (getScrollX() > image.getWidth() - getWidth()) newScrollX = image.getWidth() - getWidth();

int newScrollY = getScrollY();

if (getScrollY() < 0) newScrollY = 0;

else if (getScrollY() > image.getHeight() - getHeight()) newScrollY = image.getHeight() - getHeight();

if ((newScrollX!= getScrollX()) || (newScrollY!= getScrollY()))

{

scroller.startScroll(getScrollX(), getScrollY(), newScrollX - getScrollX(), newScrollY - getScrollY());

awakenScrollBars(scroller.getDuration());

}

}

return true;

}

 

Вот теперь с уверенностью можно сказать, что со скроллингом мы разобрались. Можем переходить к последнему жесту, который хотелось бы реализовать – это жест pinch zoom.

 

Со стороны жест выглядит как растягивание или сжатие чего-то воображаемого на экране смартфона двумя пальцами. Пошагово жест происходит так: нажатие одним пальцем, нажатие вторым пальцем, изменение положения одного или двух пальцев не отпуская, отпускание второго пальца. Для определения величины масштабирования нужно вычислить соотношение между расстояниями между пальцами в момент начала жеста и в момент окончания жеста. Расстояние между пальцами находится по формуле sqrt(pow(x2 – x1, 2) + pow(y2 – y1, 2)). Также нужно отметить некоторое положение скроллинга, которое нужно сохранять – ведь если жестом увеличить картинку, то положение скроллинга изменится (из-за измененного размера картинки). Это положение – а точнее точка, положение которой нужно сохранить, в терминологии Android SDK называется фокальной точкой, и находиться она посередине между двумя пальцами.

Реализовать жест как всегда можно самому, но и это, к счастью, уже реализовано в Android SDK (правда, только начиная с версии 2.2). Поможет в этом класс ScaleGestureDetector, инстанс которого добавим в наш класс. ScaleGestureDetector инициализируется обьектом, поддерживающим интерфейс OnScaleGestureListener, поэтому создадим также внутренний класс MyScaleGestureListener, который реализует методы onScaleBegin, onScale и onScaleEnd. Не забываем передать управление ScaleGestureDetector из метода onTouchEvent. Ну и самое главное – нужно как-то использовать данные масштабирования: их нужно учитывать во всех местах, где раньше фигурировали ширина и высота картинки (т.е. фактически нужно умножить эти параметры на коэффициент масштабирования). Финальный код класса ImageViewer можно посмотреть в исходниках.

https://habr.com/ru/post/120931/



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2022-09-06 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: