0

In my app, I have a tab layout with 2 tabs. I am using FragmentPagerAdapter to create fragments for each tab. In one of these fragments, I have a RecyclerView that has a RecyclerView.Adapter attached which takes care of the data binding to the list items in the RecyclerView. When the user selects an item from this list, I am adding the corresponding item to another list. I want to show a badge on the second tab when an item from the first list is selected. I am following the guidelines in https://material.io/develop/android/components/tabs but I am not able to add the badge to the second tab.

How can I get a reference to the second tab in my RecyclerView adapter class (because this is where the handler for item selection is present) so that I can change the tab badge? Is there a better place where I can change the tab badge?

Following is code for the RecyclerView adapter:

class RecyclerAdapter(val context: Context): RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    inner class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
        var itemTitle: TextView
        var checkBox: CheckBox
        init {
            itemTitle = itemView.findViewById(R.id.li_item_title)
            checkBox = itemView.findViewById(R.id.li_chk_add)

            checkBox.setOnClickListener {
                SharedData.allItems[adapterPosition].isSelected = checkBox.isChecked
                if(checkBox.isChecked) {
                    SharedData.selectedItems.add(SharedData.allItems[adapterPosition])
                } else {
                    SharedData.removeItem(itemTitle.text.toString())
                }
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val v = LayoutInflater.from(context).inflate(R.layout.list_item_view, parent, false)
        return MyViewHolder(v)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        holder.itemView.findViewById<TextView>(R.id.li_item_title).text =
            SharedData.allItems[position].title
        holder.itemView.findViewById<CheckBox>(R.id.li_chk_add).isChecked =
            SharedData.allItems[position].isSelected
    }

    override fun getItemCount(): Int {
        return SharedData.allItems.size
    }
}

Following is the code for the fragment that contains the RecyclerView:

class FragmentItems: Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // recycler view specific
        val view = inflater.inflate(R.layout.fragment_items, null)
        val layoutManager = LinearLayoutManager(context)
        val recycler_view = view.findViewById<RecyclerView>(R.id.items_list)
        recycler_view.layoutManager = layoutManager
        val recyclerAdapter = RecyclerAdapter(context!!)
        recycler_view.adapter = recyclerAdapter
        return view
    }
}

Here's the code for the layout of the fragment:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_marginStart="10dp"
    android:layout_marginEnd="10dp"
    android:layout_marginTop="5dp"
    android:layout_marginBottom="5dp">
    <LinearLayout
        android:id="@+id/text_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="3dp"
        android:clickable="true">
        <TextView
            android:id="@+id/li_item_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="22sp"/>
    </LinearLayout>
    <CheckBox
        android:id="@+id/li_chk_add"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:layout_alignParentEnd="true"
        android:layout_centerVertical="true"/>
</RelativeLayout>

Following is the code from MainActivity where the tabs are added:

val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
val viewPager = findViewById<ViewPager>(R.id.tab_view_pager)
val viewPagerAdapter = MyViewAdapter(supportFragmentManager, tabLayout.tabCount)
viewPager.adapter = viewPagerAdapter
tabLayout.setupWithViewPager(viewPager)

Here's the code for the FragmentPagerAdapter:

class MyViewAdapter(fragmentMangaer:FragmentManager, tabCount: Int) : FragmentPagerAdapter(fragmentMangaer, tabCount) {

    lateinit var listFragment:Fragment

    override fun getItem(position: Int): Fragment {
        println("in getItem")
        var fragment:Fragment = Fragment()
        when(position) {
            0 -> {
                fragment = FragmentItems()
            }
            1 -> {
                fragment =  FragmentList()
                listFragment = fragment
            }
        }
        return fragment
    }

    override fun getCount(): Int {
        return 2
    }

    override fun getPageTitle(position: Int): CharSequence? {
        var title = "None"
        when(position) {
            0 -> {title = "Items"}
            1 -> {title = "List"}
        }
        return title
    }

    fun getShoppingListFragment(): Fragment {
        return listFragment
    }
}
Son Truong
  • 13,661
  • 5
  • 32
  • 58
Rakesh K
  • 8,237
  • 18
  • 51
  • 64
  • The code you have added nothing to do with tab. Where are the Tabs ? in Activity or in Fragment ? Add only required code with question . – ADM Dec 08 '20 at 04:40
  • @ADM I've added the code for tabs and the FragmentPagerAdapter – Rakesh K Dec 08 '20 at 04:55
  • So tabs are in `Activity` . So you need to somehow call a method of `Activity` to update the badge . You can do it in various ways if you are using MVVM then you can use `ViewModel` for this purpose . See [This](https://stackoverflow.com/questions/12659747/call-an-activity-method-from-a-fragment). – ADM Dec 08 '20 at 05:01

1 Answers1

0

After a bit of research, I came across this article - https://medium.com/android-gate/recyclerview-item-click-listener-the-right-way-daecc838fbb9. Although this article talks about adding a custom listener to the RecyclerView, it actually gave me the idea to solve my problem - add a custom listener to the Checkbox view in my RecyclerView's ViewHolder class. Here's what I did:

  1. Create a custom interface that contains my listener function declaration:
interface OnCheckboxSelectedListener {
    fun onSelected(position: Int, isChecked: Boolean, title: String)
}
  1. Implement this interface in MainActivity(since this has the reference to TabLayout):
override fun onSelected(position: Int, isChecked: Boolean, title: String) {
    SharedData.allItems[position].isSelected = isChecked
    if(isChecked) {
        SharedData.selectedItems.add(SharedData.allItems[position])
    } else {
        SharedData.removeItemFromSelectedItems(title)
    }
    updateTabBadge(SharedData.selectedItems.size)
}

fun updateTabBadge(count: Int) {
    val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
    tabLayout.getTabAt(1)!!.orCreateBadge.number = count
}
  1. Pass the instance of MainActivity from my fragment to recycler view adapter:
val recyclerAdapter = RecyclerAdapter(context!!, activity as MainActivity)
  1. Modify the RecyclerAdapter class to take the listener implementer:
class RecyclerAdapter(val context: Context, val checkboxListener: OnCheckboxSelectedListener): RecyclerView.Adapter<RecyclerView.ViewHolder>()
  1. Add a new function in ViewHolder class to bind the listener to the Checkbox:
fun bind(checkboxListener: OnCheckboxSelectedListener) {
        checkBox.setOnClickListener {
                checkboxListener.onSelected(adapterPosition, checkBox.isChecked, itemTitle.text.toString())
        }
}

With this whole chaining in place, I now get my desired output - update the badge on second tab with the count of items in its fragment's list whenever a checkbox is selected/unselected in the list present in fragment of first tab.

Rakesh K
  • 8,237
  • 18
  • 51
  • 64