栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Flutter 内嵌原生组件 for android

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Flutter 内嵌原生组件 for android

前言:

前面做过 原生内嵌 flutter,

今天尝试下 flutter 内嵌原生组件 ImageView;

效果如下: flutter按钮 控制原生组件切换显示网络图片

第一步创建 Flutter Plugins 工程

这个步骤没啥说的;估计 AndroidStuido 版本不同 界面会有差异;

当然你也可以 命令行 进行创建

工程创建完毕后,下面的目录大致如下:

 

你可以直接运行这个工程,就能把这个 flutter plugins 模板工程跑起来,

第二步编写 插件脚本  lib 目录

flutter_plugins.dart 是系统自动帮你创建的我们不用管

我们创建 native_image.dart,  native_image_controller.dart 这两个脚本

 native_image.dart   用于UI展示

 这里注册了一个 切换图片的事件

void initState() {
  // TODO: implement initState
  super.initState();
  
  // 执行的切换图命令, 参数就是 图片的url
  widget.controller.addListener(() {
    // print(
    //     '-----------------------------imageUrl:${widget.controller.imageUrl}');
    _channel
        .invokeListMethod("changeImage", {'url': widget.controller.imageUrl});
  });
}

这里是实际创建原生界面的容器 根据不同系统进行的创建 (ios部分在下一章我们会讲到)

_loadNativeView() {
  if (Platform.isAndroid) {
    return AndroidView(
      viewType: 'imageView', //视图标识符 要和原生 保持一致 要不然加载不到视图
      onPlatformViewCreated: onPlatformViewCreated, //原生视图创建成功的回调
      creationParams: {
        'initUrl': widget.initUrl
      }, //给原生传递初始化参数 就是上面定义的初始化参数
      // 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
      // 如果存在 creationParams,则该值不能为null
      creationParamsCodec: const StandardMessageCodec(),
    );
  } else if (Platform.isIOS) {
    return UiKitView(viewType: 'imageView');
  }
}

完整的脚本

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_plugins/native_image_controller.dart';

class NativeImage extends StatefulWidget {
  final NativeImageController controller;
  final String initUrl;
  const NativeImage({Key? key, required this.initUrl, required this.controller})
      : super(key: key);
  @override
  _NativeImageState createState() => _NativeImageState();
}

class _NativeImageState extends State {
  static const MethodChannel _channel =
      MethodChannel('flutter_plugins_native_image');
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    
    // 执行的切换图命令, 参数就是 图片的url
    widget.controller.addListener(() {
      // print(
      //     '-----------------------------imageUrl:${widget.controller.imageUrl}');
      _channel
          .invokeListMethod("changeImage", {'url': widget.controller.imageUrl});
    });
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    widget.controller.removeListener(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey,
      width: MediaQuery.of(context).size.width,
      height: 200,
      child: _loadNativeView(),
    );
  }

  Future onPlatformViewCreated(int id) async {
    // return widget.onCreated( new FlutterPlugins().)
    print('-----创建成功');
  }

  _loadNativeView() {
    if (Platform.isAndroid) {
      return AndroidView(
        viewType: 'imageView', //视图标识符 要和原生 保持一致 要不然加载不到视图
        onPlatformViewCreated: onPlatformViewCreated, //原生视图创建成功的回调
        creationParams: {
          'initUrl': widget.initUrl
        }, //给原生传递初始化参数 就是上面定义的初始化参数
        // 用来编码 creationParams 的形式,可选 [StandardMessageCodec], [JSONMessageCodec], [StringCodec], or [BinaryCodec]
        // 如果存在 creationParams,则该值不能为null
        creationParamsCodec: const StandardMessageCodec(),
      );
    } else if (Platform.isIOS) {
      return UiKitView(viewType: 'imageView');
    }
  }
}

native_image_controller.dart  用于原生的ImageView 显示图片的控制

 代码很简单

import 'package:flutter/cupertino.dart';

class NativeImageController extends ChangeNotifier {
  late String imageUrl;
  changeImage(String url) {
    imageUrl = url;
    notifyListeners();
  }
}

第三步编写 原生脚本  Android 目录

点击右上角  Open for Editing in Android Studio

  

