Development for Android. A bit about fast work with lists
Hello! My posts - the desire to help in working with some elements of Android. If you are a developer who has not yet formed an algorithm for building lists for himself - it may be useful for you to read this material. Basically, I would like to offer ready-made solutions for development, revealing in the course of the narration some thoughts about how I came to them .
In this article:
Well! Everyone has already forgotten about ListView and safely write to RecyclerView ( Rv ). The times when we implemented the ViewHolder pattern themselves have sunk into oblivion. Rv provides us with a set of ready-made classes for implementing lists and a fairly large selection of LayoutManagers for displaying them. In fact, looking at the many screens, the list can provide most of them - precisely because of the possibility for each element to implement its ViewHolder . For more details of the story we were told at I of the Google / About .
But, there is always a pair of "but"! .. Standard answers to Stackoverflow suggest common solutions that lead to copy-paste, especially in the place of implementation of the Adapter.
At the moment, Rv is already three years old. Infa is a cloud over it, and there are a lot of libraries with ready-made solutions, but what to do if you don’t need all the functionality, or if you climb to look at someone else’s code - and you see thereAncient Horror is not what you would like to see, or not that at all imagined? During these three years, Android finally finally took to itself Kotlin = improved readability of the code, a lot of interesting articles appeared on Rv , which fully reveal its capabilities.
The purpose of this is to collectyour bike from the best practices.basis, framework for working with lists for new applications. This framework can be supplemented with logic from the application to the application, using what you need, and discarding too much. I think this approach is much better than someone else's library - in your classes you have the opportunity to figure out how everything works, and to control the cases you need without being tied to someone else's decision.
We will decide what the component should do, the interface, not the class , but at the end we will close the specific implementation logic to the class that will implement and implement this interface. But, if it turns out that when an interface is implemented, a copy-paste is formed - we can hide it behind the abstract class, and after it - a class that inherits from the abstract. I’ll show my implementation of the base interfaces, but my goal is for the developer to simply try to think in the same direction. Once again - the plan is this: A set of interfaces -> abstract class , which takes copy-paste (if necessary) -> and already a specific class with unique code . You can implement interfaces differently.
What can an adapter do with the list? The answer to this question is easiest to get when you look at some kind of example. You can look at the RecyclerView.Adapter, you will find a couple of tips. If you think a little, you can imagine something like this:
Note the generic T . In general, the adapter works with any list object (item), so there is no clarification here - we have not yet chosen our approach. And in this article there will be at least two, the first interface looks like this:
Well, yes, it seems logical - we are talking about a list element, which means that each element must have some kind of layout, and you can refer to it using layoutId. More than anything a novice developer probably will not need, unless of course you take more advanced approaches . If you have enough experience in development, you can of course make a delegate or a wrapper, but is it worth it with a small project - and even less development experience? All my links somewhere in YouTube are very useful, if you don’t have time now - just memorize them and read further, because here the approach is simpler - I think that with standard work with Rv , judging by the official documentation , what is offered above is not implied.
Time to unite ourIBaseListAdapter with interfaces, and the following class will be abstract:
* Note: Note the override function getItemViewType (position: Int) . We need some kind of new key, according to which Rv will understand which ViewHolder suits us. ValidId for our item is very useful for this ; The Android every time helpsfully makes the layout id unique, and all values are greater than zero - we will use this further, “inflating” itemView for our viewholders in the inflateByViewType () method (next line).
Take for example the settings screen. Android offers us its own version, but what if the design needs something more sophisticated? I prefer to fill this screen as a list. There will be such a case:

We see two different elements of the list, it means that SimpleListAdapter and Rv are perfect here!
Let's get started! You can start with layout layouts for item'ov:
Then, we define the classes themselves, into which we want to pass the values that interact with the list: the first is the header and any value that comes from the outside (we will have a stub about requests another time), the second is the header and boolean variable , by which we must perform an action. To distinguish between Switch elements, id entities from the server will do; if they are not there, we can create them ourselves during initialization.
In a simple implementation, each element will also need a ViewHolder:
Well, the most interesting part is the concrete implementation of SimpleListAdapter'a:
* Note: Do not forget that under the hood of the inflateByViewType method (context, viewType, parent): viewType = layoutId.
All components are ready! Now, the Activation code remains and you can run the program:
As a result, when building a list, all the work comes down to the following:
1. Calculate the number of different layouts for the items
2. Find the names for them . I use the rule: Something Item.kt, item_ something .xml, Something ViewHolder.kt
3. We write an adapter to these classes . In principle, if you do not pretend to optimize, then one common adapter will suffice. But in large projects I would still do a few, on the screens, because in the first case the onBindViewHolder () method inevitably grows (readability of the code suffers) in your adapter (in our case, it is the SettingsListAdapter) + the program will have to run through this method every time for each item + using the onCreateViewHolder () method
. 4. Run the code and enjoy!
Up to this point, we have applied the standard data binding approach from Item.kt - to our item_layout.xml . But we can unify the onBindViewHolder () method , keep it minimal, and transfer the logic to Item and layout.
Head over to the official Android JetPack page:

