Нативная реклама

Нативная реклама (Native) — реклама, внешний вид которой может определяться на стороне приложения. Эта особенность позволяет изменять визуальный стиль объявлений и места их размещения с учетом особенностей дизайна приложения.

Примечание

Пример работы всех типов форматов есть в демопроекте.

Сущность

Описание

onAdFailedToLoad

При получении ошибки в onAdFailedToLoad() не пытайтесь загружать новое объявление снова из этого же метода.

NativeAdRequestConfiguration

В параметрах запроса вы можете указать идентификатор рекламного блока (RM-id), способ загрузки изображения, возраст, пол и другие данные, которые могут повысить релевантность показов. Подробнее читайте в разделе Таргетирование рекламы.

adUnitId

Используйте:

  • development mode — для работы с демоблоками;

  • production mode — для работы с R-M-XXXXXX-Y (уточните реальный ID в интерфейсе Рекламной сети Яндекса). R-M-XXXXXX-Y — это вид рабочего рекламного ID, по которому будут приходить разные креативы.

Пример создания нативной рекламы

Добавьте создание класса CustomNative и NativeAdView.xml:

class CustomNativeAdActivity : AppCompatActivity(), NativeAdEventListener {

    private val nativeAdView get() = binding.nativeAd.root
    private val additionalContainer get() = binding.additionalContainer
    private val adUnitId = "demo-native-app-yandex" // Use R-M-XXXXXX-Y or demo-block (look for the description below)
    private var nativeAdLoader: NativeAdLoader? = null
    private lateinit var binding: ActivityCustomNativeAdBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityCustomNativeAdBinding.inflate(layoutInflater)
        binding.btnShowBanner.setOnClickListener {
            loadNative()
        }
        setContentView(binding.root)
    }

    private fun loadNative() {
        createNative()
        additionalContainer.removeAllViews()
        nativeAdView.isVisible = false
        nativeAdLoader?.loadAd(buildAdRequestConfiguration())
    }

    private fun createNative() {
        nativeAdLoader = NativeAdLoader(this).apply {
            setNativeAdLoadListener(object : NativeAdLoadListener {
                override fun onAdLoaded(ad: NativeAd) {
                    bindNative(ad)
                }
                override fun onAdFailedToLoad(error: AdRequestError) {}
            })
        }
    }

    private fun buildAdRequestConfiguration(): NativeAdRequestConfiguration {
        return NativeAdRequestConfiguration
            .Builder(adUnitId)
            .setShouldLoadImagesAutomatically(true)
            .build()
    }

    private fun bindNative(nativeAd: NativeAd) {
        val nativeAdViewBinder = binding.nativeAd.run {
            NativeAdViewBinder.Builder(nativeAdView)
                .setAgeView(age)
                .setBodyView(body)
                .setCallToActionView(callToAction)
                .setDomainView(domain)
                .setFaviconView(favicon)
                .setFeedbackView(feedback)
                .setIconView(icon)
                .setMediaView(media)
                .setPriceView(price)
                .setRatingView(rating)
                .setReviewCountView(reviewCount)
                .setSponsoredView(sponsored)
                .setTitleView(title)
                .setWarningView(warning)
                .build()
        }

        try {
            nativeAd.bindNativeAd(nativeAdViewBinder)
            nativeAd.info?.let { processAdditionalText(it) }
            nativeAd.setNativeAdEventListener(this@CustomNativeAdActivity)
            nativeAdView.isVisible = true
        } catch (exception: NativeAdException) {
            println("Error binding native ad: ${exception.message}")
        }
    }

    private fun processAdditionalText(rawJson: String) {
        try {
            val jsonObject = JSONObject(rawJson)
            val contentJSONArray = jsonObject.getJSONArray("content")

            repeat(contentJSONArray.length()) { index ->
                val elementName = contentJSONArray.getString(index)
                val elementConfiguration = jsonObject.getJSONObject(elementName)

                when (elementName) {
                    "text" -> addTextToLayout(elementConfiguration, index)
                    "button" -> addButtonToLayout(elementConfiguration, index)
                }
            }
        } catch (e: JSONException) {
            println("Error processing additional text: ${e.message}")
        }
    }

    private fun addTextToLayout(textConfiguration: JSONObject, index: Int) {
        val textView = TextView(this).apply {
            setPadding(16)
            text = textConfiguration.getString("value")
        }
        additionalContainer.addView(textView, index)
    }

    private fun addButtonToLayout(buttonConfiguration: JSONObject, index: Int) {
        val buttonView = Button(this).apply {
            setPadding(16)
            text = buttonConfiguration.getString("label")
            val colorHex = buttonConfiguration.getString("backgroundColor")
            setBackgroundColor(Color.parseColor(colorHex))
            setOnClickListener {
                val link = buttonConfiguration.getString("link")
                val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
                startActivity(intent)
            }
        }
        additionalContainer.addView(buttonView, index)
    }

    override fun onDestroy() {
        nativeAdLoader?.setNativeAdLoadListener(null)
        nativeAdLoader = null
        super.onDestroy()
    }

    override fun onAdClicked() {}
    override fun onImpression(impressionData: ImpressionData?) {}
    override fun onLeftApplication() {}
    override fun onReturnedToApplication() {}
}
public class CustomNativeAdActivity extends AppCompatActivity implements NativeAdEventListener {

