Skip to content

Commit 104d6d1

Browse files
authored
Refactor the Test App OPDS catalog UI (#9)
* Renamed `item_recycle_catalog_list` to `item_recycle_button` and removed data binding in it. This offers better reusability of it, and data binding doesn't seem to be the future anymore, so will probably move away from it. I still want to use view binding though, as it doesn't require any changes to the XML and is type safe. * Each type of list on the OPDS catalog screen is a different RecyclerView, with different adapters: Publication, Group, and Navigation. The Group RecyclerView also uses the Publication and Navigation components in it. * This may have been something I originally changed and was overruled, but I did rename `CatalogDetailFragment` to `PublicationDetailFragment`. I think this more accurately reflects what it is, since it's not really the detail screen of the entire catalog. So now we have `CatalogFeedListFragment` which has the buttons for the different catalogs, `CatalogFragment` which has the contents of a specific catalog, and `PublicationDetailFragment` which serves as the detail screen for a specific publication
1 parent 553c7d3 commit 104d6d1

14 files changed

+332
-176
lines changed

test-app/src/main/java/org/readium/r2/testapp/catalogs/CatalogFeedListAdapter.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@ package org.readium.r2.testapp.catalogs
99
import android.view.LayoutInflater
1010
import android.view.ViewGroup
1111
import androidx.core.os.bundleOf
12-
import androidx.databinding.DataBindingUtil
1312
import androidx.navigation.Navigation
1413
import androidx.recyclerview.widget.DiffUtil
1514
import androidx.recyclerview.widget.ListAdapter
1615
import androidx.recyclerview.widget.RecyclerView
1716
import org.readium.r2.testapp.R
18-
import org.readium.r2.testapp.databinding.ItemRecycleCatalogListBinding
17+
import org.readium.r2.testapp.databinding.ItemRecycleButtonBinding
1918
import org.readium.r2.testapp.domain.model.Catalog
2019

2120
class CatalogFeedListAdapter(private val onLongClick: (Catalog) -> Unit) :
@@ -26,10 +25,7 @@ class CatalogFeedListAdapter(private val onLongClick: (Catalog) -> Unit) :
2625
viewType: Int
2726
): ViewHolder {
2827
return ViewHolder(
29-
DataBindingUtil.inflate(
30-
LayoutInflater.from(parent.context),
31-
R.layout.item_recycle_catalog_list, parent, false
32-
)
28+
ItemRecycleButtonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
3329
)
3430
}
3531

@@ -40,11 +36,11 @@ class CatalogFeedListAdapter(private val onLongClick: (Catalog) -> Unit) :
4036
viewHolder.bind(catalog)
4137
}
4238

