Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

목차

Table of Contents

개요

Feed의 각 컴포넌트는 다음과 같이 구분합니다.

...

Note

FeedToolbarHolder, AdsAdapter, FeedHeaderViewAdapter class의 custom class를 생성할때 다음 조건중 하나를 사용해야합니다.

  • 각 클래스는 inner class 가 아니어야 합니다.

  • Inner class로 생성을 해야할 경우, public static class로 선언이 되어야 합니다.

위의 조건에 맞지 않는 경우, class를 찾지못하는 현상이 발생하여 customization이 적용되지 않습니다.

툴바 커스터마이징

Feed 상단의 툴바를 커스터마이징할 수 있습니다.

FeedToolbarHolderinterface를 구현하는 Class 를 생성합니다.

Code Block
languagejava
public class CustomFeedToolbarHolder implements FeedToolbarHolder {
    private FeedActivityToolbar toolbar;

    @Override
    public View getView(final Activity activity, @NonNull final String unitId) {
        // FeedActivityToolbar is a default toolbar view that you can only change the title and view's attributes. 
        // Create a custom view and return it if you would like to add more UI components.
        this.toolbar = new FeedActivityToolbar(activity, unitId);
        toolbar.setTitle("BuzzAd Feed");
        toolbar.setBackgroundColor(Color.parseColor("#1290FF")); // 변경하려는 "색상코드" 를 입력
        return toolbar;
    }

    @Override
    public void onTotalRewardUpdated(int totalReward) {
        // Use this callback if you want to show the current total points that users can get
    }
}

...

FeedConfig에 CustomFeedToolbarHolder를 설정합니다.

Code Block
languagejava
final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
    ...생략...
    .feedToolbarHolderClass(CustomFeedToolbarHolder.class)
    .build();

툴바 Height 변경

커스터마이징한 FeedToolbarHolder가 들어가는 layout의 height값이 기본값으로 고정되어 있습니다.
아래 가이드를 통해 툴바의 height 값을 수정할 수 있습니다.

Code Block
languagexml
// AndroidManifest.xml

...생략...
<activity
    android:name="com.buzzvil.buzzad.benefit.presentation.feed.FeedBottomSheetActivity"
    android:theme="@style/MyAppTheme"
    tools:replace="android:theme"/>
...생략...
Code Block
languagexml
// styles.xml
<style name="MyAppTheme" parent=...>
    ...생략...
    <!-- Customize your theme here. -->
    <item name="actionBarSize">DESIRED_ACTION_BAR_HEIGHT</item>
    ...생략...
</style>

헤더 커스터마이징

Feed 상단의 헤더 영역을 커스터마이징하여, 유저에게 피드가 어떤 공간인지 상세히 안내할 수 있습니다.

FeedHeaderViewAdapter interface 를 구현하는 Class 를 생성합니다.

Code Block
languagejava
public class CustomFeedHeaderViewAdapter implements FeedHeaderViewAdapter {
    @Override
    public View onCreateView(final Context context, final ViewGroup parent) {
        final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        return inflater.inflate(R.layout.bz_view_feed_header, parent, false);
    }

    @Override
    public void onBindView(final View view, final int reward) {
        // Display total reward on the header if needed.
        final View rewardLayout = view.findViewById(R.id.rewardLayout);
        if (reward == 0) {
            rewardLayout.setVisibility(View.GONE);
        } else {
            rewardLayout.setVisibility(View.VISIBLE);
            final TextView rewardTextView = view.findViewById(R.id.rewardText);
            final String rewardText = view.getContext().getString(R.string.bz_feed_header_view_reward_text, reward);
            rewardTextView.setText(rewardText);
        }
    }
    
    @Override 
    public void onDestroyView() {
        // Use this this callback for clearing memory 
    }
}

...

FeedConfigFeedHeaderViewAdapter를 설정합니다.

Code Block
languagejava
final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
    ...생략...
    .feedHeaderViewAdapterClass(CustomFeedHeaderViewAdapter.class)
    .build();

광고 아이템 영역 커스터마이징 - 일반 광고

...

Code Block
// your_feed_ad.xml


<com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView
    android:id="@+id/native_ad_view"
    ...생략... >
    <LinearLayout
        ...생략... >
        <com.buzzvil.buzzad.benefit.presentation.media.MediaView
            android:id="@+id/mediaView"
            ...생략... />
        <TextView
            android:id="@+id/textTitle"
            ...생략... />
        <TextView
            android:id="@+id/textDescription"
            ...생략... />
        <ImageView
            android:id="@+id/imageIcon"
            ...생략... />
        <com.buzzvil.buzzad.benefit.presentation.media.CtaView
            android:id="@+id/ctaView"
            ...생략... />
    ...생략...
    </LinearLayout>
    ...생략...
