Android native screen capture development flutter plug-in


RepaintBoundary is used as a flutter screen capture control. The screen capture content cannot be captured to the playback content page with the player, so consider calling Android's native screen capture


Plugin directory structure

Initial command line creation
flutter create --org com.example --template=plugin --platforms=android,ios -a java my_screenshot
insert image description here

在工程 	的android/src/main/java/com/example/my_screenshot/MyScreenshotPlugin
package com.example.my_screenshot;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import android.view.View;

import androidx.annotation.NonNull;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.embedding.engine.renderer.FlutterRenderer;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.view.FlutterView;

/**
 * FlutterNativeScreenshotPlugin
 */
public class MyScreenshotPlugin
        implements FlutterPlugin, MethodCallHandler, ActivityAware {
    
    
  private static final String TAG = "FNSPlugin";
  //声明context对象
  private Context context;
  //声明channel
  private MethodChannel channel;
  //声明activity对象
  private Activity activity;
  //声明对象
  private Object renderer;
  //声明状态
  private boolean ssError = false;
  //声明图片的路径
  private String ssPath;

  // Default constructor for old registrar
  public MyScreenshotPlugin() {
    
    
  } // FlutterNativeScreenshotPlugin()

  // Condensed logic to initialize the plugin
  private void initPlugin(Context context, BinaryMessenger messenger, Activity activity,
                          Object renderer) {
    
    
    this.context = context;
    this.activity = activity;
    this.renderer = renderer;

    this.channel = new MethodChannel(messenger, "my_screenshot");
    this.channel.setMethodCallHandler(this);
  } // initPlugin()

  //**************************注册插件******************************************
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    
    
    Log.println(Log.INFO, TAG, "Using *NEW* registrar method!");

    initPlugin(
            flutterPluginBinding.getApplicationContext(),
            flutterPluginBinding.getBinaryMessenger(),
            null,
            flutterPluginBinding.getFlutterEngine().getRenderer()
    ); // initPlugin()
  } // onAttachedToEngine()

  // New v2 listener methods
  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    
    
    this.channel.setMethodCallHandler(null);
    this.channel = null;
    this.context = null;
  } // onDetachedFromEngine()

  //旧的注册方法
  //插件仍应包含静态的 registerWith() 方法,
  //与不使用 v2 embedding 的应用程序保持兼容。
  public static void registerWith(Registrar registrar) {
    
    
    Log.println(Log.INFO, TAG, "Using *OLD* registrar method!");

    MyScreenshotPlugin instance = new MyScreenshotPlugin();

    instance.initPlugin(
            registrar.context(),
            registrar.messenger(),
            registrar.activity(),
            registrar.view()
    ); // initPlugin()
  } // registerWith()

  //***************************************在插件中引入activity对象***************
  // Activity condensed methods
  private void attachActivity(ActivityPluginBinding binding) {
    
    
    //获取当前flutter页面所处的Activity.
    this.activity = binding.getActivity();
  } // attachActivity()

  private void detachActivity() {
    
    
    this.activity = null;
  } // attachActivity()


  // Activity listener methods
  @Override
  public void onAttachedToActivity(ActivityPluginBinding binding) {
    
    
    attachActivity(binding);
  } // onAttachedToActivity()

  @Override
  public void onDetachedFromActivityForConfigChanges() {
    
    
    detachActivity();
  } // onDetachedFromActivityForConfigChanges()

  @Override
  public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
    
    
    attachActivity(binding);
  } // onReattachedToActivityForConfigChanges()

  @Override
  public void onDetachedFromActivity() {
    
    
    detachActivity();
  } // onDetachedFromActivity()


  //************************方法回调,这里主要是回调管道调用的方法***************************
  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    
    
    if (!call.method.equals("takeScreenshot")) {
    
    
      Log.println(Log.ERROR, TAG, "Method not implemented!");
      result.notImplemented();
      return;
    }
    takeScreenshotOld();
    result.success(ssPath);

  } // onMethodCall()

  //*************************封装自己的方法*************************************
  //用当前时间命名图片
  private String getScreenshotName() {
    
    
    java.text.SimpleDateFormat sf = new java.text.SimpleDateFormat("yyyyMMddHHmmss");
    String sDate = sf.format(new Date());

    return "flutter_native_screenshot-" + sDate + ".png";
  } // getScreenshotName()

  //获取
  private String getScreenshotPath() {
    
    
    String pathTemporary = context.getCacheDir().getPath();
    Log.println(Log.INFO, TAG, "path temporary: " + pathTemporary);

    String dirPath = pathTemporary + "/" + getScreenshotName();

    Log.println(Log.INFO, TAG, "Built ScreeshotPath: " + dirPath);

    return dirPath;
  } // getScreenshotPath()

  private String writeBitmap(Bitmap bitmap) {
    
    
    try {
    
    
      String path = getScreenshotPath();
      File imageFile = new File(path);
      FileOutputStream oStream = new FileOutputStream(imageFile);

      bitmap.compress(Bitmap.CompressFormat.PNG, 100, oStream);
      oStream.flush();
      oStream.close();

      return path;
    } catch (Exception ex) {
    
    
      Log.println(Log.INFO, TAG, "Error writing bitmap: " + ex.getMessage());
    }

    return null;
  } // writeBitmap()

  private void reloadMedia() {
    
    
    try {
    
    
      //扫描媒体文件
      Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
      File file = new File(this.ssPath);
      Uri uri = Uri.fromFile(file);

      intent.setData(uri);
      this.activity.sendBroadcast(intent);
    } catch (Exception ex) {
    
    
      Log.println(Log.INFO, TAG, "Error reloading media lib: " + ex.getMessage());
    }
  } // reloadMedia()

  private void takeScreenshotOld() {
    
    
    Log.println(Log.INFO, TAG, "Trying to take screenshot [old way]");

    try {
    
    
      //使用getWindow().getDecorView().getRootView()是获取当前屏幕的activity
      View view = this.activity.getWindow().getDecorView().getRootView();
      //从ImageView对象获取图像之前
      view.setDrawingCacheEnabled(true);

      Bitmap bitmap = null;
      if (this.renderer.getClass() == FlutterView.class) {
    
    
        bitmap = ((FlutterView) this.renderer).getBitmap();
      } else if (this.renderer.getClass() == FlutterRenderer.class) {
    
    
        bitmap = ((FlutterRenderer) this.renderer).getBitmap();
      }

      if (bitmap == null) {
    
    
        this.ssError = true;
        this.ssPath = null;

        Log.println(Log.INFO, TAG, "The bitmap cannot be created :(");

        return;
      } // if
      //从ImageView对象获取图像之后
      view.setDrawingCacheEnabled(false);
      //这样才能以清空画图缓冲区否则会导致下次还是上次的图
      String path = writeBitmap(bitmap);
      if (path == null || path.isEmpty()) {
    
    
        this.ssError = true;
        this.ssPath = null;

        Log.println(Log.INFO, TAG, "The bitmap cannot be written, invalid path.");

        return;
      } // if

      this.ssError = false;
      this.ssPath = path;

      reloadMedia();
    } catch (Exception ex) {
    
    
      Log.println(Log.INFO, TAG, "Error taking screenshot: " + ex.getMessage());
    }
  } // takeScreenshot()
}


