해당 문서는 BuzzAd Ext.point SDK의 광고 지면 타입 중 하나인, Feed Type 을 연동하는 문서입니다. Feed Type 은 여러개의 광고를 쉽게 피드 형태로 노출할 수 있도록 별도의 지면으로 구성되어 있습니다. 일반적으로 Native Type 의 광고에서 더보기 버튼을 통해 진입하는 형태로 사용하고 있습니다.

다음 과정이 완료 되었는지 확인이 필요합니다.

위의 과정 중 완료되지 않은 항목이 있다면, 이전 단계의 연동을 먼저 완료해야 합니다.


연동하기

1. 기본 설정을 적용하여 구현하기

Feed Config 및 Feed Handler 설정하기

[1] Feed unit id로 FeedConfig를 설정합니다. FeedConfig 객체를 사용하여 FeedHandler를 생성합니다.

// 예시에 적힌 "Feed_Unit_Id" 를 '버즈빌에서 발급한 Unit Id'로 교체해 주세요.
final FeedConfig feedConfig = new FeedConfig.Builder(context, "Feed_Unit_Id")
    .feedHeaderViewAdapterClass(DefaultFeedHeaderViewAdapter.class)
    .build();
final FeedHandler feedHandler = new FeedHandler(feedConfig);

// Preload 를 사용하지 않을 경우 다음을 바로 적용
// feedHandler.startFeedActivity(this);

[2] 만약 Preload() 를 사용하지 않을 경우, startFeedActivity()를 호출하여 Feed 액티비티를 실행합니다. (이 경우, 유저에게 보여줄 수 있는 광고가 없어 빈 화면이 노출 될 수 있습니다.)

[3] 오퍼월을 Feed 에 연동하는 경우, FeedConfig 설정에 autoLoadingEnabled 값을 true 로 설정해야 합니다.

final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
                                ...
                                .autoLoadingEnabled(true)
                                ...
                                .build();

(선택 사항) Feed의 광고 현황 확인

[1] Feed 로 진입하는 경로가 항시 노출되어야 하는 상황이 아닌 경우, Preload 를 사용하여 현재 노출 가능한 광고가 있는지 확인하여 광고가 있을 경우, 피드로 진입하도록 사용할 수 있습니다.

feedHandler.preload(new FeedHandler.FeedPreloadListener() {
    @Override
    public void onPreloaded() {
        // 노출 가능한 광고의 개수를 알 수 있습니다.
        int feedAdSize = feedHandler.getSize();
        // 적립 가능한 총 포인트 금액을 알 수 있습니다.
        int feedTotalReward = feedHandler.getTotalReward();
    }

    @Override
    public void onError(AdError error) {
        // 광고가 없을 경우 호출됩니다. error를 통해 발생한 error의 원인을 알 수 있습니다
    }
});

[2] (중요) Preload 를 사용할 경우, 다음의 주의사항을 고려하여 사용해야 합니다.


연동하기

2. 추가 설정, 기능을 적용하기

다음 항목에 대한 확인이 필요합니다.

디자인 커스터마이즈 전 확인 사항

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

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

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

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

툴바 Customization

다음 코드를 통해 피드의 툴바 디자인을 변경할 수 있습니다.

[1] FeedToolbarHolder를 구현하는 class 를 생성하여, 상단의 툴바를 변경할 수 있습니다.

public class CustomFeedToolbarHolder implements FeedToolbarHolder {
    private FeedActivityToolbar toolbar;

    @Override
    public View getView(final Activity activity) {
        // 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);
        toolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                activity.finish();
            }
        });
        toolbar.setTitle("BuzzAdBenefit 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
    }
}

[2] FeedConfig 빌드 시점에 해당 FeedToolbarHolder class 를 지정합니다.

final FeedConfig feedConfig = new FeedConfig.Builder(context, "Feed_Unit_ID")
	...
    .feedToolbarHolderClass(CustomFeedToolbarHolder.class)
    .build();

다음 코드를 통해 피드의 툴바 Height 를 변경할 수 있습니다.

// AndroidManifest.xml
<activity
    android:name="com.buzzvil.buzzad.benefit.presentation.feed.FeedActivity"
    android:theme="@style/AppTheme"
    tools:replace="android:theme"/>

// styles.xml
<style name="AppTheme" parent=...>
    <!-- Customize your theme here. -->
    ...
    <item name="actionBarSize>DESIRED_ACTION_BAR_HEIGHT</item>
</style>

헤더 Customization

다음 코드를 통해 헤더 영역을 변경할 수 있습니다. 유저에게 해당 피드가 어떤 공간인지 상세히 안내할 수 있습니다.

[1] FeedHeaderViewAdapter 를 구현하는 class 를 생성합니다.

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);
        }
    }
}


[2] FeedConfig 빌드 시점에 해당 FeedHeaderViewAdapter class 를 지정합니다.

final FeedConfig feedConfig = new FeedConfig.Builder(context, "Feed_Unit_ID")
	...
    .feedHeaderViewAdapterClass(CustomFeedHeaderViewAdapter.class)
    .build();

광고 리스트 아이템 영역 Customization

다음 코드를 통해 광고 리스트 아이템 뷰를 변경하고, 광고 이벤트에 대한 콜백을 등록할 수 있습니다.

[1] AdsAdapter 를 상속받는 class 를 구현합니다.

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.bz_view_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();
        final Creative.Type creativeType = ad.getCreative() == null ? null : ad.getCreative().getType();

        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);
        ctaView.setBackgroundColor(Color.parseColor(“#3976FD”)); // (선택) 변경하려는 "색상코드"
        final CtaPresenter ctaPresenter = new CtaPresenter(ctaView); // CtaView should not be null
        ctaPresenter.bind(nativeAd);

        if (mediaView != null) {
            mediaView.setCreative(ad.getCreative());
            mediaView.setVideoEventListener(new VideoEventListener() 
                // Override and implement methods
            });
        }
        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) {

            }

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

            }

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

            }

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

[2] FeedConfig 빌드 시점에 해당 AdsAdapter class 를 지정합니다.

final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
	...
    .adsAdapterClass(CustomAdsAdapter.class)
    .build();

CtaView (버튼) Customization

[1] 다음 코드를 통해 CtaView를 다른 모양의 View로 만들 수 있습니다.

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

    final Ad ad = nativeAd.getAd();
    final Creative.Type creativeType = ad.getCreative() == null ? null : ad.getCreative().getType();
    
    ...
    // View를 상속받는 CustomCtaView class를 생성하여 원하는 모양의 CtaView를 사용 할 수 있습니다.
    final CustomCtaView customizedCtaView = view.findViewById(R.id.customCtaView);

    // 아래 정보를 이용하여 cta view를 업데이트
    final String callToAction = nativeAd.getAd().getCallToAction();
    final int reward = nativeAd.getAd().getReward(); // 주의사항 참조
    final boolean participated = nativeAd.isParticipated();
    
    // 다음과 같은 function을 CustomCtaView class에 생성하여 텍스트 및 아이콘을 업데이트 할 수 있습니다.
    customizedCtaView.setCtaText(callToAction);
    customizedCtaView.setRewardText(reward);
    customizedCtaView.setRewardIcon(R.drawable.your_reward_icon);
    customizedCtaView.setParticiapted(participated); // 참여 완료된 광고에 대해서 UI를 처리하는 부분
    customizedCtaView.setBackgroundColor(Color.parseColor(“#3976FD”)); // 변경하려는 "색상코드"

    ...

    final List<View> clickableViews = new ArrayList<>();
    clickableViews.add(customizedCtaView);
    ...

    nativeAdView.addOnNativeAdEventListener(new NativeAdView.OnNativeAdEventListener() {
        ...

        @Override
        public void onParticipated(@NonNull NativeAdView view, @NonNull NativeAd nativeAd) {
            // 매체사 기획에 따라 UI 처리
            // Customize 된 CtaView를 participated 상태에 맞게 업데이트
            final boolean participated = nativeAd.isParticipated();
            customizedCtaView.setParticipated(participated);
        }
    });
}

[2] 다음의 주의사항을 고려하여 사용해야 합니다.

if (reward > 0) {
    customizedCtaVIew.showRewardImage(CtaView.ImageType.Default);
    customizedCtaVIew.setRewardText(String.format(Locale.US, "+%d", reward));
} else {
    customizedCtaVIew.showRewardImage(null);
    customizedCtaVIew.setRewardText(null);
}

Image 타입의 광고 서포트

할당받는 광고의 수를 극대화 하기 위해, 기존에 버즈스크린 잠금화면에 사용하는 Full Screen 광고를 이미지의 일부를 Cropping 한 후 사용 (이하 "Image 타입의 광고")하실 수 있습니다. (자세한 구현 사항은 아래 그림 참조)

[1] View의 Height 조정: Image 타입의 광고는 기존의 mediaView 보다 상하 길이가 길기 때문에, customization 하시는 모든 view의 height를 wrap_content로 적용하고 기타 height를 고정하는 로직을 제거해야 합니다.

[2] View의 레이아웃 조정: NativeAd 를 이용해서 광고 뷰를 그리는 과정에서 현재 타입이 Image 인지 확인하여 title, description 을 위한 layout 을 없앱니다.

if (Creative.Type.IMAGE.equals(ad.getCreative().getType())) {
    titleLayout.setVisibility(View.GONE);
    descriptionView.setVisibility(View.GONE);
} else {
    titleLayout.setVisibility(View.VISIBLE);
    descriptionView.setVisibility(View.VISIBLE);
}

[3] FeedConfig 설정: FeedConfig 빌드 시점에 imageTypeEnabled 를 true 로 설정해서 Image 타입 광고를 받을 수 있게 합니다.

final FeedConfig feedConfig = new FeedConfig.Builder(context, "YOUR_FEED_UNIT_ID")
	...
	.imageTypeEnabled(true)
	.build();

FeedFragment를 사용한 방식

기본적으로 제공되는 FeedActivity가 아닌 다른 방식으로 Feed를 보여주고 싶을때, FeedFragment를 사용할 수 있습니다. FeedFragment 의 기본 사용 방법은 다음과 같습니다.

<fragment
    android:id="@+id/feed_fragment"
    android:name="com.buzzvil.buzzad.benefit.presentation.feed.FeedFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

// XML에 추가 후 아래와 같이 init을 해야합니다
final FeedFragment feedFragment = (FeedFragment) getSupportFragmentManager().findFragmentById(R.id.feed_fragment);
if (feedFragment != null) {
    feedFragment.init(YOUR_FEED_CONFIG);
}

[1] FeedFragment에서도 FeedActivity와 마찬가지로 FeedHandler를 생성한 후 preload()를 호출하고 startFeedActivity()를 호출하지 않으면 Preload와 관련한 기능을 모두 사용할 수 있습니다.

[2] FeedFragment에서도 FeedActivity에서 제공하는 Customization 옵션 중 Toolbar를 제외한 AdsAdapter와 Header, Image 타입 광고의 경우 feedConfig에 추가하는 방식을 통해 Customize가 가능합니다.