</com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView>

...

onCreateViewHolder에서 NativeAdViewHolderNativeAdView를 설정합니다.

...

 onBindViewHolder에서는 NativeAdView에 광고 데이터(NativeAd)를 바인딩합니다.

  • clickableViews 리스트에 UI 컴포넌트를 추가하여, 클릭 이벤트를 수신할 컴포넌트를 설정합니다. 추가된 UI 컴포넌트를 클릭 시 광고 페이지로 이동하게 됩니다.

  • CTA 버튼은 CtaPresenter 를 이용하여 상황에 맞는 적절한 문구로 변경됩니다.
    CTA 버튼의 UI 혹은 문구의 커스터마이징은 가이드에 따라 진행할 수 있습니다.

  • OnNativeAdEventListener 를 통해 광고 콜백 이벤트를 수신할 수 있습니다.
    기획에 따라 추가로 UI 등을 추가할 수 있습니다.

Code Block
languagejava
public class CustomAdsAdapter extends AdsAdapter<AdsAdapter.NativeAdViewHolder> {

    @Override
    public NativeAdViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        final NativeAdView feedNativeAdView = (NativeAdView) inflater.inflate(R.layout.your_feed_ad, parent, false);
        return new NativeAdViewHolder(feedNativeAdView);
    }

    @Override
    public void onBindViewHolder(NativeAdViewHolder holder, NativeAd nativeAd) {
        final NativeAdView view = (NativeAdView) holder.itemView;

        final Ad ad = nativeAd.getAd();
        
        // create ad component
        final MediaView mediaView = view.findViewById(R.id.mediaView);
        final LinearLayout titleLayout = view.findViewById(R.id.titleLayout);
        final TextView titleView = view.findViewById(R.id.textTitle);
        final ImageView iconView = view.findViewById(R.id.imageIcon);
        final TextView descriptionView = view.findViewById(R.id.textDescription);
        final CtaView ctaView = view.findViewById(R.id.ctaView);
        final CtaPresenter ctaPresenter = new CtaPresenter(ctaView); // CtaView should not be null

        // data binding 
        ctaPresenter.bind(nativeAd);

        if (mediaView != null) {
            mediaView.setCreative(ad.getCreative());
            mediaView.setVideoEventListener(new VideoEventListener() {
                // Override and implement methods

                @Override
                public void onVideoStarted() {}

                ...생략...
          
            });
        }
        
        if (titleView != null) {
            titleView.setText(ad.getTitle());
        }

        if (iconView != null) {
            ImageLoader.getInstance().displayImage(ad.getIconUrl(), iconView);
        }
        if (descriptionView != null) {
            descriptionView.setText(ad.getDescription());
        }

        final Collection<View> clickableViews = new ArrayList<>();
        clickableViews.add(ctaView);
        clickableViews.add(mediaView);
        clickableViews.add(titleLayout);
        clickableViews.add(descriptionView);

        view.setMediaView(mediaView);
        view.setClickableViews(clickableViews);
        view.setNativeAd(nativeAd);
        view.addOnNativeAdEventListener(new NativeAdView.OnNativeAdEventListener() {
            
            @Override
            public void onImpressed(final @NonNull NativeAdView view, final @NonNull NativeAd nativeAd) {

            }

            @Override
            public void onClicked(@NonNull NativeAdView view, @NonNull NativeAd nativeAd) {
                ctaPresenter.bind(nativeAd);
            }

            @Override
            public void onRewardRequested(@NonNull NativeAdView view, @NonNull NativeAd nativeAd) {

            }

            @Override
            public void onRewarded(@NonNull NativeAdView nativeAdView, @NonNull NativeAd nativeAd, @Nullable RewardResult rewardResult) {

            }

            @Override
            public void onParticipated(final @NonNull NativeAdView view, final @NonNull NativeAd nativeAd) {
                ctaPresenter.bind(nativeAd);
            }
        });
    }
}

...

Code Block
languagejava
final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
    ...생략...
    .adsAdapterClass(CustomAdsAdapter.class)
    .build();

광고 아이템 영역 커스터마이징 - 쇼핑 적립 광고 (CPS)

