Skip to content

Commit 9c96fad

Browse files
committed
RecyclerView dynamic items added
1 parent 504bfa1 commit 9c96fad

File tree

16 files changed

+199
-25
lines changed

16 files changed

+199
-25
lines changed

entities/presentation/src/main/java/ru/sir/presentation/base/recycler_view/RecyclerViewAdapter.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,21 @@ package ru.sir.presentation.base.recycler_view
33
import android.util.SparseArray
44
import android.view.ViewGroup
55
import androidx.annotation.LayoutRes
6+
import androidx.databinding.ObservableArrayList
67
import androidx.recyclerview.widget.RecyclerView
8+
import kotlinx.coroutines.CoroutineScope
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.launch
711
import ru.sir.presentation.base.BaseViewModel
12+
import ru.sir.presentation.extensions.onChanged
813

9-
class RecyclerViewAdapter(private val producers: SparseArray<ViewHolderProducer<in Any, *, *>>, private val data: MutableList<RecyclerViewBaseDataModel>) : RecyclerView.Adapter<BaseViewHolder<in Any, *>>() {
14+
class RecyclerViewAdapter(private val producers: SparseArray<ViewHolderProducer<in Any, *, *>>) : RecyclerView.Adapter<BaseViewHolder<in Any, *>>() {
15+
16+
val data = ObservableArrayList<RecyclerViewBaseDataModel>().onChanged {
17+
CoroutineScope(Dispatchers.Main).launch {
18+
notifyDataSetChanged()
19+
}
20+
}
1021

1122
class Builder<VM : BaseViewModel>(private val viewModel: VM, private val viewModelId: Int) {
1223
private val producers = SparseArray<ViewHolderProducer<*, *, *>>()
@@ -29,7 +40,7 @@ class RecyclerViewAdapter(private val producers: SparseArray<ViewHolderProducer<
2940
}
3041

3142
@Suppress("UNCHECKED_CAST")
32-
fun build(data: MutableList<RecyclerViewBaseDataModel>) = RecyclerViewAdapter(producers as SparseArray<ViewHolderProducer<in Any, *, *>>, data)
43+
fun build() = RecyclerViewAdapter(producers as SparseArray<ViewHolderProducer<in Any, *, *>>)
3344
}
3445

3546
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<in Any, *> {
@@ -46,4 +57,9 @@ class RecyclerViewAdapter(private val producers: SparseArray<ViewHolderProducer<
4657
override fun getItemViewType(position: Int): Int {
4758
return data[position].getType()
4859
}
60+
61+
fun getObserver(f: (observer: ObservableArrayList<RecyclerViewBaseDataModel>) -> Unit): RecyclerViewAdapter {
62+
f(data)
63+
return this
64+
}
4965
}

entities/presentation/src/main/java/ru/sir/presentation/extensions/BindingExtensions.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package ru.sir.presentation.extensions
22

33
import androidx.databinding.Observable
4+
import androidx.databinding.ObservableArrayList
45
import androidx.databinding.ObservableBoolean
6+
import androidx.databinding.ObservableList
57

68
fun ObservableBoolean.onChanged(f: (newValue: Boolean) -> Unit): ObservableBoolean {
79
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
@@ -10,4 +12,15 @@ fun ObservableBoolean.onChanged(f: (newValue: Boolean) -> Unit): ObservableBoole
1012
}
1113
})
1214
return this
15+
}
16+
17+
fun <T> ObservableArrayList<T>.onChanged(f: ObservableArrayList<T>.() -> Unit): ObservableArrayList<T> {
18+
addOnListChangedCallback(object : ObservableList.OnListChangedCallback<ObservableList<*>>() {
19+
override fun onChanged(sender: ObservableList<*>?) { f() }
20+
override fun onItemRangeChanged(sender: ObservableList<*>?, positionStart: Int, itemCount: Int) { f() }
21+
override fun onItemRangeInserted(sender: ObservableList<*>?, positionStart: Int, itemCount: Int) { f() }
22+
override fun onItemRangeMoved(sender: ObservableList<*>?, fromPosition: Int, toPosition: Int, itemCount: Int) { f() }
23+
override fun onItemRangeRemoved(sender: ObservableList<*>?, positionStart: Int, itemCount: Int) { f() }
24+
})
25+
return this
1326
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ru.sir.recycler_view_example.data
2+
3+
import ru.sir.core.Either
4+
import ru.sir.core.None
5+
import ru.sir.recycler_view_example_api.models.Item
6+
7+
interface Remote {
8+
fun loadDataFromServer(): Either<None, List<Item>>
9+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ru.sir.recycler_view_example.data
2+
3+
import ru.sir.core.Either
4+
import ru.sir.core.None
5+
import ru.sir.recycler_view_example_api.interfaces.Repository
6+
import ru.sir.recycler_view_example_api.models.Item
7+
8+
class RepositoryImpl(private val remote: Remote) : Repository {
9+
override fun loadData(): Either<None, List<Item>> = remote.loadDataFromServer()
10+
}

features/recycler_view_example/src/main/java/ru/sir/recycler_view_example/di/components/RecyclerViewExampleComponent.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package ru.sir.recycler_view_example.di.components
22

33
import dagger.Subcomponent
44
import ru.sir.presentation.base.BaseDaggerComponent
5-
import ru.sir.recycler_view_example.di.modules.CacheModule
6-
import ru.sir.recycler_view_example.di.modules.ViewModelModule
5+
import ru.sir.recycler_view_example.di.modules.*
76
import ru.sir.recycler_view_example.ui.RecyclerViewExample
87
import javax.inject.Singleton
98

109
@Singleton
11-
@Subcomponent(modules = [ViewModelModule::class, CacheModule::class])
10+
@Subcomponent(modules = [ViewModelModule::class, DomainModule::class, DataModule::class, RemoteModule::class, CacheModule::class])
1211
interface RecyclerViewExampleComponent : BaseDaggerComponent {
1312

1413
@Subcomponent.Factory
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ru.sir.recycler_view_example.di.modules
2+
3+
import dagger.Module
4+
import dagger.Provides
5+
import ru.sir.recycler_view_example.data.Remote
6+
import ru.sir.recycler_view_example.data.RepositoryImpl
7+
import ru.sir.recycler_view_example_api.interfaces.Repository
8+
import javax.inject.Singleton
9+
10+
@Module
11+
class DataModule {
12+
13+
@Provides
14+
@Singleton
15+
fun provideRepository(remote: Remote): Repository = RepositoryImpl(remote)
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ru.sir.recycler_view_example.di.modules
2+
3+
import dagger.Module
4+
import dagger.Provides
5+
import ru.sir.recycler_view_example_api.interactor.LoadData
6+
import ru.sir.recycler_view_example_api.interfaces.Repository
7+
8+
@Module
9+
class DomainModule {
10+
11+
@Provides
12+
fun provideLoadData(repository: Repository): LoadData = LoadData(repository)
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package ru.sir.recycler_view_example.di.modules
2+
3+
import dagger.Module
4+
import dagger.Provides
5+
import ru.sir.recycler_view_example.data.Remote
6+
import ru.sir.recycler_view_example.remote.RemoteImpl
7+
import javax.inject.Singleton
8+
9+
@Module
10+
class RemoteModule {
11+
12+
@Provides
13+
@Singleton
14+
fun provideRemote(): Remote = RemoteImpl()
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package ru.sir.recycler_view_example.remote
2+
3+
import ru.sir.core.Either
4+
import ru.sir.core.None
5+
import ru.sir.recycler_view_example.data.Remote
6+
import ru.sir.recycler_view_example_api.models.Item
7+
8+
class RemoteImpl : Remote {
9+
companion object {
10+
private const val RV_TITLE = 1
11+
private const val RV_ITEM = 2
12+
}
13+
14+
override fun loadDataFromServer(): Either<None, List<Item>> {
15+
val items = mutableListOf(
16+
Item("Title 1", RV_TITLE),
17+
Item("Label 1", RV_ITEM),
18+
Item("Title 2", RV_TITLE),
19+
Item("Label 1", RV_ITEM),
20+
Item("Label 2", RV_ITEM),
21+
Item("Label 3", RV_ITEM),
22+
Item("Title 3", RV_TITLE),
23+
Item("Label 1", RV_ITEM),
24+
Item("Label 2", RV_ITEM),
25+
Item("Label 3", RV_ITEM),
26+
Item("Label 4", RV_ITEM)
27+
)
28+
29+
for (i in 0 .. Int.MAX_VALUE) {
30+
//Имитация работы сервера
31+
}
32+
33+
return Either.Right(items)
34+
}
35+
}
Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,50 @@
11
package ru.sir.recycler_view_example.view_models
22

33
import android.app.Application
4+
import androidx.databinding.ObservableArrayList
5+
import androidx.databinding.ObservableBoolean
6+
import androidx.databinding.ObservableList
7+
import ru.sir.core.None
48
import ru.sir.presentation.base.BaseViewModel
59
import ru.sir.presentation.base.recycler_view.RecyclerViewAdapter
610
import ru.sir.presentation.base.recycler_view.RecyclerViewBaseDataModel
711
import ru.sir.recycler_view_example.BR
812
import ru.sir.recycler_view_example.R
13+
import ru.sir.recycler_view_example_api.interactor.LoadData
14+
import ru.sir.recycler_view_example_api.models.Item
915
import javax.inject.Inject
1016

11-
class RecyclerViewExampleViewModel @Inject constructor(application: Application) : BaseViewModel(application) {
12-
17+
class RecyclerViewExampleViewModel @Inject constructor(
18+
application: Application,
19+
private val loadData: LoadData
20+
) : BaseViewModel(application) {
1321
companion object {
1422
private const val RV_TITLE = 1
1523
private const val RV_ITEM = 2
1624
}
1725

18-
val items = mutableListOf(
19-
RecyclerViewBaseDataModel("Title 1", RV_TITLE),
20-
RecyclerViewBaseDataModel("Label 1", RV_ITEM),
21-
RecyclerViewBaseDataModel("Title 2", RV_TITLE),
22-
RecyclerViewBaseDataModel("Label 1", RV_ITEM),
23-
RecyclerViewBaseDataModel("Label 2", RV_ITEM),
24-
RecyclerViewBaseDataModel("Label 3", RV_ITEM),
25-
RecyclerViewBaseDataModel("Title 3", RV_TITLE),
26-
RecyclerViewBaseDataModel("Label 1", RV_ITEM),
27-
RecyclerViewBaseDataModel("Label 2", RV_ITEM),
28-
RecyclerViewBaseDataModel("Label 3", RV_ITEM),
29-
RecyclerViewBaseDataModel("Label 4", RV_ITEM)
30-
)
26+
val isLoading = ObservableBoolean(false)
27+
private lateinit var items: ObservableArrayList<RecyclerViewBaseDataModel>
28+
29+
override fun init() {
30+
isLoading.set(true)
31+
loadData(None()) { it.either({}, ::onDataLoaded) }
32+
}
33+
34+
private fun onDataLoaded(data: List<Item>) {
35+
items.addAll(data.toRecyclerViewItems())
36+
isLoading.set(false)
37+
}
3138

3239
fun recyclerViewAdapter() = RecyclerViewAdapter.Builder(this, BR.viewModel)
3340
.addProducer(RV_TITLE, R.layout.item_title, String::class.java, TitleViewModel::class.java)
3441
.addProducer(RV_ITEM, R.layout.item_example1, String::class.java, ItemViewModel::class.java)
35-
.build(items)
42+
.build()
43+
.getObserver { items = it }
44+
45+
private fun List<Item>.toRecyclerViewItems(): List<RecyclerViewBaseDataModel> {
46+
val newList = mutableListOf<RecyclerViewBaseDataModel>()
47+
this.forEach { newList.add(RecyclerViewBaseDataModel(it.name, it.type)) }
48+
return newList
49+
}
3650
}

0 commit comments

Comments
 (0)