Note the first tab in the Architecture section. Android Databinding is a very extensive topic, I would like to talk about it in more detail in other articles, but now we will use only the current one - we will make our Item.kt - variable for item.xml (or you can call it view mode for the layout).
At the time of this writing,Databinding could be connected like this:
Go through the base classes again. The interface for the item complements the previous one:
Also, we will expand our ViewHolder, therefore we have associated with databinding. We will pass the ViewDataBinding into it , and then safely forget about creating the layout and data binding
The same approach is used here , but on Cotlin it looks much shorter, doesn't it? =)
In general, pay attention to the onCreateViewHolder () , onBindViewHolder () methods . The idea is that they no longer grow. So you get one adapter for any screen, with any list items.
Our items:
Here you can see where the logic of the onBindViewHolder () method has gone . Android Databinding took over it - now any of our layout is supported by its view model, and it will calmly handle all the logic of clicks, animations, requests and other things. What do you come up with. Binding Adapters will help to do this well - allowing you to link the view to data of any kind. Also, it is possible to improve communication due to bilateral dating . Probably he will flash in any of the following articles, in this example, you can make everything easier. We only need one adapter adapter:
After that, we associate our variable values with our Item inside xml:
* Note: For fair reasons, it may seem to someone that we are writing a lot more code in xml - but this is a question of knowledge of the Android Databinding library. It complements the layout, reads quickly and, in principle, for the most part removes the boilerplate. I think Google is going to develop this library well, since it is the first in the Architecture tab in Android Jetpack. Try changing MVP to MVVM in a couple of projects - and many may be pleasantly surprised.
Well then! .. And, the code in SettingsActivity:
We received an algorithm for building lists and tools for working with them. In my case (I almost always use Databinding ) all the preparation is reduced to initializing the base classes into folders, layout of the items in .xml and then binding to variables in .kt.
Accelerate development
for faster work, I used templates from Apache to Android Studio - and wrote their own templates with a small demonstration of how it all works. I really hope that someone will come in handy. Please note that when you work, you need to call the template from the root folder of the project - this is done because the project applicationId parameter may lie to you if you changed it in Gradle. But packageNameso just can not fool, which I took advantage of. Available language about templating can be found at the links below.
1. Modern Android development: Android Jetpack, Kotlin, and more (Google I / O 2018, 40 m.) - a short guide to what is in fashion today, hence also in general terms it will be clear how RecyclerView has developed;
2. Droidcon NYC 2016 - Radical RecyclerView, 36 m. - A detailed report on RecyclerView from Lisa Wray ;
3. Create a List with RecyclerView - official documentation
4. Interfaces vs. classes
5. Android IDE Template Format , Total Standardization , the FreeMarker manual is a convenient approach that in this article will help you quickly create the necessary files for working with lists
6.The code for the article (there are slightly different class names, be careful), templates for work and video, how to work with templates
7. English version of the article
In this article:
- we form several base classes and interfaces for working with RecyclerView and RecyclerView.Adapter
- connect one library from Android Jetpack (optional, first without it)
- for even faster development - template option at the end of the article;)
Introduction
Well! Everyone has already forgotten about ListView and safely write to RecyclerView ( Rv ). The times when we implemented the ViewHolder pattern themselves have sunk into oblivion. Rv provides us with a set of ready-made classes for implementing lists and a fairly large selection of LayoutManagers for displaying them. In fact, looking at the many screens, the list can provide most of them - precisely because of the possibility for each element to implement its ViewHolder . For more details of the story we were told at I of the Google / About .
But, there is always a pair of "but"! .. Standard answers to Stackoverflow suggest common solutions that lead to copy-paste, especially in the place of implementation of the Adapter.
At the moment, Rv is already three years old. Infa is a cloud over it, and there are a lot of libraries with ready-made solutions, but what to do if you don’t need all the functionality, or if you climb to look at someone else’s code - and you see there
The purpose of this is to collect
Let's think logically and right from the start.
We will decide what the component should do, the interface, not the class , but at the end we will close the specific implementation logic to the class that will implement and implement this interface. But, if it turns out that when an interface is implemented, a copy-paste is formed - we can hide it behind the abstract class, and after it - a class that inherits from the abstract. I’ll show my implementation of the base interfaces, but my goal is for the developer to simply try to think in the same direction. Once again - the plan is this: A set of interfaces -> abstract class , which takes copy-paste (if necessary) -> and already a specific class with unique code . You can implement interfaces differently.
What can an adapter do with the list? The answer to this question is easiest to get when you look at some kind of example. You can look at the RecyclerView.Adapter, you will find a couple of tips. If you think a little, you can imagine something like this:
IBaseListAdapter
* Перебирая проекты, я нашел несколько других методов, которые здесь опущу, например getItemByPos(position: Int),или даже subList(startIndex: Int, endIndex: Int).Повторюсь: вы сами должны смотреть, что вам нужно от проекта, и включать функции в интерфейс. Это не сложно, когда знаешь что все происходит в одном классе. Аскетизм в данном вопросе позволит избавиться от лишней логики, которая ухудшает читаемость кода, потому что конкретная реализация занимает больше строк.
interfaceIBaseListAdapter<T> {
funadd(newItem: T)funadd(newItems: ArrayList<T>?)funaddAtPosition(pos : Int, newItem : T)funremove(position: Int)funclearAll()
}
* Перебирая проекты, я нашел несколько других методов, которые здесь опущу, например getItemByPos(position: Int),или даже subList(startIndex: Int, endIndex: Int).Повторюсь: вы сами должны смотреть, что вам нужно от проекта, и включать функции в интерфейс. Это не сложно, когда знаешь что все происходит в одном классе. Аскетизм в данном вопросе позволит избавиться от лишней логики, которая ухудшает читаемость кода, потому что конкретная реализация занимает больше строк.
Note the generic T . In general, the adapter works with any list object (item), so there is no clarification here - we have not yet chosen our approach. And in this article there will be at least two, the first interface looks like this:
interfaceIBaseListItem{
fungetLayoutId(): Int
}
Well, yes, it seems logical - we are talking about a list element, which means that each element must have some kind of layout, and you can refer to it using layoutId. More than anything a novice developer probably will not need, unless of course you take more advanced approaches . If you have enough experience in development, you can of course make a delegate or a wrapper, but is it worth it with a small project - and even less development experience? All my links somewhere in YouTube are very useful, if you don’t have time now - just memorize them and read further, because here the approach is simpler - I think that with standard work with Rv , judging by the official documentation , what is offered above is not implied.
Time to unite ourIBaseListAdapter with interfaces, and the following class will be abstract:
SimpleListAdapter
abstractclassSimpleListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IBaseListAdapter<IBaseListItem> {
protectedval items: ArrayList<IBaseListItem> = ArrayList()
overridefungetItemCount() = items.size
overridefungetItemViewType(position: Int) = items[position].layoutId
protectedfuninflateByViewType(context: Context?, viewType: Int, parent: ViewGroup) =
LayoutInflater.from(context).inflate(viewType, parent, false)
overridefunadd(newItem: IBaseListItem) {
items.add(newItem)
notifyDataSetChanged()
}
overridefunadd(newItems: ArrayList<IBaseListItem>?) {
for (newItem in newItems ?: return) {
items.add(newItem)
notifyDataSetChanged()
}
}
overridefunaddAtPosition(pos: Int, newItem: IBaseListItem) {
items.add(pos, newItem)
notifyDataSetChanged()
}
overridefunclearAll() {
items.clear()
notifyDataSetChanged()
}
overridefunremove(position: Int) {
items.removeAt(position)
notifyDataSetChanged()
}
}
* Note: Note the override function getItemViewType (position: Int) . We need some kind of new key, according to which Rv will understand which ViewHolder suits us. ValidId for our item is very useful for this ; The Android every time helpsfully makes the layout id unique, and all values are greater than zero - we will use this further, “inflating” itemView for our viewholders in the inflateByViewType () method (next line).
Create a list
Take for example the settings screen. Android offers us its own version, but what if the design needs something more sophisticated? I prefer to fill this screen as a list. There will be such a case:

