- Об открытости данных в Android-приложениях
- Display feature information
- Display a toast message
- Display a popup
- Рисование собственных представлений (View) в Android
- Получите полный контроль над представлением и оптимизируйте его производительность
- Введение
- Общий подход
- Обновление представления
- invalidate()
- requestLayout()
Об открытости данных в Android-приложениях
Немного информации о том, какие данные в вашем приложении могут быть доступны для других программ и какие меры можно предпринять, чтобы это предотвратить.
Компоненты
Архитектуру Android-приложения можно представить в виде набора компонент (activities, services и пр.) и связей между ними. Android API предоставляет несколько стандартных способов общения между компонентами приложения: старт активити/сервис через контекст, отправка сообщений-интентов, использование ServiceConnection или ContentProvider’а и другое. Обо всём этом можно почитать в любом туториале о передаче данных в Android. Однако, есть один нюанс, о котором, как правило, умалчивается. Речь идет о доступности ваших компонент и данных, передаваемых между ними, для других приложений.
Activity
Для запуска одной из своих активити вы, наверняка, используете специально подготовленный intent:
Используя Context и Class, которые вы передали в конструктор интента, Android определяет package приложения и сам класс нужной активити, после чего запускает её.
Вы также можете попробовать запустить чужую активити, зная ее package name и class name:
Результат — запущенная активити.
Значит ли это, что все активити могут быть запущены сторонним приложением? Нет. Если заменить в предыдущем примере classname на
то мы получим java.lang.SecurityException.
На самом деле разработчик приложения сам решает, какие активити будут доступны, а какие нет, указывая для каждой активити в манифесте тэг android:exported равным true или false соответственно. Значение по умолчанию для этого тега ‒ false, т.е. все активити в AndroidManifest.xml, не имеющие этого тега, доступны только внутри приложения.
Однако, посмотрев на манифест приложения Контакты (например, тут) можно заметить, что у PeopleActivity нет тега android:exported=«true», почему же нам удалось её запустить? Ответ можно найти в официальной документации: если активити содержит какой-либо интент-фильтр, то значением по умолчанию для android:exported будет true.
Разработчику приложения не нужно беспокоиться о видимости своих activity вне приложения до тех пор, пока в какой-либо активити не появляется intent-filter. Как только вы объявляете intent-filter, спросите себя, готовы ли вы к тому, что эта активити может быть запущена кем-либо еще. Если нет — не забудьте указать .
Service
Видимость сервиса определяется также как и видимость активити, и мы бы не стали уделять сервисам отдельный пункт, если бы не одно “но”. В случае с сервисом, его видимость может завести куда дальше, чем незапланированный старт, как в случае с активити. Если в вашем сервисе предусмотрена возможность биндинга, то этой возможностью могут воспользоваться и другие и получить ссылку на ваш сервис через ServiceConnection. Давайте рассмотрим пример подключения к стандартному сервису воспроизведения музыки.
Примечание: следующий пример актуален только на достаточно старых версиях плеера. В свежей версии разработчики разобрались с флагом exported и установили его в false 🙂
После биндинга в переменной boundService будет храниться ссылка на сервис. Далее вы можете воспользоваться java reflection или Android Interface Definition Language (AIDL), чтобы иметь возможность вызывать методы сервиса напрямую, в результате чего можно, например, остановить воспроизведение, если стандартный плеер уже что-то играет.
ContentProvider
Основное предназначение ContentProvider’а — предоставление данных другим приложениям. Именно поэтому по умолчанию значение тега для этого компонента приложения равно true. Но основное предназначение — не единственное. ContentProvider часто используется:
- при реализации подсказок при поиске
- при обмене данных через Sync Adapter
- как уровень абстракции над базой данных
Во всех этих случаях вам, скорее всего, не нужно предоставлять данные вне приложения. Если это действительно так, то не забудьте указать провайдеру .
BroadCast
Общение с помощью широковещательных сообщений можно разделить на 3 этапа:
- Создание intent’а
- Отправка intent’а через Context#sendBroadcast(. )
- Получение intent’а всеми зарегистрированными BroadcastReceiver’ами.
Каждый приёмник (receiver) при регистрации указывает некий IntentFilter, и сообщения будут доставляться этому приёмнику в соответствии с этим фильтром. Как можно догадаться, приёмник сообщения может удовлетворять фильтру и находиться вне нашего приложения. Вы можете добиться приватности рассылаемых сообщений, указав интенту нужный package:
или воспользоваться LocalBroadcastManager (доступен в support library), который также не даст улететь сообщению за границы приложения (на самом деле процесса):
Более того, LocalBroadcastManager позволяет регистрировать приёмники, так что вы можете быть уверены, что получаемые такими приёмниками интенты тоже являются локальными, а не прилетают извне.
Ресурсы
Если коротко, то все используемые в приложении ресурсы доступны для чтения вне приложения. Для получения некоторых из них достаточно знать только package name целевого приложения:
Если вам известна информация об активити внутри приложения, вы можете проделать подобные операции для каждой из них отдельно.
Узнав имя конкретного ресурса, можно получить и его:
Этим способом мы смогли отобразить картинку-превью для виджета Youtube внутри собственного приложения.
Главная загвоздка с ресурсами в том, что здесь нет способа их скрыть. Хранение приватных данных в ресурсах — плохая идея, но если у вас есть такая необходимость, лучше хранить их в зашифрованном виде.
В завершении статьи хотелось бы сказать еще несколько слов о файловой системе в Android. Этот вопрос уже хорошо освещен, многие знают, что в Android для каждого приложения создается собственная директория /data/data/«app package name», доступ к которой имеет только само приложение (или группа приложений c одним sharedUserId). В этой директории находятся файлы настроек SharedPreferences, файлы базы данных приложения, кэш и многое другое, однако, не стоит полагаться на защищенность этого хранилища. Файлы в этой директории недоступны только до тех пор, пока:
Источник
Display feature information
Spatial data is often represented using points, lines, and polygons. This data often has metadata information associated with it. For example, a point may represent the location of a restaurant and metadata about that restaurant may be its name, address, and type of food it serves. This metadata can be added as properties of a GeoJSON Feature . The following code creates a simple point feature with a title property that has a value of «Hello World!»
See the Create a data source documentation for ways to create and add data to the map.
When a user interacts with a feature on the map, events can be used to react to those actions. A common scenario is to display a message made of the metadata properties of a feature the user interacted with. The OnFeatureClick event is the main event used to detect when the user tapped a feature on the map. There’s also an OnLongFeatureClick event. When adding the OnFeatureClick event to the map, it can be limited to a single layer by passing in the ID of a layer to limit it to. If no layer ID is passed in, tapping any feature on the map, regardless of which layer it is in, would fire this event. The following code creates a symbol layer to render point data on the map, then adds an OnFeatureClick event and limits it to this symbol layer.
Display a toast message
A toast message is one of the easiest ways to display information to the user and is available in all versions of Android. It doesn’t support any type of user input and is only displayed for a short period of time. If you want to quickly let the user know something about what they tapped on, a toast message might be a good option. The following code shows how a toast message can be used with the OnFeatureClick event.
In addition to toast messages, There are many other ways to present the metadata properties of a feature, such as:
- Snackbar widget — Snackbars provide lightweight feedback about an operation. They show a brief message at the bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other elements on screen and only one can be displayed at a time.
- Dialogs — A dialog is a small window that prompts the user to make a decision or enter additional information. A dialog doesn’t fill the screen and is normally used for modal events that require users to take an action before they can continue.
- Add a Fragment to the current activity.
- Navigate to another activity or view.
Display a popup
The Azure Maps Android SDK provides a Popup class that makes it easy to create UI annotation elements that are anchored to a position on the map. For popups, you have to pass in a view with a relative layout into the content option of the popup. Here is a simple layout example that displays dark text on top of a while background.
Assuming the above layout is stored in a file called popup_text.xml in the res -> layout folder of an app, the following code creates a popup, adds it to the map. When a feature is clicked, the title property is displayed using the popup_text.xml layout, with the bottom center of the layout anchored to the specified position on the map.
The following screen capture shows popups appearing when features are clicked and staying anchored to their specified location on the map as it moves.
Источник
Рисование собственных представлений (View) в Android
Получите полный контроль над представлением и оптимизируйте его производительность
В преддверии старта курса «Android Developer. Professional» приглашаем всех желающих принять участие в открытом вебинаре на тему «Пишем gradle plugin».
А пока делимся переводом полезного материала.
Введение
Разработчики постоянно проектируют различные виды пользовательских интерфейсов с помощью XML, но в дополнение к этому можно довольно легко освоить создание собственных представлений, которые открывают новые преимущества и позволяют избежать повторного использования шаблонного кода.
В Android доступен широкий набор готовых виджетов и макетов для создания пользовательского интерфейса, однако они не могут удовлетворить все требования наших приложений. И здесь на помощь приходит возможность создания собственных представлений. Создав собственный подкласс представления, можно получить максимально полный контроль за внешним видом и функционалом экранного элемента.
Прежде чем приниматься за работу с собственными представлениями, полезно изучить жизненный цикл представления.
Зачем создавать собственные представления?
Чтобы реализовать собственное представление, в большинстве случаев понадобится больше времени, чем если использовать обычные представления. Создавать собственные представления стоит лишь в том случае, если нет другого, более простого способа реализовать нужную вам возможность или если у вас есть какие-либо из указанных ниже проблем, которые можно устранить за счет создания собственного представления.
Производительность: в вашем макете много представлений и вы хотите оптимизировать их, нарисовав одно, более легкое собственное представление.
Имеется сложная иерархия представлений, которую трудно использовать и поддерживать.
Необходимо создать специализированное представление, требующее рисования вручную.
Общий подход
Чтобы приступить к созданию компонентов для реализации собственных представлений, необходимо выполнить следующие основные шаги.
Создать класс, расширяющий базовый класс или подкласс представления.
Реализовать конструкторы, использующие атрибуты из XML-файла.
Переопределить некоторые методы родительского класса (onDraw(), onMeasure() и т. д.) в соответствии с нашими требованиями.
После выполнения этих шагов созданный расширяющий класс можно использовать вместо представления, на основе которого он был создан.
Пример
В одном из моих проектов мне нужно было создать круглый виджет TextView для отображения количества уведомлений. Чтобы достичь этой цели, нужно создать подкласс TextView.
Шаг 1. Создадим класс с именем CircularTextView .
Шаг 2. Расширим класс виджета TextView. Здесь под TextView в IDE выдается ошибка, в которой сообщается, что у этого типа есть конструктор и он должен быть инициализирован.
Шаг 3. Добавим конструкторы в класс.
Это можно сделать двумя способами.
Первый способ добавления конструкторов в класс показан ниже.
Другой способ заключается в добавлении аннотации @JvmOverloads к вызову конструктора, как показано ниже.
Часто нас сбивает с толку то, что у представления есть несколько разных типов конструкторов.
View(Context context)
Простой конструктор для динамического создания представления из программного кода. Здесь параметр context — это контекст, в котором работает представление и через который можно получить доступ к текущей теме, ресурсам и т. д.
View(Context context, @Nullable AttributeSet attrs)
Конструктор, который вызывается при формировании представления из XML-файла. Он вызывается, когда представление создается из XML-файла, содержащего атрибуты представления. В этом варианте конструктора используется стиль по умолчанию (0), поэтому применяются только те значения атрибутов, которые есть в теме контекста и заданном наборе AttributeSet .
Шаг 4. Самый важный шаг в отрисовке собственного представления — это переопределение метода onDraw() и реализация необходимой логики отрисовки внутри этого метода.
Метод OnDraw (canvas: Canvas?) имеет параметр Canvas (холст), с помощью которого компонент представления может отрисовывать себя. Для рисования на холсте необходимо создать объект Paint.
Как правило, процесс рисования определяется двумя аспектами:
что рисовать (определяется объектом Canvas);
как рисовать (определяется объектом Paint).
Например, Canvas предоставляет метод для рисования линии, а Paint предоставляет методы для определения цвета этой линии. В нашем случае объект Canvas в классе CircularTextView предоставляет метод для рисования окружности, а объект Paint заполняет ее цветом. Проще говоря, Canvas определяет, какие фигуры можно нарисовать на экране, а Paint определяет свойства нарисованных фигур — цвет, стиль, шрифт и т. д.
Давайте займемся кодом. Мы создаем объект Paint и присваиваем ему некоторые свойства, а затем рисуем фигуру на холсте (объект Canvas), используя наш объект Paint. Метод onDraw() будет выглядеть так:
IDE показывает предупреждение о том, что следует избегать выделения объектов во время операций отрисовки или операций с макетом. Это предупреждение возникает потому, что метод onDraw() много раз вызывается при отрисовке представления, в котором каждый раз создаются ненужные объекты. Поэтому, чтобы избежать ненужного создания объектов, мы вынесем соответствующую часть кода за пределы метода onDraw() , как показано ниже.
При выполнении отрисовки всегда помните о том, что следует повторно использовать объекты вместо создания новых. Ваша IDE может указать на потенциальные проблемы, но полагаться на нее не стоит. Например, она не сможет отследить случай, когда объекты создаются внутри методов, вызываемых из метода onDraw() . Поэтому лучше проверять все самостоятельно.
Шаг 5. Мы закончили с рисованием. Теперь давайте внесем этот класс представления в XML.
Добавьте этот XML-макет в вашу активность (Activity) и запустите приложение. Вот что будет на экране.
Выглядит неплохо, правда? Теперь сделаем так, чтобы значение динамическому свойству цвета в circlePaint назначалось из активности, а также добавим контур к кружку. Для этого в классе CircularTextView необходимо создать несколько методов-сеттеров, чтобы можно было вызывать эти методы и устанавливать свойства динамически.
Для начала давайте реализуем настройку цвета отрисовки. Для этого создадим сеттер, как показано ниже.
Теперь мы можем устанавливать цвет из нашей активности динамически, вызывая этот метод.
Неплохо, правда? Теперь давайте добавим контур к кружку. Контур будет задаваться двумя входными параметрами: шириной линии контура и ее цветом. Чтобы задать цвет линии контура, нам нужно создать объект Paint точно так же, как мы это делали для кружка. Чтобы задать ширину линии контура, мы создадим переменную, установим для нее нужное значение и используем его в методе onDraw() . Полный код будет выглядеть так:
Теперь в активности можно динамически настраивать эти атрибуты нужным образом.
Далее давайте запустим приложение, устанавливая различные цвета для нашего виджета.
Итак, теперь стало ясно, как динамически устанавливать свойства из активности, но возникает вопрос о том, как устанавливать атрибуты из XML. Продолжим наше исследование.
Для начала создадим файл с именем attrs.xml в папке values. Этот файл будет содержать все атрибуты для различных представлений, которые мы создаем сами. В приведенном ниже примере у нашего представления под названием CircularTextView имеется атрибут ct_circle_fill_color , который принимает значение цвета. Аналогичным образом мы можем добавить и другие атрибуты.
Затем нам нужно будет прочитать эти свойства в классе, который мы создали для реализации собственного представления. В блоке инициализации мы считываем набор атрибутов, как показано ниже.
Теперь просто переходим к XML-макету и устанавливаем значение свойства, соответствующее нужному цвету, после чего запускаем приложение. На выходе мы увидим нужный результат.
В моем случае результат был таким:
Примечание. При рисовании не задавайте жестко размер вашего представления, так как им могут воспользоваться другие разработчики с применением других размеров. Рисуйте представление в соответствии с его текущим размером.
Обновление представления
Итак, мы задали собственное представление. Если мы хотим обновлять представление при изменении какого-нибудь свойства или по какой-то другой причине, этого можно добиться двумя основными способами.
invalidate()
invalidate() — это метод, который инициирует принудительную перерисовку определенного представления. Проще говоря, метод invalidate() следует вызывать в случае, когда требуется изменение внешнего вида представления.
requestLayout()
Если в какой-то момент происходит изменение состояния представления, то метод requestLayout() сообщает системе представлений, что необходимо сделать перерасчет фаз «измерение» (Measure) и «макет» (Layout) для данного представления (измерение → макет → рисование). Проще говоря, метод requestLayout() следует вызывать в случае, когда требуется изменение границ представления.
Теперь, я надеюсь, вы знаете в общих чертах, как создавать собственные представления. Чтобы они демонстрировали отличную производительность, необходимо освоить все описанные здесь методы.
Источник