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 !
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()
}