Search code examples
androidxmlrefactoringreusability

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


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!


Solution

  • 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" />