Включить макет с настраиваемыми атрибутами
Я делаю сложную компоновку, и я использую "include" для моего настраиваемого компонента, например
<include layout="@layout/topbar"/>
И верхняя панель определяется следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<my.package.TopBarLayout
... a lot of code
Теперь я хочу передать свои пользовательские определенные атрибуты в "topbar", как этот
<include layout="@layout/topbar" txt:trName="@string/contacts"/>
Но у меня нет результата. Я понял из этой страницы, что я не могу установить никаких атрибутов, но id, height и width.
Итак, как я могу передать свои настраиваемые атрибуты для включения и как заставить его работать?
Ответы
Ответ 1
Я знаю, что это старый вопрос, но я наткнулся на него и обнаружил, что теперь это возможно благодаря привязке данных.
Сначала вам нужно включить Связывание данных в вашем проекте.
Затем добавьте привязку данных к макету, который вы хотите включить:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="title" type="java.lang.String"/>
</data>
<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/screen_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:gravity="center">
...
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="20sp"
android:textStyle="bold"
android:text="@{title}"/>
...
</RelativeLayout>
</layout>
Наконец, передайте переменную от основного макета к включенному макету следующим образом:
...
xmlns:app="http://schemas.android.com/apk/res-auto"
...
<include layout="@layout/included_layout"
android:id="@+id/title"
app:title="@{@string/title}"/>
...
Ответ 2
Невозможно использовать атрибуты, отличные от параметров макета, видимости или идентификатора в теге include. Сюда входят пользовательские атрибуты.
Вы можете проверить это, посмотрев на источник метода LayoutInflater.parseInclude по строке 705:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/view/LayoutInflater.java#640
Надуватель только применяет атрибуты идентификатора и видимости к включенному макету.
Ответ 3
Сегодня я столкнулся с этой проблемой. Что бы это ни стоило, я думаю, что есть прямая работа. Вместо добавления атрибутов в тег include создайте собственное представление оболочки для включения и добавления атрибутов к этому. Затем сделайте включение из обертки. Попросите реализацию класса-оболочки извлечь атрибуты и передать их одному дочернему элементу, который является корневым представлением макета include.
Итак, скажем, мы объявляем некоторые пользовательские атрибуты для обертки, называемой SingleSettingWrapper, как это -
<declare-styleable name="SingleSettingWrapper">
<attr name="labelText" format="string"/>
</declare-styleable>
Затем мы создаем два пользовательских класса представления - один для обертки (SingleSettingWrapper) и один для дочернего (SingleSettingChild), который будет включен -
<!-- You will never end up including this wrapper - it will be pasted where ever you wanted to include. But since the bulk of the XML is in the child, that ok -->
<com.something.SingleSettingWrapper
android:id="@+id/wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:labelText="@string/my_label_string">
<!-- Include the child layout -->
<include layout="@layout/setting_single_item"/>
</com.something.SingleSettingWrapper>
Для ребенка мы можем поставить любой сложный макет, который мы хотим. Я просто поставлю что-то основное, но на самом деле вы можете включить что угодно -
<com.something.SingleSettingItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout >
<!-- add whatever custom stuff here -->
<!-- in this example there would be a text view for the label and maybe a bunch of other stuff -->
<!-- blah blah blah -->
</RelativeLayout>
</com.something.SingleSettingItem>
Для обертки (это ключ) мы читаем все наши пользовательские атрибуты в конструкторе. Затем мы переопределяем onViewAdded() и передаем эти пользовательские атрибуты нашему дочернему элементу.
public class SingleSettingWrapper extends FrameLayout
{
private String mLabel;
public SingleSettingWrapper(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.SingleSettingWrapper,
0, 0);
mLabel = a.getString(R.styleable.SingleSettingWrapper_labelText);
a.recycle();
}
public void onViewAdded(View child)
{
super.onViewAdded(child);
if (!(child instanceof SingleSettingItem))
return;
((TextView)child.findViewById(R.id.setting_single_label)).setText(mLabel);
/*
Or, alternatively, call a custom method on the child implementation -
((SingleSettingItem)child)setLabel(mLabel);
*/
}
}
По желанию вы также можете реализовать ребенка и получать его от обертки и изменять сами (вместо того, чтобы обертка модифицировала дочерний элемент, как я уже говорил выше).
public class SingleSettingItem extends LinearLayout
{
public SingleSettingItem(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public void setLabel(String l)
{
// set the string into the resource here if desired, for example
}
}
В конце дня каждый из XML файлов, где вы хотели бы <include>
вашего макета, будет содержать около 7 строк XML для оболочки + include вместо одного, который вы хотите, но если включенный вид содержит сотни строк, которые вам все еще лучше. Например -
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- this is the beginning of your custom attribute include -->
<com.something.SingleSettingWrapper
android:id="@+id/my_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:labelText="@string/auto_lock_heading">
<include layout="@layout/setting_single_item"/>
</com.something.SingleSettingWrapper>
<!-- this is the end of your custom attribute include -->
</LinearLayout>
На практике это работает очень хорошо и относительно просто настроить. Надеюсь, это поможет кому-то.
Ответ 4
К сожалению, единственное, что я могу внести, это то, что я также не смог установить пользовательские атрибуты в теге include и передать их во включенную компоновку.
На данном этапе это может быть невозможно.
Ответ 5
Невозможно использовать с пользовательскими атрибутами или любыми атрибутами, отличными от тех, которые указаны на странице API (не менее 5.0.0):
https://code.google.com/p/android/issues/detail?id=38023
http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-1/android/view/LayoutInflater.java
Ответ 6
Вы должны включить в свой корневой элемент xml свое собственное пространство имен.
Если ваше имя пакета com.example.test, ваш xml shold будет выглядеть примерно так:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:txt="http://schemas.android.com/apk/res/com.example.test" />
Хороший учебник: http://blog.infidian.com/2008/05/02/android-tutorial-42-passing-custom-variables-via-xml-resource-files/
Ответ 7
У меня был тот же вопрос. После посещения этого потока я закончил использование методов View
setTag()
, чтобы прикрепить идентификационную информацию к каждому View
во время onCreate()
, а затем getTag()
методы для ее получения позже.