I have two tabs in which I add two different lists of data, both tabs share a single recyclerview, so at my viewpager adapter, I just create a new instance to populate the data
From the View
val allProducts = completeProductList
val productsList = mutableListOf<Products>()
val drinksList = mutableListOf<Products>()
for (product in allProducts) {
if (product.isDrink) {
drinksList.add(product)
} else {
productsList.add(product)
}
}
viewPagerAdapter.productsList = productsList
viewPagerAdapter.drinksList = drinksList
viewPagerAdapter.notifyDataSetChanged()
Adapter
class PagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
var productsList: MutableList<Product> = arrayListOf()
var drinksList: MutableList<Product> = arrayListOf()
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> {
FragmentProducts.newInstance(productsList)
}
else -> {
FragmentProducts.newInstance(drinksList)
}
}
}
}
Then in my FragmentProducts
companion object {
fun newInstance(product: MutableList<Product>) = FragmentProducts().apply {
arguments = Bundle().apply {
putParcelableArrayList(ARG_PROD,ArrayList<Parcelable>(product))
}
}
}
// I get the product list from the adapter, either drinks or normal products
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
productsList = it.getParcelableArrayList<Product>(ARG_PROD)
}
}
// Then I just set it up to the shared recyclerview
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
adapter.productsList = productsList!!
adapter.notifyDataSetChanged()
}
So, the list is displayed correctly, lets say I have two tabs, first tab has 2 items and second tab has 1 item, so, If I click on item 1 at tab 1 I get its id and get the right product, then if I click item 2 on tab 1 it also works, when I swipe to tab 2 and click item 1 it will display correctly again the item, but, if I go back to tab 1 and click item 2 it will throw a IndexOutOfBoundsException, it seems like when swiping back it takes the latest recyclerview data set
I dont know how to fix this to prevent creating a different fragment for tab 2 since they show the same data
I need to know what is happening, it seems that the last FragmentProducts.newInstance(drinksList) is replacing the whole recyclerview at tab 1
StackTrace
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.get(ArrayList.java:411)
at com.StoreAdapter.getItem(StoreAdapter.kt:45)
at com.FragmentProducts.onCartClick(FragmentProducts.kt:65)
at com.StoreAdapter$StoreViewHolder$bind$1.onClick(StoreAdapter.kt:59)
StoreAdapter error at this line
fun getItem(position: Int):Product{
return productList[position]
}
viewPagerAdapter.productsList = productsList
viewPagerAdapter.drinksList = drinksList
viewPagerAdapter.notifyDataSetChanged()
If i didn't understand wrong when you came here your viewPagerAdapter already created and you defined your lists as nonNull in adapter( MutableList< Product >).
fun getItem(position: Int):Product{
return productList[position]
}
For example this block tries to get position 1 when adapter is created and before you set data. It gonna return IndexOutOfBoundsException.
Maybe you must try to move this lists in viewPagerAdapter's constructor.
Related
I have a viewpager2 with FragmentStateAdapter() adapter inside it. I also have a tab layout with 4 tabs. I use a single fragment for all tab. that is named AllOrdersTab.
in my architecture I just send different value to load different API data to AllOrdersTab fragment.
when each tab layout is selected , a fragment created and works fine for the first time for all 4 fragments. after that if I swipe back to previous tab it is not created or refreshed again.
I want to recreate the fragment or a way to call API again when swiping between tabs. I also read this page.
FragmentStateAdapter not recreating currentFragment after notifyDataSetChanged
I tried to do this. so I decided to create 4 instance of Allorderstab() fragment. but never work for me because I guess hash codes of fragments are same.
ViewPager2 Adapter:
class ViewPagerOrdersAdapter(fm: FragmentManager,val listFragments:MutableList<Fragment>, viewlifecycler: Lifecycle) : FragmentStateAdapter(fm, viewlifecycler)
{
override fun getItemCount(): Int
{
return listFragments.size
}
override fun createFragment(position: Int): Fragment {
val args = Bundle()
when (position) {
1 -> {
args.putString("KEY_ID", "inProgress")
listFragments[position].arguments = args
return listFragments[position]
}
2 -> {
args.putString("KEY_ID", "cancel")
listFragments[position].arguments = args
return listFragments[position]
}
3 ->
{
args.putString("KEY_ID","deliver")
listFragments[position].arguments=args
return listFragments[position]
}
else -> {
args.putString("KEY_ID", "all")
listFragments[position].arguments = args
return listFragments[position]
}
}
}
override fun getItemId(position: Int): Long {
return listFragments[position].hashCode().toLong()
}
override fun containsItem(itemId: Long): Boolean {
return listFragments.find {it.id.hashCode().toLong() == itemId } != null
}
}
Here I created 4 instance of Allorderstab() Fragment.
Set ViewPager2 Adapter
MainFragment:
val fragments:MutableList<Fragment> = mutableListOf(Allorderstab(), Allorderstab(), Allorderstab(), Allorderstab())
vp.setAdapter(ViewPagerOrdersAdapter(this.childFragmentManager,fragments, lifecycle))
AllOrderstab Fragment:
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?, savedInstanceState: Bundle?): View? {
val bundle = arguments
bundle?.let {
val myStatus = bundle.getString("KEY_ID")
myStatus?.let{
//getting history of each tab orders - calling API
myviewModel.getOrdersHistory(tempTn,myStatus)
}
}
}
All in all I don't know how to refresh while swiping between tabs . if I have a single fragment for all 4 tabs.
For those who wasted a day for this problem like me , wanting to call API each time for refreshing data, Move your code to onResume function. fortunately it is run each time your fragment visible.
AllOrderstab Fragment:
override fun onResume() {
val bundle = arguments
bundle?.let {
val myStatus = bundle.getString("KEY_ID")
myStatus?.let{
//getting history of all orders
myviewModel.getOrdersHistory(tempTn,myStatus)
}
}
super.onResume()
}
You have 4 different instances of the same tab. If you need to refresh the tabs everytime the user navigates to your tab you need to add a PageChangeListener to your tabs view. Then whenever you change the page you need to notify the fragment that it has come to foreground you can do so by calling a method on Allorderstab class and then refreshing the data from this method.
I am using nested recyclerview.
In the picture, the red box is the Routine Item (Parent Item), and the blue box is the Detail Item (Child Item) in the Routine Item.
You can add a parent item dynamically by clicking the ADD ROUTINE button.
Similarly, child items can be added dynamically by clicking the ADD button of the parent item.
As a result, this function works just fine.
But the problem is in the code I wrote.
I use a ViewModel to observe and update parent item addition/deletion.
However, it does not observe changes in the detail item within the parent item.
I think it's because LiveData only detects additions and deletions to the List.
So I put _items.value = _items.value code to make it observable when child items are added and deleted.
This way, I didn't even have to use update code like notifyDataSetChanged() in the child adapter.
In the end it is a success, but I don't know if this is the correct code.
Let me know if you have additional code you want!
In Fragment.kt
class WriteRoutineFragment : Fragment() {
private var _binding : FragmentWriteRoutineBinding? = null
private val binding get() = _binding!!
private lateinit var adapter : RoutineAdapter
private val vm : WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentWriteRoutineBinding.inflate(inflater, container, false)
adapter = RoutineAdapter(::addDetail, ::deleteDetail)
binding.rv.adapter = this.adapter
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
getTabPageResult()
// RecyclerView Update
vm.items.observe(viewLifecycleOwner) { updatedItems ->
adapter.setItems(updatedItems)
}
}
private fun getTabPageResult() {
val navController = findNavController()
navController.currentBackStackEntry?.also { stack ->
stack.savedStateHandle.getLiveData<String>("workout")?.observe(
viewLifecycleOwner, Observer { result ->
vm.addRoutine(result) // ADD ROUTINE
stack.savedStateHandle?.remove<String>("workout")
}
)
}
}
private fun addDetail(pos: Int) {
vm.addDetail(pos)
}
private fun deleteDetail(pos: Int) {
vm.deleteDetail(pos)
}
}
ViewModel
class WriteRoutineViewModel : ViewModel() {
private var _items: MutableLiveData<ArrayList<RoutineModel>> = MutableLiveData(arrayListOf())
val items: LiveData<ArrayList<RoutineModel>> = _items
fun addRoutine(workout: String) {
val item = RoutineModel(workout, "TEST")
_items.value?.add(item)
// _items.value = _items.value
}
fun addDetail(pos: Int) {
val detail = RoutineDetailModel("TEST", "TEST")
_items.value?.get(pos)?.addSubItem(detail) // Changing the parent item's details cannot be observed by LiveData.
_items.value = _items.value // is this right way?
}
fun deleteDetail(pos: Int) {
if(_items.value?.get(pos)?.getSubItemSize()!! > 1)
_items.value?.get(pos)?.deleteSubItem() // is this right way?
else
_items.value?.removeAt(pos)
_items.value = _items.value // is this right way?
}
}
This is pretty standard practice when using a LiveData with a mutable List type. The code looks like a smell, but it is so common that I think it's acceptable and people who understand LiveData will understand what your code is doing.
However, I much prefer using read-only Lists and immutable model objects if they will be used with RecyclerViews. It's less error prone, and it's necessary if you want to use ListAdapter, which is much better for performance than a regular Adapter. Your current code reloads the entire list into the RecyclerView every time there is any change, which can make your UI feel laggy. ListAdapter analyzes automatically on a background thread your List for which items specifically changed and only rebinds the changed items. But it requires a brand new List instance each time there is a change, so it makes sense to only use read-only Lists if you want to support using it.
I have two tabs in which I add two different lists of data, both tabs share a single recyclerview, so at my viewpager adapter, I just create a new instance to populate the data
From the View
val allProducts = completeProductList
val productsList = mutableListOf<Products>()
val drinksList = mutableListOf<Products>()
for (product in allProducts) {
if (product.isDrink) {
drinksList.add(product)
} else {
productsList.add(product)
}
}
viewPagerAdapter.productsList = productsList
viewPagerAdapter.drinksList = drinksList
viewPagerAdapter.notifyDataSetChanged()
Adapter
class PagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
var productsList: MutableList<Product> = arrayListOf()
var drinksList: MutableList<Product> = arrayListOf()
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> {
FragmentProducts.newInstance(productsList)
}
else -> {
FragmentProducts.newInstance(drinksList)
}
}
}
}
Then in my FragmentProducts
companion object {
fun newInstance(product: MutableList<Product>) = FragmentProducts().apply {
arguments = Bundle().apply {
putParcelableArrayList(ARG_PROD,ArrayList<Parcelable>(product))
}
}
}
// I get the product list from the adapter, either drinks or normal products
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
productsList = it.getParcelableArrayList<Product>(ARG_PROD)
}
}
// Then I just set it up to the shared recyclerview
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
adapter.productsList = productsList!!
adapter.notifyDataSetChanged()
}
So, the list is displayed correctly, lets say I have two tabs, first tab has 2 items and second tab has 1 item, so, If I click on item 1 at tab 1 I get its id and get the right product, then if I click item 2 on tab 1 it also works, when I swipe to tab 2 and click item 1 it will display correctly again the item, but, if I go back to tab 1 and click item 2 it will throw a IndexOutOfBoundsException, it seems like when swiping back it takes the latest recyclerview data set
I dont know how to fix this to prevent creating a different fragment for tab 2 since they show the same data
I need to know what is happening, it seems that the last FragmentProducts.newInstance(drinksList) is replacing the whole recyclerview at tab 1
StackTrace
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.get(ArrayList.java:411)
at com.StoreAdapter.getItem(StoreAdapter.kt:45)
at com.FragmentProducts.onCartClick(FragmentProducts.kt:65)
at com.StoreAdapter$StoreViewHolder$bind$1.onClick(StoreAdapter.kt:59)
StoreAdapter error at this line
fun getItem(position: Int):Product{
return productList[position]
}
viewPagerAdapter.productsList = productsList
viewPagerAdapter.drinksList = drinksList
viewPagerAdapter.notifyDataSetChanged()
If i didn't understand wrong when you came here your viewPagerAdapter already created and you defined your lists as nonNull in adapter( MutableList< Product >).
fun getItem(position: Int):Product{
return productList[position]
}
For example this block tries to get position 1 when adapter is created and before you set data. It gonna return IndexOutOfBoundsException.
Maybe you must try to move this lists in viewPagerAdapter's constructor.
My app is displaying a list of various categories (herbs, side dishes, ..) in a RecyclerView. Depending on the category you clicked on, a new Activity with a new RecylcerView opens containing all the ingredients.
Right now I have an ArrayList which gets filled with the ingredients via ".add" depending on the choosen category.
The problem im facing right now is, that I want to implement an option for the user to add own Ingredients. I tried storing the ArrayList containing the ingredients in SharedPreferences by using Gson, but I couldn't manage to add elements, since it always overwrote the current list.
What would be the best way to store the ingredients? A room, sqlite, ..?
Without further explanation, the ingredient list will only contain about 70 items max.
Thanks in advance.
Edit:
CatList.kt
class CatList : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cat_list)
//Create List for categories
val cats = ArrayList<IngCat>()
//Fill categories
cats.add(IngCat(R.drawable.herbs, "Herbs"))
cats.add(IngCat(R.drawable.fluessiges, "Liquids"))
cats.add(IngCat(R.drawable.festes, "Solids"))
cats.add(IngCat(R.drawable.beilagen, "Sides"))
//Recyclerview
id_rv_CatList.layoutManager = LinearLayoutManager(this)
id_rv_CatList.adapter =
CatListAdapter(cats) {listItem, position -> //go to Ingredient List Activity
goToIngList(position, listItem.name)
}
//id_rv_CatList.addItemDecoration(DividerItemDecoration(this,DividerItemDecoration.HORIZONTAL))
//actionbar
val actionbar = supportActionBar
//set actionbar title
actionbar!!.title = "Ingredient - Categories"
}
private fun goToIngList(cat: Int, name: String){
val intent = Intent(this, IngList::class.java)
intent.putExtra("Category", cat)
intent.putExtra("Name", name)
startActivity(intent)
}
}
data class IngCat(var mImageResource:Int, var name:String)
IngList.kt
class IngList : AppCompatActivity() {
companion object {
var categoryChoosen : Int = 0
var catName : String = "Err"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_ing_list)
//Initilize Ingredient List
val ings :ArrayList<IngIng> = ArrayList()
//Get category and category name
categoryChoosen = intent.getIntExtra("Kategorie",0)
catName = intent.getStringExtra("Name")!!
when (categoryChoosen) {
0 -> {
ings.add(IngIng("https://doeel.com/images/thumbnails/1100/900/detailed /92/Turmeric_Powder___Holud_Gura__.png", "Turmeric Powder"))
}
1 -> ings.add(IngIng("https://www.miraherba.de/4923-large_default/bio-ghee-300-g.jpg", "Ghee"))
2 -> ings.add(IngIng("https://www.organicfacts.net/wp-content/uploads/coriander-1.jpg", "Coriander leaves"))
3 -> ings.add(IngIng("https://gbc-cdn-public-media.azureedge.net/img75602.1426x713.jpg", "Potatoes"))
}
//Actionbar Settings
setSupportActionBar(toolbar)
val actionbar = supportActionBar
actionbar!!.title = "Ingredients- $catName"
actionbar.setDisplayHomeAsUpEnabled(true)
//Recyclerview
id_rv_IngList.layoutManager = GridLayoutManager(this,2)
id_rv_IngList.adapter =
IngListAdapter(ings) {//ClickListener RecyclerView
Toast.makeText(this, "Item clicked: ${it.name}", Toast.LENGTH_SHORT).show()
}
//Actionbar
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.actionbar_ing_list, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.id_menu_action_add -> {
val intent = Intent(this, AddIngredient::class.java)
startActivity(intent)
true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}
IngListAdapter.kt
class IngListAdapter (private val ings: ArrayList<IngIng>, val clickListener: (IngIng)->Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun getItemCount(): Int = ings.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val v: View = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_ing_list_item, parent, false)
return IngViewHolder(v)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var currentItem = ings.get(position)
when (holder) {
is IngViewHolder -> {
holder.tvIngList.text = currentItem.name
// holder.ivIngImage.setImageResource(currentItem.mImageResource)
Picasso.get().load(currentItem.mImageResource).placeholder(R.drawable.ic_broken_image_black_200dp).error(R.drawable.ic_broken_image_red_24dp).into(holder.ivIngImage)
holder.cvIngCard.setOnClickListener{
clickListener(currentItem)
}
}
}
}
}
class IngViewHolder (view: View) : RecyclerView.ViewHolder(view) {
val tvIngList: TextView = view.id_text_ing
val ivIngImage: ImageView = view.id_img_ing
val cvIngCard: MaterialCardView = view.id_cv_ing_list
}
I personally think Json/Gson in a SharedPreference is the easiest way to go if there are so few items. The way I would handle it is to store the list in memory at application startup, and persist the list back to the SharedPreference when the app is shut down. Also when the app gets stopped for good measure because you can't 100% be sure onDestroy will be called.
So first I'd make a class to store the data. If you were using Fragments that all are in the same Activity, you'd put this in a ViewModel. But since they are separate Activities, you need a singleton for them. (Google doesn't recommend using multiple Activities because it's hard to share data between them. But it's not impossible. It's what we did before Fragments.)
To do it as a singleton, you could have a class like this:
class IngredientsRepo private constructor (application: Application) {
companion object {
private val INSTANCE: IngredientsRepo? = null
fun getInstance(application: Application) =
INSTANCE ?: IngredientsRepo(application).also { INSTANCE = it }
private const KEY_JSON_PREF = "ingredientsJson"
}
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
val herbsList: MutableList<IngCat>
val liquidsList: MutableList<IngCat>
val solidsList: MutableList<IngCat>
val sidesList: MutableList<IngCat>
init {
val json = sharedPreferences.getString(KEY_JSON_PREF, null)
if (json == null) {
// initialize your list contents for the first time
} else {
// convert your json and fill the data into your lists
}
}
fun save {
val jsonString = // Convert your lists to Json
sharedPreferences.edit().putString(KEY_JSON_PREF, jsonString).apply()
}
}
This class becomes responsible for setting up your lists. You can retrieve it from any Activity with IngredientsRepo.getInstance(this) and you can add and remove items from the lists whenever you like. You can also call save on it whenever you like to persist the latest data. It's probably sufficient to do this in onStop() of any Activity that modifies the list.
More properly, the data in this class would only be exposed with immutable lists, and you'd add functions for adding and removing items, so only this class directly modifies the lists. I didn't want to overcomplicate the example, but it would be better for encapsulation to not have Activities (which are supposed to be pure UI components) directly modifying data structures.
I have a TabLayout/ViewPager with two fragments in my activity. The tabs are created and then I request some information. When I get this information, I update the RecyclerView of each tab.
Main Activity
private fun setViews() {
val adapter = TabsAdapter(supportFragmentManager)
adapter.addFragment(NewsFragment(), getString(R.string.dossier_activity_news))
adapter.addFragment(PhotosFragment(), resources.getString(R.string.dossier_activity_photos))
view_pager.adapter = adapter
tab_layout.setupWithViewPager(view_pager)
// request info
}
fun setPages(pages: List<Page>) {
// got info
((view_pager.adapter as TabsAdapter).getItem(0) as NewsFragment).setPages(pages)
((view_pager.adapter as TabsAdapter).getItem(1) as PhotosFragment).setPages(pages)
}
The problem is: In the second tab(PhotosFragment), I want for remove all pages that does not contain a photo/thumbnail.
NewsFragment
fun setPages(pages: List<Page>) {
if (pages.isNotEmpty()) recycler_view.adapter = PagesAdapter(this, pages)
}
PhotosFragment
fun setPages(pages: List<Page>) {
val iterator = (pages as ArrayList<Page>).iterator()
while (iterator.hasNext()) if (iterator.next().thumbnail == null) iterator.remove()
if (pages.isNotEmpty()) recycler_view.adapter = PagesAdapter(this, pages)
}
When I create this iterator on the second tab, all the pages on the first fragment are also updated to no photos/thumbnails pages. Each fragment has is own life cycle and by changing the content of one fragment it should not update the other right?
Could this be because of my PagesAdapter extend RecyclerView?
PagesAdapter
class PagesAdapter(private var fragment: Fragment, private var pages: List<Page>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, type: Int): RecyclerView.ViewHolder {
return when (type) {
0 -> HeaderViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.article_header_item, parent, false))
else -> ArticleViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.article_item, parent, false))
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val type = getItemViewType(position)
when (type) {
0 -> setHeader(holder as PagesAdapter.HeaderViewHolder, position)
else -> setArticle(holder as PagesAdapter.ArticleViewHolder, position)
}
}
inner class HeaderViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) {
val layout: RelativeLayout = view.article_header_layout
val image: ImageView = view.article_header_image
val title: AOTextView = view.title_header_text
val date: AOTextView = view.date_header_text
}
inner class ArticleViewHolder internal constructor(view: View) : RecyclerView.ViewHolder(view) {
val layout: RelativeLayout = view.article_layout
val image: ImageView = view.article_image
val strapline: AOTextView = view.strapline_text
val title: AOTextView = view.title_text
val date: AOTextView = view.date_text
}
}
Both fragments have the same pages list, but I want the second fragment to filter this list to show only pages with photos/thumbnails. By filtering the list in the second fragment, the first one shows the same filtered list. Should I aproach this differently? Or is some bug in my code?
You are sharing one list between two fragments, which is not a good idea.
Kotlin favors immutability and in it should be the default way of doing things, unless you have very good reason.
Two fragments are sharing list reference and that's why when one of it changes it, those changes are reflected in the other fragment.
What you should do is instead of
fun setPages(pages: List<Page>) {
val iterator = (pages as ArrayList<Page>).iterator()
while (iterator.hasNext()) if (iterator.next().thumbnail == null) iterator.remove()
if (pages.isNotEmpty()) recycler_view.adapter = PagesAdapter(this, pages)
}
you should write:
fun setPages(pages: List<Page>) {
val pagesWithThumbnails = pages.filter { it.thumbnail != null }
if (pagesWithThumbnails.isNotEmpty()) recycler_view.adapter = PagesAdapter(this, pagesWithThumbnails)
}
There you don't modify original list while having more readable and error-proof code.