动态访问自定义 ViewGroup 实例中的子视图

发布于 2025-01-12 02:55:13 字数 4967 浏览 0 评论 0原文

我想创建一个自定义 CardView,在创建实例时可以在其中添加其他子组件,如下所示:

<com.example.app.CardComponent 
tools:context=".MainActivity"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title_text="title"
app:description_text="description"
app:clickable_button="true"
xmlns:android="http://schemas.android.com/apk/res/android">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="testButton"/>

</com.example.app.CardComponent>

其中 CardComponent 是我创建的自定义 CardView,testButton 是我想要插入到 CardComponent 中的附加视图的示例。我想将其插入到这一层中,而不是直接插入到 CardComponent 的 xml 文件中,以实现自定义目的和可重用性。

问题是我不知道如何动态访问自定义视图组实例的子视图(例如 CardComponent 类中的 testButton),以便将它们动态添加到布局等。

以下是 CardComponent 类和 xml 文件以及CardComponent 的 attr 文件。

class CardComponent @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    MaterialCardView(context, attrs, defStyleAttr) {

    private val bindings: CardComponentBinding = CardComponentBinding.inflate(LayoutInflater.from(context), this, true)

    init {
        attrs?.let {

            val styledAttributes = context.obtainStyledAttributes(it, R.styleable.CardComponent)

            val titleText = styledAttributes.getString(R.styleable.CardComponent_title_text)
            bindings.cardTitle.text = titleText

            val descriptionText = styledAttributes.getString(R.styleable.CardComponent_description_text)
            bindings.cardDescription.text = descriptionText

            val clickableButton = styledAttributes.getBoolean(R.styleable.CardComponent_clickable_button, false)
            if (clickableButton){
                bindings.cardDescriptionButton.setOnClickListener{
                    val v = bindings.retractableLayout.visibility
                    bindings.retractableLayout.visibility = if(v == GONE) VISIBLE else GONE
                }
            }
            // Here I would have something like this but can't find a way to do it:
            // val button = styledAttributes.getChild()
            // bindings.relative_layout.addChild(button)

        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:animateLayoutChanges="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/card_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentStart="true"
                android:layout_alignParentTop="true"
                android:padding="15dp"/>

            <Button
                android:id="@+id/card_description_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:padding="15dp"
                android:text="test"/>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/retractable_layout"
            android:visibility="gone"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/card_description"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"/>
        </RelativeLayout>
    </LinearLayout>

</com.google.android.material.card.MaterialCardView>

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CardComponent">
        <attr name="title_text" format="string"/>
        <attr name="description_text" format="string"/>
        <attr name="clickable_button" format="boolean"/>
    </declare-styleable>
</resources>

感谢您的帮助 !

I would like to create a Custom CardView where I can add additional child components when I create an instance as follows :

<com.example.app.CardComponent 
tools:context=".MainActivity"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title_text="title"
app:description_text="description"
app:clickable_button="true"
xmlns:android="http://schemas.android.com/apk/res/android">

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="testButton"/>

</com.example.app.CardComponent>

where CardComponent is the custom CardView I created and testButton an example of an additional view I'd like to insert in my CardComponent. I would like to insert it in this layer and not directly in the xml file of the CardComponent for customization purposes and reusability.

The problem is I don't know how to dynamically access child views of a custom viewgroup instance like this testButton in the CardComponent class in order to then dynamically add them to the layout, etc.

Here are the CardComponent class and xml file as well as the attr file for the CardComponent.

class CardComponent @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
    MaterialCardView(context, attrs, defStyleAttr) {

    private val bindings: CardComponentBinding = CardComponentBinding.inflate(LayoutInflater.from(context), this, true)

    init {
        attrs?.let {

            val styledAttributes = context.obtainStyledAttributes(it, R.styleable.CardComponent)

            val titleText = styledAttributes.getString(R.styleable.CardComponent_title_text)
            bindings.cardTitle.text = titleText

            val descriptionText = styledAttributes.getString(R.styleable.CardComponent_description_text)
            bindings.cardDescription.text = descriptionText

            val clickableButton = styledAttributes.getBoolean(R.styleable.CardComponent_clickable_button, false)
            if (clickableButton){
                bindings.cardDescriptionButton.setOnClickListener{
                    val v = bindings.retractableLayout.visibility
                    bindings.retractableLayout.visibility = if(v == GONE) VISIBLE else GONE
                }
            }
            // Here I would have something like this but can't find a way to do it:
            // val button = styledAttributes.getChild()
            // bindings.relative_layout.addChild(button)

        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools">

    <LinearLayout
        android:animateLayoutChanges="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/card_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentStart="true"
                android:layout_alignParentTop="true"
                android:padding="15dp"/>

            <Button
                android:id="@+id/card_description_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"
                android:padding="15dp"
                android:text="test"/>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/retractable_layout"
            android:visibility="gone"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/card_description"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignParentEnd="true"/>
        </RelativeLayout>
    </LinearLayout>

</com.google.android.material.card.MaterialCardView>

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CardComponent">
        <attr name="title_text" format="string"/>
        <attr name="description_text" format="string"/>
        <attr name="clickable_button" format="boolean"/>
    </declare-styleable>
</resources>

Thank you for your help !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挥剑断情 2025-01-19 02:55:13

我的解决方案是使用 OnFinishInflate 方法,在该方法中获取 CardComponent 的子级(第一张图片),删除它们的父视图并将它们添加到我在自定义视图的 xml 文件中设置的正确布局(第三张图片) 。我最终跳过第一个子项,因为它是 MaterialCardView 的一个实例(这个子项代表我制作的自定义卡片,第三张图片),然后将所有剩余的子项添加到合适的位置。

private val bindings: CardComponentBinding = CardComponentBinding.inflate(layoutInflater, this, true)
private var customComponentLayout: RelativeLayout = bindings.customComponentLayout
    
override fun onFinishInflate() {
    val children = this.children.toList()

    for (child in children) {
        if (child is MaterialCardView) continue
        else {
            this.removeView(child)
            this.customComponentLayout.addView(child)
        }
    }
    super.onFinishInflate()
}

My solution was to use the OnFinishInflate method where I get the children of my CardComponent (1st picture), I delete their parent view and add them to the correct layout I'd set up in the xml file of my custom view (3rd picture). I eventually skip the first child because it's an instance of MaterialCardView (this child represents the custom Card I made, 3rd picture) and then take all the remaining children and add them to the good spot.

private val bindings: CardComponentBinding = CardComponentBinding.inflate(layoutInflater, this, true)
private var customComponentLayout: RelativeLayout = bindings.customComponentLayout
    
override fun onFinishInflate() {
    val children = this.children.toList()

    for (child in children) {
        if (child is MaterialCardView) continue
        else {
            this.removeView(child)
            this.customComponentLayout.addView(child)
        }
    }
    super.onFinishInflate()
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文