【问题标题】:Include layout in XML depends on condition在 XML 中包含布局取决于条件
【发布时间】:2021-10-03 15:07:06
【问题描述】:

我有带有数据绑定的父布局。我想添加子布局取决于逻辑。 我预计需要类似的代码:

    <include
        android:id="@+id/layout_id"
        layout="@{model.isOk ? @layout/layout_container_1 : @layout/layout_container_2}"
        app:model="@{model}" />

但我得到错误:

android.databinding.tool.processing.ScopedException: [databinding] {"msg":"included value (@{model.isOk ? @layout/layout_container_1 : @layout/layout_container_2}) must start with @layout/.","file":"x.xml","pos":[]}

是否可以通过“if-​​else”条件动态包含子布局?如果是,最好的方法是什么?欢迎任何样品!

【问题讨论】:

    标签: android xml layout data-binding include


    【解决方案1】:

    是否可以通过“if-​​else”条件动态包含子布局?

    到目前为止,这是不可能的,因为layout 属性必须在数据绑定起作用之前指向某个布局:

    (数据绑定)顾名思义就是将数据绑定到一个布局,所以必须先有一个布局,才能绑定数据。

    换句话说,layout="@{model.isOk ? @layout/layout_container_1 : @layout/layout_container_2}" 不起作用,因为在检查model.isOk 之前应该有一个现有的布局,而且layout 属性需要在@ 符号之后有一个布局资源。

    所以,we need to do that programmatically instead of the XML layout。这可以通过将 &lt;include&gt; 替换为 ViewStub 来完成,这是在活动中完成的一种延迟布局膨胀。

    所以在膨胀ViewStub布局之前,你可以有机会检查一下Activity中的model.isOk是否在决定ViewStub上可以膨胀哪个布局。

    因此基于model.isOk 的值,我们可以使用binding.myLayoutStub.myViewStub?.layoutResource = R.layout.layout_container_1 设置布局

    最终我们可以使用binding.myLayoutStub.myViewStub?.inflate() 方法来扩充新的ViewStub 布局。

    使用ViewStub,将使您拥有一对绑定对象,一个用于活动(或片段),另一个用于ViewStub的膨胀布局。

    这是一个像你这样的例子:

    activity_main.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
    
            <variable
                name="model"
                type="com.example.android.databindingexample.ActivityViewModel" />
    
        </data>
    
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <ViewStub
                android:id="@+id/layout_stub"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    
    </layout>
    

    我们有 layout_container_1layout_container_2,它们仅在 textview 值上有所不同:

    layout_container_1.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <layout>
    
        <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/purple_200">
    
            <TextView
                android:id="@+id/textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Layout 1"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
    
        </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>
    

    视图模型:

    class ActivityViewModel : ViewModel() {
        var isOk = true
    }
    

    然后根据model.isOk 决定Activity 中的哪个布局,每当您对ViewStub 进行充气时:

    class MainActivity : AppCompatActivity() {
    
        private lateinit var binding: ActivityMainBinding
    
        // This is of Any type as it will be casted later to the proper DataBinding generated class when the ViewStub layout inflated
        private lateinit var stubBinding: Any
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    
            val model = ViewModelProvider(this).get(ActivityViewModel::class.java)
    
            binding.model = model
    
            // Listener to ViewStub inflation so that we can change its layout
            binding.layoutStub.setOnInflateListener { _, inflated ->
                stubBinding =
                    (if (model.isOk) DataBindingUtil.bind<LayoutContainer1Binding>(
                        inflated
                    )
                    else DataBindingUtil.bind<LayoutContainer2Binding>(
                        inflated
                    ))!!
                
                // Changing the text of the inflated layout in the ViewStub using the ViewStubBinding
                if (model.isOk)
                    (stubBinding as (LayoutContainer1Binding)).textview.text = "This is Layout container 1"
                else
                    (stubBinding as (LayoutContainer2Binding)).textview.text = "This is Layout container 2"
            }
    
    
            // Inflating the ViewStub
            if (!binding.layoutStub.isInflated) {
                binding.layoutStub.viewStub?.layoutResource = if (model.isOk)
                    R.layout.layout_container_1 else R.layout.layout_container_2
    
                binding.layoutStub.viewStub?.inflate()
            }
    
        }
    
    }
    

    预览:

    【讨论】:

      【解决方案2】:

      这在&lt;include&gt; 标签的当前实现中是不可能的。 “实现”是指 SDK 实际使用它的方式。

      &lt;include&gt;标签在布局膨胀过程中被解析,这意味着&lt;include&gt;标签的layout属性必须有一个布局资源ID定义之前你的任何代码逻辑可以执行。虽然数据绑定是您可以在布局成功膨胀后使用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-04
        • 1970-01-01
        • 2020-07-19
        • 2011-12-17
        • 2011-08-03
        • 1970-01-01
        相关资源
        最近更新 更多