We see two different elements of the list, it means that SimpleListAdapter and Rv are perfect here!
Let's get started! You can start with layout layouts for item'ov:
item_info.xml; item_switch.xml
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="56dp"><TextViewandroid:id="@+id/tv_info_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginStart="28dp"android:textColor="@color/black"android:textSize="20sp"tools:text="Balance" /><TextViewandroid:id="@+id/tv_info_value"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical|end"android:layout_marginEnd="48dp"tools:text="1000 $" /></FrameLayout><!----><?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="56dp"><TextViewandroid:id="@+id/tv_switch_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginStart="28dp"android:textColor="@color/black"android:textSize="20sp"tools:text="Send notifications" /><Switchandroid:id="@+id/tv_switch_value"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical|end"android:layout_marginEnd="48dp"tools:checked="true" /></FrameLayout>
Then, we define the classes themselves, into which we want to pass the values that interact with the list: the first is the header and any value that comes from the outside (we will have a stub about requests another time), the second is the header and boolean variable , by which we must perform an action. To distinguish between Switch elements, id entities from the server will do; if they are not there, we can create them ourselves during initialization.
InfoItem.kt, SwitchItem.kt
classInfoItem(val title: String, val value: String): IBaseListItem {
overrideval layoutId = R.layout.item_info
}
classSwitchItem(
val id: Int,
val title: String,
val actionOnReceive: (itemId: Int, userChoice: Boolean) -> Unit
) : IBaseListItem {
overrideval layoutId = R.layout.item_switch
}
In a simple implementation, each element will also need a ViewHolder:
InfoViewHolder.kt, SwitchViewHolder.kt
class InfoViewHolder.kt(view: View) : RecyclerView.ViewHolder(view) {
val tvTitle = view.tv_info_title
val tvValue = view.tv_info_value
}
class SwitchViewHolder.kt(view: View) : RecyclerView.ViewHolder(view) {
val tvTitle = view.tv_switch_title
val tvValue = view.tv_switch_value
}
Well, the most interesting part is the concrete implementation of SimpleListAdapter'a:
SettingsListAdapter.kt
classSettingsListAdapter : SimpleListAdapter() {
overridefunonCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val context = parent.context
returnwhen (viewType) {
R.layout.item_info -> InfoHolder(inflateByViewType(context, viewType, parent))
R.layout.item_switch -> SwitchHolder(inflateByViewType(context, viewType, parent))
else -> throw IllegalStateException("There is no match with current layoutId")
}
}
overridefunonBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is InfoHolder -> {
val infoItem = items[position] as InfoItem
holder.tvTitle.text = infoItem.title
holder.tvValue.text = infoItem.value
}
is SwitchHolder -> {
val switchItem = items[position] as SwitchItem
holder.tvTitle.text = switchItem.title
holder.tvValue.setOnCheckedChangeListener { _, isChecked ->
switchItem.actionOnReceive.invoke(switchItem.id, isChecked)
}
}
else -> throw IllegalStateException("There is no match with current holder instance")
}
}
}
* Note: Do not forget that under the hood of the inflateByViewType method (context, viewType, parent): viewType = layoutId.
All components are ready! Now, the Activation code remains and you can run the program:
activity_settings.xml
<?xml version="1.0" encoding="utf-8"?><FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/rView"android:layout_width="match_parent"android:layout_height="match_parent" /></FrameLayout>
SettingsActivity.kt
classSettingsActivity : AppCompatActivity() {
overridefunonCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
val adapter = SettingsListAdapter()
rView.layoutManager = LinearLayoutManager(this)
rView.adapter = adapter
adapter.add(InfoItem("User Name", "Leo Allford"))
adapter.add(InfoItem("Balance", "350 $"))
adapter.add(InfoItem("Tariff", "Business"))
adapter.add(SwitchItem(1, "Send Notifications") { itemId, userChoice -> onCheck(itemId, userChoice) })
adapter.add(SwitchItem(2, "Send News on Email") { itemId, userChoice -> onCheck(itemId, userChoice) })
}
privatefunonCheck(itemId: Int, userChoice: Boolean) {
when (itemId) {
1 -> Toast.makeText(this, "Notification now set as $userChoice", Toast.LENGTH_SHORT).show()
2 -> Toast.makeText(this, "Send news now set as $userChoice", Toast.LENGTH_SHORT).show()
}
}
}
As a result, when building a list, all the work comes down to the following:
1. Calculate the number of different layouts for the items
2. Find the names for them . I use the rule: Something Item.kt, item_ something .xml, Something ViewHolder.kt
3. We write an adapter to these classes . In principle, if you do not pretend to optimize, then one common adapter will suffice. But in large projects I would still do a few, on the screens, because in the first case the onBindViewHolder () method inevitably grows (readability of the code suffers) in your adapter (in our case, it is the SettingsListAdapter) + the program will have to run through this method every time for each item + using the onCreateViewHolder () method
. 4. Run the code and enjoy!
Jetpack
Up to this point, we have applied the standard data binding approach from Item.kt - to our item_layout.xml . But we can unify the onBindViewHolder () method , keep it minimal, and transfer the logic to Item and layout.
Head over to the official Android JetPack page:

