- Введение
- Специфические задачи, специфические view
- Создание custom view
- Добавление логики отображения
- Использование полученного view
- Добавление xml атрибутов
- Android navigation component. Простые вещи, которые приходится делать самому
- Переключение между пунктами меню
- Открытие новой Activity
- Передача параметров в startDestination
- Xmlns app http schemas android com apk res auto что это
Введение
Это будет серия постов на тему создания пользовательского view компонента для Android. Я покажу как нарисовать содержимое компонента, как работают layouting и measuring, как реализовать view groups и как анимировать содержимое компонента. В этом посте я объясню, как расширить стандартный view, как его использовать и как создать свои собственные xml атрибуты.
Специфические задачи, специфические view
Стандартные view компоненты, которые предоставляет Android, могут использоваться для многих задач и ситуаций. Тем не менее, в наших приложениях наибольшую часть кода часто занимает конфигурация этих view для специфических задач. Этот конфигурационный код часто находится в activity вашего приложения, поэтому их сложно содержать в чистоте. А при наличии большого количества различного кода возникают сложности и с его разделением в отдельные классы.
Предположим, вы работаете над приложением, которое содержит статистику тренировок пользователя. Например, общая дистанция, общее время и так далее, в зависимости от типа тренинга. Для того чтобы отображать данные в удобном виде, вы решили их адаптировать, основываясь на их продолжительности. Если это 2378 секунд, вы хотите отображать это время как «40 минут», а если это 18550 секунд, то лучше было бы отображать на дисплей «5 часов 9 минут».
Создание custom view
Один из способов решения этой задачи — создать специализированный view, который будет должным образом обрабатывать ваш текст. Давайте начнем с создания нашего собственного класса DurationTextView, который будет расширять класс TextView.
TextView, как и все view классы, имеет три различных конструктора: первый просто принимает контекст (Context), второй принимает контекст и набор атрибутов (AttributeSet), а третий еще дополнительно принимает стиль «по умолчанию». В большинстве случаев вам достаточно реализовать второй конструктор, который показан выше.
Теперь мы создали view и чтобы его использовать, мы добавим его в файл разметки:
Обратите внимание, вам необходимо полностью указывать имя вашего класса, который вы реализовали.
В данный момент этот класс практически идентичен стандартному TextView, поэтому давайте добавим ему некоторую функциональность.
Добавление логики отображения
Поскольку этот view будет отображать длительность, давайте добавим метод setDuration().
Что делает этот метод? Он получает значение длительности, переводит его в текстовую строку в определенном формате, используя некую логику, а затем выводит с помощью метода setText(). В данном случае логика заключается в переводе секунд в минуты и дальнейшем разделении на часы и минуты. Также в методе присутствует логика для корректного отображения одной минуты: «1 minute» вместо «1 minute(s)».
В конце метода отформатированное время преобразуется в строку с помощью шаблона.
private static final String TEMPLATE = «Duration: %s«;
Шаблон начинается со слова «Duration», а далее жирными буквами отображается отформатированная строка.
Для простоты я использовал в коде строковые литералы. В нормальном коде, эти строковые литералы лучше перенести в strings.xml, это позволяет в дальнейшем локализовать их.
Использование полученного view
Попробуем использовать наш view. В xml файл разметки добавим 5 custom View.
А в activity в методе onCreate() после метода setContentView() добавим следующие строки.
Соберем проект и загрузим его в эмулятор. Полученный результат можно видеть на изображении ниже.
Добавление xml атрибутов
Если бы мы могли задавать шаблон, то созданный нами компонент был бы более универсальным. По идее можно добавить метод, который позволит нам устанавливать шаблон, однако это не так просто. Если подумать, длительности нам нужно устанавливать динамически, а шаблон скорее всего будет все время одинаковым. Поэтому вместо метода, давайте добавим нашему custom view новые xml атрибуты.
В первую очередь нужно создать в папке values файл attrs.xml, где мы сможем определить атрибуты. Для этого view мы добавим один атрибут — template типа string. Выглядеть это будет так:
Мы записали тег declare-styleable и задали имя TemplateTextView. Имя может быть произвольным, но обычно задают имя, совпадающее с именем view. В данном случае я не задаю имя DurationTextView, потому что планирую использовать template атрибут еще в другом view.
Далее мы определили новый атрибут, установив ему имя template и формат string. Помимо string существует множество других форматов, например, Color, Integer, Boolean, Reference и так далее.
Использовать новый атрибут в layout xml можно так.
Обратите внимание, в корневом элементе мы добавляем такую строчку.
xmlns:app= «http://schemas.android.com/apk/res-auto»
Это позволяет нам использовать атрибуты, которые определены в файле attrs.xml, в нашем layout. В данном случае мы задали префикс «app», хотя он может быть произвольным.
Для того чтобы использовать новый атрибут, нужно получить его значение в нашем custom view. Сначала заменяем приватную статическую константу «TEMPLATE» переменной «template». Затем добавляем в наш конструктор следующий код.
В первой строке мы получаем набор атрибутов, который содержит все атрибуты, существующие в xml файле. Метод obtainStyledAttributes() делает две основные вещи. Во-первых, он фильтрует все атрибуты из attrs с помощью контекста, применяет стили и resolves reference to values. Во-вторых, он возвращает только те атрибуты, которые вы определили. Они задаются вторым аргументом, который представляет собой массив ссылок на требуемые атрибуты. В данном случае в R.styleable.TemplateTextView у нас только один атрибут R.attr.template, который мы создали в файле attrs.xml.
Далее мы используем массив attributes, чтобы получить значение атрибута. Если значение шаблона не было определено или не содержит «%s», мы устанавливаем template = «%s». И в заключении нужно не забыть освободить ресурсы с помощью функции recycle().
После некоторых изменений в layout, приложение будет выглядеть так:
Здесь я установил шаблон для первых трех custom view
Большая часть view в Android имеет методы, которые позволяют устанавливать значения XML атрибутов в коде. Возможно вам тоже понадобиться добавить какой-нибудь метод, это зависит от того, как вы будете использовать view.
В следующей части мы посмотрим как нарисовать свой собственное содержимое view компонента и сделаем view для отображения графика.
Исходники к статье можно скачать c GitHub.
По материалам сайта Jayway
Вольный перевод — Pavel Bobkov.
Источник
Android navigation component. Простые вещи, которые приходится делать самому
Всем привет! Хочу рассказать об особенностях в работе Navigation Architecture Component, из-за которых у меня сложилось неоднозначное впечатление о библиотеке.
Эта статья не пошаговая инструкция, в ней опущены детали реализации, чтобы сосредоточить внимание на ключевых моментах. В интернете есть немало одинаковых примеров использования (есть и переводы) — они помогут познакомиться с библиотекой. Так же перед чтением предлагаю изучить документацию.
Сразу скажу, библиотеку безусловно считаю полезной и не исключаю возможности неверного использования, но, пожалуй, я перепробовал всё прежде чем писать эту статью.
Итак, вот сценарии, при реализации которых ожидания по функционалу не совпали с реальностью в реализации:
- переключение между пунктами меню в navigation drawer
- открытие новой Activity со своим графом навигации
- передача параметров в startDestination
Переключение между пунктами меню
Это одна из тех функций, которые повлияли на решение использовать Navigation Component.
Нужно всего лишь сделать одинаковыми id пунктов меню
и id экранов (destination в графе навигации)
затем нужно связать меню с контроллером навигации:
Навигация в меню заработала — ну разве не чудо?!
Обратите внимание на «гамбургер» (иконка меню), при переключении между пунктами меню он меняет своё состояние на кнопку «назад». Такое поведение показалось непривычным (привычное — как в приложении play market) и, какое-то время, я пытался разобраться, что же сделал не так?
Всё так! Перечитав документацию по принципам навигации (а именно: пункты два и три), понял, что «гамбургер» показывается только для startDestination, вернее так: кнопка «назад» показывается для всех, кроме startDestination. Ситуацию можно поменять применив различные уловки в подписке (addOnNavigatedListener()) на изменение destination, но их даже описывать не стоит. Работает так, нужно смириться.
Открытие новой Activity
Activity может выступать в качестве navigation host и, в то же время, в графе навигации может выступать в роли одного из destination. Открытие Activity без вложенного графа навигации работает как ожидается, то есть вызов:
осуществит переход (как в случае с фрагментами) и откроет запрошенную Activity.
Гораздо интереснее рассмотреть случай, когда целевая Activity сама выступает в роли navigation host, то есть вариант 2 из документации:
В качестве примера давайте рассмотрим Activity для добавления заметки. В ней будет основной фрагмент с полями ввода EditFragment, он в графе навигации будет startDestination. Давайте положим, что при редактировании нам нужно прикрепить фото, для этого будем переходить к PhotoFragment для получения снимка с камеры. Граф навигации будет выглядеть так:
EditActivity мало отличается от MainActivity. Основное отличие в том, что на EditActivity нет меню:
Activity открывается, навигация внутри неё работает:
Опять обратим внимание на кнопку навигации в toolbar — на стартовом EditFragment нет кнопки «Назад к parent Activity» (а хотелось бы). С точки зрения документации, тут всё законно: новый граф навигации, новое значение startDestination, на startDestination не показывается кнопка «Назад», конец.
Для тех, кому хочется вернуть привычное поведение c parent activity, сохранив при этом функционал переключения между фрагментами, могу предложить такой костыль подход:
Подписка нужна для того, чтобы для NavigationUI.ActionBarOnNavigatedListener все destination не являлись startDestination. Таким образом NavigationUI.ActionBarOnNavigatedListener не будет скрывать кнопку навигации (за деталями стоит обратиться к исходникам). Добавим к этому обработку onSupportNavigateUp() штатным образом на startDestination и получим то, что хотелось.
Стоит сказать, что решение это далеко от идеала хотя бы потому, что это неочевидное вмешательство в поведение библиотеки. Полагаю, могут возникнуть проблемы в случае использования deep links (ещё не проверял).
Передача параметров в startDestination
В Navigation Component есть механизм передачи параметров от одного destination другому. Есть даже инструмент для обеспечения безопасности типов за счёт кодогенерации (неплохо).
Сейчас мы разберём случай, из-за которого я не смог поставить твёрдую пятёрку этому функционалу.
Вернёмся к EditActivity, достаточно привычный сценарий, когда одна Activity используется для создания и редактирования объектов. При открытии объекта для редактирования в Activity нужно передать, например, id объекта — давайте сделаем это штатным образом:
Я добавил параметр непосредственно в корневой элемент графа (navigation), но можно добавить в целевой фрагмент. От этого изменится только способ получения параметра.
Я добавил add и edit action’s в одни из фрагментов, так они будут доступны только из него.
В этом примере ImportFragmentDirections — автоматически сгенерированый safe-args класс.
Вы, наверняка, обратили внимание на особенности получения параметров в EditFragment. Так работает, потому что edit action (из пункта 1) передаёт аргументы в EditActivity, а она, в свою очередь, почему-то жадничает не передаёт её в граф (например, вызовом navController.graph.setDefaultArguments()). Эту особенность можно обойти, если вручную подготовить navigation controller. Один из способов описан на StackOwerflow.
Пожалуй, наибольшая сложность возникнет при одновременном использовании в качестве startDestination и обычного destination. То есть, при переходе и передаче параметров в startDestination из любого другого destination этого графа, фрагменту придётся самостоятельно определять, откуда извлекать параметры: из arguments или из intent.extras. Это нужно иметь ввиду при проектировании переходов с передачей параметров.
Резюмируя, хочу отметить, что сам не перестал использовать библиотеку и, несмотря на перечисленные недостатки особенности, считаю её достаточно полезной, чтобы рекомендовать к использованию. Очень надеюсь, что в следующих релизах изменится ситуация по крайней мере с передачей параметров в startDestination.
Источник
Xmlns app http schemas android com apk res auto что это
Сообщение отредактировал sasha9182 — 04.02.21, 23:20
Ну же,мне нужна ваша помощь.Я опустил руки и смирился с тем что наши телефоны плохо дружат с градиентами.Ещё service.jar нужно разбирать,и редактировать там classes.dex,чего я не умею.
Сообщение отредактировал Димар — 01.02.16, 10:36
Насчет services.jar и classes.dex посмотри как я делал
а насчет градиента сам ничего толкового не добился.
Сообщение отредактировал sk0t — 20.08.14, 17:56
sk0t,
спасибо,нашел уже,и мне кажется что там не все.хочу заменить цвет текста на кнопке,и он видимо хранится в фреймворке,в файле btn_default.xml.но он не хочет открываться,или я не правильно его вытащил.просто как из архива.
фреймворк не хочет распаковываться.
Сообщение отредактировал 12enie — 28.10.10, 22:35
Сообщение отредактировал sk0t — 29.10.10, 00:12
Извини что не по теме не много, я редактирую LauncherProPlus, всё бы ничего вот только пересобранный файл launcher.xml в папке /res/layout-port/ не даёт лаунчеру запустится вылетая в ФК, может у тебя есть решение проблемы? Был бы очень благодарен!
Сообщение отредактировал Димар — 15.01.16, 20:25
Простые вопросы:
apk подписан?
что менялось в файле?
пересобран означает — разобран с помощью apk-tool, отредактирован, и собран с помощью apk-tool?
ещё по умолчанию собранный файл находится в папка куда распаковывался apk/dist и называется out.apk
Сообщение отредактировал sk0t — 29.10.10, 16:50
java -jar apktool.jar d -s file.apk foldername
tools.zip ( 1.13 КБ )
в архиве три файла
apktool-d.cmd — переносишь на него apk-файл содержащий классы(classes.dex): разбирает его в папку с тем же именем (phone.apk -> phone)
apktool-d-s.cmd — переносишь на него apk-файл не содержащий классы: разбирает его в папку с тем же именем (framework-res.apk -> framework-res)
apktool-b.cmd — переносишь на него папку с отредактированными файлами: собирает её в apk с именем out.apk (framework-res -> framework-res/dist/out.apk)
соответственно apktool.jar должен быть в той же папке.
2 Rang3r
скинь исходный apk и тот который ФК вызывает
PS 2 12enie
открываешь файл framework-res\res\values\styles.xml
ищешь там примерно такое (btn_default)
а здесь уже задается сам цвет
Первые два знака — прозрачность, остальные шесть — цвет в RGB
#ff000000 — ff — непрозрачный, 000000 — черный
можно изменить в любом из файлов
Сообщение отредактировал sk0t — 29.10.10, 20:35
sk0t, скинул в ЛС. Ответить можно и здесь, может кому-нибудь ещё тоже пригодится ответ :happy:
Сообщение отредактировал Rang3r — 29.10.10, 20:59
Источник