Android создает представление программно

Обновлено: 24.09.2022

В XML-файле мы можем назначить идентификатор представлению, например android:id="@+id/something", а затем вызвать findViewById() , но как при программном создании представления назначить идентификатор?< /p>

Я думаю, что setId() — это не то же самое, что назначение по умолчанию. setId() является дополнительным.

Кто-нибудь может меня исправить?

Ответ 4

Я не эксперт по Android Kotlin.

Как видно из приведенного ниже кода, я программно создал scrollView для 40 представлений с изображением и текстом в каждом. Я не понял, как установить идентификатор для каждого представления в представлении, доступ к которому выберет пользователь.

Будем признательны за любую помощь.

Да, вы можете вызвать setId(value) в любом представлении с любым (положительным) целочисленным значением, которое вам нравится, а затем найти его в родительском контейнере с помощью findViewById(value) . Обратите внимание, что можно вызывать setId() с одним и тем же значением для разных одноуровневых представлений, но findViewById() вернет только первое.

Для этого можно просто использовать View.setId(integer). В XML, даже если вы устанавливаете идентификатор строки, он преобразуется в целое число. Благодаря этому вы можете использовать любое (положительное) целое число для представлений, которые вы добавляете программно.

Согласно документации представления

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

Таким образом, вы можете использовать любое положительное целое число, которое вам нравится, но в этом случае могут быть некоторые представления с эквивалентными идентификаторами. Если вы хотите найти какое-либо представление в иерархии, вызов setTag с некоторыми ключевыми объектами может оказаться удобным.

Обзор идентификатора Android

Идентификатор Android – это целое число, обычно используемое для идентификации просмотров. этот идентификатор может быть назначен с помощью XML (когда это возможно) и с помощью кода (программно). Этот идентификатор наиболее полезен для получения ссылок на XML-определенные представления, сгенерированные Inflater (например, с помощью setContentView .)

Назначить идентификатор через XML

  • Добавьте в представление атрибут android:id.
  • При создании приложения идентификатору android:id будет присвоено уникальное целое число для использования в коде.
  • Ссылайтесь на значение int вашего android:id в коде, используя "R.id. somename" (по сути, константу).
  • это целое число может меняться от сборки к сборке, поэтому никогда не копируйте идентификатор из gen/package.name/ R.java , просто используйте "R.id. somename".
  • (Кроме того, идентификатор, присвоенный предпочтению в XML, не используется, когда предпочтение создает свое представление.)

Назначить идентификатор через код (программно)

  • Установите идентификаторы вручную, используя someView.setId(int);
  • Int должен быть положительным, но в противном случае он может быть произвольным — он может быть любым (продолжайте читать, если это страшно).
  • Например, при создании и нумерации нескольких представлений, представляющих элементы, можно использовать их номера элементов.

Уникальность идентификатора

  • Идентификаторы, назначенные XML, будут уникальными.
  • Идентификаторы с присвоенным кодом не должны быть уникальными
  • Идентификаторы, назначенные кодом, могут (теоретически) конфликтовать с идентификаторами, назначенными XML.
  • Эти конфликтующие идентификаторы не будут иметь значения при правильном запросе (продолжайте читать).

Когда (и почему) конфликтующие идентификаторы не имеют значения

  • findViewById(int) будет рекурсивно перебирать иерархию представлений в глубину от указанного вами представления и возвращать первое найденное представление с совпадающим идентификатором .
  • Если перед идентификатором, определенным в XML, в иерархии нет назначенных кодом идентификаторов, findViewById(R.id.somename) всегда будет возвращать представление, определенное в XML, поэтому идентификатор 'd.

Динамическое создание представлений и назначение идентификаторов

  • В макете XML определите пустую ViewGroup с идентификатором .
  • Например, LinearLayout с android:id="@+id/placeholder" .
  • Используйте код для заполнения заполнителя ViewGroup представлениями.
  • Если вам нужно или хотите, назначьте любые идентификаторы, удобные для каждого представления.

Запросить эти дочерние представления с помощью placeholder.findViewById(convenientInt);

В API 17 появился View.generateViewId(), который позволяет создавать уникальный идентификатор.

Если вы решите сохранить ссылки на свои представления, обязательно создайте их экземпляры с помощью getApplicationContext() и обязательно установите для каждой ссылки значение null в onDestroy . По-видимому, утечка действия (зависание от него после его уничтожения) является расточительным.. :)

Зарезервировать XML android:id для использования в коде

Представлен API 17 View.generateViewId(), который генерирует уникальный идентификатор. (Спасибо take-chances-make-changes за указание на это.)*

Если ваша ViewGroup не может быть определена с помощью XML (или вы этого не хотите), вы можете зарезервировать идентификатор с помощью XML, чтобы гарантировать, что он останется уникальным:

Здесь values/ids.xml определяет пользовательский идентификатор:

После создания ViewGroup или View вы можете прикрепить собственный идентификатор

Пример конфликтующего идентификатора

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

макет/mylayout.xml

Для имитации конфликта предположим, что наша последняя сборка присвоила R.id.placeholder ( @+id/placeholder ) значение int 12 ..

Далее MyActivity.java программно (через код) определяет некоторые представления добавления:

Итак, заполнитель и один из наших новых объектов TextView имеют идентификатор 12! Но это не проблема, если мы запрашиваем дочерние представления плейсхолдера:

Допустим, у меня есть LinearLayout , и я хочу добавить к нему представление в своей программе из кода Java. Какой метод используется для этого? Я не спрашиваю, как это делается в XML, который я знаю, а скорее, как я могу сделать что-то подобное этому примеру кода?

Как в Swing.

7 ответов 7

Вызвать addView — правильный ответ, но вам нужно сделать немного больше, чтобы заставить его работать.

Если вы создаете представление с помощью конструктора (например, Button myButton = new Button(); ), вам необходимо вызвать setLayoutParams для вновь созданного представления, передав экземпляр внутреннего класса родительского представления LayoutParams перед вы добавляете только что созданный дочерний элемент в родительское представление.

Например, у вас может быть следующий код в вашей функции onCreate(), предполагая, что ваш LinearLayout имеет идентификатор R.id.main :

Очень важно установить параметры LayoutParams. Каждому представлению нужны как минимум параметры layout_width и layout_height. Также важно получить правильный внутренний класс. Я изо всех сил пытался заставить представления, добавленные в TableRow, отображаться правильно, пока не понял, что не передал экземпляр TableRow.LayoutParams в setLayoutParams дочернего представления.


Как бы вы создали представление программно, но с помощью XML-файла макета, который вы специально написали для этого нового представления?

@SK9 Вы должны использовать LayoutInflater, который можно получить из контекста, обычно текущего действия. Что-то вроде: LayoutInflater myInflater = getLayoutInflater; View myView = myInflater.inflate(R.layout.myLayout, parent, false);

На самом деле getLayoutInflater() исходит из класса Window (а не из Context) и является удобным методом в Activity.

В качестве практики кодирования, findViewById приводит к ViewGroup или всегда к самой общей форме объекта, чтобы при изменении LinearLayout на RelativeLayout не было рефакторинга.

Лучший способ, который я нашел, — использовать статический метод inflate View.

где yourViewXML — это что-то вроде R.layout.myView

Обратите внимание, что вам нужна ViewGroup, чтобы добавить представление (это любой макет, который вы можете придумать)

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

ИЗМЕНИТЬ:

Код Kotlin для приведенного выше примера (представление — это функция getView() фрагмента)


Вы также можете написать статический метод, что-то вроде addView(View v) , который будет принимать представление, найденное с помощью findViewById(int resourceID) или расширенное, как ваш inflatedView . просто чтобы расширить пример вашего фрагмента.

После возни с раздуванием макета и попытки сделать myLinearLayout.add(view) это было единственное, что сработало. Спасибо за это!

Чтобы добавить представление программно, вы можете сделать следующее:

Вы также можете добавить любое количество просмотров.



LinearLayout — это подкласс ViewGroup, в котором есть метод addView. Метод addView должен быть тем, что вам нужно.

Идея программной установки ограничений может быть утомительной. Это решение ниже будет работать для любого макета, будь то ограничение, линейный и т. д. Лучшим способом было бы установить заполнитель, то есть FrameLayout с соответствующими ограничениями (или правильное размещение в другом макете, таком как линейный) в позиции, где вы ожидаете, что программно созданное представление иметь.

