How to add Header to recyclerview in kotlin? - android

after getting successful response, Header added to first position of recyclerview but first item of list not showing.
list having 5 items but its shows only last 4 items.
how to show all list items with extra added header in recyclerview.
class AllCategoryAdapter(val categoryList : List<AllCategoryBean>) : RecyclerView.Adapter<RecyclerView.ViewHolder>()
{
private val TYPE_HEADER : Int = 0
private val TYPE_LIST : Int = 1
override fun getItemViewType(position: Int): Int {
if(position == 0)
{
return TYPE_HEADER
}
return TYPE_LIST
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if(viewType == TYPE_HEADER)
{
val header = LayoutInflater.from(parent.context).inflate(R.layout.cv_all_category_header,parent,false)
return ViewHolderHeader(header)
}
val header = LayoutInflater.from(parent.context).inflate(R.layout.cv_all_category,parent,false)
return ViewHolder(header)
}
override fun getItemCount(): Int {
return categoryList.size + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val listItem : AllCategoryBean = categoryList[position]
if(holder is ViewHolderHeader)
{
holder.tvCategoyName.setText("All Category")
}
if(holder is ViewHolder)
{
holder.tvCategoyName.setText(listItem.getCategoryName())
}
}
class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
{
val tvCategoyName = itemView.findViewById(R.id.tvCategoyName) as TextView
}
class ViewHolderHeader(itemView : View) : RecyclerView.ViewHolder(itemView)
{
val tvCategoyName = itemView.findViewById(R.id.tvCategoyName) as TextView
}
}

As #hiddeneyes02 commented in your question, you should be getting ArrayIndexOutOfBoundsException.
BTW when you increased categoryList size by override fun getItemCount(), you must also decrease position by one for getting related item in your list when holder is instance of your view, not header.
So your onBindViewHolder must look like this:
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder instanceof ViewHolderHeader) {
holder.tvCategoyName.setText("All Category")
} else if (holder instanceof ViewHolder) {
val listItem : AllCategoryBean = categoryList[position - 1]
holder.tvCategoyName.setText(listItem.getCategoryName())
}

Related

Expand/Collapse groups RecyclerView header

I'm trying to create a RecyclerView adapter with two types of items. First one is Header, second one is child item. And when user tap on Header item it will expand child items. Next tap will collapse current section of items. But when I tap on Header in my RecyclerView it shows expanded items after second header. I don't know how to fix it
class SectionedAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var items = mutableListOf<ListItem>()
fun setItems(items: MutableList<ListItem>) {
this.items = items
notifyDataSetChanged()
}
class HeaderItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: HeaderItem) = with(itemView) {
textViewHeader.text = item.title
}
}
class TextItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: TextItem) = with(itemView) {
textViewTitle.text = item.title
textViewContent.text = item.content
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when(viewType) {
TYPE_HEADER -> {
val headerView = LayoutInflater.from(parent.context).inflate(R.layout.item_header, parent, false)
return HeaderItemViewHolder(headerView)
}
TYPE_TEXT -> {
val textView = LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
return TextItemViewHolder(textView)
}
else -> throw IllegalArgumentException()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
when(holder) {
is HeaderItemViewHolder -> {
holder.bind(item as HeaderItem)
holder.itemView.setOnClickListener {
if(item.expanded) {
items.addAll(item.items)
notifyItemRangeInserted(position, item.items.size)
}
else {
items.removeAll(item.items)
notifyItemRangeRemoved(position, item.items.size)
}
item.expanded = !item.expanded
}
}
is TextItemViewHolder -> {
holder.bind(item as TextItem)
}
else -> throw IllegalArgumentException()
}
}
override fun getItemCount(): Int {
return items.count()
}
override fun getItemViewType(position: Int): Int {
val item = items[position]
return when(item) {
is HeaderItem -> TYPE_HEADER
is TextItem -> TYPE_TEXT
else -> throw IllegalArgumentException()
}
}
companion object {
const val TYPE_HEADER = 0
const val TYPE_TEXT = 1
}
}
I think what you are trying to achieve would be easier to do with nested Recyclerviews.
In your item_header.xml add a recyclerview, in onBindViewHolder, initialize this subRecyclerView for every sublist, initialize its adapter etc, populate this sub recyclerview with your required items, and let your onclick listener expand / collapse this sub recyclerView by setting its visibility to View.GONE (parent views layout height needs to be set to wrap_content for this to work)

Setting RecyclerView adapter into ViewPager2

