필자는 틴더 앱의 스와이프와 비슷한 기능을 구현했다.
광고 또한 스와이프로 넘기면서 볼 수 있게 만들고 싶었다.
그래서 선택한것이 native ad이다.
native ad는 전면광고나 배너광고와는 달리 광고의 데이터만 받고 보여주는 레이아웃은 커스텀하게 짤 수 있다.
먼저 광고 스와이프를 구현하기 위해서는 카드(스와이프하는 객체)들 사이에 광고를 넣어야했다.
게다가 카드와 광고는 레이아웃을 다르게하고 싶었다.
그러기 위해서 두가지 타입의 뷰홀더를 만들었다.
class CardViewHolder(
val layout: View,
val swipImg: ImageView, val tv_swipe_title: TextView, val tv_swipe_userName: TextView, val tv_swipe_content: TextView,
val btn_swipe_report: View
) : RecyclerView.ViewHolder(layout)
class AdViewHolder(val view: UnifiedNativeAdView) : RecyclerView.ViewHolder(view) {
init {
view.mediaView = view.mv_ad
// Set other ad assets.
view.headlineView = view.tv_ad_headline
view.bodyView = view.tv_ad_body
view.callToActionView = view.btn_ad_learnMore
view.iconView = view.iv_ad_icon
view.priceView = view.tv_ad_price
view.starRatingView = view.rb_ad_stars
view.storeView = view.tv_ad_store
view.advertiserView = view.tv_ad_advertiser
}
CardViewHolder는 말그대로 카드레이아웃을 가지고있는 뷰홀더이고
AdViewHolder는 광고 레이아웃을 가지고 있는 뷰홀더이다.
private val DATA_VIEW_TYPE = 0
private val NATIVE_AD_VIEW_TYPE = 1
override fun getItemViewType(position: Int): Int {
return if (mDataset[position].isAd) NATIVE_AD_VIEW_TYPE else DATA_VIEW_TYPE
}
getItemViewType은 onBindViewHolder나 onCreateViewHolder 함수에서 참고할 뷰 홀더타입을 정해주는 함수다.
만약 현재 참고할 데이터가 광고라면 광고타입을, 아니면 데이터 뷰 타입을 리턴한다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
DATA_VIEW_TYPE -> {
val layout: View = LayoutInflater.from(parent.context).inflate(R.layout.swipe_item, parent, false)
CardViewHolder(
layout, layout.swipImg, layout.tv_swipe_title, layout.tv_swipe_userName, layout.tv_swipe_content,
layout.btn_swipe_report
)
}
NATIVE_AD_VIEW_TYPE -> {
AdViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.swipe_ad, parent, false) as UnifiedNativeAdView)
}
else -> {
AdViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.swipe_ad, parent, false) as UnifiedNativeAdView)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
DATA_VIEW_TYPE -> {
val cardViewHolder = holder as CardViewHolder
setCardData(mDataset[position], cardViewHolder)
}
NATIVE_AD_VIEW_TYPE -> {
val adViewHolder = holder as AdViewHolder
mAdQueue.peek()?.let { setCardAdData(it, adViewHolder.view) }
}
}
}
이 뷰홀더 타입을 참고해서 뷰를 inflate하거나 데이터를 바인딩한다.
즉, 뷰홀더 타입에 따라서 뷰를 다르게 생성하고, 다르게 데이터를 바인딩한다.
여기까지는 좋다. 그런데 이것과는 별개로 문제가 하나 있었다.
광고를 요청하고 받아오는데는 체감상 5초정도의 간격이 있었다. 꽤나 오래걸리는 셈이다.
만약 광고를 요청하는 시점과 광고를 보여줘야하는 시점이 동일하다면 광고는 5초 후에나 보여줄 수 있을 것이다.
이 문제를 해결하기 위해 나는 queue를 썼다.
//광고데이터 큐
private val mAdQueue: Queue<UnifiedNativeAd> = LinkedList<UnifiedNativeAd>()
( 코틀린에서 큐는 인터페이스고, 연결리스트로 구현된다)
큐의 원소갯수가 3개보다 적을시에 광고서버에 광고를 5개씩 요청한다.
즉 어플리케이션이 실행이 되자마자 광고를 요청하고 광고를 보여줄 시점이 되면 큐에서 하나 꺼내서 보여준다.
그부분이 아래 코드이다.
if (mAdQueue.size <= AD_DATA_ABOUT_TO_EMPTY && !mCardAd.isLoading) {
loadAds()
}
when (getItemViewType(position)) {
DATA_VIEW_TYPE -> {
val cardViewHolder = holder as CardViewHolder
setCardData(mDataset[position], cardViewHolder)
}
NATIVE_AD_VIEW_TYPE -> {
val adViewHolder = holder as AdViewHolder
mAdQueue.peek()?.let { setCardAdData(it, adViewHolder.view) }
}
}
'안드로이드 앱개발' 카테고리의 다른 글
파일입출력2 : 저장소 (Android Studio Internal/External Storage) (0) | 2020.10.27 |
---|---|
파일입출력 1 : 쓰고 읽기 (File I/O in Kotlin) (0) | 2020.10.27 |
volley로 이미지 업로드 (volley multipart request) (0) | 2020.10.27 |
라이브러리는 이해하고 사용하자. (0) | 2020.10.27 |
액티비티(Activity)와 인텐트(Intent) (0) | 2020.10.27 |