Все, что вам нужно сделать, это программно расширить представление и сделать его дочерним по отношению к FrameLayout с помощью метода addChild(). Затем во время выполнения ваш вид будет раздут и помещен в правильное положение. Согласно рекомендации Android, вы должны добавить только один дочерний вид во FrameLayout [ссылка].

Вот как будет выглядеть ваш код, если вы хотите программно создать TextView в определенной позиции:

Шаг 1:

В вашем макете, который будет содержать расширяемое представление, поместите FrameLayout в правильное положение и присвойте ему идентификатор, например, "контейнер".

Шаг 2. Создайте макет с корневым элементом в качестве представления, которое вы хотите расширить во время выполнения, назовите файл макета как "textview.xml":

Кстати, всегда устанавливайте параметры макета вашего frameLayout равным wrap_content, иначе макет фрейма станет таким же большим, как родительский, то есть активность, то есть экран телефона.

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

Шаг 3

В методе onCreate сделайте следующее:

(Обратите внимание, что установка последнего параметра findViewById в значение null и добавление представления путем вызова addView() для представления контейнера (frameLayout) аналогичны простому присоединению расширенного представления путем передачи true в 3-м параметре findViewById() . Подробнее см. это.)

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

Мы также можем использовать ограниченный макет для создания плоского дизайна. Constraint Layout может быть реализован как через xml, так и через код. Создание макетов с использованием XML имеет свои преимущества, например, представления можно быстро создавать с помощью редактора макетов, и вы можете проверить, как выглядит макет во время выполнения.

Чтобы создать макет с помощью Constraint Layout, нам нужно использовать ConstraintSet. Используя его, мы можем добавлять ограничения к виджету, определять цепочки, изменять размеры представлений и т. д.

В этом примере мы создадим макет примерно так, как показано ниже, чтобы программно продемонстрировать макет ограничения.

 Ограничение макета

Создание макета ограничения

Давайте сначала создадим экземпляр объекта ConstraintLayout, передав контекст конструктору.

Давайте также добавим layoutParams в ограничениеLayout .

Добавление виджетов в код

Теперь добавим виджеты. Как видно из изображения выше, все виджеты нам понадобятся для создания представления. Из представления видно, что нам нужны два TextView , ImageView и checkBox . Итак, давайте добавим их по одному.

TextView для имени службы:

TextView по цене:

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

Наконец, давайте продолжим и создадим оставшиеся представления. Значок ImageView для информации

AppCompatCheckBox для флажка`

Давайте добавим два новых виджета в наш limitedLayout .

Наконец-то мы создали все необходимые виджеты и добавили их в ограничивающий макет.

Добавление ограничений с помощью ConstraintSet

Теперь начинается самое интересное. Добавление ограничений для каждого виджета. Чтобы добавить ограничения к виджетам, нам нужно создать экземпляр ConstraintSet, а затем нам придется клонировать наш ConstraintLayout. Приведенный ниже код создаст экземпляр ConstraintSet и клонирует экземпляр ConstraintLayout.

Добавление ограничений в виджет serviceName

Эквивалент приведенного выше Java-кода с использованием xml

А затем добавим ограничение и в наш виджет priceText.

Эквивалент приведенного выше Java-кода с использованием xml

Чтобы завершить это сразу, давайте проделаем то же самое для infoIcon и checkBoxItem.

Эквивалент приведенного выше Java-кода с использованием xml

Применение набора ограничений к макету.

До сих пор мы создавали разные виджеты и соответственно добавляли к ним ограничения. Теперь нам, наконец, нужно применить все эти ограничения, установленные для ConstraintLayout. Мы можем применить эти ограничения, используя приведенный ниже код. Это обеспечит добавление ограничений и их применение к макету ограничений.

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

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

До сих пор мы создавали экраны с помощью layout-файлов. Но мы можем сделать то же самое программно.

Давайте создадим проект:

Имя проекта: P0161_DynamicLayout
Цель сборки: Android 2.3.3
Имя приложения: DynamicLayout
Имя пакета: ru.startandroid.develop.dinamiclayout
Создать действие: MainActivity

Откройте MainActivity.java и обратите внимание на следующую строку:

Напоминаем, что в этой строке мы указываем, что Activity будет использовать layout-файл main.xml в качестве экрана. Есть другая реализация этого метода, которая принимает в качестве параметра не layout-файл, а View-элемент и делает его корневым элементом. В layout-файлах корневым элементом обычно является LinearLayout, поэтому мы будем использовать и его.

Обновить импорт CTRL+SHIFT+O. Eclipse спросит, какие именно LayoutParams мы хотим использовать. Мы рассмотрим это более подробно. Вспомним теорию об экранах. Экран состоит из элементов ViewGroup и View внутри него.


Классы ViewGroup, с которыми мы уже знакомы — LinearLayout, TableLayout, RelativeLayout и другие. Каждый из этих классов ViewGroup имеет внутренний класс LayoutParams. Базовым классом для этих LayoutParams является ViewGroup.LayoutParams.

ViewGroup.LayoutParams имеет только два атрибута: высоту и ширину. Его подкласс — ViewGroup.MarginLayoutParams наследует эти два атрибута и имеет четыре собственных: bottomMargin, leftMargin, rightMargin, topMargin. Класс LinearLayout.LayoutParams, который в свою очередь является подклассом ViewGroup.MarginLayoutParams, наследует от него уже 6 атрибутов и добавляет два своих: гравитацию и вес.

Таким образом, объект LinearLayout имеет внутренний класс LinearLayout.LayoutParams с атрибутами макета. И эти атрибуты распространяются на все дочерние представления и группы представлений.


Итак, View, который находится внутри LinearLayout, имеет один набор параметров макета:


Вид из RelativeLayout — отличается:


Есть некоторые общие элементы, поскольку у этих ViewGroups одни и те же родители.

Вернемся к Eclipse, он еще ждет нашего выбора. Давайте воспользуемся базовым классом ViewGroup.LayoutParams


Давайте посмотрим на код. Мы создаем LinearLayout и указываем вертикальную ориентацию. Затем мы создаем LayoutParams. Конструктор принимает два параметра: ширину и высоту. Мы устанавливаем оба как MATCH_PARENT. После этого вызывается метод setContentView. В качестве параметров ему передаются LinearLayout и LayoutParams. Это означает, что LinearLayout с атрибутами макета из LayoutParams будет корневым элементом.

Если мы запустим приложение сейчас, мы ничего не увидим, так как LinearLayout прозрачен. Давайте начнем добавлять View-компоненты в наш LinearLayout.

Мы снова создаем объект LayoutParams с атрибутами width = wrap_content и height = wrap_content. Теперь, если мы назначим этот объект одному из представлений, ширина и высота этого представления будут определяться его содержимым.

После этого мы создаем TextView, обновляем его текст, устанавливаем ранее созданный объект LayoutParams и добавляем его в LinearLayout с помощью метода addView(View child).

То же самое с Button — создать, обновить текст, а затем использовать другую реализацию метода addView(View child, ViewGroup.LayoutParams params), который одновременно добавляет Button в LinearLayout и устанавливает для Button указанные LayoutParams. Результат будет таким же, как и с TextView, но вместо двух строк кода мы используем только одну.

Обратите внимание, что для двух объектов View я использовал один объект LayoutParams — lpView. И если я сейчас изменю свойства этих объектов, соответственно изменятся оба представления.

Сохраните и запустите приложение. Мы видим, что компоненты появились на экране. И мы видим, что их высота и ширина определяются их содержимым (wrap_content).


Объект lpView имеет базовый тип android.view.ViewGroup.LayoutParams. Это означает, что он позволит настроить только ширину и высоту. Но для View в LinearLayout, например, доступно левое поле или выравнивание по правому краю. И если мы хотим их применить, нам нужно использовать LinearLayout.LayoutParams:

Посмотрите на код. Мы создаем объект типа LinearLayout.LayoutParams, используя тот же конструктор, что и для обычных LayoutParams, указав ширину и высоту. Затем мы указываем левое поле = 50. Маржа здесь указывается в пикселях. Дальше алгоритм тот же: создаем объект, обновляем текст и добавляем его в LinearLayout с установкой LayoutParams.

Аналогично добавьте компонент с выравниванием:

Сохраните и запустите. Button1 имеет поле 50px. А кнопка2 выравнивается по правому краю:


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

Полный код урока:

На следующем уроке мы:

- добавлять компоненты на экран во время работы приложения.

- в чатах решаем возникающие вопросы и проблемы по использованию тем: Android, Kotlin, RxJava, Dagger, Тестирование

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- новый чат Производительность для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме

Читайте также: