I successfully added the AdMob native ads in my app, every 10 items it loads and show one ad, the problem is when I scroll out from it it's destroyed and the second list (10 items) load the second ad, and so on..., to understand issue see this video
this makes some delay in loading ads each time it depends of the internet speed of the user, so I wanna make the ad unit "space" in reclcerview not recycled when the user scrolled, I tried some methods like onViewRecycled
and holder.setIsRecyclable(false)
on bindViewHolder but neither of it is working
here's my Adapter class
class PostAdapter(
items: List<Item>, private val titleAndGridLayout: TitleAndGridLayout
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val items: List<Item>
private var nativeAd: NativeAd? = null
private val VIEW_TYPE_CONTENT = 1
private val VIEW_TYPE_AD_CARD_LAYOUT = 2
internal val VIEW_TYPE_AD_GRID_LAYOUT = 3
private var adsCnt = 3
var viewType = 0
set(value) {
field = value
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int {
return when (viewType) {
CARD, CARD_MAGAZINE -> {
if (position != 0 && position % 10 == 0) VIEW_TYPE_AD_CARD_LAYOUT else VIEW_TYPE_CONTENT
}
TITLE -> {
if ((position + 1) % 9 == 0 && (position + 1) != 1) VIEW_TYPE_AD_GRID_LAYOUT else VIEW_TYPE_CONTENT
}
GRID -> {
if ((position + 1) % 10 == 0 && (position + 1) != 1) VIEW_TYPE_AD_GRID_LAYOUT else VIEW_TYPE_CONTENT
}
else -> VIEW_TYPE_CONTENT
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val nativeAdRowBinding = AdUnifiedBinding.inflate(inflater, parent, false)
val nativeAdRowTitleGridBinding =
NativeAdRowTitleGridBinding.inflate(inflater, parent, false)
when (viewType) {
VIEW_TYPE_CONTENT -> {
when (this.viewType) {
CARD -> {
val cardLayoutBinding: CardLayoutBinding =
CardLayoutBinding.inflate(inflater, parent, false)
return CardViewHolder(cardLayoutBinding)
}
CARD_MAGAZINE -> {
val cardMagazineBinding: CardMagazineBinding =
CardMagazineBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return CardMagazineViewHolder(cardMagazineBinding)
}
TITLE -> {
val titleLayoutBinding: TitleLayoutBinding =
TitleLayoutBinding.inflate(inflater, parent, false)
return TitleViewHolder(titleLayoutBinding)
}
else -> {
val gridLayoutBinding: GridLayoutBinding =
GridLayoutBinding.inflate(inflater, parent, false)
return GridViewHolder(gridLayoutBinding)
}
}
}
VIEW_TYPE_AD_CARD_LAYOUT -> {
// view = inflater.inflate(R.layout.ad_unified, false)
return AdViewHolder(nativeAdRowBinding)
}
else -> {
// view = inflater.inflate(R.layout.ad_unified, false)
return AdViewHolderGrid(nativeAdRowTitleGridBinding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item: Item = items[position]
// val document = Jsoup.parse(item.content)
// val elements = document.select("img")
val intent = Intent(holder.itemView.context, DetailsActivity::class.java)
if (getItemViewType(position) == VIEW_TYPE_CONTENT) {
when (this.viewType) {
CARD -> if (holder is CardViewHolder) {
holder.bind(item)
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
CARD_MAGAZINE -> if (holder is CardMagazineViewHolder) {
holder.bind(item)
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
TITLE -> if (holder is TitleViewHolder) {
holder.bind(item)
if (position == itemCount - 1)
titleAndGridLayout.tellFragmentToGetItems("titleLayout")
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
GRID -> if (holder is GridViewHolder) {
holder.bind(item)
if (position == itemCount - 1) {
titleAndGridLayout.tellFragmentToGetItems("gridLayout")
}
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
}
} else if (getItemViewType(position) == VIEW_TYPE_AD_CARD_LAYOUT) {
if (holder is AdViewHolder) {
holder.bindAdData()
holder.setIsRecyclable(false)
}
} else {
holder as AdViewHolderGrid
holder.bindAdData()
holder.setIsRecyclable(false)
if (position == itemCount - 1) {
titleAndGridLayout.tellFragmentToGetItems("gridLayout")
}
}
}
// override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
// super.onViewRecycled(holder)
// if(holder is AdViewHolder){
// getItemViewType(holder.bindingAdapterPosition)
//
// }
// }
override fun getItemCount(): Int {
return items.size
}
// override fun getItemId(position: Int): Long {
// return position.toLong()
// }
class CardViewHolder(private val cardLayoutBinding: CardLayoutBinding) :
RecyclerView.ViewHolder(cardLayoutBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
cardLayoutBinding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(cardLayoutBinding.root).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardLayoutBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardLayoutBinding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
cardLayoutBinding.postDescription.text = document.text()
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardLayoutBinding.postDate.text = prettyTime.format(date)
}
}
class CardMagazineViewHolder(private val cardMagazineBinding: CardMagazineBinding) :
RecyclerView.ViewHolder(cardMagazineBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
cardMagazineBinding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardMagazineBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardMagazineBinding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardMagazineBinding.postDate.text = prettyTime.format(date)
}
}
inner class TitleViewHolder(private val binding: TitleLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
binding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
}
}
inner class GridViewHolder constructor(private val binding: GridLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
binding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
}
}
companion object {
private const val CARD = 0
private const val CARD_MAGAZINE = 1
private const val TITLE = 2
private const val GRID = 3
private const val TAG = "POST_ADAPTER"
}
init {
this.items = items
// this.fragment = fragment
// this.postViewModel = postViewModel
}
inner class AdViewHolder(private val binding: AdUnifiedBinding) :
RecyclerView.ViewHolder(binding.root) {
private val videoOptions = VideoOptions.Builder()
.setStartMuted(false)
.build()
private var adOptions = NativeAdOptions.Builder()
.setVideoOptions(videoOptions)
.build()
fun bindAdData() {
val builder =
AdLoader.Builder(binding.root.context, "ca-app-pub-3940256099942544/2247696110")
builder.forNativeAd { nativeAd ->
// OnUnifiedNativeAdLoadedListener implementation.
// If this callback occurs after the activity is destroyed, you must call
// destroy and return or you may get a memory leak.
this@PostAdapter.nativeAd = nativeAd
populateNativeAdView(nativeAd,binding)
}
builder.withNativeAdOptions(adOptions)
val adLoader = builder
.withAdListener(
object : AdListener() {
override fun onAdFailedToLoad(loadAdError: LoadAdError) {
if(adsCnt > 0) {
bindAdData()
}
else {
adsCnt-=1
}
val error =
"""
domain: ${loadAdError.domain}, code: ${loadAdError.code}, message: ${loadAdError.message}
""""
Toast.makeText(
binding.root.context,
"Failed to load native ad with error $error",
Toast.LENGTH_SHORT
)
.show()
}
}
)
.build()
adLoader.loadAd(AdRequest.Builder().build())
}
private fun populateNativeAdView(nativeAd: NativeAd, unifiedAdBinding: AdUnifiedBinding) {
val nativeAdView = unifiedAdBinding.root
// Set the media view.
nativeAdView.mediaView = unifiedAdBinding.adMedia
// Set other ad assets.
nativeAdView.headlineView = unifiedAdBinding.adHeadline
nativeAdView.bodyView = unifiedAdBinding.adBody
nativeAdView.callToActionView = unifiedAdBinding.adCallToAction
nativeAdView.iconView = unifiedAdBinding.adAppIcon
nativeAdView.priceView = unifiedAdBinding.adPrice
nativeAdView.starRatingView = unifiedAdBinding.adStars
nativeAdView.storeView = unifiedAdBinding.adStore
nativeAdView.advertiserView = unifiedAdBinding.adAdvertiser
// The headline and media content are guaranteed to be in every UnifiedNativeAd.
unifiedAdBinding.adHeadline.text = nativeAd.headline
nativeAd.mediaContent?.let { unifiedAdBinding.adMedia.setMediaContent(it) }
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.body == null) {
unifiedAdBinding.adBody.visibility = INVISIBLE
} else {
unifiedAdBinding.adBody.visibility = View.VISIBLE
unifiedAdBinding.adBody.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
unifiedAdBinding.adCallToAction.visibility = INVISIBLE
} else {
unifiedAdBinding.adCallToAction.visibility = View.VISIBLE
unifiedAdBinding.adCallToAction.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
unifiedAdBinding.adAppIcon.visibility = View.GONE
} else {
unifiedAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable)
unifiedAdBinding.adAppIcon.visibility = View.VISIBLE
}
if (nativeAd.price == null) {
unifiedAdBinding.adPrice.visibility = INVISIBLE
} else {
unifiedAdBinding.adPrice.visibility = View.VISIBLE
unifiedAdBinding.adPrice.text = nativeAd.price
}
if (nativeAd.store == null) {
unifiedAdBinding.adStore.visibility = INVISIBLE
} else {
unifiedAdBinding.adStore.visibility = View.VISIBLE
unifiedAdBinding.adStore.text = nativeAd.store
}
if (nativeAd.starRating == null) {
unifiedAdBinding.adStars.visibility = INVISIBLE
} else {
unifiedAdBinding.adStars.rating = nativeAd.starRating!!.toFloat()
unifiedAdBinding.adStars.visibility = View.VISIBLE
}
if (nativeAd.advertiser == null) {
unifiedAdBinding.adAdvertiser.visibility = INVISIBLE
} else {
unifiedAdBinding.adAdvertiser.text = nativeAd.advertiser
unifiedAdBinding.adAdvertiser.visibility = View.VISIBLE
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad.
nativeAdView.setNativeAd(nativeAd)
}
}
inner class AdViewHolderGrid(private val nativeAdRowTitleGridBinding: NativeAdRowTitleGridBinding) :
RecyclerView.ViewHolder(nativeAdRowTitleGridBinding.root) {
fun bindAdData() {
val adLoader =
AdLoader.Builder(
nativeAdRowTitleGridBinding.root.context,
"ca-app-pub-3940256099942544/2247696110"
)
.forNativeAd { nativeAd: NativeAd ->
this@PostAdapter.nativeAd = nativeAd
// populateNativeADView(nativeAd)
val styles =
NativeTemplateStyle.Builder().withMainBackgroundColor(
ColorDrawable(
ContextCompat.getColor(
nativeAdRowTitleGridBinding.root.context,
R.color.backgroundColor
)
)
).build()
val template: TemplateView = nativeAdRowTitleGridBinding.myTemplate
Log.d(TAG, "bindAdData: ${nativeAd.body}")
template.setStyles(styles)
template.setNativeAd(nativeAd)
}
.withAdListener(object : AdListener() {
override fun onAdClicked() {
super.onAdClicked()
Log.d(TAG, "onAdClicked: ")
}
override fun onAdClosed() {
super.onAdClosed()
Log.d(TAG, "onAdClosed: ")
}
override fun onAdLoaded() {
super.onAdLoaded()
Log.d(TAG, "onAdLoaded: ")
}
override fun onAdOpened() {
super.onAdOpened()
Log.d(TAG, "onAdOpened: ")
}
override fun onAdFailedToLoad(adError: LoadAdError) {
// Handle the failure by logging, altering the UI, and so on.
Toast.makeText(
nativeAdRowTitleGridBinding.root.context,
adError.message,
Toast.LENGTH_SHORT
).show()
Log.e(TAG, "onAdFailedToLoad: ${adError.cause.toString()}")
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
// Methods in the NativeAdOptions.Builder class can be
// used here to specify individual options settings.
.build()
).build()
adLoader.loadAd(AdRequest.Builder().build())
}
}
fun destroyNativeAd() {
nativeAd?.destroy()
Log.d(TAG, "destroyNativeAd: ${nativeAd?.body}")
}
}
The trick was in the following
replace this method
loadAd(AdManagerAdRequest adManagerAdRequest)
with this, the second parameter is for the number of ads requested for the first time to preload ads
loadAds(AdRequest adRequest, int maxNumberOfAds)
Okay, I contacted Google Mobile Ads SDK Technical Forum, and their replay was
You may check this guide which will teach you how to preload native ads.
and I followed the steps in the guide they provided to me, but unfortunately, I was facing some troubles with implementation, it contains many deprecated libs and tools, such as 'com.google.firebase:firebase-ads:15.0.0'
and the current version is 21.1.0, also classpath 'com.android.tools.build:gradle:3.3.1'
in build.gradle
(project module), I tried to update all of this but the app not working, then I tried to run the final code with minimum acceptable libs and it's works and the ads cached successfully, I requested from them to update this guide or send another one with the last version of Admob ads,
thier replied
Aside from what my colleague's implementation guide provided, there is no other sample app that shows this kind of implementation. You can then try to recreate one and have this as a reference for it. Kindly note that it's important that publishers not keep old ads around too long without displaying them. Any ad objects that have been held for longer than an hour without being displayed should be discarded and replaced with new ads from a new request.
then I decided to discover the issue by myself and after spending a lot of hours trying to solve this issue, the last result I reached is I was able to implement the sample old work (as reference) with my project but and I see the first ad is cached successfully, but unfortunately, all next native ads in recyclerView not showing when scrolling and I got this in logcat
2022-08-07 10:36:34.226 3498-3498/com.mml.dummyapp_kotlin D/DynamitePackage: Instantiating com.google.android.gms.ads.ChimeraNativeAdViewDelegateCreatorImpl
2022-08-07 10:36:34.258 3498-3498/com.mml.dummyapp_kotlin D/DynamitePackage: Instantiating com.google.android.gms.ads.ChimeraNativeAdViewDelegateCreatorImpl
2022-08-07 10:36:38.006 3498-3498/com.mml.dummyapp_kotlin D/DynamitePackage: Instantiating com.google.android.gms.ads.ChimeraNativeAdViewDelegateCreatorImpl
2022-08-07 10:36:38.023 3498-3498/com.mml.dummyapp_kotlin D/DynamitePackage: Instantiating com.google.android.gms.ads.ChimeraNativeAdViewDelegateCreatorImpl
2022-08-07 10:36:44.094 3498-3498/com.mml.dummyapp_kotlin D/DynamitePackage: Instantiating com.google.android.gms.ads.ChimeraNativeAdViewDelegateCreatorImpl
2022-08-07 10:36:48.596 3498-3498/com.mml.dummyapp_kotlin I/chatty: uid=10428(com.mml.dummyapp_kotlin) identical 4 lines
I replied to their email with the current result and issue, so I'll post my result here as a solution until they replied o my last email
AdViewHolder
with a new file and update it like thisclass AdViewHolder(private val binding: AdUnifiedBinding) :
RecyclerView.ViewHolder(binding.root) {
val adView get() = binding
}
MutableList<Any>
instead of List<Item>
and some other changes on
class PostAdapter(
items: MutableList<Any>, private val titleAndGridLayout: TitleAndGridLayout
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val items: MutableList<Any>
// private var nativeAd: NativeAd? = null
val VIEW_TYPE_CONTENT = 1
val VIEW_TYPE_AD_CARD_LAYOUT = 2
val VIEW_TYPE_AD_GRID_LAYOUT = 3
private var adsCnt = 3
var viewType = 0
set(value) {
field = value
notifyDataSetChanged()
}
// var itemViewType = 0
// set(value) {
// field = value
// notifyDataSetChanged()
// }
override fun getItemViewType(position: Int): Int {
// val recyclerViewItem:Any = this.items[position]
return when (viewType) {
CARD, CARD_MAGAZINE -> {
if ( position != 0 && position % 10 == 0) VIEW_TYPE_AD_CARD_LAYOUT else VIEW_TYPE_CONTENT
}
TITLE -> {
if ( (position + 1) % 9 == 0 && (position + 1) != 1) VIEW_TYPE_AD_GRID_LAYOUT else VIEW_TYPE_CONTENT
}
GRID -> {
if ( (position + 1) % 10 == 0 && (position + 1) != 1) VIEW_TYPE_AD_GRID_LAYOUT else VIEW_TYPE_CONTENT
}
else -> VIEW_TYPE_CONTENT
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val nativeAdRowTitleGridBinding =
NativeAdRowTitleGridBinding.inflate(inflater, parent, false)
when (viewType) {
VIEW_TYPE_CONTENT -> {
when (this.viewType) {
CARD -> {
val cardLayoutBinding: CardLayoutBinding =
CardLayoutBinding.inflate(inflater, parent, false)
return CardViewHolder(cardLayoutBinding)
}
CARD_MAGAZINE -> {
val cardMagazineBinding: CardMagazineBinding =
CardMagazineBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return CardMagazineViewHolder(cardMagazineBinding)
}
TITLE -> {
val titleLayoutBinding: TitleLayoutBinding =
TitleLayoutBinding.inflate(inflater, parent, false)
return TitleViewHolder(titleLayoutBinding)
}
else -> {
val gridLayoutBinding: GridLayoutBinding =
GridLayoutBinding.inflate(inflater, parent, false)
return GridViewHolder(gridLayoutBinding)
}
}
}
VIEW_TYPE_AD_CARD_LAYOUT -> {
// view = inflater.inflate(R.layout.ad_unified, false)
val nativeAdRowBinding = AdUnifiedBinding.inflate(inflater, parent, false)
return AdViewHolder(nativeAdRowBinding)
}
else -> {
// view = inflater.inflate(R.layout.ad_unified, false)
return AdViewHolderGrid(nativeAdRowTitleGridBinding)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var item: Item? = null
if (items[position] is Item) {
item = items[position] as Item
}
// val document = Jsoup.parse(item.content)
// val elements = document.select("img")
val intent = Intent(holder.itemView.context, DetailsActivity::class.java)
if (getItemViewType(position) == VIEW_TYPE_CONTENT) {
when (this.viewType) {
CARD -> if (holder is CardViewHolder) {
if (item != null) {
holder.bind(item)
}
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
CARD_MAGAZINE -> if (holder is CardMagazineViewHolder) {
if (item != null) {
holder.bind(item)
}
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
TITLE -> if (holder is TitleViewHolder) {
if (item != null) {
holder.bind(item)
}
if (position == itemCount - 1)
titleAndGridLayout.tellFragmentToGetItems("titleLayout")
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
GRID -> if (holder is GridViewHolder) {
if (item != null) {
holder.bind(item)
}
if (position == itemCount - 1) {
titleAndGridLayout.tellFragmentToGetItems("gridLayout")
}
holder.itemView.setOnClickListener { view: View ->
intent.putExtra("postItem", item)
view.context.startActivity(intent)
}
}
}
} else if (getItemViewType(position) == VIEW_TYPE_AD_CARD_LAYOUT) {
if (holder is AdViewHolder) {
val adItem: Any = items[position]
if (adItem is NativeAd) {
// val nativeAd = items[position] as NativeAd
populateNativeAdView(adItem, holder.adView)
}
// holder.setIsRecyclable(false)
}
} else {
holder as AdViewHolderGrid
holder.bindAdData()
// holder.setIsRecyclable(false)
if (position == itemCount - 1) {
titleAndGridLayout.tellFragmentToGetItems("gridLayout")
}
}
}
//
// override fun onFailedToRecycleView(holder: RecyclerView.ViewHolder): Boolean {
// return if (holder is AdViewHolder) {
// // Don't recycle the ad view, keep it around
// true
// } else {
// super.onFailedToRecycleView(holder)
// }
// }
// override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
// super.onViewRecycled(holder)
// if(holder is AdViewHolder){
// getItemViewType(holder.bindingAdapterPosition)
//
// }
// }
override fun getItemCount(): Int {
return items.size
}
class CardViewHolder(private val cardLayoutBinding: CardLayoutBinding) :
RecyclerView.ViewHolder(cardLayoutBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
cardLayoutBinding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(cardLayoutBinding.root).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardLayoutBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardLayoutBinding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
cardLayoutBinding.postDescription.text = document.text()
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardLayoutBinding.postDate.text = prettyTime.format(date)
}
}
class CardMagazineViewHolder(private val cardMagazineBinding: CardMagazineBinding) :
RecyclerView.ViewHolder(cardMagazineBinding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
var date: Date? = Date()
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault())
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
cardMagazineBinding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(cardMagazineBinding.postImage)
} catch (e: IndexOutOfBoundsException) {
cardMagazineBinding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
try {
date = format.parse(item.published)
} catch (e: ParseException) {
e.printStackTrace()
}
val prettyTime = PrettyTime()
cardMagazineBinding.postDate.text = prettyTime.format(date)
}
}
inner class TitleViewHolder(private val binding: TitleLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
binding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
}
}
inner class GridViewHolder constructor(private val binding: GridLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Item) {
val document = Jsoup.parse(item.content)
val elements = document.select("img")
// Log.e("IMAGE", document.getAllElements().select("img").get(0).attr("src"));
binding.postTitle.text = item.title
try {
// Log.e("IMAGE", elements[0].attr("src"))
Glide.with(itemView.context).load(elements[0].attr("src"))
.transition(DrawableTransitionOptions.withCrossFade(600))
.placeholder(R.drawable.loading_animation)
.error(R.drawable.no_image)
.into(binding.postImage)
} catch (e: IndexOutOfBoundsException) {
binding.postImage.setImageResource(R.drawable.no_image)
// Log.e(TAG, e.toString())
}
}
}
companion object {
private const val CARD = 0
private const val CARD_MAGAZINE = 1
private const val TITLE = 2
private const val GRID = 3
private const val TAG = "POST_ADAPTER"
}
init {
this.items = items
// this.fragment = fragment
// this.postViewModel = postViewModel
}
private fun populateNativeAdView(nativeAd: NativeAd, unifiedAdBinding: AdUnifiedBinding) {
val nativeAdView = unifiedAdBinding.root
// Set the media view.
nativeAdView.mediaView = unifiedAdBinding.adMedia
// Set other ad assets.
nativeAdView.headlineView = unifiedAdBinding.adHeadline
nativeAdView.bodyView = unifiedAdBinding.adBody
nativeAdView.callToActionView = unifiedAdBinding.adCallToAction
nativeAdView.iconView = unifiedAdBinding.adAppIcon
nativeAdView.priceView = unifiedAdBinding.adPrice
nativeAdView.starRatingView = unifiedAdBinding.adStars
nativeAdView.storeView = unifiedAdBinding.adStore
nativeAdView.advertiserView = unifiedAdBinding.adAdvertiser
// The headline and media content are guaranteed to be in every UnifiedNativeAd.
unifiedAdBinding.adHeadline.text = nativeAd.headline
nativeAd.mediaContent?.let { unifiedAdBinding.adMedia.setMediaContent(it) }
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.body == null) {
unifiedAdBinding.adBody.visibility = INVISIBLE
} else {
unifiedAdBinding.adBody.visibility = View.VISIBLE
unifiedAdBinding.adBody.text = nativeAd.body
}
if (nativeAd.callToAction == null) {
unifiedAdBinding.adCallToAction.visibility = INVISIBLE
} else {
unifiedAdBinding.adCallToAction.visibility = View.VISIBLE
unifiedAdBinding.adCallToAction.text = nativeAd.callToAction
}
if (nativeAd.icon == null) {
unifiedAdBinding.adAppIcon.visibility = View.GONE
} else {
unifiedAdBinding.adAppIcon.setImageDrawable(nativeAd.icon?.drawable)
unifiedAdBinding.adAppIcon.visibility = View.VISIBLE
}
if (nativeAd.price == null) {
unifiedAdBinding.adPrice.visibility = INVISIBLE
} else {
unifiedAdBinding.adPrice.visibility = View.VISIBLE
unifiedAdBinding.adPrice.text = nativeAd.price
}
if (nativeAd.store == null) {
unifiedAdBinding.adStore.visibility = INVISIBLE
} else {
unifiedAdBinding.adStore.visibility = View.VISIBLE
unifiedAdBinding.adStore.text = nativeAd.store
}
if (nativeAd.starRating == null) {
unifiedAdBinding.adStars.visibility = INVISIBLE
} else {
unifiedAdBinding.adStars.rating = nativeAd.starRating!!.toFloat()
unifiedAdBinding.adStars.visibility = View.VISIBLE
}
if (nativeAd.advertiser == null) {
unifiedAdBinding.adAdvertiser.visibility = INVISIBLE
} else {
unifiedAdBinding.adAdvertiser.text = nativeAd.advertiser
unifiedAdBinding.adAdvertiser.visibility = View.VISIBLE
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad.
nativeAdView.setNativeAd(nativeAd)
}
inner class AdViewHolderGrid(private val nativeAdRowTitleGridBinding: NativeAdRowTitleGridBinding) :
RecyclerView.ViewHolder(nativeAdRowTitleGridBinding.root) {
fun bindAdData() {
val adLoader =
AdLoader.Builder(
nativeAdRowTitleGridBinding.root.context,
"ca-app-pub-3940256099942544/2247696110"
)
.forNativeAd { nativeAd: NativeAd ->
// this@PostAdapter.nativeAd = nativeAd
// populateNativeADView(nativeAd)
val styles =
NativeTemplateStyle.Builder().withMainBackgroundColor(
ColorDrawable(
ContextCompat.getColor(
nativeAdRowTitleGridBinding.root.context,
R.color.backgroundColor
)
)
).build()
val template: TemplateView = nativeAdRowTitleGridBinding.myTemplate
Log.d(TAG, "bindAdData: ${nativeAd.body}")
template.setStyles(styles)
template.setNativeAd(nativeAd)
}
.withAdListener(object : AdListener() {
override fun onAdClicked() {
super.onAdClicked()
Log.d(TAG, "onAdClicked: ")
}
override fun onAdClosed() {
super.onAdClosed()
Log.d(TAG, "onAdClosed: ")
}
override fun onAdLoaded() {
super.onAdLoaded()
Log.d(TAG, "onAdLoaded: ")
}
override fun onAdOpened() {
super.onAdOpened()
Log.d(TAG, "onAdOpened: ")
}
override fun onAdFailedToLoad(adError: LoadAdError) {
// Handle the failure by logging, altering the UI, and so on.
Toast.makeText(
nativeAdRowTitleGridBinding.root.context,
adError.message,
Toast.LENGTH_SHORT
).show()
Log.e(TAG, "onAdFailedToLoad: ${adError.cause.toString()}")
}
})
.withNativeAdOptions(
NativeAdOptions.Builder()
// Methods in the NativeAdOptions.Builder class can be
// used here to specify individual options settings.
.build()
).build()
adLoader.loadAd(AdRequest.Builder().build())
}
}
}
private lateinit var adLoader: AdLoader
// List of MenuItems and native ads that populate the RecyclerView.
private val mRecyclerViewItems: MutableList<Any> = ArrayList()
// List of native ads that have been successfully loaded.
private val mNativeAds: MutableList<NativeAd> = ArrayList()
val NUMBER_OF_ADS = 5
then I declared a function to load native ads like this
private fun loadNativeAds() {
val builder =
AdLoader.Builder(requireContext(), "ca-app-pub-3940256099942544/2247696110")
adLoader =
builder.forNativeAd { nativeAd -> // A native ad loaded successfully, check if the ad loader has finished loading
// and if so, insert the ads into the list.
// A native ad loaded successfully, check if the ad loader has finished loading
// and if so, insert the ads into the list.
// val localNativeAd = nativeAd
mNativeAds.add(nativeAd)
if (!adLoader.isLoading) {
mRecyclerViewItems.add(nativeAd)
}
}.withAdListener(
object : AdListener() {
override fun onAdFailedToLoad(p0: LoadAdError) {
super.onAdFailedToLoad(p0)
// A native ad failed to load, check if the ad loader has finished loading
// and if so, insert the ads into the list.
Log.e(
"MainActivity", "The previous native ad failed to load. Attempting to"
+ " load another."
)
if (!adLoader.isLoading) {
// insertAdsInMenuItems()
loadNativeAds()
}
}
}).build()
// Load the Native ads.
adLoader.loadAds(AdRequest.Builder().build(), NUMBER_OF_ADS)
}
and called it when the Internet is available
networkListener.checkNetworkAvailability(requireContext()).collect { stats ->
Log.d(TAG, "networkListener: $stats")
postViewModel.networkStats = stats
postViewModel.showNetworkStats()
if (stats && savedInstanceState == null) {
requestApiData()
requestHomeBanner()
loadNativeAds()
I'll waiting their replay on last issue and If they discover the cause I'll post update with solution