Ресурсы: шрифты, изображения и видео

Карточка только ссылается на шрифты, изображения и видео, а загружает и показывает их приложение. На этой странице описано, что для этого нужно подключить на каждой платформе. Свойства элементов описаны на страницах Пользовательские шрифты, Изображения и Видео.

Шрифты

Имя шрифта из параметра font_family приложение сопоставляет с конкретным шрифтом. Для этого на каждой платформе реализуется провайдер шрифтов, который передается в DivKit при инициализации.

Android

На платформе Android для использования пользовательских шрифтов необходимо реализовать интерфейс DivTypefaceProvider.

Пример реализации DivTypefaceProvider

import android.content.Context;
import android.graphics.Typeface;
import androidx.annotation.Nullable;
import com.yandex.div.core.font.DivTypefaceProvider;

public class CustomTypefaceProvider implements DivTypefaceProvider {
    private final Context context;

    // Кэшируем шрифты для повышения производительности
    private Typeface regularTypeface;
    private Typeface mediumTypeface;
    private Typeface lightTypeface;
    private Typeface boldTypeface;

    public CustomTypefaceProvider(Context context) {
        this.context = context;
    }

    @Nullable
    @Override
    public Typeface getRegular() {
        if (regularTypeface == null) {
            regularTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/MyFont-Regular.ttf");
        }
        return regularTypeface;
    }

    @Nullable
    @Override
    public Typeface getMedium() {
        if (mediumTypeface == null) {
            mediumTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/MyFont-Medium.ttf");
        }
        return mediumTypeface;
    }

    @Nullable
    @Override
    public Typeface getLight() {
        if (lightTypeface == null) {
            lightTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/MyFont-Light.ttf");
        }
        return lightTypeface;
    }

    @Nullable
    @Override
    public Typeface getBold() {
        if (boldTypeface == null) {
            boldTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/MyFont-Bold.ttf");
        }
        return boldTypeface;
    }

    // Опционально: переопределите метод getTypefaceFor для более точного контроля
    // над соответствием весов шрифтов
    @Nullable
    @Override
    public Typeface getTypefaceFor(int weight) {
        if (weight >= 0 && weight < 350) {
            return getLight();
        } else if (weight >= 350 && weight < 450) {
            return getRegular();
        } else if (weight >= 450 && weight < 650) {
            return getMedium();
        } else {
            return getBold();
        }
    }
}

Интеграция в приложение

import com.yandex.div.core.Div2Context;
import com.yandex.div.core.DivConfiguration;
import java.util.Collections;

// При инициализации DivKit: activity — ваша Activity, imageLoader — реализация DivImageLoader
DivConfiguration configuration = new DivConfiguration.Builder(imageLoader)
    // Шрифт по умолчанию: для текста без font_family и для неизвестных имен
    .typefaceProvider(new CustomTypefaceProvider(activity))
    // Шрифты, которые выбираются по имени из font_family
    .additionalTypefaceProviders(
        Collections.singletonMap("MyCustomFont", new CustomTypefaceProvider(activity))
    )
    // другие настройки
    .build();

Div2Context div2Context = new Div2Context(
    activity,
    configuration
);

iOS

На платформе iOS для использования пользовательских шрифтов необходимо реализовать протокол DivFontProvider.

Добавление шрифтов в проект

  1. Добавьте файлы шрифтов (.ttf или .otf) в ваш проект
  2. Убедитесь, что шрифты включены в ресурсы приложения (Bundle Resources)
  3. Добавьте информацию о шрифтах в Info.plist в раздел Fonts provided by application:
<key>UIAppFonts</key>
<array>
    <string>MyFont-Regular.ttf</string>
    <string>MyFont-Medium.ttf</string>
    <string>MyFont-Light.ttf</string>
    <string>MyFont-Bold.ttf</string>
</array>

Пример реализации DivFontProvider

import UIKit
import DivKit
import VGSL

class CustomFontProvider: DivFontProvider {
    func font(family: String, weight: DivFontWeight, size: CGFloat) -> Font {
        // Если указано конкретное семейство шрифтов
        if family == "MyCustomFont" {
            let fontName: String
            switch weight {
            case .light:
                fontName = "MyFont-Light"
            case .regular:
                fontName = "MyFont-Regular"
            case .medium:
                fontName = "MyFont-Medium"
            case .bold:
                fontName = "MyFont-Bold"
            }

            if let font = UIFont(name: fontName, size: size) {
                return Font(uiFont: font)
            }
        }

        // Возвращаем системный шрифт, если пользовательский не найден
        let uiFont: UIFont
        switch weight {
        case .light:
            uiFont = UIFont.systemFont(ofSize: size, weight: .light)
        case .regular:
            uiFont = UIFont.systemFont(ofSize: size, weight: .regular)
        case .medium:
            uiFont = UIFont.systemFont(ofSize: size, weight: .medium)
        case .bold:
            uiFont = UIFont.systemFont(ofSize: size, weight: .bold)
        }

        return Font(uiFont: uiFont)
    }

