54

我正在构建一个复杂的布局,我想include为我的自定义组件使用标签,如下所示:

<include layout="@layout/topbar"/>

布局具有自定义根topbar组件,在布局 xml 文件中定义如下:

<my.package.TopBarLayout
 ... a lot of code

现在,我想将自定义属性传递给“topbar”,如下所示:

<include layout="@layout/topbar" txt:trName="@string/contacts"/>

然后在自定义组件代码或理想情况下在 xml 中获取这些自定义属性的值。

可悲的是,我无法获得txt:trName属性值以使其进入topbar布局,我只是没有收到任何代码。如果我从该文档页面中理解正确,我可以不为通过 、 但是 和 使用的布局设置include任何属性。idheightwidth

所以我的问题是如何将自定义属性传递给通过添加的布局include

4

7 回答 7

83

我知道这是一个老问题,但我遇到了它,发现现在可以通过数据绑定来实现。

首先,您需要在项目中启用数据绑定。使用DataBindingUtil.inflate( 而不是setContentView, 如果是Activity) 使其工作。

然后将数据绑定添加到要包含的布局中:

<?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"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        ...
    </data>
    ...
    <include layout="@layout/included_layout"
        android:id="@+id/title"
        app:title="@{@string/title}"/>
    ...
</layout>
于 2016-07-31T06:33:23.707 回答
10

除了布局参数、可见性或 ID 之外,不能在包含标签上添加属性。这包括自定义属性。

您可以通过查看 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

充气器仅将 ID 和可见性属性应用于包含的布局。

于 2013-12-10T18:35:53.387 回答
9

我今天遇到了这个问题。不管它值多少钱,我认为有一个直截了当的工作。不要向包含标签添加属性,而是为包含创建一个自定义包装器视图并为其添加属性。然后,从包装器中包含。让包装类实现提取属性并传递给它的单个孩子,这是包含布局的根视图。

因此,假设我们为一个名为 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 用于包装器+包含而不是您想要的单个包含,但是如果包含的视图包含数百行您再好不过了。例如 -

<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>

在实践中,这似乎工作得很好,而且设置起来也相对简单。我希望它可以帮助某人。

于 2016-04-12T21:00:46.250 回答
8

不幸的是,我唯一可以贡献的是我也无法在包含标签上设置自定义属性,并让它们传递到包含的布局。

在这一点上,这很可能是不可能的。

于 2013-01-11T06:49:49.340 回答
1

不能与自定义属性或 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

于 2015-07-01T17:25:24.500 回答
0

您必须在您的根 xml 元素中包含您的自定义命名空间。如果你的包名是 com.example.test 你的 xml 应该是这样的:

<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/

于 2012-06-29T16:13:44.650 回答
0

我有同样的问题。访问此线程后,我最终使用View'ssetTag()方法将识别信息附加到每个Viewduring onCreate(),然后getTag()使用方法稍后检索它。

于 2014-08-20T03:36:32.147 回答