Custom View — разбиваем функционал

от автора

В этой статье пойдет речь о вынисении UI в отдельный блок, компоновкой стандартных элементов.

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

Основной пример будет рассмотрен на простой задаче когда нам необходим Switch в котором будет и текст и описание.

Визуальный пример
Визуальный пример

Как создать что-то подобное и не нагружать верстку ?

По началу может показаться что это муторно, однако в больших проектах без этого не обойтись, иначе будет много вложенностей и код постепенно станет не читабельным.

первое что потребуется сделать это выделить один элемент и вынести верстку из файла xml.

Создаем файл CusomSwitch.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="http://schemas.android.com/apk/res-auto"     xmlns:tools="http://schemas.android.com/tools"     android:layout_width="match_parent"     android:layout_height="match_parent"     tools:context=".MainActivity">      <LinearLayout         android:layout_width="match_parent"         android:layout_height="wrap_content"          android:orientation="vertical"         android:paddingStart="22dp"         android:paddingEnd="22dp"          app:layout_constraintBottom_toBottomOf="parent"         app:layout_constraintEnd_toEndOf="parent"         app:layout_constraintStart_toStartOf="parent"         app:layout_constraintTop_toTopOf="parent">          <com.example.customviewblock.CustomSwitch             android:id="@+id/first"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:checked="false"             app:cs_subtitle="SubTitle"             android:title="Title" />          <com.example.customviewblock.CustomSwitch             android:id="@+id/second"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_marginTop="10dp"             android:checked="true"             app:cs_subtitle="SubTitle"             android:title="Title" />      </LinearLayout>  </androidx.constraintlayout.widget.ConstraintLayout>

Теперь создаем в попке с UI папку view (для хранения кодовой части ваших кастомных view). И создаем файл CustomSwitch.kt

class CustomSwitch (     context: Context,     attributeSet: AttributeSet ) : LinearLayout(context, attributeSet) {     private var binding: CustomSwitchBinding =         CustomSwitchBinding.inflate(LayoutInflater.from(context), this, false)      var addOnChangeListener: ((Boolean) -> Unit)? = null      var isChecked: Boolean         get() = binding.switchSC.isChecked         set(value) {             binding.switchSC.isChecked = value         }      init {         addView(binding.root)         val typedArray = context.theme.obtainStyledAttributes(             attributeSet,             R.styleable.CustomSwitch, 0, 0         )          binding.apply {             val title = typedArray.getString(R.styleable.CustomSwitch_android_title)             val subTitle = typedArray.getString(R.styleable.CustomSwitch_cs_subtitle)             val isChecked = typedArray.getBoolean(R.styleable.CustomSwitch_android_checked, false)              switchSC.isChecked = isChecked             titleTV.text = title             subTitleTV.text = subTitle              switchSC.setOnCheckedChangeListener { _, _isChecked ->                 addOnChangeListener?.invoke(_isChecked)             }         }     }      fun setTitle(text: String){         binding.titleTV.text = text     }      fun setSubTitle(text: String) {         binding.subTitleTV.text = text     } }

Сейчас этот файл будет ругаться на то что он не может найти R.styleable.CustomSwitch.

В этом файле вы будете указывать какие атрибуты можно задать для вашего кастового view в файле xml. давайте создадим его.

идем в res далее в values и создаем файл attrs.

   <declare-styleable name="CustomSwitch">         <attr name="cs_subtitle" format="string"/>         <attr name="android:title"/>         <attr name="android:checked" />     </declare-styleable> 

в поле name — обязательно нужно указывать имя вашего класса в нашем случае это был CustomSwitch.kt — соответственно поле name будет CustomSwitch. если назвать его иначе то в файле xml мы не будем получать подсказок, какие поля еще имеются для заполнения.

весьма полезная функция чтобы ее потерять)

Также хочу заметить что в этом файле имеется два типа полей, те которые добавляются из анроида и те которые мы создаем лично.

поле checked имеется в платформе и соответственно может переиспользываться, также как и title. вы можете переиспользовать их сколь угодно раз.

subtitle так же имеется в платформе, однако для примера я не стал указывать что хочу переиспользовать его и не указал слово android:.

будте внимательны так как новое имя может использоваться только в одной кастовой вю по этому я добавил абривиатуру cs — которая обозначает что этот атрибут будет использоваться только с CustomSwitch.

в файле CustomSwitch — обязательно в блоке init() не забудьте указать addView(binding.root). что привяжет вашу верстку к файлу kt.

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

В примере имеются несколько базовых функций для работы с этой вю:

setTitle, setSubTitle — для установки соответствующих полей, isChecked — для проверки/устаноки состояния и addOnChangeListener — для прослушивания нажатий.

вот как можно применить это в коде:

class MainActivity : AppCompatActivity() {     lateinit var binding: ActivityMainBinding     override fun onCreate(savedInstanceState: Bundle?) {         super.onCreate(savedInstanceState)         binding = ActivityMainBinding.inflate(layoutInflater)         setContentView(binding.root)         binding.run {              first.isChecked = !first.isChecked       // get or set checked             first.addOnChangeListener = { boolean -> // boolean on change                              }             first.setTitle("text") // set title             first.setSubTitle("text2") // set subTitle         }     } }


ссылка на оригинал статьи https://habr.com/ru/post/700182/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *