View class reference android

Exploring View Binding on Android

When it comes to manipulating our user interfaces within Android applications, there are a couple of approaches that we can take. In these cases, we need to obtain a reference these views in-order to manipulate them in some way. For this, we’ll either use findViewById(), followed by casting the view to the corresponding type. Or if we’re using kotlin, then we’ll use the kotlin android extensions to perform synthetic access to the views from our layout files. Whilst these two are perfectly fine approaches to take, they do come with some possible pitfalls:

  • When using findViewById, the casting of view types is not type safe. This means that whilst we may think that we are accessing a specific type of view that we have casted to, the view we are referencing from our layout could actually be of a different type. This is more common to happen than you may think — maybe it’s a simple error you’ve made in a file you’re familiar with or an incorrect reference to the wrong view component from your XML file, with casting in place this will cause a class cast exception.
  • Whether we are referencing views using findViewId or kotlin synthetics, these approaches are not null safe. This means that if a view is not available yet within a layout, or we are using an incorrect ID that does not exist within the layout of our activity/fragment, then a null pointer exception will be thrown as the referenced view is not available for use at the time of access.
  • In some cases, we may be using multiple layout files for a single screen, this may be to handle different layout configurations such as a landscape and portrait layouts. In these cases we may have different view components displayed, meaning that a component that is available within the landscape layout may not be available in the portrait layout. So for example, if we are accessing a view in our portrait layout, and then switch to our landscape layout where that view is not available and try to access it, we will see a null pointer exception as at compile time and runtime we may not be aware that the view in question is nullable.

To alleviate the above issues, View Binding introduces us to Binding classes that will bind the views defined in our XML layout to a generated class which can be used within our activities / fragments. Using this generated class we can then access the views available from the binding, giving us some of the following advantages. Note: It’s important to be aware that this View Binding is different from Data Binding — we do not use ViewBinding to bind layouts with data in XML.

  • The bound views within our binding class are generated with their corresponding type, meaning that any references made to views through this class are completely type safe. This removes any chance of incorrect casting being carried out, removing the risk of running into class cast exceptions.
  • Because the binding class is generated based off of the attached layout file, all views within this class are null safe and available to access from our activity / fragment. This means that we are unable to reference incorrect IDs from other layouts or views that may not be available, meaning that we can access views without the worry of causing null pointer exceptions.
  • The View Binding classes have the ability to mark view references as @Nullable, in the case that a view may be not be available in all layout configurations. This means that we can access views with the awareness that the view may be null, allowing us to take the appropriate precautions when accessing the specified view.

With the above in mind, we can see that View Binding provides us with an approach that makes the accessing of views less error prone — allowing us to reduce the chances of crashes occuring for our users.

Читайте также:  Свое лого для андроид

When it comes to View Binding, there are a couple of concepts which come together in-order to provide this functionality. To begin with we’ll be working the same within our XML layouts, the main difference is that we need to declare a class inside of our activity that will create the binding that create the relationship between

Note: You’ll need to be using at least Android Studio 3.6 Canary 11 to use View Binding

To get started with adding View Binding to our android application, we need to go ahead and add the following to the corresponding build.gradle file:

View Binding is module specific, so you will need to add this to each module that you wish to provide view binding for. If you are going to be using this in every module, then you can place this inside of the project build.gradle file in a manner that allows you to reuse it throughout your project.

When it comes to our XML files, we don’t actually need to do anything differently to be able to access views from the generated binding classes — most of the changes will come from within the classes that are accessing those bindings. Let’s say we have a layout file for an activity called add_profile.xml, which looks a little something like this:

We’re going to want to access these views from within our activity so that we can assign the corresponding content to them. To do this we’re going to need to declare a binding class for that layout file. When we want to declare this binding class we need to adhere to the naming convention defined by the View Binding API — so for example, for add_profile.xml this will look like so:

private lateinit var binding: AddProfileBinding

To begin with, the API will take the name of our layout file, remove any underscores and then sentance-case the words that were seperated by these underscores. Finally, you can see here that the word Binding has been added to end of our file name – this is required and will be added for every view binding class that is generated. Now that we have this binding class declared, we need to assign a reference to it. For this, we’re going to use the LayoutInflator reference within our activity and call the static inflate() method on our Binding class. Doing so will inflate our layout into our binding class so that our bound views are accessible for use.

You’ll also notice here that we are setting the content view of our screen to this root property from the binding. Every binding that is created has a root — this root represents the root component within the layout file that we inflated into the binding class. This is a convenient way for us to be able to finalise the display of our screen.

At this point we have our binding class and can finally use it to setup our screen. Looking at the previous add_profile.xml file from above, you may recall seeing a textview and button. We’ll now access these from our binding class and configure them for display:

You can see here that we’re accessing the components that are defined within our layout file (with the underscores removed and camelcase applied). Regardless of the views in our XML, the way we access them here will apply for all view types. Here you can see no casting in place — when we access the textTitle field, that is referenced as a TextView as that’s what the binding class has allocated it. The same applies for the addProfileButton — without any casting in place we avoid incorrect castings being made, as well resulting in code that is more readable. As well as this, we get the advantages mentioned previously when it comes to view binding. Another point being nullable views, allowing us to handle these cases appropriately:

In some cases there may be components that you do not wish to create bindings for — maybe you do not want them available for manipulation from with the corresponding binding class. Here, you can make use of the viewBindingIgnore attribute from within our XML layout to exclude this view from being added to the generated binding class.

Читайте также:  Betting insider для android

Whilst this has been a quick dive into View Binding on Android, I hope that it’s been enough to demonstrate the advantages that this approach to view manipulation gives us in our applications. Whether it’s type safety, null safety or an overall cleaner approach to view references in our code, View Binding offers functionality that all applications will be able to take advantage of.

Have you tried View Binding yet? Or have any thoughts / questions on the content outlined here? If so, I would love to hear from you!

Источник

Рисование собственных представлений (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.

Как правило, процесс рисования определяется двумя аспектами:

Читайте также:  Android studio hide layout

что рисовать (определяется объектом 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() следует вызывать в случае, когда требуется изменение границ представления.

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

Источник

Оцените статью