Issue #378
After having a generic RecyclerView, if we want to show multiple kinds of data in Fragment, we can use generic.
We may be tempted to use interface or protocol, but should prefer generic.
class FeedFragment() : Fragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val mainViewModel: MainViewModel = ViewModelProviders.of(activity!!).get(MainViewModel::class.java)
mainViewModel.resId.observe(viewLifecycleOwner, Observer {
when (it) {
R.id.gitHub -> { handleGitHub() }
R.id.hackerNews -> { handleHackerNews() }
R.id.reddit -> { handleReddit() }
R.id.dev -> { handleDev() }
R.id.productHunt -> { handleProductHunt() }
else -> {}
}
})
recyclerView.layoutManager = LinearLayoutManager(context)
}
}
The difference between each kind are
- The type of model
- The type of Adapter
- How to observe from viewModel
- How to load from viewModel
Here we also use lifecycleScope
from lifecycle runtime ktx
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha01"
private fun <T> handle(
makeResId: () -> Int,
makeAdapter: () -> com.myapp.Adapter<T>,
observe: ((ArrayList<T>) -> Unit) -> Unit,
load: suspend () -> Unit
) {
(activity as AppCompatActivity).toolbar.title = getString(makeResId())
val adapter = makeAdapter()
recyclerView.adapter = adapter
observe {
adapter.update(it)
}
fun doLoad() {
viewLifecycleOwner.lifecycleScope.launch {
progressBar.visibility = View.VISIBLE
load()
progressBar.visibility = View.GONE
swipeRefreshLayout.isRefreshing = false
}
}
doLoad()
swipeRefreshLayout.setOnRefreshListener {
doLoad()
}
}
Then we just need to provide the required data
private fun handleDev() {
val viewModel: com.myapp.ViewModel by viewModel()
handle(
{ R.string.menu_dev },
{ com.myapp.Adapter(items = arrayListOf()) },
{ completion ->
viewModel.items.observe(viewLifecycleOwner, Observer {
completion(it)
})
},
{
viewModel.load()
}
)
}