我制作复杂的布局,并使用“include”作为我的自定义组件,例如
<include layout="@layout/topbar"/>
topbar定义如下:
<?xml version="1.0" encoding="utf-8"?>
<my.package.TopBarLayout
... a lot of code
现在,我想将我的自定义属性传递给“topbar”,就像这样
<include layout="@layout/topbar" txt:trName="@string/contacts"/>
但我没有结果。我从that page了解到我不能设置任何属性,而是id,height和width。
那么,我如何将我的自定义属性传递给包含,以及如何使其工作?
答案 0 :(得分:61)
我知道这是一个老问题,但我发现它现在可以归功于Data Binding。
首先,您需要在项目中启用Data Binding。
然后将数据绑定添加到要包含的布局:
<?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>
最后,将变量从主布局传递到包含的布局,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
...
</data>
...
xmlns:app="http://schemas.android.com/apk/res-auto"
...
<include layout="@layout/included_layout"
android:id="@+id/title"
app:title="@{@string/title}"/>
...
</layout>
答案 1 :(得分:9)
除了包含标记上的布局参数,可见性或ID之外,其他属性是不可能的。这包括自定义属性。
您可以通过查看第705行附近的LayoutInflater.parseInclude方法的来源来验证这一点: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1/android/view/LayoutInflater.java#640
inflater仅将ID和可见性属性应用于包含的布局。
答案 2 :(得分:8)
我今天遇到了这个问题。无论它有什么价值,我认为有一个直截了当的工作。不是为include标记添加属性,而是为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's 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
}
}
在一天结束时,您希望<include>
布局的每个XML文件将包含大约7行XML for wrapper + include而不是单个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>
在实践中,这似乎工作得很好,并且设置起来相对简单。我希望它有所帮助。
答案 3 :(得分:6)
不幸的是,我唯一能做的就是我也无法在include标签上设置自定义属性,并将它们传递给包含的布局。
此时可能无法实现。
答案 4 :(得分:1)
无法使用自定义属性或API页面上指定的属性以外的任何属性(至少达到5.0.0):
答案 5 :(得分:0)
您必须在root 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" />
答案 6 :(得分:0)
我有同样的问题。在访问此主题后,我最终使用View
的{{1}}方法在setTag()
期间向每个View
附加了标识信息,然后onCreate()
稍后检索它的方法。