其中: FlutterPluginsPlugin.java 系统生成 不用管

我们 新建 NativeViewPluginsView.java  和 NativeViewPluginsFactory.java

NativeViewPluginsFactory  是我们的一个工厂方法 承接创建的 原生页面固定写法
package com.john.flutter_plugins;

import android.content.Context;

import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MessageCodec;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

// https://blog.csdn.net/WangQingLei0307/article/details/122680290
public class NativeViewPluginsFactory extends PlatformViewFactory {
    private BinaryMessenger binaryMessenger = null;
    public NativeViewPluginsFactory(BinaryMessenger messenger){
        super(StandardMessageCodec.INSTANCE);
        this.binaryMessenger = messenger;
    }
    
    public NativeViewPluginsFactory(MessageCodec createArgsCodec) {
        super(createArgsCodec);

    }

    @Override
    public PlatformView create(Context context, int viewId, Object args) {
        return new NativeViewPluginsView(context,viewId,args,this.binaryMessenger);
    }
}
 
 

NativeViewPluginsView 继承 ImageView 是我们真正用进行页面操作的地方

 这个方法中我们主要进行 原生页面的设置;以及事件的注册

public NativeViewPluginsView(Context context, int viewId, Object args, BinaryMessenger binaryMessenger) {
    super(context);
    this.context = context;

    Toast.makeText(context, "原生插件启用成功id:"+((Map)args).isEmpty(), Toast.LENGTH_LONG).show();
    // ImageView 相关设置
    setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
    setBackgroundColor(Color.argb(255,75,79,79));  //0完全透明  255不透明
    
    // 设置默认图片 参数取自默认传参方式
    imageUrl =((Map) args).get("initUrl");
    new WorkThread().start();


    /// 注册方法  用于切换显示的图片
    methodChannel = new MethodChannel(binaryMessenger, "flutter_plugins_native_image");
    methodChannel.setMethodCallHandler(this);
}

完整脚本

package com.john.flutter_plugins;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.SurfaceTexture;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;

import androidx.annotation.NonNull;


import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

import io.flutter.Log;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.platform.PlatformView;

public class NativeViewPluginsView extends ImageView implements PlatformView, MethodChannel.MethodCallHandler, TextureView.SurfaceTextureListener {
    public Context context;
    private  MethodChannel methodChannel = null;
    private  String imageUrl = "https://img.gbm001.com/media/catalog/product/pic/x/PCVJYJD1.png";

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.i("---------------------","获得图片");
            setImageBitmap( (Bitmap)msg.obj);
        }
    };

public NativeViewPluginsView(Context context, int viewId, Object args, BinaryMessenger binaryMessenger) {
    super(context);
    this.context = context;

    Toast.makeText(context, "原生插件启用成功id:"+((Map)args).isEmpty(), Toast.LENGTH_LONG).show();
    // ImageView 相关设置
    setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
    setBackgroundColor(Color.argb(255,75,79,79));  //0完全透明  255不透明
    
    // 设置默认图片 参数取自默认传参方式
    imageUrl =((Map) args).get("initUrl");
    new WorkThread().start();


    /// 注册方法  用于切换显示的图片
    methodChannel = new MethodChannel(binaryMessenger, "flutter_plugins_native_image");
    methodChannel.setMethodCallHandler(this);
}

    @Override
    public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {

    }

    @Override
    public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
        handleCall(call, result);
    }
    // 事件回调
    private void handleCall(MethodCall methodCall, MethodChannel.Result result){
        //Toast.makeText(context, "收到 Flutter 调用", Toast.LENGTH_LONG).show();
        if (methodCall.method.equals("changeImage")){
          //Log.i("url",  methodCall.argument("url"));
            imageUrl = methodCall.argument("url");
            new WorkThread().start();
        }

    }

    @Override
    public View getView() {
        return this;
    }

    @Override
    public void dispose() {

    }


    //工作线程
    private class WorkThread extends Thread {
        @Override
        public void run() {
            //......处理比较耗时的操作
            Message msg = new Message();
            Bitmap bitmap = getHttpBitmap(imageUrl);
            msg.obj = bitmap;
            handler.sendMessage(msg);
        }
        public Bitmap getHttpBitmap(String url){

            URL myFileURL;
            Bitmap bitmap=null;
            try{
                myFileURL = new URL(url);
                //获得连接
                HttpURLConnection conn=(HttpURLConnection)myFileURL.openConnection();
                //设置超时时间为6000毫秒,conn.setConnectionTiem(0);表示没有时间限制
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(6000);
                //连接设置获得数据流
                conn.setDoInput(true);
                //不使用缓存
                conn.setUseCaches(false);
                //这句可有可无,没有影响
                //conn.connect();
                //得到数据流
                InputStream is = conn.getInputStream();
                //解析得到图片
                bitmap = BitmapFactory.decodeStream(is);
                //关闭数据流
                is.close();
            }catch(Exception e){
                e.printStackTrace();
                //Toast.makeText(context, "图片出错", Toast.LENGTH_LONG).show();
                Log.i("-----------图片出错",e.toString());

            }
            return bitmap;
        }
    }


}

