undefined
undefined

Reputation: 695

How to use ViewBindings for mutiple layouts

I'm migrating from kotlinx.android.synthetic to ViewBindings.

I have two layouts(for phones and tablets) with the same set of ids:

class GameActivity: AppCompatActivity() {
    lateinit var binding: ViewBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (AppData.instance.isTablet)
            binding=ActivityGameTabletBinding.inflate(layoutInflater)
        else
            binding=ActivityGamePhoneBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val btn=binding.menuBtn //no such property
    }
    ...

}

The problem is binding contains only one property which is root.

Thus I'm forced to fallback to old getViewById. Is there way to have viewBinding features for different layouts?

Upvotes: 3

Views: 76

Answers (2)

undefined
undefined

Reputation: 695

For those who might facing the same problem. I found two solutions:

Method 1: Use kotlin-reflection library to get properties by it's string name:

private inline fun <T>getViewByName(name:String):T{
        val prop= binding::class.memberProperties.find { it.name==name }
        val view=prop?.call(binding) as T
        if (view!=null) {
            return view
        } else
            throw Exception("view not found")
    }

I find this method quite hacky, so I sticked to the second one:

class GameActivityBinding(private val binding:ViewBinding) {
  val menuBtn:ImageButton
        get() {
            return when (binding) {
                is ActivityGameTabletBinding -> {
                    binding.menuBtn
                }

                is ActivityGamePhoneBinding -> {
                    binding.menuBtn
                }
                else -> throw Exception("incorrect layout")
            }
        }
    // rest of the views
}

No need to litter code with dozens of if-else's

Upvotes: 1

Jayanta Sarkar
Jayanta Sarkar

Reputation: 644

You cannot have a variable have binded to 2 different views instead you can create 2 variables and initialize both of them and use them accordingly.

Here is an example.

private lateinit var phoneBinding : ActivityGamePhoneBinding
private lateinit var tabletBinding : ActivityGameTabletBinding

Then initialize both of them.

phoneBinding = ActivityGamePhoneBinding.inflate(layoutInflater)
tabletBinding = ActivityGameTabletBinding.inflate(layoutInflater)

  if (AppData.instance.isTablet){
       val btn = tabletBinding.button
       //Do rest of your tablet code
  } else {
       val btn = phoneBinding.button
       //Do rest of your phone code
  }

If you are doing common things you should create a function and call the same function in both scenarios.

phoneBinding = ActivityGamePhoneBinding.inflate(layoutInflater)
tabletBinding = ActivityGameTabletBinding.inflate(layoutInflater)

  if (AppData.instance.isTablet){
       val btn = tabletBinding.button
       onButtonClick(btn)
  } else {
       val btn = phoneBinding.button
       onButtonClick(btn)
  }

  fun onButtonClick(button : Button){
   button.setOnclickListner{
    //rest of you code.
   }
  }

Upvotes: 1

Related Questions