    // Метод для получения шрифта по числовому весу уже реализован в расширении протокола
}

Интеграция в приложение

import DivKit

// При инициализации DivKit
let components = DivKitComponents(
    divCustomBlockFactory: nil,
    fontProvider: CustomFontProvider(),
    // другие параметры
)

Web

На платформе Web для использования пользовательских шрифтов необходимо:

  1. Подключить шрифты стандартными способами CSS (через @font-face или внешние сервисы)
  2. Использовать параметр typefaceProvider при инициализации DivKit

Подключение шрифтов через CSS

Имя шрифта в CSS не обязательно должно совпадать с именем в JSON.

Не забудьте предоставить все варианты начертаний и насыщенности, которые понадобятся в вашей разметке (в данном примере добавлены 400, 500 и 700). Ещё один вариант - использовать вариативные шрифты.

@font-face {
  font-family: 'MyCustomFont';
  src: url('fonts/MyFont-Regular.woff2') format('woff2'),
       url('fonts/MyFont-Regular.woff') format('woff');
  font-weight: 400;
  font-style: normal;
}

@font-face {
  font-family: 'MyCustomFont';
  src: url('fonts/MyFont-Medium.woff2') format('woff2'),
       url('fonts/MyFont-Medium.woff') format('woff');
  font-weight: 500;
  font-style: normal;
}

@font-face {
  font-family: 'MyCustomFont';
  src: url('fonts/MyFont-Bold.woff2') format('woff2'),
       url('fonts/MyFont-Bold.woff') format('woff');
  font-weight: 700;
  font-style: normal;
}

Использование typefaceProvider

import { render } from '@divkitframework/divkit/client';

render({
    id: 'my-divkit-container',
    target: document.querySelector('#root'),
    json: {
        card: {
            log_id: 'custom_fonts_example',
            states: [{
                state_id: 0,
                div: {
                    type: 'text',
                    font_family: 'MyCustomFont',
                    font_weight_value: 500,
                    text: 'Текст с пользовательским шрифтом'
                }
            }]
        }
    },
    typefaceProvider: (fontFamily, opts) => {
        // Можно реализовать сопоставление имен шрифтов из JSON с CSS
        if (fontFamily === 'MyCustomFont') {
            return 'MyCustomFont, sans-serif';
        }

        // Для варианта по умолчанию можно вернуть пустую строку
        return '';
    }
});

Рекомендации

  1. Оптимизация производительности: кэшируйте экземпляры шрифтов, чтобы не загружать их повторно.
  2. Поддержка разных начертаний: реализуйте поддержку разной толщины шрифта, чтобы дизайн оставался гибким.
  3. Запасные варианты: всегда предусматривайте запасной шрифт на случай, если пользовательский шрифт не загрузится.
  4. Предзагрузка шрифтов: на платформе Web рекомендуется предзагружать шрифты, чтобы текст не мерцал при загрузке страницы.
  5. Тестирование: проверяйте отображение шрифтов на разных устройствах и в разных условиях.

Изображения

Изображения загружает приложение: на Android для этого подключается загрузчик изображений, на iOS локальные изображения берутся из bundle приложения.

iOS

На iOS, для ограничения доступа к ресурсам, все изображения ассетов divkit должны начинаться с префикса 'divkit.'. Например, если вы используете "image_url": "divkit-asset://image.png", изображение должно иметь имя divkit.image.png в main bundle вашего приложения.

Поддержка нескольких bundle

iOS поддерживает указание конкретного bundle для загрузки изображений через параметр bundle в URL:

{
  "type": "image",
  "image_url": "divkit-asset://image.png?bundle=com.example.app"
}

Эта функция полезна в модульных архитектурах или при использовании нескольких bundle в приложении. На Android параметр bundle игнорируется.

Android

Поддержка загрузчиков изображений

DivKit для Android поддерживает популярные библиотеки загрузки изображений:

  • Coil - современный загрузчик изображений на Kotlin
  • Glide - традиционный загрузчик изображений

Оба загрузчика поддерживают все форматы изображений и эффекты, включая анимированные WebP и GIF.

Ограничение размера изображений