这里有个地方要注意下;

  • 因为是网络图片 需要的权限等等是要加上的;
    
    
    
     
    
    
  • 而且这个地方下载图片的过程中使用多线程;多线程不能直接访问UI线程;所以用到 Handler 进行数据传递; 我在想这里换成异步网络请求应该会优雅一些
第四步 在Flutter 工程中使用我们的插件

马上就要大功告成 了! 我们返回到 Flutter 工程中,在 lib 文件夹中 编辑我们的 main.dart

直接使用 我们插件中写的 NativeImage

完整脚本

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_plugins/flutter_plugins.dart';
import 'package:flutter_plugins/native_image.dart';
import 'package:flutter_plugins/native_image_controller.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State createState() => _MyAppState();
}

class _MyAppState extends State {
  String _platformVersion = 'Unknown';
  final NativeImageController controller = NativeImageController();
  final List images = [
    'https://gimg2.baidu.com/image_search/src=http://kaotopstatic.cdn.bcebos.com/skin/sinaskin/image/nopic.gif',
    'https://gimg2.baidu.com/image_search/src=http://kaotopstatic.cdn.bcebos.com/skin/sinaskin/image/nopic.gif',
    'https://gimg2.baidu.com/image_search/src=http://kaotopstatic.cdn.bcebos.com/skin/sinaskin/image/nopic.gif',
    'https://img1.baidu.com/it/u=3294782826,883543847&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=334',
    'https://gimg2.baidu.com/image_search/src=http://kaotopstatic.cdn.bcebos.com/skin/sinaskin/image/nopic.gif',
    'https://img0.baidu.com/it/u=2007762294,622825342&fm=253&fmt=auto&app=120&f=JPEG?w=960&h=600',
  ];

  int currentIndex = 0;

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future initPlatformState() async {
    String platformVersion;
    // Platform messages may fail, so we use a try/catch PlatformException.
    // We also handle the message potentially returning null.
    try {
      platformVersion =
          await FlutterPlugins.platformVersion ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            children: [
              NativeImage(
                initUrl:
                    "https://gimg2.baidu.com/image_search/src=http://kaotopstatic.cdn.bcebos.com/skin/sinaskin/image/nopic.gif",
                controller: controller,
              ),
              Text('Running on: $_platformVersionn'),
              Spacer(),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  OutlinedButton(
                    onPressed: () {
                      if (currentIndex < 0) {
                        currentIndex = images.length - 1;
                      }
                      if (currentIndex >= images.length) {
                        currentIndex = 0;
                      }
                      controller.changeImage(images[currentIndex]);
                      currentIndex--;
                    },
                    child: Text("上一张"),
                  ),
                  OutlinedButton(
                    onPressed: () {
                      if (currentIndex >= images.length) {
                        currentIndex = 0;
                      }
                      if (currentIndex < 0) {
                        currentIndex = images.length - 1;
                      }
                      controller.changeImage(images[currentIndex]);
                      currentIndex++;
                    },
                    child: Text("下一张"),
                  ),
                  // TextField(controller: ,)
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

到此为止都OK了

 工程链接https://download.csdn.net/download/nicepainkiller/85733897

Flutter 内嵌原生组件 for ios_nicepainkiller的博客-CSDN博客https://johns.blog.csdn.net/article/details/125420308 

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号