Note the first tab in the Architecture section. Android Databinding is a very extensive topic, I would like to talk about it in more detail in other articles, but now we will use only the current one - we will make our Item.kt - variable for item.xml (or you can call it view mode for the layout).
At the time of this writing,Databinding could be connected like this:
android {
compileSdkVersion 27
defaultConfig {...}
buildTypes {...}
dataBinding {
enabled = true
}
dependencies {
kapt "com.android.databinding:compiler:3.1.3"//...
}
}
Go through the base classes again. The interface for the item complements the previous one:
interfaceIBaseItemVm: IBaseListItem {val brVariableId: Int
}
Also, we will expand our ViewHolder, therefore we have associated with databinding. We will pass the ViewDataBinding into it , and then safely forget about creating the layout and data binding
classVmViewHolder(valbinding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root)
The same approach is used here , but on Cotlin it looks much shorter, doesn't it? =)
VmListAdapter
classVmListAdapter : RecyclerView.Adapter<VmViewHolder>(), IBaseListAdapter<IBaseItemVm> {
privatevar mItems = ArrayList<IBaseItemVm>()
overridefungetItemCount() = mItems.size
overridefungetItemViewType(position: Int) = mItems[position].layoutId
overridefunonCreateViewHolder(parent: ViewGroup, viewType: Int): VmViewHolder {
val inflater = LayoutInflater.from(parent.context)
val viewDataBinding = DataBindingUtil.inflate<ViewDataBinding>(inflater!!, viewType, parent, false)
return VmViewHolder(viewDataBinding)
}
overridefunonBindViewHolder(holder: VmViewHolder, position: Int) {
holder.binding.setVariable(mItems[position].brVariableId, mItems[position])
holder.binding.executePendingBindings()
}
overridefunadd(newItem: IBaseItemVm) {
mItems.add(newItem)
notifyItemInserted(mItems.lastIndex)
}
overridefunadd(newItems: ArrayList<IBaseItemVm>?) {
val oldSize = mItems.size
mItems.addAll(newItems!!)
notifyItemRangeInserted(oldSize, newItems.size)
}
overridefunclearAll() {
mItems.clear()
notifyDataSetChanged()
}
overridefungetItemId(position: Int): Long {
val pos = mItems.size - position
returnsuper.getItemId(pos)
}
overridefunaddAtPosition(pos: Int, newItem: IBaseItemVm) {
mItems.add(pos, newItem)
notifyItemInserted(pos)
}
overridefunremove(position: Int) {
mItems.removeAt(position)
notifyItemRemoved(position)
}
}
In general, pay attention to the onCreateViewHolder () , onBindViewHolder () methods . The idea is that they no longer grow. So you get one adapter for any screen, with any list items.
Our items:
InfoItem.kt, SwitchItem.kt
classInfoItem(val title: String, val value: String) : IBaseItemVm {
overrideval brVariableId = BR.vmInfo
overrideval layoutId = R.layout.item_info
}
//classSwitchItem(
val id: Int,
val title: String,
privateval actionOnReceive: (itemId: Int, userChoice: Boolean) -> Unit
) : IBaseItemVm {
overrideval brVariableId = BR.vmSwitch
overrideval layoutId = R.layout.item_switch
val listener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
actionOnReceive.invoke(id, isChecked) }
}
Here you can see where the logic of the onBindViewHolder () method has gone . Android Databinding took over it - now any of our layout is supported by its view model, and it will calmly handle all the logic of clicks, animations, requests and other things. What do you come up with. Binding Adapters will help to do this well - allowing you to link the view to data of any kind. Also, it is possible to improve communication due to bilateral dating . Probably he will flash in any of the following articles, in this example, you can make everything easier. We only need one adapter adapter:
@BindingAdapter("switchListener")funsetSwitchListener(sw: Switch, listener: CompoundButton.OnCheckedChangeListener) {
sw.setOnCheckedChangeListener(listener)
}
After that, we associate our variable values with our Item inside xml:
item_info.xml; item_switch.xml
app:switchListener="@{vmSwitch.listener}" — в этой строке мы воспользовались нашим BindingAdapter'ом
<?xml version="1.0" encoding="utf-8"?><layoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><importtype="com.lfkekpoint.adapters.adapters.presentation.modules.bindableItemsSettings.InfoItem" /><variablename="vmInfo"type="InfoItem" /></data><FrameLayoutandroid:layout_width="match_parent"android:layout_height="56dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginStart="28dp"android:text="@{vmInfo.title}"android:textColor="@color/black"android:textSize="20sp"tools:text="Balance" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical|end"android:layout_marginEnd="48dp"android:text="@{vmInfo.value}"tools:text="1000 $" /></FrameLayout></layout><!----><?xml version="1.0" encoding="utf-8"?><layoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"><data><importtype="com.lfkekpoint.adapters.adapters.presentation.modules.bindableItemsSettings.SwitchItem" /><variablename="vmSwitch"type="SwitchItem" /></data><FrameLayoutandroid:layout_width="match_parent"android:layout_height="56dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginStart="28dp"android:text="@{vmSwitch.title}"android:textColor="@color/black"android:textSize="20sp"tools:text="Send notifications" /><Switchandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical|end"android:layout_marginEnd="48dp"app:switchListener="@{vmSwitch.listener}"tools:checked="true" /></FrameLayout></layout>
app:switchListener="@{vmSwitch.listener}" — в этой строке мы воспользовались нашим BindingAdapter'ом
* Note: For fair reasons, it may seem to someone that we are writing a lot more code in xml - but this is a question of knowledge of the Android Databinding library. It complements the layout, reads quickly and, in principle, for the most part removes the boilerplate. I think Google is going to develop this library well, since it is the first in the Architecture tab in Android Jetpack. Try changing MVP to MVVM in a couple of projects - and many may be pleasantly surprised.
Well then! .. And, the code in SettingsActivity:
SettingsActivity.kt
… не изменился, разве что поменялся адаптер! =) Но чтобы не прыгать по статье:
classSettingsActivity : AppCompatActivity() {
overridefunonCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings)
val adapter = BaseVmListAdapter()
rView.layoutManager = LinearLayoutManager(this)
rView.adapter = adapter
adapter.add(InfoItem("User Name", "Leo Allford"))
adapter.add(InfoItem("Balance", "350 $"))
adapter.add(InfoItem("Tariff", "Business"))
adapter.add(SwitchItem(1, "Send Notifications") { itemId, userChoice -> onCheck(itemId, userChoice) })
adapter.add(SwitchItem(2, "Send News on Email") { itemId, userChoice -> onCheck(itemId, userChoice) })
}
privatefunonCheck(itemId: Int, userChoice: Boolean) {
when (itemId) {
1 -> Toast.makeText(this, "Notification now set as $userChoice", Toast.LENGTH_SHORT).show()
2 -> Toast.makeText(this, "Send news now set as $userChoice", Toast.LENGTH_SHORT).show()
}
}
}
Total
We received an algorithm for building lists and tools for working with them. In my case (I almost always use Databinding ) all the preparation is reduced to initializing the base classes into folders, layout of the items in .xml and then binding to variables in .kt.
Accelerate development
for faster work, I used templates from Apache to Android Studio - and wrote their own templates with a small demonstration of how it all works. I really hope that someone will come in handy. Please note that when you work, you need to call the template from the root folder of the project - this is done because the project applicationId parameter may lie to you if you changed it in Gradle. But packageNameso just can not fool, which I took advantage of. Available language about templating can be found at the links below.
References / Media
1. Modern Android development: Android Jetpack, Kotlin, and more (Google I / O 2018, 40 m.) - a short guide to what is in fashion today, hence also in general terms it will be clear how RecyclerView has developed;
2. Droidcon NYC 2016 - Radical RecyclerView, 36 m. - A detailed report on RecyclerView from Lisa Wray ;
3. Create a List with RecyclerView - official documentation
4. Interfaces vs. classes
5. Android IDE Template Format , Total Standardization , the FreeMarker manual is a convenient approach that in this article will help you quickly create the necessary files for working with lists
6.The code for the article (there are slightly different class names, be careful), templates for work and video, how to work with templates
7. English version of the article