View Binding - 如何为包含的布局获取绑定?

236

在使用视图绑定时,我遇到了几个未记录的情况。

第一:如何获取包含的视图布局部分的绑定?主要的绑定只能看到在主布局中定义的项目。

第二:如何获取合并布局部分的绑定。同样,主要的绑定只能看到主布局中的项目?


另一种简单的方法是使用数据绑定库。然后,将您的XML布局包装在<layout>标记中,以便如果您使用该库,它会自动生成用于将布局中的视图与数据对象绑定的类。 老实说,我认为这是可行的方式。 请按照此处的指南进行操作(https://developer.android.com/topic/libraries/data-binding)。 - Arun Gurung
2
嗨,我写了一篇博客文章,完全解释了视图绑定,并介绍了如何处理包含合并和包含布局。请查看Androidbites|ViewBinding - Chetan Gupta
我有点卡在这里:https://stackoverflow.com/questions/67808297/viewbinding-of-included-library-will-not-generated/67819728#67819728 - SharePeng Hu
顺便提一下,如果有人想知道如何使用视图绑定来设置 应用栏标志__,只需使用绑定对象:__binding.toolbar.setLogo(R.drawable.bar_logo);. 根据需要,'binding' 和 'bar_logo' 可以更改。 - Ajowi
9个回答

345

如果出现以下情况:

  1. 包含通用布局(非合并节点),我们需要为包含的部分分配ID,这样在绑定时我们就可以访问包含的子部分
<include
    android:id="@+id/your_id"
    layout="@layout/some_layout" />

在您的活动代码中,可以这样写:

答案:在您的活动代码中这样写:
private lateinit var exampleBinding: ActivityExampleBinding  //activity_example.xml layout

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
    setContentView(exampleBinding.root)
    //we will be able to access included layouts view like this
    val includedView: View = exampleBinding.yourId.idOfIncludedView
//[...]
}
  1. 在外部布局中包含合并块。我们无法为其添加ID,因为合并块不是视图。假设我们有这样的外部合并布局(merge_layout.xm):
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="@layout/activity_example">

    <TextView
        android:id="@+id/some_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World" />
</merge>

为了正确绑定此合并布局,我们需要:

在您的活动代码中:

private lateinit var exampleBinding: ActivityExampleBinding  //activity_example.xml layout
private lateinit var mergeBinding: MergeLayoutBinding  //merge_layout.xml layout

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    exampleBinding = ActivityExampleBinding.inflate(layoutInflater)
    //we need to bind the root layout with our binder for external layout
    mergeBinding = MergeLayoutBinding.bind(exampleBinding.root)
    setContentView(exampleBinding.root)
    //we will be able to access included in merge layout views like this
    val mergedView: View = mergeBinding.someView
//[...]
}

10
需要在包含合并视图的文件中添加 layout 标签。 - João Carlos
1
我试图在片段中完成这个任务,而不是在活动中,但它没有起作用。我已经将global_header设置为我的包含项的ID,并且实际上,在Android Studio中的自动完成功能完美地工作,并自动完成viewBindings.globalHeader,因此它已生成绑定,但在运行时它会崩溃:“java.lang.NullPointerException:缺少必需的视图ID:com.daon.ps.daonsonboard:id/global_header” - Fran Marzoa
@FranMarzoa 你确定在Fragments的onCreateView方法中执行了所有必要的操作吗? 例如: val binding = FrgamentSomethingSomething.inflate(layoutInflater)return binding.root - Artur Kasprzak
@TrầnLeo 我认为这并不需要。当然,记住不要在片段/活动之外提供这些绑定,除非你想要内存泄漏。 - Artur Kasprzak
有没有其他解决方法? - IntelliJ Amiya
显示剩余7条评论

48

关于您的第一个问题,您可以为包含的布局获取视图绑定。

这是一个示例 main_fragment.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view_main"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <include
        android:id="@+id/toolbar"
        layout="@layout/toolbar" />

</LinearLayout>

MainFragment.java 可以像这样:

public class MainFragment extends Fragment {

    private MainFragmentBinding binding;
    private ToolbarBinding toolbarBinding;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

        binding = MainFragmentBinding.inflate(inflater, container, false);
        toolbarBinding = binding.toolbar;

        return binding.getRoot();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        toolbarBinding = null;
        binding = null;
    }
}

现在你有两个绑定:一个对应主布局,另一个对应包含的布局。


