RecyclerView, ViewPager2, FragmentStateAdapter
Biblioteka oferuje trzy adaptery do automatycznego wstrzykiwania reklam w listy i pagery:
| Adapter | Użycie | Typ reklam |
|---|---|---|
AdInjectedAdapter |
RecyclerView (listy) | Banner, Native |
AdInjectedPagerAdapter |
ViewPager2 (strony z ViewHolder) | Banner, Native |
AdInjectedFragmentStateAdapter |
ViewPager2 (strony jako Fragment) | Banner, Native |
AdGate (gdy reklamy są zablokowane, sloty znikają)Wszystkie adaptery przyjmują obiekt AdsSpec do konfiguracji pozycji i typów reklam:
AdsSpec(
zoneName = "feed", // Strefa konfiguracji reklam
callbackName = "default", // Callback dla eventów
firstAdPosition = 5, // Pierwsza reklama po 5 elementach contentu
interval = 8, // Kolejne reklamy co 8 elementów
bannerAdSize = AdViewContainer.BANNER, // Rozmiar bannera (jeśli używasz)
binder = myNativeBinder, // Binder dla reklam natywnych (opcjonalny)
adKindForSlot = { slotIndex -> // Typ reklamy per slot
if (slotIndex % 2 == 0) AdKind.NATIVE else AdKind.BANNER
}
)
| Parametr | Typ | Domyślnie | Opis |
|---|---|---|---|
zoneName | String | "default" | Nazwa strefy konfiguracji reklam |
callbackName | String | "default" | Nazwa callbacka dla eventów |
firstAdPosition | Int | 4 | Po ilu elementach contentu pierwsza reklama |
interval | Int | 6 | Co ile elementów contentu kolejna reklama |
bannerAdSize | Int | BANNER | Rozmiar bannera (z AdViewContainer) |
binder | NativeAdViewBinder? | null | Customowy binder dla reklam natywnych |
adKindForSlot | (Int) → AdKind | { NATIVE } | Funkcja określająca typ reklamy dla slotu |
Adapter dla standardowego RecyclerView z automatycznym wstrzykiwaniem reklam między elementami listy.
class FeedAdapter(ctx: Context) : AdInjectedAdapter<FeedItem, RecyclerView.ViewHolder>(
ctx,
AdsSpec(
zoneName = "feed",
callbackName = "default",
firstAdPosition = 5, // Pierwsza reklama po 5 itemach
interval = 8, // Kolejne co 8 itemów contentu
bannerAdSize = AdViewContainer.LARGE_BANNER,
adKindForSlot = { idx ->
// Parzyste sloty → native, nieparzyste → banner
if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
}
)
) {
companion object {
private const val TYPE_TEXT = 1
private const val TYPE_PHOTO = 2
}
// Mapowanie contentu na viewType (tylko dla własnych typów!)
override fun getContentItemViewType(contentPosition: Int): Int =
when (getContentItem(contentPosition)) {
is FeedItem.Text -> TYPE_TEXT
is FeedItem.Photo -> TYPE_PHOTO
}
override fun onCreateContentViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
TYPE_TEXT -> TextVH(inflater.inflate(R.layout.item_text, parent, false))
TYPE_PHOTO -> PhotoVH(inflater.inflate(R.layout.item_photo, parent, false))
else -> error("Unknown viewType=$viewType")
}
}
override fun onBindContentViewHolder(
holder: RecyclerView.ViewHolder,
item: FeedItem,
contentPosition: Int
) {
when (holder) {
is TextVH -> holder.bind(item as FeedItem.Text)
is PhotoVH -> holder.bind(item as FeedItem.Photo)
}
}
}
val adapter = FeedAdapter(requireContext())
binding.recyclerView.adapter = adapter
// Załaduj dane
adapter.submitList(listOf(
FeedItem.Text("Tytuł 1", "Treść..."),
FeedItem.Photo("Zdjęcie 1", R.drawable.photo1),
FeedItem.Text("Tytuł 2", "Treść..."),
// ... więcej itemów
))
// Ręczny refresh reklam (np. po zmianie reguł AdGate)
adapter.refreshAds()
| Metoda | Opis |
|---|---|
submitList(items: List<T>) | Ustawia nową listę contentu i przebudowuje sloty reklam |
getContentItem(position: Int): T | Pobiera element contentu na danej pozycji |
refreshAds() | Ręczne odświeżenie slotów reklam (po zmianach AdGate) |
Adapter dla ViewPager2 używający ViewHolderów. Idealny do galerii, stories, onboardingu.
class StoryPagerAdapter(ctx: Context) : AdInjectedPagerAdapter<Story, RecyclerView.ViewHolder>(
ctx,
AdsSpec(
zoneName = "stories",
callbackName = "default",
firstAdPosition = 3, // Pierwsza reklama na stronie #3
interval = 4, // Kolejne co 4 strony contentu
adKindForSlot = { idx ->
if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
}
)
) {
companion object {
private const val TYPE_TEXT = 1
private const val TYPE_VIDEO = 2
}
override fun getContentItemViewType(contentPosition: Int): Int =
when (getContentItem(contentPosition)) {
is Story.Text -> TYPE_TEXT
is Story.Video -> TYPE_VIDEO
}
override fun onCreateContentViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
TYPE_TEXT -> TextStoryVH(inflater.inflate(R.layout.page_text_story, parent, false))
TYPE_VIDEO -> VideoStoryVH(inflater.inflate(R.layout.page_video_story, parent, false))
else -> error("Unknown viewType=$viewType")
}
}
override fun onBindContentViewHolder(
holder: RecyclerView.ViewHolder,
item: Story,
contentPosition: Int
) {
when (holder) {
is TextStoryVH -> holder.bind(item as Story.Text)
is VideoStoryVH -> holder.bind(item as Story.Video)
}
}
}
val adapter = StoryPagerAdapter(requireContext())
binding.viewPager.adapter = adapter
adapter.submitList(listOf(
Story.Text("Historia 1", "Treść..."),
Story.Video("Film 1", videoUri),
// ...
))
Adapter dla ViewPager2 używający Fragmentów. Zalecany dla złożonych stron wymagających własnego lifecycle.
class OnboardingPagerAdapter(
hostFragment: Fragment
) : AdInjectedFragmentStateAdapter<OnboardingPage>(
hostFragment,
AdsSpec(
zoneName = "onboarding",
callbackName = "default",
firstAdPosition = 3,
interval = 4,
adKindForSlot = { idx ->
if (idx % 2 == 0) AdKind.NATIVE else AdKind.BANNER
}
)
) {
// Twórz fragment dla danego elementu contentu
override fun createContentFragment(item: OnboardingPage, contentPosition: Int): Fragment =
when (item) {
is OnboardingPage.Welcome -> WelcomeFragment.newInstance(item.title)
is OnboardingPage.Feature -> FeatureFragment.newInstance(item.title, item.description)
is OnboardingPage.Final -> FinalFragment.newInstance()
}
// (Opcjonalnie) Stabilne ID dla lepszych animacji i zachowania stanu
override fun stableId(item: OnboardingPage): Long =
item.title.hashCode().toLong()
}
class OnboardingFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Przekaż `this` (Fragment) jako host
val adapter = OnboardingPagerAdapter(this)
binding.viewPager.adapter = adapter
adapter.submitList(listOf(
OnboardingPage.Welcome("Witaj!"),
OnboardingPage.Feature("Funkcja 1", "Opis..."),
OnboardingPage.Feature("Funkcja 2", "Opis..."),
OnboardingPage.Final()
))
}
}
Biblioteka dostarcza gotowe fragmenty dla reklam:
BannerAdFragment - pełnoekranowy fragment z centowanym banneremNativeAdFragment - pełnoekranowy fragment z reklamą natywnąSą one automatycznie używane przez adapter - nie musisz ich tworzyć ręcznie.
Wszystkie adaptery automatycznie reagują na zmiany stanu AdGate:
// Zablokuj reklamy globalnie (np. użytkownik premium)
AdGate.block("premium_user")
// Odblokuj
AdGate.unblock("premium_user")
// Lokalna reguła dla strefy (np. blokuj tylko native)
AdGate.addLocalRule("feed") { request ->
// true = pozwól, false = zablokuj
request.format != AdGate.Format.NATIVE
}
// ⚠️ Po zmianie lokalnych reguł wywołaj refresh!
adapter.refreshAds()
Zmiany globalne AdGate (block/unblock) są obsługiwane automatycznie przez adaptery.
Tylko po addLocalRule() / removeLocalRule() musisz wywołać adapter.refreshAds().
AdGate.state (StateFlow)blocked zmieni się na true → wszystkie sloty reklam znikająblocked zmieni się na false → sloty są ponownie dodawane