Для предотвращения крашей при загрузке больших изображений и GIF-файлов, DivKit автоматически ограничивает размер bitmap. Эта функция включена по умолчанию и помогает избежать проблем с памятью на устройствах.

Как работает ограничение:

  • Максимальный размер bitmap определяется как максимальное разрешение экрана устройства
  • Изображения, превышающие этот размер, автоматически масштабируются вниз
  • Для SVG изображений применяется отдельный механизм ограничения
  • Функция поддерживается загрузчиками CoilDivImageLoader и GlideDivImageLoader

Настройка ограничения:

Ограничение размера bitmap включено по умолчанию. Если вам нужно отключить эту функцию (например, для работы с очень большими изображениями), вы можете настроить это в реализации загрузчика изображений:

// Пример для CoilDivImageLoader
val imageLoader = CoilDivImageLoader(
    context = context,
    limitImageBitmapSizeEnabled = false // Отключить ограничение
)

Важно

Отключение ограничения размера bitmap может привести к крашам приложения при работе с очень большими изображениями. Используйте эту настройку с осторожностью.

Видео

DivKit позволяет использовать стандартный плеер или собственный, например для воспроизведения роликов не поддерживаемого стандартным плеером формата или для реализации других необходимых функций.

Стандартный плеер

По умолчанию DivKit использует стандартный плеер для всех платформ за исключением Android, где требуется подключить плеер явно.

Android

В качестве стандартного плеера для Android в DivKit используется ExoPlayer.

Так как инициализация ExoPlayer тяжелая операция, плеер подключается только при явном указании его необходимости в классе DivConfiguration. Если вы не планируете использовать видеоплеер в своем приложении, не вызывайте метод .divPlayerFactory в билдере (строителе) класса DivConfiguration.

Для работы со стандартным плеером передайте ExoDivPlayerFactory из пакета com.yandex.div.video.m3 (модуль div-video-m3) в билдер DivConfiguration как аргумент метода .divPlayerFactory().

В общем случае класс DivConfiguration для работы с поддержкой видео будет выглядеть следующим образом:

DivConfiguration.Builder(imageLoader)
     .divPlayerFactory(ExoDivPlayerFactory(context))
     .build()

Если в классе DivConfiguration не будет подключен плеер, то при рендеринге экрана с элементом video на его месте будет находиться пустой блок соответствующих размеров.

Остальные платформы

На iOS и в Web подключать стандартный плеер не нужно: при создании элемента video используется стандартный для платформы видеоплеер.

Поддержка кастомного плеера

Собственный видеоплеер подключается на каждой платформе по-своему:

  • Android: фабрика, реализующая интерфейс DivPlayerFactory, передается в метод .divPlayerFactory() билдера DivConfiguration. Подробности приведены ниже.
  • iOS: фабрика, реализующая протокол PlayerFactory, передается в DivKitComponents в параметре playerFactory.
  • Web: провайдер плеера передается в функцию render в параметре videoPlayerProvider, см. Параметры функции render.

Android

Чтобы подключить в DivKit собственный видеоплеер, необходимо создать фабрику, которая будет по запросу DivKit-приложения предоставлять плеер и представление (view) для него. Для этого фабрика должна реализовывать интерфейс DivPlayerFactory.

Видеоплеер, создаваемый фабрикой, должен реализовывать интерфейс DivPlayer и таким образом являться прокси для вызовов из DivKit. При реализации кастомного плеера учитывайте, что свойства типа JSON-объект (например, player_settings_payload) могут содержать выражения, которые необходимо обрабатывать с помощью expressionResolver.

Примечание

Когда player_settings_payload изменяется во время работы приложения, DivKit автоматически вызывает метод setSource с обновленными настройками. Убедитесь, что ваш плеер корректно обрабатывает динамическое обновление настроек воспроизведения.

Например, для ExoPlayer реализация методов DivPlayer на языке Kotlin выглядит следующим образом.

Посмотреть пример кода
val player: ExoPlayer by lazy {
     ExoPlayer.Builder(context).build()
     }

     override fun play() {
     player.play()
     }

     override fun pause() {
     player.pause()
     }

     override fun seek(toMs: Long) {
     player.seekTo(toMs)
     }

     override fun setSource(sourceVariants: List<DivVideoSource>, config: DivPlayerPlaybackConfig) {
     val mediaSource = sourceVariants.toMediaSource()

     player.setMediaSource(mediaSource)
     player.prepare()
     }

     override fun release() {
     player.release()
     }

Узнать больше

Вы можете обсуждать интересующие вас темы в сообществе пользователей DivKit в Telegram: https://t.me/divkit_community_ru.

Репозиторий DivKit