Configuration of AndroidManifest.xml

Add permissions to AndroidManifest.xml under your flutter project android

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.my_screenshot">

    <!-- 添加权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

</manifest>

Encapsulated in the plugin lib

import 'dart:async';

import 'package:flutter/services.dart';

class MyScreenshot {
  //这里MethodChannel(‘通道名字’)
  //通道名字MyScreenshotPlugin里面的的通道名字要一致
  static const MethodChannel _channel = const MethodChannel('my_screenshot');

  static Future<String> takeScreenshot() async {
    final String path = await _channel.invokeMethod('takeScreenshot');
    print('调用成功');
    return path;
  }
}

Summarize

available, simple after
calling the method
import
String path = await MyScreenshot.takeScreenshot();

If you need to do follow-up processing, such as reading the file data,
it can be used as an image, or the background of the box, or uploaded to the Internet through dio

//提醒一下:这里的widget.picturePath实际上是上述的path
      File _dataFile = File(widget.picturePath);
      Uint8List _screenImgData = await _dataFile.readAsBytesSync();
      //在widget那里可以通过
      Image.memory(_screenImgData);
      //或者作为盒子的背景
      Container(
         // decoration: BoxDecoration(color: Colors.white),
         decoration: BoxDecoration(
             image: DecorationImage(
                 image: MemoryImage(
                   _screenImgData,
                 ),
                 fit: BoxFit.fill)),
       )
       //或者上传(这里的 NetUtils.postFormData实际上是对dio进行了一层封装)
       NetUtils.postFormData(
              Api.saveOrshareImg,
              {"file": await MultipartFile.fromFile(widget.picturePath, filename: 'save.png')},
              success: (response) {
                print(response);
                var jsonData = json.decode(response);
                var res = ResponseModel.fromJson(jsonData);
                if (res.status == 0) {;
                  print('success');
                  // }
                } else {
                  print("${res.info.detail}${res.info.message}");
                }
              },
              fail: (e) {
                print(e.toString());
              },
            );

Download
Baidu Netdisk
Extract code: 0001

Note:
the environment is in

  • sdk: “>=2.7.0 < 3.0.0”
  • flutter: “>=1.20.0”

You can download it first, then cd to example and then run flutter once to see if it works. If it works, you can register the plugin
in the pubspec.yaml of your flutter project

  my_screenshot:
  	path: 你的路径(绝对路径或者相对路径都可)

Guess you like

Origin blog.csdn.net/ccy_888/article/details/120845877