Android使用自定义View的ViewBinding

19

我想尝试在自定义视图中使用ViewBinding,例如:

MainActivity <=> layout_main.xml
MyCustomView <=> layout_my_custom_view.xml

layout_main.xml

<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.example.myapplication.MyCustomView
            android:id="@+id/custom_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
</FrameLayout>

layout_my_custom_view.xml

<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/line1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Line1" />

        <View
            android:id="@+id/divider"
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:background="#2389bb" />

        <TextView
            android:id="@+id/line2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Line2" />
</LinearLayout>

主活动

class MainActivity : AppCompatActivity() {

    private lateinit var binding: LayoutMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = LayoutMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.customView.line1.text = "Hello"
        binding.customView.line2.text = "World"
    }
}
在我的MainActivity中,我可以使用绑定找到MyCustomView,但是我无法在MyCustomView中进一步找到@id/line1和@id/line2。在这种情况下,是否只能使用ViewBinding或者必须使用findViewById或Kotlin synthetic?感谢您的提前帮助。

有什么问题吗?您可以使用Kotlin合成作为custom_view.line1. - Ethan Choi
@EthanChoi 谢谢。我知道我可以使用 Kotlin Synthetics 或 findViewById,但我想知道是否只能使用 ViewBinding。 - Z.J Hung
请确保将您的视图用 <layout> </layout> 标签包装起来。 - oziomajnr
3个回答

41

ViewDataBinding.inflate 在自定义视图中不会生成子视图访问器。

因此,您不能仅使用 ViewDataBinding 来触及 line1(TextView)

如果您不想使用 findViewByIdkotlin syntheticMyCustomView 也需要应用 ViewDataBinding。尝试以下步骤。

自定义视图

class MyCustomView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
    private val binding =
        CustomLayoutBinding.inflate(LayoutInflater.from(context), this, true)

    val line1
        get() = binding.line1

    val line2
        get() = binding.line2
}

主活动

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
    setContentView(binding.root)

    with(binding.customView) {
        line1.text = "Hello"
        line2.text = "World"
    }
}

太好了。那么在你的意见中,你建议哪一个?是在MyCustomView中应用ViewBinding并将子视图公开为属性?还是使用findViewByIdkotlin synthetic - Z.J Hung
@Z.JHung 这取决于您的需求。如果存在用于 MyCustomViewMyCustomViewModel,则使用 ViewDataBinding 更为有效,否则最好使用 Kotlin synthetic - Ethan Choi
1
你能否将CustomView中的val绑定设置为public,并以以下方式在MainActivity中访问line1或line2:binding.customView.binding.line1?或者这样做是不好的实践吗? - Praveen P.
有人可以确认或纠正我的假设吗: 当在普通布局/视图中使用作为视图时(例如片段),attachToRoot = true, 并且由于我们将在Adapter的onCreateViewHolder中将其设置为attachToRoot = false(如果我们将其用作itemView),因此无论我们在此处将其设置为true都没有关系。 - Javatar

5
另一种方法是返回CustomView绑定对象
class CustomView constructor(context: Context, attrs: AttributeSet?) : 
    ConstraintLayout(context, attrs){
                
        private val _binding: CustomViewBinding = CustomViewBinding.inflate(
                        LayoutInflater.from(context), this, true)
                
                val binding get() = _binding    
        }

然后,在您的ActivityFragment中:

binding.customView.binding.line1?.text = "Hello"
binding.customView.binding.line2?.text = "World"

只需从val _binding中删除private即可。 - Shwarz Andrei
1
@ShwarzAndrei _binding 是私有的,它的 getter 是 binding。 - Igor Fridman
这是更加简洁的方式,适用于那些想要在片段中访问其自定义视图的所有子视图而不将其自定义视图绑定对象公开的人。 - Siddhivinayak
更喜欢这个。在对自定义视图进行更改时,这样做会更省力。 - falconforce

0

我相信你可以在自定义视图中使用setter。由于ViewBinding为您的主布局生成绑定类,因此它应该返回CustomView类。因此,您可以使用刚编写的setter更改文本。


听起来我需要将MyCustomView变成一个更加封装的类。我需要添加setLine1Text()setLine2Text()setLine1Visibility()setLine2Visibility()等方法。如果自定义视图没有太多的子视图,我可以尝试一下。 - Z.J Hung
或者,你甚至可以使用attrs.xml文件来设置属性。这样,它更像是一个视图而不是包装类。 - Arda Kucukoz

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