I've created a RecyclerView with Horizontal Scrolling and PageSnapHelper. Now I think to replace RecyclerView with ViewPager2.? Can I simply set RecyclerView Adapter I've created earlier for new ViewPager2.?
Adapter Class goes here
class QuoteAdapter(
val context: Context,
val list: ArrayList<ResultsItem>
) : RecyclerView.Adapter<QuoteAdapter.QuoteViewHolder>() {
var i = 0;
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
QuoteViewHolder {
val view = LayoutInflater.from(context)
.inflate(R.layout.item_quote, parent, false)
return QuoteViewHolder(view)
}
override fun getItemCount() = list.size
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
holder.quote.text = list[position].quoteText
holder.quote_by.text = "- ${list[position].quoteAuthor}"
ColorStore()
if (position == 0) {
holder.quote_bg.setBackgroundColor(ContextCompat.getColor(context, R.color.md_blue_400))
} else {
holder.quote_bg.setBackgroundColor(ContextCompat.getColor(context, colorList.random()))
}
holder.quote_bg.setOnClickListener {
holder.quote_bg.setBackgroundColor(ContextCompat.getColor(context, colorList.random()))
}
}
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val quote = itemView.quote_text
val quote_by = itemView.by_text
val quote_bg = itemView.quote_bg
}
}
I just set recyclerview adapter to viewpager2. It works.

using viewtype , onBindViewHolder and onCreateViewHolder are different

I'm studying using viewtype in recyclerview.
I use two viewtypes. when I tested for changing viewtype, there is no change.
I inserted viewtype1 but the result is returned to viewtype0.
the log is like below D/TAG: ProfileViewHolder onCreateViewHolder
D/TAG: ItemViewHolder onBindViewHolder
onCreateViewHolder is right, but the ItemViewHolder is not right. each other different.
what should I fix? here is my code.
class RecyclerViewAdapter(private val context: Context, private val items : ArrayList<DetailModel>) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(holder is ProfileViewHolder) {
holder.name.text = items[position].login
Glide.with(context)
.load(items[position].profileImage)
.centerCrop()
.override(200,200)
.error(R.drawable.image_not_found)
.into(holder.profileImage)
Log.d("TAG","ProfileViewHolder onBindViewHolder")
}else if(holder is ItemViewHolder){
holder.name.text = items[position].name
holder.description.text = items[position].description
holder.stargazersCount.text = items[position].stargazersCount.toString()
Log.d("TAG","ItemViewHolder onBindViewHolder")
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if(viewType == 0) {
val inflatedView: View = LayoutInflater.from(parent.context)
.inflate(R.layout.detail_item, parent, false)
Log.d("TAG","ProfileViewHolder onCreateViewHolder")
return ItemViewHolder(inflatedView)
}
else {
val inflatedView: View = LayoutInflater.from(parent.context)
.inflate(R.layout.profile_item, parent, false)
Log.d("TAG","ItemViewHolder onCreateViewHolder")
return ProfileViewHolder(inflatedView)
}
}
override fun getItemCount()=items.size
override fun getItemId(position: Int): Long {
return items.get(position).id.toLong()
}
class ItemViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val name :TextView = itemView.findViewById(R.id.name)
val description : TextView = itemView.findViewById(R.id.description)
val stargazersCount :TextView = itemView.findViewById(R.id.stargazersCount)
}
class ProfileViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val name :TextView = itemView.findViewById(R.id.login)
val profileImage : ImageView = itemView.findViewById(R.id.profileImage)
}
}
OnCreate
var detail = DetailModel()
detail.login= userId
detail.viewType=1
itemList.add(detail)
setAdapter()
viewType in onCreateViewHolder is returned by RecyclerView.Adapter, To assign viewType to this adapter for each position, you need to override getItemViewType.
In your case it might be like this.
override fun getItemViewType(position: Int): Int {
val detail = items[position]
return detail.viewType
}

Kotlin - How to implement AdMob ad as view type inside RecyclerView

What's the correct way to implement AdMob ads (as a different view type) inside a Kotlin RecyclerView that contains an additional (but different) view type? I've not seen any helpful tutorials online. The issue I'm having is with the onCreateViewHolder method as I'm unsure of the correct way to modify it.
class AdapterMain(
private val mCtx: Context,
var myList: MutableList<ItemRV>
) : androidx.recyclerview.widget.RecyclerView.Adapter<AdapterMain
.MyViewHolder>() {
private val myListFull = myList.toMutableList()
private var mClickListener: ItemClickListener? = null
private val itemRV = 1
private val itemAD = 2
override fun getItemViewType(position: Int): Int {
return if (position % 5 == 0) {
itemRV
} else {
itemAD
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) {
return if (viewType == itemAD) {
val adLoader = AdLoader.Builder(mCtx, "ca-app-pub-5544923106349792~1702536043")
.forUnifiedNativeAd { ad : UnifiedNativeAd ->
// Show the ad.
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(errorCode: Int) {
// Handle the failure by logging, altering the UI, and so on.
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
.build())
.build()
adLoader.loadAd(AdRequest.Builder().build())
} else {
val inflater = LayoutInflater.from(mCtx)
val v = inflater.inflate(R.layout.rv_item, parent, false)
return MyViewHolder(v)
}
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
if (position == itemAD){
} else {
val product = myList[holder.adapterPosition]
holder.tvTitle.text = product.itemName
}
}
override fun getItemCount(): Int {
return myList.size
}
inner class MyViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView), View.OnClickListener {
var tvTitle: TextView = itemView.tv_title
}
interface ItemClickListener {
fun onItemClick(view: View, position: Int)
}
}
What you want to do is create 2 classes that extend ViewHolder; one for your normal content, and one for your ad view. You'll need to change some of your functions to use the generic RecyclerView.ViewHolder instead of your MyViewHolder as it could be either one.
In your onCreateViewHolder, you'll want to create one or the other, and return that.
And then in your onBindViewHolder, that's where you'll want to load/show your ad.
class AdapterMain(
private val mCtx: Context,
var myList: MutableList<ItemRV>
) : androidx.recyclerview.widget.RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val myListFull = myList.toMutableList()
private var mClickListener: ItemClickListener? = null
private val itemRV = 1
private val itemAD = 2
override fun getItemViewType(position: Int): Int {
return if (position % 5 == 0) {
itemRV
} else {
itemAD
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) {
// Create an ad view holder
return if (viewType == itemAD) {
val v = LayoutInflater.from(mCtx).inflate(R.layout.ad_item, parent, false)
AdViewHolder(v)
} else {
val v = LayoutInflater.from(mCtx).inflate(R.layout.rv_item, parent, false)
MyViewHolder(v)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when(holder) {
// This is where you want to load/show the ad.
is AdViewHolder -> {
val adLoader = AdLoader.Builder(mCtx, "ca-app-pub-5544923106349792~1702536043")
.forUnifiedNativeAd { ad : UnifiedNativeAd ->
// Show the ad.
}
.withAdListener(object : AdListener() {
override fun onAdFailedToLoad(errorCode: Int) {
// Handle the failure by logging, altering the UI, and so on.
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
.build())
.build()
adLoader.loadAd(AdRequest.Builder().build())
}
is MyViewHolder -> {
val product = myList[holder.adapterPosition]
holder.tvTitle.text = product.itemName
}
}
}
override fun getItemCount(): Int {
return myList.size
}
inner class MyViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView), View.OnClickListener {
var tvTitle: TextView = itemView.tv_title
}
inner class AdViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView
.ViewHolder(itemView), View.OnClickListener {
// do any ad setup
}
interface ItemClickListener {
fun onItemClick(view: View, position: Int)
}
}

How to insert ProgressBar for loading more items in Recycler View?

I am obtaining 50 items per API call and inserting it into RecyclerView. When I scroll to the end, I add a ProgressBar inside the last existing ViewHolder and displaying it instead of having a separate ViewHolder for the ProgressBar. But there are some performance issues. Can anybody tell me if my approach is right? I would appreciate if anyone can suggest me a correct method to do it
Here Is the Adapter class
class AdapterClass(private var mList: MutableList<Any>?,
var mContext: Context
) : RecyclerView.Adapter< RecyclerView.ViewHolder>() {
private val DATA = 1
private val LOADER = 2
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == DATA) {
val view = LayoutInflater.from(parent.context).inflate(R.layout.view, parent, false)
Items(view)
} else {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item_loadmore, parent, false)
ViewHolderLoader(view)
}
}
override fun getItemCount(): Int {
return mList?.size?:0
}
override fun getItemViewType(position: Int): Int {
return when {
mList?.get(position) is DataItem -> DATA
mList?.get(position) is Loader -> LOADER
else -> DATA
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
if (holder?.itemViewType == DATA) {
val viewHolder = holder as AdapterClass.Items
val dataItem = mList?.get(position) as DataItem
viewHolder.bindData(dataItem)
}
}
inner class Items(itemView: View?) : RecyclerView.ViewHolder(itemView) {
#SuppressLint("SetTextI18n")
fun bindData(data: DataItem?) {
}
inner class ViewHolderLoader(itemView: View) : RecyclerView.ViewHolder(itemView)
}
For showing loader in recyler view in last you just have to create another view item for loader and override getItemViewType method of Recycler View , and pass this progress bar view ID ,based on your condition.
#Override
public int getItemViewType(int position) {
if (TextUtils.isEmpty(YOUR_LIST(position))) {
return PROGRESS_VIEW;
} else {
return ITEM_VIEW;
}
}
a progress bar during the successive API call, which can be achieved using the android paging library.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == DATA_VIEW_TYPE) NewsViewHolder.create(parent) else ListFooterViewHolder.create(retry, parent)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (getItemViewType(position) == DATA_VIEW_TYPE)
(holder as NewsViewHolder).bind(getItem(position))
else (holder as ListFooterViewHolder).bind(state)
}
Based on the view type we can add the progress bar(indicate the next API call) at the end of the list.
please refer the https://medium.com/#sharmadhiraj.np/android-paging-library-step-by-step-implementation-guide-75417753d9b9

Categories

Resources