2
非常简单的答案,使用了新的语法 - 在一个非Fragment活动中,我使用类似的语法在onCreate()中都可以工作。谢谢。(只是在使用DrawerLayout时遇到了一些麻烦) - Fat Monk
1
我遇到了一个运行时 NPE 错误,它说缺少 ID 为 blah 的必需视图。 - Asim
1
@Asim 如果你把你的包含布局从合并改成一些viewGroup,也许会更好。 - Jesús Barrera
1
在 Kotlin 中,我必须将赋值强制转换 (toolbarBinding = binding.toolbar as ToolbarBinding),否则它会被视为 View 并出现错误。奇怪的是,使用 binding.toolbar.something 将编译并运行良好,但在 AS 中显示为错误。 - CodeClown42
1
更好的是:使用工具栏绑定与转换一起构建时会引发警告。在编辑器中警告为“无需转换”,但警告内容为“此转换无法成功”(如果没有它,则视图键入错误)。 - CodeClown42

20

如果您想要绑定包含的布局,则

对于Activity

YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(getLayoutInflater);

View view = mainLayoutBinding.getRoot();

YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);

片段化编程

YourMainLayoutBinding mainLayoutBinding = MainLayoutBinding.inflate(inflater,container,false);

View view = mainLayoutBinding.getRoot();

YourIncludedLayoutBinding includedLayoutBinding = YourIncludedLayoutBinding.bind(View);
确保您的主布局绑定父根是LinearLayout,那么includedLayoutBinding父布局也应该是一个线性布局。

9
如果您在包含布局时指定了一个id,就不需要显式地绑定它,因为这个过程是自动完成的:<include android:id="@+id/myToolbar" layout="@layout/toolbar" />。之后,字段mainLayoutBinding.myToolbar将包含一个嵌套绑定。 - Dmitry K
1
@DmitryK 实际上是需要的,否则 Android Studio Arctic Fox 会出现错误 Cannot access class 'x'. Check your module classpath for missing or conflicting dependencies - Michał Dobi Dobrzański

19

假设我在我的activity_main.xml文件中包含了以下布局:

<include
    android:id="@+id/ll_layout1"
    layout="@layout/layout1"
    android:visibility="gone" />

假设我想改变它的可见性。我可以按以下方式操作:

activityMainBinding.llLayout1.root.visibility = View.VISIBLE

根函数不可用,您可以使用rootView代替,我认为它是相同的。 - bobby I.
1
@bobbyI. 这是 Kotlin,所以我们使用属性语法,因此只需使用 root。在 Java 中,您可以使用 getRoot() 来实现相同的效果。 - shivang

3
在我的情况下,我忘记给include标签分配id
现在,当您分配了id后,您将能够获取绑定对象,如下所示:
YourMainLayoutBinding.YourIncludeTagIDLayoutBinding

3

回答你关于片段的第一个问题,假设你在MainFragment的xml文件中包含了一个"error_layout"。

<include
    layout="@layout/error_layout"
    android:id="@+id/layout_error"
    android:visibility="gone"/>

现在在 "error_layout" 中有一个 id 为 "btn_try_again" 的按钮。你想给这个按钮设置一个点击监听器。
以下是如何使用绑定对象获取对 "btn_try_again" 的引用,用于你的 fragment_main.xml 文件:
binding.layoutError.btnTryAgain

1

请按照以下步骤操作:

  1. private val binding : FragmentBinding by viewBinding(FragmentBinding::bind)

  2. make sure to do the following in "onViewCreated(view: View, savedInstanceState: Bundle?)"

     val binding2 = binding.root.include_layout_id
    
现在可以在这里访问你的 include layout 和 views。例如:

val binding2 = binding.root.tool_bar_layout

binding2.textView.text = "your text"

-1
使用数据绑定库。然后用<layout>标签包装您的XML布局。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    ... 

    <include 
      android:id="@+id/toolbar"
      layout="@layout/toolbar" />
    
    ...

</LinearLayout>
</layout>

toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView 
        android:id="@+id/ivImage"
        ... />

    <TextView 
        android:id="@+id/tvTitle"
        ... />


</LinearLayout>

MainActivity.kt

private lateinit var binding: ActivityMainBinding  

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

    // Access include layout views
    binding.toolbar.rootView.ivImage.setImageResource(R.drawable.ic_back_arrow)
    binding.toolbar.rootView.tvTitle.text = getString(R.string.home)
   
    ...
}

1
问题是关于 ViewBinding 而不是 DataBinding。 - Artur Kasprzak

-2
在包含的布局中,您必须创建一个容器布局并在此处放置ID。
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/example"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent">
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接