- Creating Dynamic Layouts in Android
- What are Dynamic Layouts?
- Why do we need them?
- Where do Dynamic Layouts excel?
- Defining Custom Views
- ConstraintLayout
- Aspect ration (Соотношение сторон)
- GuideLine
- Chains — Скованные одной цепью
- Проценты
- Barriers
- Groups
- Анимация
- Circular
- Placeholder
- ConstraintLayoutStates (2.0.0)
- res/layout/activity_cl_states_start.xml
- res/layout/activity_cl_states_loading.xml
- res/layout/activity_cl_states_end.xml
- res/xml/constraint_layout_states.xml
- Layer
- Другие атрибуты
- Примеры
- Обновления
Creating Dynamic Layouts in Android
What are Dynamic Layouts?
Generally, we develop the layout for an Android application by creating the XML file. These are called static layouts.
Static you ask? Yes, because you can not add/delete any View Type in XML on runtime.
Dynamic layouts are developed using Java and can be used to create layouts that you would normally create using an XML file.
Why do we need them?
Let’s say we are fetching some data from our server and we want to create n number of fields/buttons/options in our layout. How do we do that using the only XML? That’s right!
But we can use Java to replicate the exact layout that we would normally create using XML.
It can be used to create LinearLayout, ScrollView, etc.which can further add TextView, EditText, RadioButtons, Checkbox inside it.
Where do Dynamic Layouts excel?
Let’s say we want to show some items in our layout that were frequently bought together(all items are of the same View Type).
There are quite a few ways to show them in our layout:
1. Adding static views in our XML(if we know the exact amount of items).
2. Using Recycler View to inflate the items in our layout.
3. Creating Dynamic Views using Java.
What if we do not know the exact number of items to be displayed and our
layout require different View Types( like 3 TextViews, 2 CheckBoxes, etc.)?
Possible solutions to tackle this problem:
1. Adding static views in our XML won’t work this time because we do not . know the exact number of views we need.
2. Using Recycler View could work but the same View Types should be grouped together in a list. There is not much flexibility in this case.
3. However, this is where Dynamic Layouts take the lead! They are flexible and can add multiple View Types to our layout in any order.
Источник
Defining Custom Views
Android UI elements are all based on View (single element on screen) and ViewGroup (collection of elements on screen). There are many «widgets» and «layouts» built-in that can be used to build the UI such as views like Button and TextView, and layouts like RelativeLayout
In some apps though we need to be able to customize views to suit our own needs. This might mean extending an existing view, creating your own View subclass or doing more complicated drawing with a SurfaceView.
Customizing your own views involves extending View or an existing subclass, overriding the view behavior by writing methods such as onDraw or onTouchEvent and then using your new view in an activity.
Creating custom views is centered around five primary aspects that we may need to control or modify:
- Drawing — Control the rendering of the view on screen visually by overriding the onDraw method.
- Interaction — Control the ways the user can interact with the view with the onTouchEvent and gestures.
- Measurement — Control the content dimensions of the view on screen by overriding the onMeasure method.
- Attributes — Defining custom XML attributes for your view and using them to control behavior with TypedArray
- Persistence — Storing and restoring state on configuration changes to avoid losing the state with onSaveInstanceState and onRestoreInstanceState
To take a closer look, suppose we want to create our own view control that allows the user to select between different shapes. The view will display a single shape (square, circle or triangle) and clicking on the view will toggle the shape selected between the different options.
To create our own custom toggle-able shape selector from scratch, we start by defining a ShapeSelectorView which extends from View and implements the required constructor:
Next, let’s add this view to our activity layout along with a caption and a button for selecting the shape after choosing:
Note how we define a custom namespace app . This namespace allows you to allow Android to auto-resolve the namespace, avoiding the necessity for specifying the package name in this file. See this blog post for more information.
Well-written custom views can be configured and styled via XML attributes. You need to ask yourself which aspects of your view should be customizable. For example, we might want to let the user select the color of the shape as well as give the user the option to display the name of the shape in the view as well. We might want the view to be configurable in XML as follows:
In order to be able to define shapeColor and displayShapeName , we need to define these as attributes within res/values/attrs.xml :
Notice we define the attr node along with the name and format for each custom attribute we’d like to be able to define. The format is the expected type of value for that property and valid options include string, color, dimension, boolean, integer, float, enum, and several others.
Once you define the custom attributes, you can use them in layout XML files just like built-in attributes. The only difference is that your custom attributes belong to a different namespace. You can define the namespace within the root view of the layout and configure the properties for the view. Normally you would need to specify a namespace such as http://schemas.android.com/apk/res/
(i.e. com.codepath.example.customviewdemo) but the namespace http://schemas.android.com/apk/res-auto will auto-resolve for you.
Now that we have set custom properties such as shapeColor and displayShapeName , we need to extract those properties to be used within our custom View within the constructor. To extract the custom attributes, we can use a TypedArray and the obtainStyledAttributes on the AttributeSet :
Let’s expose property methods to allow us to get and set the important properties after a view has been created:
Notice that when the view properties are changed and might require a redraw, be sure to call invalidate() and requestLayout() to update the appearance.
Next, let’s actually draw a square taking into account the properties defined above for shape color and shape name. All view drawing happens within the onDraw method using the Canvas object to paint onto the view. Let’s draw a square shape:
This will paint the square based on the shapeColor specified in the XML and will paint the shape name if specified within the displayShapeName property. Result looks like:
You can read more about drawing onto a canvas on the official Custom 2D Drawing Tutorial.
In order to understand the width and height of a view that is being custom drawn, we need to define the onMeasure method which determines the width and height of the view based on it’s contents. In this case, the height and width are determined by the shape and text drawn within the view. Let’s define the onMeasure as follows:
Note that the calculations take into account the view’s padding and calculate the content size. Also note that the onMeasure method must call the setMeasuredDimension. Widths and heights are discussed using the MeasureSpec which encapsulates all the different types of constraints imposed by the parent layout for a view. The helper method resolveSizeAndState() returns an appropriate value by comparing the view’s desired size to the spec passed into the method.
We have the square drawing, but we want the shape to toggle each time the view is clicked. Let’s setup a touch handler to ensure the shape changes as specified using the onTouchEvent method:
Now whenever the shape is clicked, the selected shape index will change and a different shape should be drawn after postInvalidate is called. Let’s update the onDraw method to paint the correct shape according to the selected index:
Now every time that we click the view, a different shape appears rotating between the three available options. Result looks like:
For more advanced view user interaction, check out the Making the View Interactive official docs.
The final touch might be to add a property to allow the activity to access the selected shape from within the view. First, let’s add the method to expose the selected shape:
and then now within the activity, we might be able to display the selected shape with a toast when a button is pressed:
The result of this is the following:
There are many events which can be customized for a view, check out the Custom Components guide for a more details.
Views are responsible for maintaining their own state when configuration changes (i.e phone is rotated) occur. You can do this by implementing View#onSaveInstanceState and View#onRestoreInstanceState in order to save and then restore the view state. For example, to maintain the selected shape index for our shape selector:
Once you’ve defined these saving and restoring methods, your view will be capable of automatically persisting state when configuration changes occur.
There is an even easier option for creating a custom View which is useful in certain circumstances. If there is a component that is already very similar to what you want, you can simply extend that component and just override the behavior that you want to change and get the rest of the behavior for free.
Incomplete: Fill this in with a relevant example
Read more about this in the Extending View Types guide for more details.
If you don’t want to create a completely customized component, but instead are looking to put together a reusable component that consists of a group of existing controls, then you may want to simply create a compound control. You might also want to create your own ViewGroup to act as a container for views or create a custom layout.
Incomplete: Fill this in with a relevant example
Check out this tutorial on Medium for a detailed overview of developing a compound view. Read more about this in the Custom Compound Components guide for more details.
There are many libraries for Android that contain custom views such as:
- Caldroid — A better calendar widget
- PullToRefresh-ListView — Pull to refresh enabled ListView
- RoundedImageView — ImageView extension that rounds the image with a border
Источник
ConstraintLayout
Новый макет ConstraintLayout появился в Android Studio 2.2 и доступен для устройств с версии Android 2.3. Его можно найти в разделе Layouts. Гугл очень расхваливает данный макет и советует всем переходить на него и даже создал специальный конвертер для этой задачи. Если у вас имеется старый проект, то достаточно щёлкнуть правой кнопкой мыши на корневом элемента макета и выбрать пункт Convert layout to ConstraintLayout. В диалоговом окне ничего не трогаем.
В build.gradle модуля прописывается ссылка на библиотеку и проект начинает синхронизироваться. Сейчас уже активно развивается ветка 2.х.x, лучше сразу переходить на неё.
В Android Studio 2.3 и выше в шаблонах по умолчанию теперь используется ConstraintLayout.
Если в режиме дизайна выбран данный компонент, то на панели инструментов вам доступны несколько кнопок.
- View Options с пунктами Show Constraints — выводит заданные ограничения в режимах предварительного просмотра и раскладки. В одних случаях этот режим просмотра полезен, в других нет. При большом количестве ограничений эта кнопка выводит слишком много информации, Show Margins, Fade Unselected views.
- Turn On Autoconnect — при включении режима Autoconnect ограничения будут автоматически настраиваться при перетаскивании представлений в область предварительного просмотра. Студия старается угадать, какие ограничения должен иметь компонент, и создавать их по мере необходимости
- Default Margins — стандартное значение для отступов. Можете устанавливать отдельно для каждого компонента. Выбрали компонент, установили значение, затем снова выбрали другой компонент и установили другое значение
- Clear All Constraints — удаляет все ограничения из макета
- Infer Constraints — автоматически создаёт ограничения. Срабатывает только при нажатии кнопки. Функциональность Autoconnect срабатывает каждый раз, когда в файл макета добавляется новый компонент
- GuideLines с двумя опциями: Add Vertical GuideLine и Add Horizontal GuideLine. Смотри ниже
Очень часто на форумах встречается вопрос, почему в режиме дизайна макет выглядит хорошо, а при запуске приложения все компоненты сбиваются в верхний левый угол. Для решения этой проблемы попробуйте нажать на кнопку Infer Constraints, которая создаст дополнительные ограничения.
ConstraintLayout является наследником ViewGroup и местами похож на RelativeLayout, но более продвинут. Код разметки в XML-представлении:
Управление компонентами внутри данного контейнера достаточно сильно отличается от старого взаимодействия. Придётся всем переучиваться.
При его использовании нет смысла использовать XML-представление, только в режиме Design, когда вы можете подвигать все компоненты в визуальном редакторе.
Рассмотрим отдельные элементы, которые используются для редактирования макета. Переключитесь в режим Blueprint, чтобы ничего нас не отвлекало от работы.
Так выглядит выбранный компонент.
Квадратные опорные точки в углах компонента позволяют изменять его размеры. Круглые опорные точки по краям позволяют управлять отступами от краёв экрана и других компонентов.
Продолговатый закругленный прямоугольник указывает на базовую линию текста. Пригодится при выравнивании по базовой линии другого компонента.
Также можно увидеть зигзагообразные направляющие-пружинки, которые нужны для особых случаев. Если провести мышкой над опорной точкой пружинки, она красиво активируется. Последующий щелчок удаляет пружинку. Если компонент удерживался двумя пружинками сверху и снизу, то удаление одной из них притянет компонент к верхней или нижней части экрана. Лучше самостоятельно проверить у себя, так как словами трудно описать.
Пружинки также можно легко восстановить, если щёлкнуть по круглой опорной точке и потянуть её к краю экрана.
Наверху на панели инструментов можно активировать автоматический режим размещения компонентов Autoconnect в виде магнита . В этом случае вы просто бросаете на экран нужный компонент, а далее студия сама пытается найти нужные параметры для него. Всё это происходит с красивой анимацией. Естественно, вы можете всё переиграть и поставить в нужную позицию.
На той же панели есть инструмент Infern Constraints в виде пары звёздочек . Действует как Autoconnect, только работает не с одним компонентом, редактируемым в данный момент, а со всем макетом сразу, используя математические расчёты, чтобы определить, какие компоненты нужно привязать к другим, исходя из их местоположения на экране.
Теперь рассмотрим настройки в панели Properties.
Набор из трёх стрелок внутри квадрата означают атрибут wrap_content. Если вы измените этот атрибут с помощью выпадающего списка или вручную напишите размер в dp, то увидите, что стрелки заменятся на прямые (фиксированный размер
) или пружинку (match_constraints
, который является приблизительным аналогом атрибута match_parent и имеет значение 0dp).
По бокам квадрата имеются числа. Если подвести к ним мышку, то появится выпадающий список с определёнными значениями: 0, 8, 16, 24, 32. Они отвечают за атрибут margin (отступы).
По бокам можно увидеть также ползунки с числами в кружочке. В примере виден только горизонтальный ползунок, при других настройках увидите и вертикальный. С их помощью можно центрировать компонент относительно экрана и других объектов.
Aspect ration (Соотношение сторон)
Если у компонента есть двусторонняя вертикальная привязка и значение высоты установлено в match_constraints (0dp), то можно настроить так, чтобы высота зависела от ширины. В углу появится треугольник, щёлкнув на котором можно затем установить желаемое соотношение. Потом можно изменять ширину, чтобы увидеть, как высота подстраивается под ширину.
Аналогично можно настроить зависимость ширины от высоты, предварительно сначала установив двустороннюю горизонтальную привязку.
GuideLine
На панели инструментов также имеется значок GuideLines с двумя опциями: Add Vertical GuideLine и Add Horizontal GuideLine. Если ими воспользоваться, то в XML-файле появятся такие строчки:
По сути, это View, размер которого 0, что соответствует View.GONE. На этапе разработки мы видим только полоски, а во время работы приложения ничего не видим. Данные элементы помогают разместить компоненты аккуратно относительно линии.
Направляющие пригодятся, если одни и те же значения отступов повторяются для нескольких компонентов. Направляющие можно указывать в dp от края экрана или задать в процентах от ширины экрана. Чтобы переключаться между разными режимами, вы можете нажать на круглый значок Guideline.
Не всегда с помощью визуального редактора можно добиться нужного результата, тогда нужно переключиться в XML-режим. Один из таких случаев описан в статье Square Island: Constraint Layout: Icon Label Text.
Если есть желание работать через XML, то следует запомнить очень много атрибутов, например, для выравнивания относительно друг друга:
- app:layout_constraintStart_toStartOf=»@id/view»
- app:layout_constraintLeft_toLeftOf=»@id/view»
- app:layout_constraintEnd_toEndOf=»@id/view»
- app:layout_constraintRight_toRightOf=»@id/view»
- app:layout_constraintTop_toTopOf=»@id/view»
- app:layout_constraintBaseline_toBaselineOf=»@id/view»
- app:layout_constraintBottom_toBottomOf=»@id/view»
- app:layout_constraintStart_toEndOf=»@id/view»
- app:layout_constraintLeft_toRightOf=»@id/view»
- app:layout_constraintEnd_toStartOf=»@id/view»
- app:layout_constraintRight_toLeftOf=»@id/view»
- app:layout_constraintTop_toBottomOf=»@id/view»
- app:layout_constraintBottom_toTopOf=»@id/view»
Атрибут app:layout_constraintHorizontal_bias используется float-значения от 0 до 1, для выравнивания по оси.
Атрибут app:layout_constraintDimensionRatio=»4:3″ указывает, что нужно использовать данные пропорции по высоте и ширине для данного компонента. Также встречается модификация атрибута app:layout_constraintDimensionRatio=»H, 1:1″.
Chains — Скованные одной цепью
Несколько элементов можно сковать одной цепью. Допустим, у нас есть три кнопки. Выделяем их и через контекстное меню выбираем Center Horizontally. Снизу у выбранных компонентов появится символ цепи, а между ними будут нарисована связь в виде цепей. Если последовательно щёлкать по значку цепи, то увидите, как кнопки будут центрироваться с разными стилями:
- spread — Свободное пространство равномерно распределяется между выбранными компонентами и краями родителя (например, экрана)
- spread_inside — Крайние компоненты прижимаются к границам родителя, свободное пространство равномерно распределяется только между остальными компонентами
- packed — Свободное пространство равномерно распределяется между крайними компонентами и границами родителя. Вы можете использовать margin для отступов
За цепи отвечают стили.
- app:layout_constraintHorizontal_chainStyle=»spread»
- app:layout_constraintVertical_chainStyle=»spread»
- app:layout_constraintHorizontal_chainStyle=»spread_inside»
- app:layout_constraintVertical_chainStyle=»spread_inside»
- app:layout_constraintHorizontal_chainStyle=»packed»
- app:layout_constraintVertical_chainStyle=»packed»
Также можно присвоить кнопкам вес, когда одна кнопка может быть больше остальных, это поведение знакомо по LinearLayout. Для этих целей используются атрибуты
Как и в LinearLayout, чтобы использовать вес, надо поставить размер компонента в 0dp.
На рисунке этот вариант представлен в третьем примере.
Проценты
Можно указывать значения ширины и высоты в процентах через атрибуты layout_constraintWidth_percent, layout_constraintHeight_percent. Все View-компоненты поддерживают данные атрибуты. Они позволяют ограничить компонент процентным значением в рамках всего доступного пространства. Например, мы хотим видеть кнопку, которая будет занимать 70% в рамках свободного для неё места.
Barriers
Barriers — это виртуальный View, который используется как шаблон. Он применяется для нескольких компонентов неизвестного размера – если один из них увеличивается, то барьер подстроит размер остальных под наибольшую высоту или ширину. Барьеры могут быть вертикальными и горизонтальными и создаваться сверху, снизу, слева или справа от нужных элементов. Другие элементы будут подстраиваться.
Барьеры полезны, когда вы создаёте локализованные строки или отображаете контент, созданный пользователем, размер которого вы не можете предсказать.
Groups
Groups — теперь можно логически группировать определённые виды. По сути, это некий контейнер, который содержит ссылки на ID компонентов, а не сами компоненты. При помощи группы вы можете установить видимость всех компонентов в контейнере. Это может пригодиться, когда сразу несколько элементов должны изменять свою видимость или другие свойства.
Анимация
Для анимации разметки ConstraintLayout используется ConstraintSet.
Circular
С помощью Circular мы можем настроить два компонента так, чтобы одно находилось на определённом расстоянии и под определённым углом от другого.
- layout_constraintCircle — указываем идентификатор компонента, который будет центром окружности
- layout_constraintCircleRadius — расстояние от центра окружности до компонента
- layout_constraintCircleAngle — угол (в градусах, от 0 до 360)
При создании круговых зависимостей углы начинаются сверху и изменяются по часовой стрелке. Например, таким образом нужно описать среднюю кнопку в примере выше:
Такой способ пригодится для анимации аналоговых часов или похожих примеров.
Placeholder
Элемент Placeholder позволяет использовать место на экране в качестве временного заполнителя. Используя анимацию, можно динамически перемещать компонент на место заполнителя.
ConstraintLayoutStates (2.0.0)
В ветке 2.х появилась новая функциональность — переключение между макетами экрана. Это удобно, когда требуются небольшие изменения на одном экране. Вы должны создать несколько макет с одинаковыми компонентами, но при этом можете изменить их свойства (видимость, местоположение и т.д). В нужный момент программно переключаетесь на нужный макет.
Создадим три разных макета в папке layout.
res/layout/activity_cl_states_start.xml
res/layout/activity_cl_states_loading.xml
res/layout/activity_cl_states_end.xml
В нашем примере во всех макетах имеются ProgressBar и Button с разной видимостью и позицией.
Следующий шаг — создать в папке res/xml новый файл, описывающий три созданных макета.
res/xml/constraint_layout_states.xml
Осталось написать программную часть. Сначала мы загружаем первый макет в стандартном методе setContentView(). Затем загружаем описания созданных нами макетов через loadLayoutDescription() объекта-контейнера нашего ConstraintLayout. Теперь мы можем переключаться между макетами через constraintLayout.setState().
В примере при нажатии кнопки мы имитируем долгоиграющую задачу и выводим ProgressBar, когда задача будет завершена, то выводим третий макет, где кнопка находится уже в другом месте.
В версии 2.0.0-alpha5 появился новый вид виртуального макета Flow, который позволяет работать с объектами разных размеров.
Layer
Новый виртуальный помощник Layer работает с группой компонентов, применяя к ним различные трансформации: вращение, перемещение, масштабирование.
Другие атрибуты
Стоит обратить внимание на следующие атрибуты. Они часто используются, если в контейнере содержится компонент RecyclerView.
- android:background=»?android:attr/selectableItemBackground»
- android:clickable=»true»
- android:focusable=»true»
Примеры
Стандартный случай: картинка и две текстовые метки. Верхней край картинки должен совпадать с верхней частью первой строки, а нижний край картинки с нижней частью второй строки. При этом картинка векторная и должна сохранять пропорции. Макет при изменении размера шрифта должен сохранять свою структуру.
Обновления
Библиотека постоянно развивается. Некоторые пункты меню были переработаны и изменены. Следите за изменениями.
Источник