Я мигрирую из 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
для разных макетов?
ViewBinding на самом деле не поддерживает ничего вроде «совместного использования» представлений. Вам нужно либо проявить творческий подход к своему макету, чтобы уместить его в одну привязку (например, если вы используете ConstraintLayout, вы можете иметь один набор представлений и применять разные наборы ограничений), либо создать уродливый обходной путь, такой как делегат для ваших привязок представлений, который может обертывать любой из их (там немного работы по копированию и вставке, и это головная боль, если вы все еще разрабатываете макеты).
Вы можете попробовать ViewStub или интерфейс.
Вы не можете привязать переменную к двум разным представлениям, вместо этого вы можете создать две переменные, инициализировать их обе и использовать их соответствующим образом.
Вот пример.
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.
}
}
Да, я пришел к такому же решению. Смотрите ответ ниже
хорошая работа..хорошо найти решение проблемы самостоятельно
Для тех, кто может столкнуться с такой же проблемой. Я нашел два решения:
Метод 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.
Я бы предложил установить каждую переменную представления в соответствующих блоках if/else (при условии, что они оба имеют одинаковые элементы в макетах, но расположены по-разному), а затем ссылаться на переменные представления, которые вы только что установили, как обычно.