【问题标题】:Include layout with custom attributes包括具有自定义属性的布局
【发布时间】:2012-07-01 04:33:44
【问题描述】:

我正在构建一个复杂的布局,我想为我的自定义组件使用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 布局,我只是没有收到任何代码。如果我对that documentation page 的理解正确,我可以不为include 使用的布局设置任何属性,但是idheightwidth

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

【问题讨论】:

标签: android android-layout


【解决方案1】:

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

首先您需要在您的项目中启用Data Binding。使用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>

【讨论】:

  • 它工作了吗?你怎么能从xml绑定?当我尝试这个时,包含的布局中没有文本
  • 如何将自定义属性放入include标签中?
  • @DanChaltiel 如果您想要硬编码字符串,您可以这样做。 app:title='@{"foo"}'
  • 为了使其工作,您需要记住必须启用数据绑定并且必须通过DataBindingUtil.inflate 进行膨胀,而不仅仅是常规视图膨胀。但是,是的,它有效。
  • 我犯了一个愚蠢的错误,使用app:title="@string/title" 而不是app:title="@{@string/title}"
【解决方案2】:

除了布局参数、可见性或 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

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

【讨论】:

    【解决方案3】:

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

    所以,假设我们像这样为名为 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
        }
    }
    

    在一天结束时,您想要&lt;include&gt; 您的布局的每个 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>
    

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

    【讨论】:

    • 将视图包装到视图中有点太复杂了
    • 这也是我采用的方法,因为不支持布局继承。我现在正在尝试设置包含元素的样式 (stackoverflow.com/questions/52211576/…),但是我可能遇到了错误 (stackoverflow.com/questions/52211138/…)。
    • @RomanTruba 您不需要将视图包装在视图中。只是扩大视野。例如,扩展 TextView 并使用 YourCustomTextView 代替 TextView
    • @ChrisSprague 我不确定您是否理解这里的问题。 include 中的布局已经很复杂了
    • @RomanTruba 我经常使用非常相似的方法,而且效果很好。上面的解释比它需要的要复杂一些,可以清理一下,但总体概念是好的。具体来说,如果做得正确,就没有理由说“你永远不会包含这个包装器”。
    【解决方案4】:

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

    目前可能不太可能。

    【讨论】:

      【解决方案5】:

      【讨论】:

        【解决方案6】:

        您必须在您的根 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/

        【讨论】:

        • 谢谢,但我知道,并且包含命名空间。问题是属性没有传递给组件
        【解决方案7】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-07
          • 1970-01-01
          • 2013-06-22
          • 1970-01-01
          • 2021-08-09
          相关资源
          最近更新 更多