How to refactor two xml files in Android into a reusable component?

42 Views Asked by At

I have two different xml files in an Android project that are very similar -- they both specify an ImageView and a TextView -- the only difference is that they have a different text and image source specified in each one.

frog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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">

    <ImageView
       android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/frog">
    </ImageView>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Frog"
        />
</LinearLayout>

penguin.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/penguin">
    </ImageView>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Penguin"
        />
</LinearLayout>

How can I refactor these two files into a single reusable component, such that I can use them like this?

<LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
>
    <CustomLayout imageSrc="@drawable/penguin" text="Penguin">
    <CustomLayout imageSrc="@drawable/frog" text="Frog">
</LinearLayout>

I hate having to copy and paste code everywhere in my app so refactoring here would be great. I've tried using ChatGPT, looking at conversations here on StackOverflow, and looking on the official documentation for Android, but haven't been able to find a simple answer that works. Can anyone help?

Thank you in advance!

1

There are 1 best solutions below

0
ΓDΛ On BEST ANSWER

custom_animal_view.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">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </ImageView>
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </TextView>
</LinearLayout>

CustomAnimalView.kt

class CustomAnimalView(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {

    private var imageView: ImageView
    private var textView: TextView

    init {
        LayoutInflater.from(context).inflate(R.layout.custom_animal_view, this, true)
        imageView = findViewById(R.id.imageView)
        textView = findViewById(R.id.textView)
    }

    fun setValues(drawableRes: Int, text: String) {
        imageView.setImageResource(drawableRes)
        textView.setText(text)
    }
}

Usage

<com.yourpackage.name.CustomAnimalView 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/customAnimalLayout"/>

You can also use it this way in Activity and Fragment.

customAnimalLayout.setValues(R.drawable.frog, "Frog")
customAnimalLayout.setValues(R.drawable.penguin, "Penguin")

Sample Your UseCase

<LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
>
   <com.yourpackage.name.CustomAnimalView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/penguinView"/>
    <com.yourpackage.name.CustomAnimalView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/frogView"/>

</LinearLayout>

If you want to do it using attr.

attr.xml

<resources>
    <declare-styleable name="CustomView">
        <attr name="imageSrc" format="reference"/>
        <attr name="text" format="string"/>
    </declare-styleable>
</resources>

CustomAnimalV2View.kt

class CustomAnimalV2View(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {

    private var imageView: ImageView
    private var textView: TextView

    init {
        LayoutInflater.from(context).inflate(R.layout.custom_animal_view., this, true)
        imageView = findViewById(R.id.imageView)
        textView = findViewById(R.id.textView)

        val attributes = context.obtainStyledAttributes(attrs, R.styleable.CustomView)
        imageView.setImageResource(attributes.getResourceId(R.styleable.CustomView_imageSrc, 0))
        textView.text = attributes.getString(R.styleable.CustomView_text)
        attributes.recycle()
    }
}

Usage

<com.yourpackage.name.CustomAnimalV2View 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:imageSrc="@drawable/penguin"
    app:text="Penguin" />