커스터마이징 방식은 일반 광고와 같습니다. 하지만 쇼핑 적립 광고는 일반 광고에 비해 많은 정보를 제공합니다. 일반 광고용 레이아웃에서 priceText, originalPriceText, discountPercentageText, categoryText 가 추가되었습니다. 따라서 위 4가지 컴포넌트에 대해 추가로 데이터를 처리해야합니다.

  • NativeAdView(쇼핑 적립 광고) 의 레이아웃

Code Block
// your_feed_ad_cps.xml

<?xml version="1.0" encoding="utf-8"?>
<com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView
    android:id="@+id/native_ad_view"
    ...생략... >
    <LinearLayout
        ...생략... >
        <com.buzzvil.buzzad.benefit.presentation.media.MediaView
            android:id="@+id/mediaView"
            ...생략... />
            
        ...생략...
    
        <TextView
            android:id="@+id/priceText"
            ...생략... />
        <TextView
            android:id="@+id/originalPriceText"
            ...생략... />
        <TextView
            android:id="@+id/discountPercentageText"
            ...생략... />
        <TextView
            android:id="@+id/categoryText"
            ...생략... />
            
        ...생략...
        
    </LinearLayout>
    ...생략...
</com.buzzvil.buzzad.benefit.presentation.nativead.NativeAdView>

Info

위에서 안내한 AdsAdapter 를 참고하여 수정합니다.

Code Block
languagejava
public class CustomCPSAdsAdapter extends AdsAdapter<AdsAdapter.NativeAdViewHolder> {

    @Override
    public NativeAdViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        final NativeAdView feedNativeAdView = (NativeAdView) inflater.inflate(R.layout.your_feed_ad_cps, parent, false);
        return new NativeAdViewHolder(feedNativeAdView);
    }

    @Override
    public void onBindViewHolder(NativeAdViewHolder holder, NativeAd nativeAd) {
        final NativeAdView view = (NativeAdView) holder.itemView;
        final Ad ad = nativeAd.getAd();
        
        // create ad component
        ...생략...
        final TextView priceText = view.findViewById(R.id.discountedPriceText);
        final TextView originalPriceText = view.findViewById(R.id.originalPriceText);
        final TextView discountPercentageText = view.findViewById(R.id.discountPercentageText);
        final TextView categoryText = view.findViewById(R.id.categoryText);

        // data binding 
        ...생략...
        final Product product = ad.getProduct();
        if (product != null) {
            if (product.getDiscountedPrice() != null) {
                // 할인이 있는 쇼핑 광고
                originalPriceText.setPaintFlags(originalPriceText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
                int percentage = 0;
                if (product.getPrice() > product.getDiscountedPrice()) {
                    percentage = Math.round(((product.getPrice() - product.getDiscountedPrice()) / product.getPrice() * 100));
                }
                if (percentage > 0) {
                    priceText.setText(getCommaSeparatedPrice(product.getDiscountedPrice().longValue()));
                    originalPriceText.setText(getCommaSeparatedPrice((long) product.getPrice()));
                    discountPercentageText.setText(String.format(Locale.ROOT, "%d%%", percentage));
                    discountPercentageText.setVisibility(View.VISIBLE);
                } else {
                    priceText.setText(getCommaSeparatedPrice((long) product.getPrice()));
                    originalPriceText.setText("");
                    discountPercentageText.setVisibility(View.GONE);
                }
            } else {
                // 할인이 없는 쇼핑 광고
                priceText.setText(getCommaSeparatedPrice((long) product.getPrice()));
                originalPriceText.setText("");
                discountPercentageText.setVisibility(View.GONE);
            }
            categoryText.setText(product.getCategory());
            if (!TextUtils.isEmpty(product.getCategory())) {
                categoryText.setVisibility(View.VISIBLE);
            }
        }

        final Collection<View> clickableViews = new ArrayList<>();
        clickableViews.add(ctaView);
        clickableViews.add(mediaView);
        clickableViews.add(descriptionView);

        view.setMediaView(mediaView);
        view.setClickableViews(clickableViews);
        view.setNativeAd(nativeAd);
        
        ...생략...
    }

    private String getCommaSeparatedPrice (long price){
        return String.format(Locale.getDefault(), "₩%,d", price);
    }
}

FeedConfig에 구현한 CustomCPSAdsAdapter를 설정합니다.

Code Block
languagejava
final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
    ...생략...
    .cpsAdsAdapterClass(CustomCPSAdsAdapter.class)
    .build();

Tab 커스터마이징

