I use tabLayout as a header for recyclerView adapter. I have no problem for scroll to static position of recyclerview Items. for example position 2 for pizza header.tab zero for pizza.
override fun onTabSelected(tab: TabLayout.Tab?) {
if (tab.position == 0)
recyclerview.smoothScrollToPosition(2)}
The problem is that I don't know how to scroll to position of some headers items that I don't have their positions. as I know I can get all Item positions until they are visible in screen by onBindViewHolder function. right. But when all items are not visible or I am in the first of list , how could I get the rest of headers positions? for example drinks header position is in end of the list and I need to know position when user clicked on drink tab to go to. It seems is not good approach.
my scenario is that , I have a recyclerview adapter. It has two item types.
1-Header items type (Food type like: Sandwich - Pizza - ... )
2-Item type (Food like: Neapolitan Pizza - Chicago Pizza - ...)
my approach was : I create a hashmap . tried to fill it with just Header Items. <Name, Position>. I filled it in onbindviewholder. as I described it before. All header items positions are not calculated at once until I scroll all the list to end .so returnPositionHedaer function always return 0.
RecyclerViewAdapter Class:
class RecyclerAdapterInside( PlaceId:String?=null, var myReceivedData: List<ItemClass>?=null, val context:Context, private val ButtonFinish:RecyclerAdapterInsideButtonFinishCallback):RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var allHeader:HashMap<String,Int> = hashMapOf()
companion object {
const val Type_Header = 1
const val Type_Item = 2
}
private inner class CategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var message: TextView = itemView.findViewById(R.id.textViewCategory)
fun bind(position: Int)
{
Log.d("inner Category","it is in CategoryViewHolder Header")
message.text = myReceivedData?.get(position)?.name
allHeader.put(message.text.toString(),position)
Log.d("itemss iewHolder",allHeader.keys.toString())
}
}
private inner class ItemViewholder(itemview: View) : RecyclerView.ViewHolder(itemview) {
val Name = itemview.findViewById(R.id.ItemName) as TextView
val Price = itemview.findViewById(R.id.ItemPrice) as TextView
val Category = itemview.findViewById(R.id.ItemCategory) as TextView
val Image = itemView.findViewById(R.id.ImgInsideMenu) as ImageView
val BtnAdd = itemView.findViewById(R.id.AddBtn) as Button
var count = itemview.findViewById(R.id.showCounter) as TextView
val BtnMin = itemView.findViewById(R.id.MinBtn) as Button
fun bind(position: Int) {
Log.d("value name is","${Name.text}")
Log.d("inner Category","it is in ItemViewHolder Header")
Name.text = myReceivedData!![position].name
Price.text = "Price :" + myReceivedData!![position].price
...
}
override fun getItemViewType(position: Int): Int {
return when (myReceivedData!!.get(position).getviewType()) {
1 -> Type_Header
2 -> Type_Item
else -> -1
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == Type_Header)
{
Log.d("OncreateViewHolder","it is in oncreateview holder type header")
return CategoryViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.categoryheader, parent, false))
}
else
{
Log.d("OncreateViewHolder","it is in oncreateview holder type item")
return ItemViewholder(LayoutInflater.from(parent.context).inflate(R.layout.itemforrecyclerviewinside, parent, false)
)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)
{
when (myReceivedData!![position].getviewType())
{
Type_Header ->
{
Log.d("OnBindViewHolder","it is in OnBind holder type header")
(holder as CategoryViewHolder).bind(position)
}
Type_Item ->
{
Log.d("OnTypeViewHolder","it is in OnBind holder type item")
(holder as ItemViewholder).bind(position)
}
}
}
fun returnPositionHedaer(name:String):Int
{
for (hashMap in allHeader)
{
Log.d("itemss",hashMap.key)
if (hashMap.key == name)
{
return hashMap.value
}
}
return 0
}
}
I fill my TabLayout tab dynamically with Category food List. my adapter uses same category for header items.
those parts are worked. I write them here to more clarify .In this part I call returnPositionHedaer and pass the tabName of selected tab to it. this method seems never worked . I repeat that I have a dynamic position in recyclerview and I need calculate positions of header items to scroll when related tab is clicked in TabLayout. my relation or link between tab in tabLayout and item in recyclerview is Name. my method is not worked.
what approach do you suggest ?
MenuFragment Class:
//to more clarify,has no problem
private fun setupTabLayout():List<String>
{
if(tabLayout!!.tabCount>0)
CoroutineScope(Dispatchers.Main).launch{tabLayout!!.removeAllTabs()}
for(i in 0 until fullMenu!!.size){
CategoryList.add(i,fullMenu!![i].category)
Log.d("category",CategoryList[i])
}
val tabCategory=CategoryList.distinct()
CoroutineScope(Dispatchers.Main).launch {
if (tabCategory.size > 3)
tabLayout!!.setTabMode(TabLayout.MODE_SCROLLABLE);
else {
tabLayout!!.setTabMode(TabLayout.MODE_FIXED);
tabLayout!!.setTabGravity(TabLayout.GRAVITY_FILL);
}
for (i in 0 until tabCategory.size)
{
tabLayout?.addTab(tabLayout!!.newTab().setText(tabCategory[i]), i)
Log.d("addingtab",tabCategory[i])
}
} } //to more clarify,has no problem
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupTabLayout()
tabLayout!!.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener{
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
isUserScrolling = false
val position = tab!!.position
val tabName = tab.text
CoroutineScope(Dispatchers.Main).launch {
recyclerview.smoothScrollToPosition( recyclerAdapter.returnPositionHedaer(tabName.toString()))
//always return 0 position
}
}
})
}
For those who have this challenge , I could handle it easily . You just need create an array from your recyclerview adapter data model. before passing to adapter create a property, for example named it poistion. the purpose is for saving position of each item in your array data model and use counter to assign position to each array item. finally pass it to adapter. Now you know what item has what position.
MenuClass()
open class ItemClass()
{
private var position = 0
var name: String?=null
var _id: String? =null
var __v: Int? =0
var category: String?=null
var description: String?=null
var image: String? =null
var price: String? =null
var rating: String? =null
open fun ItemHeaderClass(position:Int,viewType: Int, name: String) {
this.position=position
this.viewType = viewType
this.name=name
}
}
MenuFragment
for(item in category)
{
val myclass=ItemClass()
myclass.ItemHeaderClass(counter++, 1, item)
calculatedAllItemOfList.add(myclass)
val items = createItemListForRecycler(item, allItem)
calculatedAllItemOfList.addAll(items)
}