Search code examples
javaandroidandroid-layoutandroid-viewbinding

View Binding with 2 possible layouts, assign binding variable to 2 generated binding classes


Desired functionality:

I have an Activity that has a value received from backend which indicates to use either one of two layouts. Let's call this value layoutType and let's assume for simplicity in this example code below that we don't care how it will be assigned. Thus, I have two layout xml files, let's call them layout1.xml & layout2.xml.

Implementation: I'd like to use View Binding. I've created a variable of type ViewBinding and I tried assigning to it either a Layout1Binding or a Layout2Binding. A summary of this logic in pseudocode is this:

 private ViewBinding binding;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);

     if(layoutType == 1){
         binding = Layout1Binding.inflate(getLayoutInflater());
     } else {
         binding = Layout2Binding.inflate(getLayoutInflater());
     }
     setContentView(binding.getRoot());
 }

Result: Of course, this doesn't work and variable binding appears to have no inner children that can be referenced. Also, of course if I convert the variable's type to Layout1Binding and layoutType equals 1, then I can use it correctly. Same goes if I use Layout2Binding and layoutType is not equal to 1. All these make sense, since ViewBinding is just an Interface implemented by the generated classes Layout1Binding & Layout2Binding.

Question: How can I achieve the above desired behaviour by using only one binding variable which can be assigned to two different generated classes? Is there any other alternative way?


Solution

  • Here's an example of how I used an adapter for a custom view that uses three layouts. In my case, most views in the different layout files had common IDs.

    Define your binding adapter class

    class MyCustomViewBindingAdapter(
        b1: Layout1Binding?,
        b2: Layout2Binding?,
        b3: Layout3Binding?
    ) {
    
        val text =
            b1?.myTextView
                ?: b2?.myTextView
                ?: b3?.myTextView
    
        val badgeText = b3?.myBadgeTextView
    }
    

    Create a binding variable

    private lateinit var binding: MyCustomViewBindingAdapter
    

    Create a method to get the binding adapter

    private fun getBinding(layoutType: Int): MyCustomViewBindingAdapter {
        val inflater = LayoutInflater.from(context)
    
        return when(layoutType) {
            TEXT_ONLY -> {
                val textBinding = Layout1Binding.inflate(inflater, this, true)
                MyCustomViewBindingAdapter(textBinding, null, null)
            }
            IMAGE_AND_TEXT -> {
                val imageTextBinding = Layout2Binding.inflate(inflater, this, true)
                MyCustomViewBindingAdapter(null, imageTextBinding, null)
            }
            TEXT_AND_BADGE -> {
                val textBadgeBinding = Layout3Binding.inflate(inflater, this, true)
                MyCustomViewBindingAdapter(null, null, textBadgeBinding)
            }
            else -> throw IllegalArgumentException("Invalid view type")
        }
    }
    

    Be sure to initialize the binding before using it

    binding = getBinding(layoutType)