Feed Tab이 활성화되어 있는 경우, 아래의 방법으로 Tab의 UI를 바꿀 수 있습니다.

...

languagejava

...

minLevel1
maxLevel1
exclude목차

개요

본 가이드에서는 BuzzAd Android SDK에서 제공하는 UI의 구성을 지키며 디자인을 변경하기 위한 방법을 안내합니다. 추가적인 디자인 변경을 원하시는 경우에는 고급 설정에서 UI를 자체 구현하는 방법으로 진행할 수 있습니다.

탭 UI 변경

...

아래의 방법으로 탭의 디자인을 수정할 수 있습니다.

  • 탭의 색상은 테마 적용을 통해 변경할 수 있습니다.

  • 탭의 문구는 FeedConfig를 설정하여 변경합니다.

    Code Block
    languagejava
    final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID"

...

  • )

...

  • 
    

...

  •  

...

  •  

...

  •  

...

  •      .tabTextArray(new String[] { FIRST_TAB_NAME, SECOND_TAB_NAME }) // 탭에 들어갈 문구
        

...

Tab의 UI 색상을 변경하기 위해서는 Theme 커스터마이징 가이드를 통해 변경할 수 있습니다.

Filter 커스터마이징

...

Filter의 UI 색상을 변경하기 위해서는 Theme 커스터마이징 가이드를 통해 변경할 수 있습니다.

CtaView (버튼) 커스터마이징

2.21 버전 이상에서는 아래 방법보다는 Theme을 활용하는 방법을 권장합니다.

BuzzAd SDK 에서 제공하는 CtaView UI 및 처리 로직을 사용하지 않고 기획에 맞게 구현할 경우 다음과 같이 진행할 수 있습니다.

Info

위에서 안내한 AdsAdapter 의 onBindViewHolder 에서, CtaView 부분을 아래와 같이 수정합니다.

...

languagejava

...

  •  

...

  •  

...

  •  

...

  •  

...

베이스 리워드 지급 안내 UI 커스터마이징

...

베이스 리워드 지급시 유저에게 제공되는 피드백을 커스터마이즈 할 수 있습니다.

  1. DefaultFeedFeedbackHandler를 상속받는 클래스를 생성합니다.

    Code Block
    languagekotlin
    public class CustomFeedFeedbackHandler extends DefaultFeedFeedbackHandler {
       @NotNull
       public View getBaseRewardNotificationView(@NotNull Context context, int reward) {
          // 베이스 리워드 지급시 피드 상단에 보여지는 notification view 입니다.
       }
    
       public void onBridgePointBaseRewardReceived(@NotNull Context context, int reward) {
          // 베이스 리워드 지급시 브릿지 포인트 연동 중일 경우 호출되는 콜백입니다.
       }
    }
  2. FeedConfig을 초기화할 때, 이전 스텝에서 생성한 CustomFeedFeedbackHandler를 넘겨줍니다.

    Code Block
    languagejava
    final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
        ...생략...
        .feedFeedbackHandler(CustomFeedFeedbackHandler.class)
        .build();

Pop 활성화 UI 커스터마이징

...

  • .build();

필터 UI 변경

...

아래의 방법으로 필터의 디자인을 수정할 수 있습니다. 필터의 색상을 변경하기 위해서는테마 적용을 참고하시기 바랍니다.

Pop 활성화 버튼

...

Pop 활성화 버튼의 디자인은 아래 가이드에 따라 수정할 수 있습니다.

  • 활성화 버튼의 색상과 아이콘은 테마 적용을 통해 변경할 수 있습니다.

  • 활성화 버튼의 텍스트는 DefaultOptInAndShowPopButtonHandler를 상속받는 클래스를 생성하여 변경할 수 있습니다문구는 DefaultOptInAndShowPopButtonHandler의 상속 클래스에서 설정합니다. 상속 클래스를 작성하고 FeedConfig에 설정합니다.

    Code Block
    languagejava
    class 

...

  • YourOptInAndShowPopButtonHandler extends DefaultOptInAndShowPopButtonHandler {
        // 

...

  • 활성화 버튼에 보여지는 

...

  • 문구입니다.
        @Override
        public String getOptInAndShowPopButtonText(Context context) {
            return "YOUR_BUTTON_TEXT"
        }
    }

...

  • Code Block
    languagejava
    final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
        

...

  •     

...

  • .optInAndShowPopButtonHandler(CustomOptInAndShowPopButtonHandler.class)
            .build();