Как использовать ViewBindings для нескольких макетов

Я мигрирую из kotlinx.android.synthetic в ViewBindings.

У меня есть два макета (для телефонов и планшетов) с одинаковым набором идентификаторов:

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
    }
    ...

}

Проблема в том, что binding содержит только одно свойство — root.

Таким образом, я вынужден вернуться к старому getViewById. Есть ли способ добавить функции viewBinding для разных макетов?

Я бы предложил установить каждую переменную представления в соответствующих блоках if/else (при условии, что они оба имеют одинаковые элементы в макетах, но расположены по-разному), а затем ссылаться на переменные представления, которые вы только что установили, как обычно.

tyczj 13.06.2024 21:30

ViewBinding на самом деле не поддерживает ничего вроде «совместного использования» представлений. Вам нужно либо проявить творческий подход к своему макету, чтобы уместить его в одну привязку (например, если вы используете ConstraintLayout, вы можете иметь один набор представлений и применять разные наборы ограничений), либо создать уродливый обходной путь, такой как делегат для ваших привязок представлений, который может обертывать любой из их (там немного работы по копированию и вставке, и это головная боль, если вы все еще разрабатываете макеты).

Pawel 14.06.2024 00:12

Вы можете попробовать ViewStub или интерфейс.

storytellerF 14.06.2024 03:47
3
3
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы не можете привязать переменную к двум разным представлениям, вместо этого вы можете создать две переменные, инициализировать их обе и использовать их соответствующим образом.

Вот пример.

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

Затем инициализируйте их обоих.

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
  }

Если вы делаете общие вещи, вам следует создать функцию и вызывать одну и ту же функцию в обоих сценариях.

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.
   }
  }

Да, я пришел к такому же решению. Смотрите ответ ниже

undefined 17.06.2024 17:47

хорошая работа..хорошо найти решение проблемы самостоятельно

Laser 18.06.2024 07:55

Для тех, кто может столкнуться с такой же проблемой. Я нашел два решения:

Метод 1: Используйте библиотеку kotlin-reflection для получения свойств по имени строки:

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")
    }

Я считаю этот метод довольно хакерским, поэтому я остановился на втором:

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
}

Не нужно засорять код десятками if-else.

Другие вопросы по теме