커스터마이징
전체 테마
Buzzvil SDK에서 제공하는 기본 UI를 사용해 광고 지면이나 CTA의 색상, 아이콘 이미지 등 테마를 변경할 수 있습니다.
주요 색상
Buzzvil SDK에서 제공하는 UI 중 다이얼로그의 버튼 색상, 바텀 시트 UI의 확인 버튼 등 앱에 연동한 모든 지면 내 주요 UI의 색상(Primary color)을 변경할 수 있습니다.
Primary 색상 변경을 위해 아래와 같이 색상 리소스를 추가하세요.
✅ 중요
리소스 이름은buzzvil_benefit_hub_brand_primary
,buzzvil_benefit_hub_brand_primary_light
를 그대로 사용해야 합니다.
✏️ 참고
primary light 색상은 primary 색상에 투명도 60%를 적용한 값을 권장합니다. 예를 들어, primary 색상값이#FF0000
이라면 primary light 색상값은#99FF0000
를 사용하세요.
<!-- res/values/colors.xml -->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="your_primary_color">#FF0000</color>
<color name="your_primary_light_color">#99FF0000</color>
<color name="buzzvil_benefit_hub_brand_primary">@color/your_primary_color</color>
<color name="buzzvil_benefit_hub_brand_primary_light">@color/your_primary_light_color</color>
</resources>
리워드 아이콘
Buzzvil SDK에서 제공하는 UI 중 CTA 버튼, 팝 버튼, 유저 프로필 입력 배너 등의 아이콘을 변경할 수 있습니다.
다음은 BuzzAdTheme
에 rewardIcon
를 추가해 리워드 아이콘을 변경하는 예시입니다. 각 속성 값에서 커스터마이징할 수 있는 UI에 대해서는 아래의 표를 참고하세요.
속성 값 | 커스터마이징이 가능한 UI |
---|---|
rewardIcon |
|
participatedIcon |
|
✏️ 참고
테마를 변경하려면BuzzvilSdk.initialize()
을 호출해 Buzzvil SDK를 초기화하기 전에BuzzAdTheme.setGloabalTheme()
을 호출해야 합니다.
import com.buzzvil.buzzad.benefit.BuzzAdBenefitConfig
import com.buzzvil.buzzad.benefit.presentation.BuzzAdTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
val buzzAdTheme = BuzzAdTheme()
.rewardIcon(R.drawable.your_reward_icon)
.participatedIcon(R.drawable.your_participated_icon)
BuzzAdTheme.setGlobalTheme(buzzAdTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
CTA 버튼 자체 구현하기
Buzzvil SDK에서 기본으로 제공하는 UI를 사용하지 않고 CTA 버튼을 자체적으로 구현할 수 있습니다. 자체적으로 구현한 CTA 버튼을 베네핏허브와 네이티브에서 사용할 수 있습니다.
CTA 버튼을 직접 구현하려면 다음의 절차를 따르세요.
뷰 레이아웃(
view_customized_cta.xml
)을 구현하세요. 다음은 레이아웃 리소스로 뷰 레이아웃을 구현한 예시입니다.<!-- view_customized_cta.xml -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryDark">
<!-- CTA 버튼의 텍스트 -->
<TextView
android:id="@+id/textCta"/>
<!-- CTA 버튼의 아이콘 이미지 -->
<ImageView
android:id="@+id/imageReward"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="아이콘 이미지 리소스"/>
<!-- CTA 버튼의 리워드 텍스트 -->
<TextView
android:id="@+id/textReward"/>
</LinearLayout>CtaView
클래스를 상속하는 서브클래스인CustomCtaView
를 생성하세요.CtaView
클래스에서 사용하던 CTA View 레이아웃을inflate
하도록 구현하세요.유저의 광고 참여 상태에 따라 호출되는 아래의 오버라이딩 메소드에서 CTA 버튼의 아이콘 이미지, 텍스트, 리워드 값을 구현하세요.
renderViewParticipatingState
: 유저가 광고에 참여 중인 상태 (예: 액션형 광고의 랜딩 페이지에 진입한 상태)renderViewParticipatedState
: 유저가 광고 참여를 완료한 상태renderViewRewardAvailableState
: 유저가 아직 광고에 참여하지 않은 상태renderViewRewardNotAvailableState
: 유저가 획득할 리워드가 없는 광고import com.buzzvil.buzzad.benefit.presentation.theme.CtaView
class CustomCtaView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
CtaView(context, attrs, defStyleAttr) {
private val rewardImageView: ImageView
private val rewardTextView: TextView
private val ctaTextView: TextView
constructor(context: Context) : this(
context,
null
)
constructor(context: Context, attrs: AttributeSet?) : this(
context,
attrs,
0
)
init {
inflate(getContext(), R.layout.custom_cta_view, this)
rewardImageView = findViewById(R.id.imageReward)
rewardTextView = findViewById(R.id.textReward)
ctaTextView = findViewById(R.id.textCta)
}
private fun setRewardIcon(@DrawableRes iconResId: Int) {
rewardImageView.setImageResource(iconResId)
rewardImageView.visibility = View.VISIBLE
}
private fun hideRewardIcon() {
rewardImageView.visibility = View.GONE
}
private fun setCtaText(ctaText: String) {
ctaTextView.text = ctaText
}
private fun setRewardText(rewardText: String) {
rewardTextView.text = rewardText
}
override fun renderViewParticipatingState(callToAction: String) {
setCtaText("참여 확인 중")
hideRewardIcon()
setRewardText("")
}
override fun renderViewParticipatedState(callToAction: String) {
setRewardIcon(R.drawable.custom_participated_icon)
setRewardText("")
setCtaText("참여 완료")
}
override fun renderViewRewardAvailableState(callToAction: String, reward: Int) {
setRewardIcon(R.drawable.custom_reward_icon)
setRewardText(String.format(Locale.US, "+%,d", reward))
setCtaText(callToAction)
}
override fun renderViewRewardNotAvailableState(callToAction: String) {
hideRewardIcon()
setRewardText("")
setCtaText(callToAction)
}
}
⚠️ 주의
CustomCtaView
클래스는 난독화 되지 않도록 Proguard 설정에서 제외해야 합니다. Proguard 설정 파일에 다음 라인을 추가하세요.-keep class com.your.packagename.CustomCtaView { *; }
자체 구현한 CTA 버튼을 GlobalTheme에 적용하기
✅ 중요
커스텀 CTA 버튼을 글로벌 테마에 적용하는 것은 Buzzvil SDK v5.17.x부터 지원합니다.
BuzzAdTheme
에 customCtaView
를 추가해 자체 구현한 CTA 버튼을 적용하세요.
✏️ 참고
- 기본 광고 디자인을 사용하는 베네핏허브, 인터스티셜, 팝 등에 적용됩니다.
- 네이티브 또는 커스텀 광고 디자인을 사용하는 베네핏허브 등에는 적용되지 않습니다.
import com.buzzvil.buzzad.benefit.presentation.BuzzAdTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
val buzzAdTheme = BuzzAdTheme()
.ctaViewClass(CustomCtaView::class.java)
BuzzAdTheme.setGlobalTheme(buzzAdTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
네이티브
네이티브 오버레이 UI
버즈빌 대시 어드민을 통해 오버레이 UI를 구성하는 이미지와 텍스트를 커스터마이징할 수 있습니다.
✏️ 참고
오버레이 UI를 커스텀하지 않거나 커스터마이징 토글을 OFF 하면 버즈빌이 제공하는 UI가 적용됩니다.
네이티브 오버레이 UI를 커스터마이징하려면 다음의 절차를 따르세요.
- 버즈빌 대시 어드민(https://dashboard.buzzvil.com/)에 접속하세요.
- Inventory Manager에서 앱을 검색하고 선택한 후 왼쪽 메뉴 패널에서 Entry Point Contents 메뉴로 이동하세요.
- 진입점 유형 드롭다운 목록에서 ‘네이티브 오버레이’를 선택하세요.
- 오버레이 유형별로 커스터마이징 토글을 켜세요.
- 원하는 이미지와 UI 스트링을 등록하세요.
- 커스터마이징 UI를 실시간으로 미리보기할 수 있습니다.
- 오버레이 UI를 커스텀하지 않으면 버즈빌이 제공하는 UI가 적용됩니다.
- 이미지와 UI 스트링이 지원하는 규격은 다음과 같습니다.
- 이미지 포맷: PNG, JPG, JSON(Lottie)
- 파일 크기: 최대 2 MB
- 이미지 비율: 312 x 212 (가로 x 세로)
- UI 스트링 글자 수 & 줄 수: 최대 20 자 (공백 포함) & 두 줄
- 오른쪽 상단의 저장하기 버튼을 클릭하세요.
✏️ 참고
SDK Configuration 에서 비활성화한 기능의 오버레이는 접힌 상태로 표시됩니다.
네이티브 오버레이 영역
네이티브 오버레이가 표시되는 영역을 변경하려면 다음과 같이 NativeAd2ViewBinder
에 nativeOverlayViewLayout()
를 사용하여 네이티브 오버레이가 보여질 영역을 설정합니다.
✏️ 참고
yourCustomNativeAdLayout
은NativeAd2View
의 하위 컴포넌트로 구현된ViewGroup
이어야 합니다. e.g.CardView
,ConstraintLayout
nativeOverlayViewLayout()
를 설정하지 않으면 모서리의 radius 값이 12dp인 기본 배경이 적용됩니다.
import com.buzzvil.buzzad.benefit.nativead2.api.NativeAd2ViewBinder
nativeAd2ViewBinder = NativeAd2ViewBinder.Builder()
.nativeAd2View(nativeAd2View)
...
.nativeOverlayViewLayout(yourCustomNativeAdLayout)
.build(Constant.YOUR_NATIVE_ID)
베네핏허브
Buzzvil SDK에서 제공하는 UI의 디자인을 변경하기 위한 방법을 안내합니다.
럭키박스
SDK 패치 없이 UI 스트링 및 이미지 소재를 풀 커스텀하여 럭키박스의 스타일을 원하는 대로 변경할 수 있습니다. 커스텀하고 싶은 항목과 내용을 버즈빌 담당자(help@buzzvil.com)에게 전달해 주세요.
항목 | 설명 |
---|---|
프로모션 명 |
|
참여 전후 진입점 |
|
참여도 영역 아이콘 |
|
럭키박스 참여 전후 CTA |
|
럭키박스 참여 전후 이미지 |
|
추가보상 배너 |
|
일일 리워드 금액 |
|
연속 참여 성공 리워드 금액 |
|
연속 참여 성공 문구 |
|
연속 참여 성공 문구 색상 |
|
연속 참여 성공 아이콘 |
|
연속 참여 성공 주변 효과 |
|
유의 사항 |
|
미션팩
SDK 패치 없이 UI 스트링 및 이미지 소재를 풀 커스텀하여 미션팩의 스타일을 원하는 대로 변경할 수 있습니다. 커스텀하고 싶은 항목과 내용을 버즈빌 담당자(help@buzzvil.com)에게 전달해 주세요.
항목 | 세부항목 | 설명 |
---|---|---|
베네핏허브 진입점 | 타이틀 |
|
베네핏허브 진입점 | 아이콘 |
|
배경 | 색상 |
|
배경 | 그라데이션 효과 |
|
프로모션 타이틀 | 텍스트 | - |
미션 보너스 | 지급 비율 |
|
미션 보너스 CTA | 텍스트 | - |
도전 중 미션 | 도전 중 타이틀 | - |
도전 중 미션 | 도전 중 아이콘 |
|
도전 중 미션 | 도전 중 미션 설명 |
|
도전 중 미션 | 도전 중 미션 CTA |
|
도전 중 미션 | 미션 완료 CTA |
|
참여 완료 | 참여 완료 타이틀 | - |
미션 구성 | 미션 타이틀 | - |
미션 구성 | 설정 기준 |
|
미션 구성 | 미션별 진행도 |
|
럭키박스 진입점 | CTA |
|
유의 사항 | - |
|
성과 기록 유닛 | - |
|
광고 인정 유닛 | - |
|
미션팩 배경 Gradation 설정하기
import com.buzzvil.buzzad.benefit.BuzzAdBenefitConfig
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme.getDefaultFeedTheme()
.missionPackGradientColors(intArrayOf(0xAF568DE8.toInt(), 0xAFF4FFB3.toInt()))
.missionPackGradientColorPositions(floatArrayOf(0.0f, 1.0f))
BuzzAdFeed.setDefaultTheme(buzzAdTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
내비게이션 바
베네핏허브 내비게이션 바의 UI 스트링을 수정하거나 내비게이션 바 영역을 자체 구현할 수 있습니다.
내비게이션 바 UI 스트링 변경하기
내비게이션 바의 UI 스트링을 변경할 수 있습니다.
<!-- res/values/strings.xml -->
<resources>
<string name="your_custom_title">커스텀 타이틀</string>
</resources>
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
val buzzAdFeedTheme = BuzzAdFeedTheme.getDefault()
.navigationBarTitle(R.string.your_custom_title)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
내비게이션 바 자체 구현하기
프래그먼트로 베네핏허브 연동하기 항목을 참고하여 내비게이션 바를 자체 구현할 수 있습니다.
유저 프로필 입력 배너
유저 프로픽 입력 배너의 아이콘을 변경할 수 있습니다.
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.profileBannerIcon(R.drawable.your_profile_banner_icon)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
헤더
베네핏허브 상단의 헤더를 원하는 목적에 맞게 자유롭게 활용할 수 있습니다. 예를 들어, 베네핏허브 영역을 설명하는 공간으로 활용하거나 적립 가능한 금액을 표시할 수도 있습니다.
✏️ 참고
헤더 영역을 자체 구현하지 않으면총 적립 가능 금액
을 보여주는 기본 헤더가 표시됩니다.
헤더 영역을 사용하지 않으려면 버즈빌 담당자(help@buzzvil.com)에게 연락하세요.
다음은 헤더 영역을 변경하는 예시입니다.
FeedHeaderViewAdapter
인터페이스를 구현하는 클래스를 생성하고 Custom View(your_feed_header_layout
)를 헤더 영역에 구현합니다. 그리고 FeedConfig
의 feedHeaderViewAdapterClass()
속성에 새로 구현한 클래스를 추가합니다.
import com.buzzvil.buzzad.benefit.presentation.feed.header.FeedHeaderViewAdapter
class CustomFeedHeaderViewAdapter: FeedHeaderViewAdapter {
override fun onCreateView(context: Context, parent: ViewGroup): View {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
return inflater.inflate(R.layout.feed_header_layout, parent, false)
}
override fun onBindView(view: View, reward: Int) {
val rewardTextView: TextView = view.findViewById(R.id.textReward)
rewardTextView.text = reward.toString()
}
override fun onDestroyView() {
// Use this callback for clearing memory
}
}
import com.buzzvil.buzzad.benefit.presentation.feed.FeedConfig
val feedConfig = FeedConfig.Builder("YOUR_FEED_UNIT_ID")
.feedHeaderViewAdapterClass(CustomFeedHeaderViewAdapteri::class.java)
.build();
광고 분류 필터
광고 분류 필터의 기본 색상은 monochrome이며, primary color를 따라가도록 변경할 수 있습니다.
import com.buzzvil.buzzad.benefit.presentation.BuzzAdTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
val buzzAdTheme = BuzzAdTheme()
.usePrimaryColorInFilter(true) // true: primary color, false: monocrhome
BuzzAdTheme.setGlobalTheme(buzzAdTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
광고 디자인
Buzzvil SDK에서 제공하는 광고는 일반 광고와 쇼핑 광고가 있습니다.
- 일반 광고: 쇼핑 광고를 제외한 나머지 유형의 광고입니다.
- 쇼핑 광고: 유저가 광고를 통해 물건 구매를 달성하는 경우 유저에게 구매 금액의 일정 비율을 포인트로 돌려주는 광고입니다.
두 유형 모두 광고를 표현할 수 있는 다양한 정보로 구성됩니다. 일반 광고와 쇼핑 광고를 구성하는 요소와 UI에 대해서는 아래의 표를 참고하세요.
구성 요소 | 일반 | 쇼핑 | 설명 |
---|---|---|---|
광고 소재 | 필수 | 필수 |
|
광고 제목 | 필수 | 선택 | 광고의 제목입니다. 최대 10자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. |
광고 설명 | 필수 | 선택 | 광고에 대한 상세 설명입니다. 최대 40자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. |
광고주 아이콘 | 필수 | 선택 |
|
CTA 버튼 | 필수 | 필수 |
✏️ 참고 CTA 버튼의 디자인을 변경하려면 CTA 버튼 자체 구현하기 토픽을 참고하세요. |
광고 알림 문구 | 권장 | 권장 | 광고임을 명시하는 문구입니다. (예: 광고 , ad , 스폰서 , Sponsored ) |
OriginalPrice View | - | 권장 | 상품의 원가를 표시합니다. |
Price View | - | 필수 | 상품의 할인된 가격을 표시합니다. |
DiscountRate View | - | 권장 | 상품 가격의 할인율을 표시합니다. 할인율은 원가와 할인가로 비교하여 산출해서 표시해야 합니다. |
광고 디자인 자체 구현하기
광고의 디자인을 변경하려면 다음의 절차를 따르세요.
❗️ 주의
NativeAdView
는 광고 디자인 자체 구현에서만 사용해야 합니다. 네이티브 구현은NativeAd2View
를 사용하세요. 자세한 내용은 네이티브를 참고하세요.
광고용
NativeAdView
의 규격에 맞는 레이아웃(your_benefit_hub_ad.xml
)을 구현하세요.<!-- your_benefit_hub_ad.xml -->
<com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView
android:id="@+id/nativeAdView" ...>
<!-- MediaView와 DefaultCtaView는 NativeAdView의 하위 컴포넌트로 구현해야 합니다. -->
<!-- 참고: NativeAdView는 FrameLayout 입니다. -->
<LinearLayout ... >
<!-- MediaView는 일반 광고와 쇼핑 광고에서 모두 사용됩니다. -->
<com.buzzvil.buzzad.benefit.presentation.media.MediaView
android:id="@+id/mediaView" ... />
<!-- 일반 광고에서 사용되는 뷰 레이아웃입니다. -->
<LinearLayout android:id="@+id/excpsLayout" ... >
<androidx.appcompat.widget.AppCompatImageView android:id="@+id/adIconImage" ... />
<TextView android:id="@+id/adTitleText" ... />
<TextView android:id="@+id/adDescriptionText" ... />
</LinearLayout>
<!-- 쇼핑 광고에서 사용되는 뷰 레이아웃입니다. -->
<LinearLayout android:id="@+id/cpsLayout" ...>
<TextView android:id="@+id/discountPercentageText" ... />
<TextView android:id="@+id/priceText" ... />
<TextView android:id="@+id/originalPriceText" ... />
</LinearLayout>
<!-- DefaultCtaView 대신 직접 구현한 CustomCtaView를 사용할 수 있습니다. -->
<com.buzzvil.buzzad.benefit.presentation.media.DefaultCtaView
android:id="@+id/ctaView" ... />
</LinearLayout>
</com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView>BenefitHubAdViewAdapter
의 상속 클래스를 구현하세요.onCreateView()
에서your_benefit_hub_ad.xml
을 사용하여 뷰를 생성하세요.onBindView()
에서NativeAdViewBinder
를 이용하여 광고 데이터(NativeAd
)를 앞서 생성한NativeAdView
에 바인딩하세요.onBindView()
는NativeAdView
를 반환해야 합니다.
onDestroyView()
에서 리스너 및 뷰를 해제하세요.NativeAdEventListener
를 통해 광고 콜백 이벤트 수신 기능을 부가적으로 추가할 수도 있습니다.
import com.buzzvil.buzzad.benefit.core.models.Product
import com.buzzvil.buzzad.benefit.feed.benefithub.list.adapter.BenefitHubAdViewAdapter
import com.buzzvil.buzzad.benefit.presentation.nativead.NativeAd
import com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdEventListener
import com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView
import com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdViewBinder
import com.buzzvil.buzzad.benefit.presentation.reward.RewardResult
class CustomBenefitHubAdViewAdapter : BenefitHubAdViewAdapter {
private lateinit var binding: YourBenefitHubAdBinding
private var nativeAd: NativeAd? = null
private var nativeAdViewBinder: NativeAdViewBinder? = null
// 리스너 구현은 선택 사항입니다.
private val listener = object : NativeAdEventListener {
override fun onImpressed(nativeAd: NativeAd) {}
override fun onClicked(nativeAd: NativeAd) {}
override fun onRewardRequested(nativeAd: NativeAd) {}
override fun onRewarded(nativeAd: NativeAd, nativeAdRewardResult: RewardResult?) {}
override fun onParticipated(nativeAd: NativeAd) {}
}
override fun onCreateView(context: Context, parent: ViewGroup): View {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
binding = YourBenefitHubAdBinding.inflate(inflater, parent, false)
return binding.root
}
override fun getNativeAdView(): NativeAdView {
return binding.nativeAdView
}
override fun onBindView(nativeAd: NativeAd) {
val builder = NativeAdViewBinder.Builder(binding.root, binding.adMediaView)
.ctaView(binding.adCtaView)
.iconImageView(binding.adIconImage)
.titleTextView(binding.adTitleText)
.descriptionTextView(binding.adDescriptionText)
.addClickableView(binding.yourClickableArea) // (Optional) 클릭 가능한 영역을 추가합니다.
if (nativeAd.shoppingProduct == null) {
// 일반 광고 (CPS 외)
binding.excpsLayout.visibility = View.VISIBLE
binding.cpsLayout.visibility = View.GONE
} else {
// 쇼핑 광고 (CPS)
binding.excpsLayout.visibility = View.GONE
binding.cpsLayout.visibility = View.VISIBLE
setCpsView(nativeAd.shoppingProduct!!)
}
val nativeAdViewBinder = builder.build()
nativeAdViewBinder.bind(nativeAd)
nativeAd.addNativeAdEventListener(listener)
this.nativeAd = nativeAd
this.nativeAdViewBinder = nativeAdViewBinder
}
override fun onDestroyView() {
nativeAd?.removeNativeAdEventListener(listener)
nativeAdViewBinder?.unbind()
nativeAd = null
nativeAdViewBinder = null
}
private fun setCpsView(product: Product) {
val discountedPrice = product.discountedPrice
if (discountedPrice != null) {
// 할인이 있는 쇼핑 광고
binding.originalPriceText.paintFlags = binding.originalPriceText.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
var percentage = 0
if (product.price > discountedPrice) {
percentage = ((product.price - discountedPrice) / product.price * 100).roundToInt()
}
if (percentage > 0) {
binding.priceText.text = getCommaSeparatedPrice(discountedPrice.toLong())
binding.originalPriceText.text = getCommaSeparatedPrice(product.price.toLong())
binding.discountPercentageText.text = String.format(Locale.ROOT, "%d%%", percentage)
binding.discountPercentageText.visibility = View.VISIBLE
} else {
binding.priceText.text = getCommaSeparatedPrice(product.price.toLong())
binding.originalPriceText.text = ""
binding.discountPercentageText.visibility = View.GONE
}
} else {
// 할인이 없는 쇼핑 광고
binding.priceText.text = getCommaSeparatedPrice(product.price.toLong())
binding.originalPriceText.text = ""
binding.discountPercentageText.visibility = View.GONE
}
}
private fun getCommaSeparatedPrice(price: Long): String {
val contextForResource = binding.root.context
return String.format(
Locale.getDefault(),
contextForResource.getString(R.string.custom_cps_price_unit),
price
)
}
}<!-- res/values/strings.xml -->
<resources>
<string name="custom_cps_price_unit">%,d원</string>
</resources>FeedConfig
에 위에서 작성한CustomBenefitHubAdViewAdapter
를 광고 어댑터 클래스로 설정하세요.import com.buzzvil.buzzad.benefit.presentation.feed.FeedConfig
val feedConfig = FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
.benefitHubAdViewAdapterClass(CustomBenefitHubAdViewAdapter::class.java)
.build()
광고 미할당 안내 UI
광고 미할당 시 이탈을 막기 위해 유저는 3번까지 재시도하는 UX로 설계되어 있습니다. 광고 미할당 안내 UI에서는 각 재시도 회차별 UI 스트링과 이미지를 변경할 수 있습니다.
재시도 회차 | 제목 | 내용 | 버튼 |
---|---|---|---|
0회차 | 미션을 준비 중이에요 | 다시 미션을 불러와서 포인트를 모아보시겠어요? | 미션 불러오기 |
1회차 | 미션을 준비 중이에요 | 미션을 로딩 중이에요. 잠시 후 다시 시도해주세요. | 다시 불러오기 |
2회차 | 미션을 준비 중이에요 | 미션 준비에 시간이 걸리고 있습니다. 잠시 후 다시 시도해주세요. | 한번 더 불러오기 |
3회차(마지막) | 미션을 준비 중이에요 | 전체 필터: 지금은 참여 가능한 미션이 없습니다. 잠시 후 다시 방문해주세요. 그 외 필터: 지금은 참여 가능한 미션이 없습니다. 다른 미션에 참여해서 포인트를 모아보세요! | 전체 필터: 나중에 다시 올게요 그 외 필터: 다른 미션 참여하기 |
<!-- res/values/strings.xml -->
<string name="your_no_fill_error_title">미션을 준비 중이에요</string>
<string name="your_no_fill_error_description_1st">다시 미션을 불러와서 포인트를 모아보시겠어요?</string>
<string name="your_no_fill_error_description_2nd">미션을 로딩 중이에요. 잠시 후 다시 시도해주세요.</string>
<string name="your_no_fill_error_description_3rd">미션 준비에 시간이 걸리고 있습니다. 잠시 후 다시 시도해주세요.</string>
<string name="your_no_fill_error_description_final_all_filter">지금은 참여 가능한 미션이 없습니다. 잠시 후 다시 방문해주세요.</string>
<string name="your_no_fill_error_description_final_other_filters">다른 미션 참여하기</string>
<string name="your_no_fill_error_button_1st">미션 불러오기</string>
<string name="your_no_fill_error_button_2nd">다시 불러오기</string>
<string name="your_no_fill_error_button_3rd">한번 더 불러오기</string>
<string name="your_no_fill_error_button_final_all_filter">나중에 다시 올게요</string>
<string name="your_no_fill_error_button_final_other_filters">지금은 참여 가능한 미션이 없습니다. 다른 미션에 참여해서 포인트를 모아보세요!</string>
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.noFillErrorImage(R.drawable.your_no_fill_error_image)
.noFillErrorTitle(R.string.your_no_fill_error_title)
.noFillErrorDescription1st(R.string.your_no_fill_error_description_1st)
.noFillErrorDescription2nd(R.string.your_no_fill_error_description_2nd)
.noFillErrorDescription3rd(R.string.your_no_fill_error_description_3rd)
.noFillErrorDescriptionFinalAllFilter(R.string.your_no_fill_error_description_final_all_filter)
.noFillErrorDescriptionFinalOtherFilters(R.string.your_no_fill_error_description_final_other_filters)
.noFillErrorButton1st(R.string.your_no_fill_error_button_1st)
.noFillErrorButton2nd(R.string.your_no_fill_error_button_2nd)
.noFillErrorButton3rd(R.string.your_no_fill_error_button_3rd)
.noFillErrorButtonFinalAllFilter(R.string.your_no_fill_error_button_final_all_filter)
.noFillErrorButtonFinalOtherFilters(R.string.your_no_fill_error_button_final_other_filters)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
유저 프로필 오류 안내 UI
유저 프로필이 설정되지 않았을 때 유저에게 노출되는 화면의 UI 스트링과 이미지를 변경할 수 있습니다.
<!-- res/values/strings.xml -->
<string name="your_user_profile_error_title">미션을 준비 중이에요</string>
<string name="your_user_profile_error_description">잠시 후 다시 시도해주세요.</string>
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.userProfileErrorImage(R.drawable.your_user_profile_error_image)
.userProfileErrorTitle(R.string.your_user_profile_error_title)
.userProfileErrorDescription(R.string.your_user_profile_error_description)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
서버 오류 안내 UI
서버가 유저에게 노출되는 화면의 UI 스트링과 이미지를 변경할 수 있습니다.
<!-- res/values/strings.xml -->
<string name="your_server_error_title">미션을 준비 중이에요</string>
<string name="your_server_error_description">잠시 후 다시 시도해주세요.</string>
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.serverErrorImage(R.drawable.your_server_error_image)
.serverErrorTitle(R.string.your_user_profile_error_title)
.serverErrorDescription(R.string.your_server_error_description)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
기타 오류 화면 UI
기타 오류 화면별 이미지를 변경할 수 있습니다.
속성 | 설명 |
---|---|
privacyPolicyErrorIcon | 개인정보 제3자 제공 동의를 받아야 하는 경우 노출되는 이미지를 변경합니다. |
agePolicyErrorIcon | 네트워크 에러 발생 시 노출되는 이미지를 변경합니다. |
networkErrorIcon | 네트워크 에러 발생 시 노출되는 이미지를 변경합니다. |
unknownErrorIcon | 알 수 없는 오류 발생 시 노출되는 이미지를 변경합니다. |
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.privacyPolicyErrorImage(R.drawable.your_privacy_policy_error_image)
.agePolicyErrorImage(R.drawable.your_age_policy_error_image)
.networkErrorImage(R.drawable.your_nework_error_image)
.unknownErrorImage(R.drawable.your_unknown_error_image)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
팝 추가 유도 모달
팝을 연동한 경우 유저에게 팝 버튼 활성화를 유도하는 모달의 이미지를 변경할 수 있습니다.
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeed
import com.buzzvil.buzzad.benefit.presentation.feed.BuzzAdFeedTheme
import com.buzzvil.sdk.BuzzvilSdk
class App : Application() {
override fun onCreate() {
super.onCreate()
val buzzAdBenefitConfig = ...
// BuzzvilSdk.initialize()를 호출하기 전에 FeedTheme을 설정합니다.
val buzzAdFeedTheme = BuzzAdFeedTheme()
.popModalImage(R.drawable.your_pop_modal_image)
BuzzAdFeed.setDefaultTheme(buzzAdFeedTheme)
// Buzzvil SDK 초기화
BuzzvilSdk.initialize(...)
}
}
개인정보 제3자 제공 동의 UI 자체 구현
개인정보 제3자 제공 동의 UI를 자체 구현하는 경우, SDK에 동의 여부를 전달해야 합니다. 아래 표를 참고하여 적절한 함수를 호출 해 주세요.
Class | API | 설명 |
---|---|---|
BuzzAdBenefit | getPrivacyPolicyManager() (정적 메소드) | PrivacyPolicyManager 인스턴스를 반환합니다. |
PrivacyPolicyManager | grantConsent() | 개인 정보 수집에 동의합니다. 사용자가 처음 피드 지면에 진입하기 전에 호출하면 사용자에게 개인정보 제3자 제공 동의 UI가 보이지 않습니다. |
revokeConsent() | 개인 정보 수집 동의를 철회합니다. 사용자가 동의를 철회하고 피드 지면에 진입하는 경우, 개인정보 제3자 제공 동의 UI가 다시 표시됩니다. | |
isConsentGranted() | 개인 정보 수집 동의 여부를 확인합니다. |
사용 예시
import com.buzzvil.buzzad.benefit.BuzzAdBenefit
val privacyPolicyManager = BuzzAdBenefit.getPrivacyPolicyManager()
// 직접 구현한 개인정보 제3자 제공 동의 UI에서 동의를 받았을 경우 아래 코드를 실행하면, 피드 화면에서 개인정보 제3자 제공 동의 UI가 노출되지 않으면서 광고 할당이 정상적으로 진행 됩니다.
// 아래의 코드를 실행하지 않을 경우 피드 진입 시 개인정보 제3자 제공 동의 UI가 노출되기 때문에 필수적으로 실행이 필요합니다.
privacyPolicyManager.grantConsent()
// 아래 코드를 통해서 수집 동의를 철회할 수 있습니다. 이 코드가 실행되면 피드에서 광고 할당이 이루어지지 않습니다.
privacyPolicyManager.revokeConsent()
베네핏허브가 열릴 때 기본 선택 필터 변경
✏️ 참고
이 기능은 실험 기능이며 경고가 뜰 수는 있으나 앱 빌드에 문제는 없습니다. 경고를 제거하려면@OptIn(BuzzExperimentalApi::class)
를 사용하세요.
베네핏허브를 화면에 표시하면서 처음 선택된 필터가 전체
필터가 아닌 다른 필터로 이동하도록 할 수 있습니다.
import com.buzzvil.buzzad.benefit.presentation.feed.FeedConfig
// Feed(베네핏허브) 설정
val feedConfig = FeedConfig.Builder("YOUR_FEED_UNIT_ID")
.initialSelectedFilterName("쇼핑적립")
.build()
베네핏허브가 열릴 때 기본 화면 변경
✏️ 참고
이 기능은 실험 기능이며 경고가 뜰 수는 있으나 앱 빌드에 문제는 없습니다. 경고를 제거하려면@OptIn(BuzzExperimentalApi::class)
를 사용하세요.
베네핏허브를 화면에 표시하면서 베네핏허브의 특정 화면을 바로 표시할 수 있습니다.
- 설정 가능한 화면 목록
- 럭키박스(LuckyBox)
import com.buzzvil.buzzad.benefit.presentation.feed.FeedConfig
// Feed(베네핏허브) 설정
val feedConfig = FeedConfig.Builder("YOUR_FEED_UNIT_ID")
.initialNavigationPage(FeedInitialNavigationPage.LUCKYBOX)
.build()
인터스티셜
주요 UI의 색상(Primary color)을 변경할 수 있는 GlobalTheme
을 사용해 진입 경로 레이아웃의 베네핏허브 진입 유도 문구 색상, CTA 버튼 배경 색상과 리워드 아이콘이 함께 변경됩니다. 자세한 내용은 전체 테마 토픽을 참고하세요.