    private ActivityCustomNativeAdBinding binding;
    private NativeAdLoader nativeAdLoader;
    private NativeAdViewBinder nativeAdViewBinder;

    private final String adUnitId = "demo-native-app-yandex"; // Use R-M-XXXXXX-Y or demo-block (look for the description below)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityCustomNativeAdBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        binding.btnShowBanner.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loadNative();
            }
        });
    }

    private void loadNative() {
        createNative();
        binding.additionalContainer.removeAllViews();
        binding.nativeAd.getRoot().setVisibility(View.GONE);
        if (nativeAdLoader != null) {
            nativeAdLoader.loadAd(buildAdRequestConfiguration());
        }
    }

    private void createNative() {
        nativeAdLoader = new NativeAdLoader(this);
        nativeAdLoader.setNativeAdLoadListener(new NativeAdLoadListener() {
            @Override
            public void onAdLoaded(NativeAd ad) {
                bindNative(ad);
            }
            @Override
            public void onAdFailedToLoad(AdRequestError error) {}
        });
    }

    private NativeAdRequestConfiguration buildAdRequestConfiguration() {
        return new NativeAdRequestConfiguration.Builder(adUnitId)
                .setShouldLoadImagesAutomatically(true)
                .build();
    }

    private void bindNative(NativeAd nativeAd) {
        NativeAdLayoutBinding adBinding = binding.nativeAd;

        // Сборка биндинга вручную (или если auto-generated ViewBinding)
        nativeAdViewBinder = new NativeAdViewBinder.Builder(adBinding.getRoot())
                .setAgeView(adBinding.age)
                .setBodyView(adBinding.body)
                .setCallToActionView(adBinding.callToAction)
                .setDomainView(adBinding.domain)
                .setFaviconView(adBinding.favicon)
                .setFeedbackView(adBinding.feedback)
                .setIconView(adBinding.icon)
                .setMediaView(adBinding.media)
                .setPriceView(adBinding.price)
                .setRatingView(adBinding.rating)
                .setReviewCountView(adBinding.reviewCount)
                .setSponsoredView(adBinding.sponsored)
                .setTitleView(adBinding.title)
                .setWarningView(adBinding.warning)
                .build();

        try {
            nativeAd.bindNativeAd(nativeAdViewBinder);
            String infoJson = nativeAd.getInfo();
            if (infoJson != null) {
                processAdditionalText(infoJson);
            }
            nativeAd.setNativeAdEventListener(this);
            adBinding.getRoot().setVisibility(View.VISIBLE);
        } catch (NativeAdException exception) {
            System.out.println("Error binding native ad: " + exception.getMessage());
        }
    }

    private void processAdditionalText(String rawJson) {
        try {
            JSONObject jsonObject = new JSONObject(rawJson);
            JSONArray contentJSONArray = jsonObject.getJSONArray("content");
            for (int i = 0; i < contentJSONArray.length(); ++i) {
                String elementName = contentJSONArray.getString(i);
                JSONObject elementConfiguration = jsonObject.getJSONObject(elementName);
                switch (elementName) {
                    case "text":
                        addTextToLayout(elementConfiguration, i);
                        break;
                    case "button":
                        addButtonToLayout(elementConfiguration, i);
                        break;
                }
            }
        } catch (JSONException e) {
            System.out.println("Error processing additional text: " + e.getMessage());
        }
    }

    private void addTextToLayout(JSONObject textConfiguration, int index) {
        TextView textView = new TextView(this);
        textView.setPadding(16, 16, 16, 16);
        try {
            textView.setText(textConfiguration.getString("value"));
        } catch (JSONException ignored) { }
        binding.additionalContainer.addView(textView, index);
    }

    private void addButtonToLayout(final JSONObject buttonConfiguration, int index) {
        Button buttonView = new Button(this);
        buttonView.setPadding(16, 16, 16, 16);
        try {
            buttonView.setText(buttonConfiguration.getString("label"));
            String colorHex = buttonConfiguration.getString("backgroundColor");
            buttonView.setBackgroundColor(Color.parseColor(colorHex));
            buttonView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        String link = buttonConfiguration.getString("link");
                        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
                        startActivity(intent);
                    } catch (JSONException ignored) { }
                }
            });
        } catch (JSONException ignored) { }
        binding.additionalContainer.addView(buttonView, index);
    }

    @Override
    protected void onDestroy() {
        if (nativeAdLoader != null) {
            nativeAdLoader.setNativeAdLoadListener(null);
            nativeAdLoader = null;
        }
        super.onDestroy();
    }
    @Override
    public void onAdClicked() {}
    @Override
    public void onImpression(@Nullable ImpressionData impressionData) {}
    @Override
    public void onLeftApplication() {}
    @Override
    public void onReturnedToApplication() {}
}
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CustomNativeAdActivity">

    <Button
        android:id="@+id/btnShowBanner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load NativeAd"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent" />

    <include
        android:id="@+id/native_ad"
        layout="@layout/native_ad_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:visibility="gone"
        app:layout_constraintVertical_weight="1"
        app:layout_constraintBottom_toTopOf="@+id/additional_container"/>

    <LinearLayout
        android:id="@+id/additional_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
