6

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?

Thanasis M
  • 1,144
  • 7
  • 22
  • 1
    You have to either write an adapter that will wrap and accept both bindings types while exposing "common" fields or create a generic activity/fragment that have different binding type. – Pawel Feb 15 '21 at 19:58
  • @Pawel could you please provide a relevant code/pseudocode sample that demonstrates this logic? I don't have an adapter, so I'm more interested in your second suggestion. – Thanasis M Feb 16 '21 at 08:55

1 Answers1

5

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)
Markymark
  • 2,804
  • 1
  • 32
  • 37