43-
inner class ViewHolder(private val binding: ItemRecycleCatalogListBinding) :
39+
inner class ViewHolder(private val binding: ItemRecycleButtonBinding) :
4440
RecyclerView.ViewHolder(binding.root) {
4541

4642
fun bind(catalog: Catalog) {
47-
binding.catalog = catalog
43+
binding.catalogListButton.text = catalog.title
4844
binding.catalogListButton.setOnClickListener {
4945
val bundle = bundleOf(CATALOGFEED to catalog)
5046
Navigation.findNavController(it)

test-app/src/main/java/org/readium/r2/testapp/catalogs/CatalogFragment.kt

Lines changed: 30 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,15 @@
77
package org.readium.r2.testapp.catalogs
88

99
import android.os.Bundle
10-
import android.view.*
11-
import android.widget.Button
12-
import android.widget.ImageView
13-
import android.widget.LinearLayout
14-
import android.widget.TextView
15-
import androidx.appcompat.content.res.AppCompatResources
10+
import android.view.LayoutInflater
11+
import android.view.Menu
12+
import android.view.View
13+
import android.view.ViewGroup
1614
import androidx.core.os.bundleOf
17-
import androidx.core.view.setPadding
1815
import androidx.fragment.app.Fragment
1916
import androidx.fragment.app.viewModels
2017
import androidx.navigation.Navigation
2118
import androidx.recyclerview.widget.LinearLayoutManager
22-
import androidx.recyclerview.widget.RecyclerView
2319
import com.google.android.material.snackbar.Snackbar
2420
import org.readium.r2.shared.opds.Facet
2521
import org.readium.r2.testapp.MainActivity
@@ -34,15 +30,16 @@ import org.readium.r2.testapp.opds.GridAutoFitLayoutManager
3430
class CatalogFragment : Fragment() {
3531

3632
private val catalogViewModel: CatalogViewModel by viewModels()
37-
private lateinit var catalogListAdapter: CatalogListAdapter
33+
private lateinit var publicationAdapter: PublicationAdapter
34+
private lateinit var groupAdapter: GroupAdapter
35+
private lateinit var navigationAdapter: NavigationAdapter
3836
private lateinit var catalog: Catalog
3937
private var showFacetMenu = false
4038
private lateinit var facets: MutableList<Facet>
4139

4240
private var _binding: FragmentCatalogBinding? = null
4341
private val binding get() = _binding!!
4442

45-
// FIXME the entire way this fragment is built feels like a hack. Need a cleaner UI
4643
override fun onCreateView(
4744
inflater: LayoutInflater, container: ViewGroup?,
4845
savedInstanceState: Bundle?
@@ -56,19 +53,36 @@ class CatalogFragment : Fragment() {
5653

5754
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
5855
super.onViewCreated(view, savedInstanceState)
59-
catalogListAdapter = CatalogListAdapter()
56+
publicationAdapter = PublicationAdapter()
57+
navigationAdapter = NavigationAdapter(catalog.type)
58+
groupAdapter = GroupAdapter(catalog.type)
6059
setHasOptionsMenu(true)
6160

62-
binding.catalogDetailList.apply {
61+
binding.catalogNavigationList.apply {
62+
layoutManager = LinearLayoutManager(requireContext())
63+
adapter = navigationAdapter
64+
addItemDecoration(
65+
CatalogFeedListFragment.VerticalSpaceItemDecoration(
66+
10
67+
)
68+
)
69+
}
70+
71+
binding.catalogPublicationsList.apply {
6372
layoutManager = GridAutoFitLayoutManager(requireContext(), 120)
64-
adapter = catalogListAdapter
73+
adapter = publicationAdapter
6574
addItemDecoration(
6675
BookshelfFragment.VerticalSpaceItemDecoration(
6776
10
6877
)
6978
)
7079
}
7180

81+
binding.catalogGroupList.apply {
82+
layoutManager = LinearLayoutManager(requireContext())
83+
adapter = groupAdapter
84+
}
85+
7286
(activity as MainActivity).supportActionBar?.title = catalog.title
7387

7488
// TODO this feels hacky, I don't want to parse the file if it has not changed
@@ -85,110 +99,10 @@ class CatalogFragment : Fragment() {
8599
}
86100
requireActivity().invalidateOptionsMenu()
87101

88-
result.feed!!.navigation.forEachIndexed { index, navigation ->
89-
val button = Button(requireContext())
90-
button.apply {
91-
layoutParams = LinearLayout.LayoutParams(
92-
LinearLayout.LayoutParams.MATCH_PARENT,
93-
LinearLayout.LayoutParams.WRAP_CONTENT
94-
)
95-
text = navigation.title
96-
setOnClickListener {
97-
val catalog1 = Catalog(
98-
href = navigation.href,
99-
title = navigation.title!!,
100-
type = catalog.type
101-
)
102-
val bundle = bundleOf(CATALOGFEED to catalog1)
103-
Navigation.findNavController(it)
104-
.navigate(R.id.action_navigation_catalog_self, bundle)
105-
}
106-
}
107-
binding.catalogLinearLayout.addView(button, index)
108-
}
109-
110-
if (result.feed!!.publications.isNotEmpty()) {
111-
catalogListAdapter.submitList(result.feed!!.publications)
112-
}
102+
navigationAdapter.submitList(result.feed!!.navigation)
103+
publicationAdapter.submitList(result.feed!!.publications)
104+
groupAdapter.submitList(result.feed!!.groups)
113105

114-
for (group in result.feed!!.groups) {
115-
if (group.publications.isNotEmpty()) {
116-
val linearLayout = LinearLayout(requireContext()).apply {
117-
orientation = LinearLayout.HORIZONTAL
118-
setPadding(10)
119-
layoutParams = LinearLayout.LayoutParams(
120-
LinearLayout.LayoutParams.MATCH_PARENT,
121-
LinearLayout.LayoutParams.WRAP_CONTENT,
122-
1f
123-
)
124-
weightSum = 2f
125-
addView(TextView(requireContext()).apply {
126-
text = group.title
127-
layoutParams = LinearLayout.LayoutParams(
128-
LinearLayout.LayoutParams.WRAP_CONTENT,
129-
LinearLayout.LayoutParams.WRAP_CONTENT,
130-
1f
131-
)
132-
})
133-
if (group.links.size > 0) {
134-
addView(ImageView(requireContext()).apply {
135-
// FIXME Have the arrow at the very end
136-
setImageDrawable(AppCompatResources.getDrawable(requireContext(), R.drawable.ic_baseline_arrow_forward_24))
137-
contentDescription = getString(R.string.catalog_list_more)
138-
gravity = Gravity.END
139-
layoutParams = LinearLayout.LayoutParams(
140-
LinearLayout.LayoutParams.WRAP_CONTENT,
141-
LinearLayout.LayoutParams.WRAP_CONTENT,
142-
1f
143-
)
144-
setOnClickListener {
145-
val catalog1 = Catalog(
146-
href = group.links.first().href,
147-
title = group.title,
148-
type = catalog.type
149-
)
150-
val bundle = bundleOf(CATALOGFEED to catalog1)
151-
Navigation.findNavController(it)
152-
.navigate(R.id.action_navigation_catalog_self, bundle)
153-
}
154-
})
155-
}
156-
}
157-
val publicationRecyclerView = RecyclerView(requireContext()).apply {
158-
layoutManager = LinearLayoutManager(requireContext())
159-
(layoutManager as LinearLayoutManager).orientation =
160-
LinearLayoutManager.HORIZONTAL
161-
adapter = CatalogListAdapter().apply {
162-
submitList(group.publications)
163-
}
164-
}
165-
binding.catalogLinearLayout.addView(linearLayout)
166-
binding.catalogLinearLayout.addView(publicationRecyclerView)
167-
}
168-
if (group.navigation.isNotEmpty()) {
169-
for (navigation in group.navigation) {
170-
val button = Button(requireContext())
171-
button.apply {
172-
layoutParams = LinearLayout.LayoutParams(
173-
LinearLayout.LayoutParams.MATCH_PARENT,
174-
LinearLayout.LayoutParams.WRAP_CONTENT
175-
)
176-
text = navigation.title
177-
setOnClickListener {
178-
val catalog1 = Catalog(
179-
href = navigation.href,
180-
title = navigation.title!!,
181-
type = catalog.type
182-
)
183-
val bundle = bundleOf(CATALOGFEED to catalog1)
184-
Navigation.findNavController(it)
185-
.navigate(R.id.action_navigation_catalog_self, bundle)
186-
}
187-
}
188-
binding.catalogLinearLayout.addView(button)
189-
}
190-
}
191-
}
192106
binding.catalogProgressBar.visibility = View.GONE
193107
})
194108
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2021 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
package org.readium.r2.testapp.catalogs
8+
9+
import android.view.LayoutInflater
10+
import android.view.View
11+
import android.view.ViewGroup
12+
import androidx.core.os.bundleOf
13+
import androidx.navigation.Navigation
14+
import androidx.recyclerview.widget.DiffUtil
15+
import androidx.recyclerview.widget.LinearLayoutManager
16+
import androidx.recyclerview.widget.ListAdapter
17+
import androidx.recyclerview.widget.RecyclerView
18+
import org.readium.r2.shared.opds.Group
19+
import org.readium.r2.testapp.R
20+
import org.readium.r2.testapp.databinding.ItemGroupViewBinding
21+
import org.readium.r2.testapp.domain.model.Catalog
22+
23+
class GroupAdapter(val type: Int) :
24+
ListAdapter<Group, GroupAdapter.ViewHolder>(GroupDiff()) {
25+
26+
override fun onCreateViewHolder(
27+
parent: ViewGroup,
28+
viewType: Int
29+
): ViewHolder {
30+
return ViewHolder(
31+
ItemGroupViewBinding.inflate(
32+
LayoutInflater.from(parent.context), parent, false
33+
)
34+
)
35+
}
36+
37+
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
38+
val group = getItem(position)
39+
40+
viewHolder.bind(group)
41+
}
42+
43+
inner class ViewHolder(private val binding: ItemGroupViewBinding) :
44+
RecyclerView.ViewHolder(binding.root) {
45+
46+
fun bind(group: Group) {
47+
binding.groupViewGroupPublications.itemRecycleHeaderTitle.text = group.title
48+
if (group.links.size > 0) {
49+
binding.groupViewGroupPublications.itemRecycleMoreButton.visibility = View.VISIBLE
50+
binding.groupViewGroupPublications.itemRecycleMoreButton.setOnClickListener {
51+
val catalog1 = Catalog(
52+
href = group.links.first().href,
53+
title = group.title,
54+
type = type
55+
)
56+
val bundle = bundleOf(CatalogFeedListAdapter.CATALOGFEED to catalog1)
57+
Navigation.findNavController(it)
58+
.navigate(R.id.action_navigation_catalog_self, bundle)
59+
}
60+
}
61+
binding.groupViewGroupPublications.recyclerView.apply {
62+
layoutManager = LinearLayoutManager(binding.root.context)
63+
(layoutManager as LinearLayoutManager).orientation =
64+
LinearLayoutManager.HORIZONTAL
65+
adapter = PublicationAdapter().apply {
66+
submitList(group.publications)
67+
}
68+
}
69+
binding.groupViewGroupLinks.apply {
70+
layoutManager = LinearLayoutManager(binding.root.context)
71+
adapter = NavigationAdapter(type).apply {
72+
submitList(group.navigation)
73+
}
74+
addItemDecoration(
75+
CatalogFeedListFragment.VerticalSpaceItemDecoration(
76+
10
77+
)
78+
)
79+
}
80+
}
81+
}
82+
83+
private class GroupDiff : DiffUtil.ItemCallback<Group>() {
84+
85+
override fun areItemsTheSame(
86+
oldItem: Group,
87+
newItem: Group
88+
): Boolean {
89+
return oldItem == newItem
90+
}
91+
92+
override fun areContentsTheSame(
93+
oldItem: Group,
94+
newItem: Group
95+
): Boolean {
96+
return oldItem == newItem
97+
}
98+
}
99+
100+
}

0 commit comments

Comments
 (0)