<com.yandex.mobile.ads.nativeads.NativeAdView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/native_ad_container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="4dp">

        <androidx.constraintlayout.widget.Group
            android:id="@+id/components"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:constraint_referenced_ids="age,sponsored,feedback,title" />

        <TextView
            android:id="@+id/age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:textColor="#7f7f7f"
            app:layout_constraintBottom_toTopOf="@id/barrier_top"
            app:layout_constraintEnd_toStartOf="@id/sponsored"
            app:layout_constraintHorizontal_chainStyle="spread_inside"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="18+"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/sponsored"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:textColor="#7f7f7f"
            android:textSize="11sp"
            app:layout_constraintBottom_toTopOf="@id/barrier_top"
            app:layout_constraintEnd_toStartOf="@id/feedback"
            app:layout_constraintStart_toEndOf="@id/age"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Yandex.Direct"
            tools:visibility="visible" />

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/feedback"
            android:layout_width="16dp"
            android:layout_height="16dp"
            app:layout_constraintBottom_toTopOf="@id/barrier_top"
            android:src="@drawable/close_button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/sponsored"
            app:layout_constraintTop_toTopOf="parent"
            tools:visibility="visible" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_top"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="age,sponsored,feedback" />

        <ImageView
            android:id="@+id/icon"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            android:layout_marginTop="4dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toTopOf="@id/barrier_body"
            app:layout_constraintEnd_toStartOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/barrier_top"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:maxLines="2"
            android:textSize="15sp"
            style="@style/TextAppearance.Material3.TitleSmall"
            app:layout_constraintBottom_toTopOf="@id/body"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/barrier_top"
            app:layout_constraintVertical_chainStyle="packed"
            tools:text="Yandex.Browser for iPhone and Android"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/body"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:maxLines="3"
            android:textSize="13sp"
            style="@style/TextAppearance.Material3.BodySmall"
            app:layout_constraintBottom_toTopOf="@id/barrier_domain"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/title"
            tools:text="Your bookmarks and favorite sites are available to you on any device with Yandex.Browser. You will never lose them, even if you lose your device or break it."
            tools:visibility="visible" />

        <ImageView
            android:id="@+id/favicon"
            android:layout_width="16dp"
            android:layout_height="16dp"
            android:layout_marginEnd="4dp"
            android:layout_marginRight="4dp"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop"
            app:layout_constraintBottom_toTopOf="@id/barrier_domain"
            app:layout_constraintEnd_toStartOf="@id/domain"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintHorizontal_chainStyle="packed"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/body"
            tools:visibility="visible" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_domain"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="top"
            app:constraint_referenced_ids="domain,favicon" />

        <TextView
            android:id="@+id/domain"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLines="1"
            android:textColor="#7f7f7f"
            android:textSize="13sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@id/barrier_body"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/favicon"
            app:layout_constraintTop_toBottomOf="@id/body"
            tools:text="https://browser.yandex.com"
            tools:visibility="visible" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_body"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="title,icon,body,domain,favicon" />

        <TextView
            android:id="@+id/price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            app:layout_constraintBottom_toTopOf="@id/rating"
            app:layout_constraintEnd_toStartOf="@id/barrier_rating"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/barrier_body"
            tools:text="Free"
            tools:visibility="visible" />

        <com.example.myapplication.RatingView
            android:id="@+id/rating"
            style="?android:attr/ratingBarStyleSmall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginBottom="4dp"
            android:numStars="5"
            android:stepSize="0.5"
            app:layout_constraintBottom_toTopOf="@id/review_count"
            app:layout_constraintEnd_toStartOf="@id/barrier_rating"
            app:layout_constraintHorizontal_chainStyle="spread_inside"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/price"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/review_count"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:maxLines="1"
            android:textColor="#7f7f7f"
            android:textSize="13sp"
            app:layout_constraintBottom_toTopOf="@id/barrier_footer"
            app:layout_constraintEnd_toStartOf="@id/barrier_rating"
            app:layout_constraintHorizontal_bias="0"
            app:layout_constraintStart_toEndOf="@id/barrier_icon"
            app:layout_constraintTop_toBottomOf="@id/rating"
            tools:text="review count"
            tools:visibility="visible" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="end"
            app:constraint_referenced_ids="icon" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_rating"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="end"
            app:constraint_referenced_ids="rating,price,review_count" />

        <Button
            android:id="@+id/call_to_action"
            style="@style/Widget.AppCompat.Button.Borderless"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/call_to_action_background"
            android:maxLines="1"
            android:minHeight="36dp"
            android:textAllCaps="false"
            android:textSize="13sp"
            app:layout_constraintBottom_toTopOf="@id/barrier_footer"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/barrier_rating"
            app:layout_constraintTop_toBottomOf="@id/barrier_body"
            app:layout_constraintHorizontal_bias="1"
            app:layout_constraintVertical_bias="1"
            tools:text="Install"
            tools:visibility="visible" />

        <androidx.constraintlayout.widget.Barrier
            android:id="@+id/barrier_footer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:barrierDirection="bottom"
            app:constraint_referenced_ids="price,domain,call_to_action,review_count" />

        <com.yandex.mobile.ads.nativeads.MediaView
            android:id="@+id/media"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constrainedHeight="true"
            app:layout_constrainedWidth="true"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="4dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/barrier_footer"
            app:layout_constraintBottom_toTopOf="@id/warning"
            tools:visibility="visible" />

        <TextView
            android:id="@+id/warning"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:background="#f5f5f5"
            android:paddingLeft="8dp"
            android:paddingTop="4dp"
            android:paddingRight="8dp"
            android:textColor="#7f7f7f"
            android:textSize="13sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/media"
            android:visibility="gone"
            tools:text="We strongly recommend avoiding this type of website and not clicking the Ignore this warning button!"
            tools:visibility="visible" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</com.yandex.mobile.ads.nativeads.NativeAdView>

Обязательные компоненты нативной рекламы

Компонент в объявлении

Компонент

Тип

Обязательность

Заголовок

title

TextView

Да

Домен

domain

TextView

Да

Предупреждение

warning

TextView

Да

Рекламная и возрастная метка

sponsored

TextView

Да

Значок меню

feedback

ImageView

Да

Медиа

media

MediaView

Да

Иконка приложения

icon

ImageView

Да, для рекламы приложений

Цена

price

TextView

Да, для рекламы приложений

Кнопка действия

callToAction

TextView

Нет

Фавиконка

title

ImageView

Нет

Количество оценок

reviewCount

TextView

Нет

Рейтинг

rating

View implements Rating interface

Нет

Основной текст

body

TextView

Нет

Проверка интеграции

Соберите и запустите проект. Успешную интеграцию SDK можно проверить в Logcat по ключевому слову YandexAds:

[Integration] Ad type native was integrated successfully