В предыдущей статье "Интеграция Android PlatformView и проблемы с клавиатурой" были рассмотрены реализация и проблемы использования PlatformView
в гибридной разработке на платформе Flutter. Для интеграции таких компонентов, как WebView
, MapView
, ранее использовался подход с VirtualDisplays
.
С версией Yöntem 1.20 официальное сообщество начало экспериментировать с новым режимом «гибридного состава», аналогичным тому, что используется на iOS. В этой статье мы сравним и объясним использование нового режима «гибридного состава».
Помните, это версия 1.20, а не 1.2!
До версии 1.20 в Flutter содержимое, которое должно было быть отображено через AndroidView
, рисовалось в VirtualDisplays
. Затем изображение в памяти можно было получить через Surface
этого VirtualDisplay
.
VirtualDisplay
работает как виртуальная область отображения, которая обычно используется вместе сDisplayManager
. Обычно он применяется при работе со вторым экраном или записи экрана.VirtualDisplay
рисует содержимое виртуальной области отображения на поверхности (Surface
).
Как показано на приведённой выше диаграмме, VirtualDisplay
создаёт виртуальную область отображения, которая затем передаётся в SurfaceTexture
, который в свою очередь передаётся в SurfaceView
. SurfaceView
отображает изображение на устройстве.
// Создание VirtualDisplay
VirtualDisplay virtualDisplay = mMediaProjection.createVirtualDisplay(
"My App Name",
width,
height,
density,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, // Surface полученный из SurfaceTexture
null);
// Запись экрана
MediaRecorder mediaRecorder = new MediaRecorder();
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setVideoEncodingBitRate(512 * 1000);
mediaRecorder.setVideoFrameRate(frameRate);
mediaRecorder.setVideoSize(width, height);
mediaRecorder.setOutputFile(outputPath);
mediaRecorder.setPreviewDisplay(surface); // Surface полученный из SurfaceTexture
mediaRecorder.prepare();
mediaRecorder.start();
// Закрытие VirtualDisplay после завершения записи
virtualDisplay.release();
VirtualDisplay
создает виртуальную область отображения, которая затем передается в SurfaceTexture
. Этот SurfaceTexture
передается в SurfaceView
, который отображает изображение на устройстве. Таким образом, VirtualDisplay
позволяет нам использовать вторые экраны или записывать экран устройства.
С версией 1.20 Flutter начал использовать новый метод «гибридного состава». Этот метод использует HybridComposition
вместо VirtualDisplay
.
HybridComposition
позволяет Flutter использовать нативные Android UI компоненты, такие какWebView
,MapView
, и другие, прямо внутри Flutter приложения. Это делает интеграцию этих компонентов более простой и эффективной.
На приведенной выше диаграмме видно, что HybridComposition
позволяет Flutter использовать нативные Android UI компоненты, такие как WebView
, MapView
, и другие, прямо внутри Flutter приложения. Это делает интеграцию этих компонентов более простой и эффективной.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter Hybrid Composition Example')),
body: Column(
children: <Widget>[
AndroidView(
viewType: 'com.example.myapp',
onPlatformViewCreated: (int id) {},
),
WebView(
initialUrl: 'https://flutter.dev/',
javascriptMode: JavascriptMode.unrestricted,
),
],
),
),
);
}
}
Новый метод «гибридного состава» предлагает несколько преимуществ:
В данной статье мы рассмотрели старый метод VirtualDisplay
и новый метод «гибридного состава» в Flutter. Новый метод предоставляет множество преимуществ, особенно когда дело доходит до интеграции нативных Android UI компонентов.## 2. Интеграция гибридного компонента
Благодаря усилиям команды разработчиков и сообщества, начиная с версии 1.20, был представлен новый способ реализации PlatformView
— «гибридный компонент». Этот подход решает большинство проблем, связанных с PlatformView
на Android, такие как неожиданное исчезновение веб-интерфейса после открытия клавиатуры на устройствах Huawei. Для использования Hybrid Composition
требуется использовать объекты PlatformViewLink, AndroidViewSurface и PlatformViewsService. Сначала нам нужно создать Dart-компонент:
viewType
в PlatformViewLink
регистрируется имя, соответствующее нативному слою, что аналогично регистрации PlatformView
;surfaceFactory
возвращается AndroidViewSurface
, который используется для отрисовки и принятия событий сенсора;onCreatePlatformView
использует PlatformViewsService
для инициализации AndroidViewSurface
и необходимых параметров, а также запускает отображение нативного слоя через Engine.Widget build(BuildContext context) {
// Это используется на стороне платформы для регистрации представления.
final String viewType = 'hybrid-view-type';
// Передаются параметры на сторону платформы.
final Map<String, dynamic> creationParams = <String, dynamic>{};
``` return PlatformViewLink(
viewType: viewType,
surfaceFactory:
(BuildContext context, PlatformViewController controller) {
return AndroidViewSurface(
controller: controller,
gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>[],
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView:
(PlatformViewCreationParams params) {
return PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: StandardMessageCodec(),
)..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)..create();
},
);
}Перейдём к нативной части на Android. В нативной части мы создаём компонент, расширяющий `PlatformView`, и возвращаем нужный элемент управления через метод `getView`.
```dart
package dev.flutter.example;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.plugin.platform.PlatformView;
Класс NativeView
реализует интерфейс PlatformView
.
class NativeView implements PlatformView {
@NonNull private final TextView textView;
NativeView(@NonNull Context context, int id, @Nullable Map<String, Object> creationParams) {
textView = new TextView(context);
textView.setTextSize(72);
textView.setBackgroundColor(Color.rgb(255, 255, 255));
textView.setText("Рenders on native Android view (id: " + id + ")");
}
@NonNull
@Override
public View getView() {
return textView;
}
@Override
public void dispose() {}
}
Затем следует создание экземпляра PlatformViewFactory
, который будет использоваться методом create
для загрузки и инициализации PlatformView
.
package dev.flutter.example;
import android.content.Context;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;
import java.util.Map;
class NativeViewFactory extends PlatformViewFactory {
@NonNull private final BinaryMessenger messenger;
@NonNull private final View containerView;
NativeViewFactory(@NonNull BinaryMessenger messenger, @NonNull View containerView) {
super(StandardMessageCodec.INSTANCE);
this.messenger = messenger;
this.containerView = containerView;
}
@NonNull
@Override
public PlatformView create(@NonNull Context context, int id, @Nullable Object args) {
final Map<String, Object> creationParams = (Map<String, Object>) args;
return new NativeView(context, id, creationParams);
}
}
```Наконец, в `MainActivity` через `flutterEngine` метод `getPlatformViewsController` регистрируется `NativeViewFactory`.
```dart
package dev.flutter.example;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
flutterEngine
.getPlatformViewsController()
.getRegistry()
.registerViewFactory("hybrid-view-type", new NativeViewFactory(null, null));
}
}
Конечно, если требуется включить Hybrid Composition
на Android, необходимо добавить следующий код в файл AndroidManifest.xml
для активации конфигурации:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.flutter.example">
<application
android:name="io.flutter.app.FlutterApplication"
android:label="hybrid"
android:icon="@mipmap/ic_launcher">
<!-- ... -->
<!-- Hybrid composition -->
<meta-data
android:name="io.flutter.embedded_views_preview"
android:value="true" />
</application>
</manifest>
Кроме того, официальные представители сообщили, что производительность Hybrid Composition
на устройствах с Android 10 и выше достаточно хорошая, а на версиях ниже Android 10 скорость отображения Flutter-интерфейса на экране замедляется. Это замедление вызвано необходимостью синхронизации кадров Flutter с системой представлений Android.
Чтобы минимизировать это влияние, следует избегать отображения нативных компонентов во время выполнения анимаций на Dart. Например, можно использовать placeholder скриншот нативного компонента и отображать его вместо реального компонента во время анимации.## 3. Характеристики и принцип реализации Hybrid Composition
Для объяснения реализации Hybrid Composition
стоит рассмотреть новый объект, добавленный в этот выпуск — FlutterImageView
.
FlutterImageView
не является обычнымImageView
.
На самом деле, смешивание нативных компонентов в Hybrid Composition
осуществляется через FlutterImageView
. Сам FlutterImageView
представляет собой обычный нативный View
, который реализует интерфейс RenderSurface
для предоставления части возможностей FlutterSurfaceView
.
Внутри FlutterImageView
используются три основных класса: ImageReader
, Image
и Bitmap
. В частности:
ImageReader
можно рассматривать как объект, способный хранить данные Image
, предоставляющий Surface
для рисования данных Image
из нативных слоёв.Image
представляет собой пиксельные данные, содержащие ByteBuffers
, обычно используемые в нативной среде, например, при работе с камерой.Bitmap
преобразует Image
в битмап, которую затем можно отрисовать внутри FlutterImageView
с помощью Canvas
.Как видно, FlutterImageView
может предоставлять Surface
, получать данные Image
с Surface
и отрисовывать их с использованием Bitmap
. В FlutterImageView
предоставляются два типа SurfaceKind
: background
и overlay
. В частности:
background
используется для режима отрисовки FlutterView
по умолчанию, то есть это значение по умолчанию для рендера основного приложения Flutter. Таким образом, FlutterView
имеет три типа RenderMode
: surface
, texture
и image
.- overlay
предназначен для использования в режиме Hybrid Composition
для совместной работы с PlatformView
.Кроме того, можно заметить, что в PlatformViewsController
присутствуют методы createAndroidViewForPlatformView
и createVirtualDisplayForPlatformView
. Это также является способом, которым команда Flutter обеспечивает совместимость с VirtualDisplay
по умолчанию вместе с предоставлением Hybrid Composition
.
В случае
Hybrid Composition
слой Dart вызывает методcreate
каналаPlatformViewsChannel
черезPlatformViewsService
, после чего отправляет запросPlatformViewCreationRequest
, который проверяет наличиеusesHybridComposition
. Если значение равно true, то следующий шаг будет использовать методcreateAndroidViewForPlatformView
.
Как работает FlutterImageView
в режиме Hybrid Composition
?
Сначала запустите пример из второго раздела, открыв границы макета на Android-устройстве. Вы увидите белый прямоугольник со значком Re
по центру экрана. Анализируя границы макета, можно понять, что этот белый прямоугольник — это нативный компонент.
Затем используйте ту же самую кодировку для создания ещё одного белого прямоугольника с значком Re
в другом месте. Вы увидите, что теперь на правом верхнем углу экрана появился ещё один белый прямоугольник с значком Re
и границами макета. Это показывает, что PlatformView
в режиме Hybrid Composition
отображается с помощью некоторого нативного компонента.
Но возникает вопрос: что особенного в том, чтобы размещать нативные компоненты в Flutter? Это просто слойовой состав? Добавьте два белых прямоугольника с значком Re
рядом друг с другом, а затем вместо использования PlatformView
создайте синюю надпись Re
с помощью стандартного Text
.
Получили ли вы это? Без использования PlatformView
надпись Text
синего цвета Re
действительно отображается поверх белого непрозрачного нативного прямоугольника Re
!> Возможно, некоторые из вас скажут, что в этом нет ничего особенного? Но те, кто знает основные принципы работы с Flutter
, должны понимать, что по умолчанию Flutter
на уровне нативной реализации представляет собой SurfaceView
. Далее движок Engine
отрисовывает все элементы управления на этот Surface
.
Однако что вы видите сейчас? Наш текстовый элемент
Text
на уровне Dart с синей надписью "Re" действительно отображается поверх белого маленького квадрата "Re". Это указывает на то, чтоHybrid Composition
не сводится к простому расположению нативных компонентов поверхFlutter
.Затем мы заметили ещё одну странную проблему — синий текст "Re", созданный с помощью стандартногоText
вFlutter
, также имеет границы нативного компонента? Поэтому мы добавили желтый текст "Re" и красный текст "Re" с использованием стандартногоText
, и можно заметить, что границы компонента отображаются только там, где текст пересекается сPlatformView
.
При перемещении желтого текста "Re" вниз, границы этого текста тоже исчезают, поэтому можно сделать вывод, что Hybrid Composition
позволяет компонентам Dart отображаться поверх нативных компонентов благодаря тому, что при пересечении с PlatformView
происходит перерисовка через какой-то нативный компонент.
Используя Layout Inspector
, можно заметить, что перекрывающиеся компоненты Text
отображаются через слой FlutterImageView
.
Кроме того, есть ещё одна интересная особенность — когда несколько стандартных компонентов Flutter
отображаются одновременно в одном PlatformView
, они используют один и тот же FlutterImageView
.
Если эти компоненты находятся в разных областях, каждый из них использует свой собственный FlutterImageView
. Также стоит отметить, что стандартный PlatformView
, используемый в Hybrid Composition
, является FlutterMutatorView
.
На самом деле FlutterMutatorView
используется для изменения положения и матрицы нативных компонентов, чтобы они корректно располагались внутри FlutterView
. Общая структура взаимодействия PlatformView
в режиме Hybrid Composition
следующая:**Таким образом,
PlatformView
добавляется в FlutterView
через FlutterMutatorView
, а затем с помощью возможностей FlutterImageView
производятся манипуляции со слоями.**Как Flutter определяет, что компоненту требуется использовать FlutterImageView
?
На самом деле можно заметить, что при отправке кадра (SubmitFrame
) в Engine, используется current_frame_view_count
для планирования каждого представления, после чего проверяется необходимость вызова функции CreateSurfaceIfNeeded
. В конечном итоге это приводит к запуску метода создания FlutterImageView
— createOverlaySurface
.
for (const SkRect& overlay_rect : overlay_layers.at(view_id)) {
std::unique_ptr<SurfaceFrame> frame = CreateSurfaceIfNeeded(
context,
view_id,
pictures.at(view_id),
overlay_rect
);
if (should_submit_current_frame) {
frame->Submit();
}
}
Что касается уровня Dart, то PlatformViewSurface
добавляется через PlatformViewRenderBox
как PlatformViewLayer
, а затем через вызов addPlatformView
в ui.SceneBuilder
передается информация о слое в Engine.
(Дополнительные детали можно найти в статье "Полное понимание рендеринга экрана в Flutter")
Кроме того, есть ещё много реализационных деталей, которые не были рассмотрены:
onDisplayPlatformView
, который при отображении PlatformView
вызывает flutterView.convertToImageView
для перехода renderSurface
в flutterImageView
;initializePlatformViewIfNeeded
повторная инициализация уже инициализированных PlatformViews
не происходит;ImageReader
и обновлении текущего битмапа в FlutterImageView
, на устройствах Android 10 возможно использование аппаратного ускорения GPU, что объясняет хорошую производительность Hybrid Composition
на этих устройствах.Из-за ограничений объёма материала остальные подробности не рассматриваются. Однако Hybrid Composition
доступна в стабильной версии 1.20, что помогло решить некоторые проблемы с клавиатурой. Останется ли она эффективной — покажет время, ведь каждый шаг может быть испытанием.Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )