creator 3.2.0 安卓任意截屏方案

项目有一个截图的需求,折腾了几天,发现论坛里的方案基本都是在WEB可行,3.0以上的安卓截图基本没戏,没办法只能是曲线救国了。
记录备查

1.安卓使用MediaProjection录屏
2.Java调用Ts 传参字符串有 4096的长度限制。
3.安卓不需要申请权限
4.测试版本安卓7.1.2

直接上代码

package com.cocos.game;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Handler;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.view.WindowManager;

import com.cocos.lib.CocosHelper;
import com.cocos.lib.CocosJavascriptJavaBridge;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import static android.content.Context.WINDOW_SERVICE;

public class Capture {
    public static final int EVENT_SCREENSHOT = 22;//截图事件
    private MediaProjectionManager mediaProjectionManager;
    private MediaProjection mediaProjection;
    private Image image;

    private AppActivity app;
    public Capture(AppActivity context,String path){
        app = context;
        mediaProjectionManager = (MediaProjectionManager)
                app.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        app.startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), EVENT_SCREENSHOT);
        this.path = path;
    }
    private String path;

//onActivityResult
    public void capture(int requestCode,int resultCode, Intent data){
        if (requestCode != EVENT_SCREENSHOT) {
            return;
        }
        DisplayMetrics displayMetrics = new DisplayMetrics();
        WindowManager windowManager = (WindowManager) app.getSystemService(WINDOW_SERVICE);
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        int width = displayMetrics.widthPixels;
        int height = displayMetrics.heightPixels;
        ImageReader mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 2);
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay("screen-mirror", width, height,
                displayMetrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mImageReader.getSurface(), null, null);
        new Handler().postDelayed(() -> {
            try {
                image = mImageReader.acquireLatestImage();
                if (image != null) {
                    final Image.Plane[] planes = image.getPlanes();
                    final ByteBuffer buffer = planes[0].getBuffer();
                    int width1 = image.getWidth();
                    int height1 = image.getHeight();

                    int pixelStride = planes[0].getPixelStride();
                    int rowStride = planes[0].getRowStride();
                    int rowPadding = rowStride - pixelStride * width1;
                    Bitmap bitmap = Bitmap.createBitmap(width1 + rowPadding / pixelStride, height1, Bitmap.Config.ARGB_8888);
                    bitmap.copyPixelsFromBuffer(buffer);
                    bitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), false);
                    if (bitmap != null) {
                        //对截屏的图片进行裁剪 位置 x 0 y= 1120 w= 1080 h =800
                        Bitmap bit = Bitmap.createBitmap(bitmap,0,1120,1080,800,null,false);
                        //裁剪后的图片进行缩放
                        Bitmap b = scaleBitmap(bit,0.1f);
                        String result = bitmapToBase64(b);

                        FileOutputStream outputStream = new FileOutputStream(path, false);// true 为 追加写入,false 为删除写入(把之前的数据清除掉,再写入)
                        outputStream.write(result.getBytes());
                        outputStream.close();
                        CocosHelper.runOnGameThread(() -> {
                            CocosJavascriptJavaBridge.evalString("Unify.onNativeCall(\"" +path+
                                    "\")");
                        });

                    }
                    bitmap.recycle();
                }
            } catch (Exception e) {

            } finally {
                if (image != null) {
                    image.close();
                }
                if (mImageReader != null) {
                    mImageReader.close();
                }
                if (virtualDisplay != null) {
                    virtualDisplay.release();
                }
                //必须代码,否则出现BufferQueueProducer: [ImageReader] dequeueBuffer: BufferQueue has been abandoned
                mImageReader.setOnImageAvailableListener(null, null);
                mediaProjection.stop();
            }
        }, 300);
    }
    private String bitmapToBase64(Bitmap bitmap) {

        String result = "";
        ByteArrayOutputStream baos = null;
        try {
            if (bitmap != null) {
                baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos);


                byte[] bitmapBytes = baos.toByteArray();
                result = Base64.encodeToString(bitmapBytes, Base64.NO_WRAP);

                baos.flush();
                baos.close();


            }
        } catch (IOException e) {
            result = "ioe";
        } finally {
            try {
                if (baos != null) {
                    baos.flush();
                    baos.close();
                }
            } catch (IOException e) {
                result = "ioe";
            }
        }
        return "data:image/jpeg;base64,"+result;

    }
    private Bitmap scaleBitmap(Bitmap origin, float ratio) {
        if (origin == null) {
            return null;
        }
        int width = origin.getWidth();
        int height = origin.getHeight();
        Matrix matrix = new Matrix();
        matrix.preScale(ratio, ratio);
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        if (newBM.equals(origin)) {
            return newBM;
        }
        origin.recycle();
        return newBM;
    }
}

AppActivity.java

   @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        SDKWrapper.shared().onActivityResult(requestCode, resultCode, data);
        capture.capture(requestCode,resultCode,data);
    }

    public static void capture(String path){
        capture = new Capture(app,path);
    }

TS

const jsb = (<any>window).jsb;

(<any>window)['Unify'] = Unify;
 static onNativeCall(string: string) {
        if (jsb) {
           this._base64 = jsb.fileUtils.getStringFromFile(string);
        }
    }

  onCaptureCamera(scribe: Subscribe) {

        if (jsb) {
            let path = ((jsb.fileUtils ? jsb.fileUtils.getWritablePath() : '/') + 'capture.json');
            jsb.reflection.callStaticMethod("com/cocos/game/AppActivity", "capture", "(Ljava/lang/String;)V", path);
        }

    }
1赞

你这个属于真截屏,其实有部分人的截屏需求并不是截取整个屏幕

嗯嗯 ,我明白 这不是没办法嘛
3.0的 RenderTexture 安卓不支持阿,这个也只能期待官方后续版本了

1赞

我也是这样子做的,我是直接把截好的图缩放一下用微信分享了,不过今天碰到部分机型会崩溃,好像是要把这个截图逻辑放到前台服务中去执行,如果要节点截图,还有个办法就是在游戏逻辑中,把需要截屏的节点放到一个黑色背景的视图中,用这个方案把这个需要截图的节点当成界面来截图,理论上应该可以实现差不多的效果,我还没试。:rofl: :rofl: :rofl: :rofl:

只能说官方连个截图功能都做不好 这个是所有前端程序员都要做一遍的东西 为什么官方就不能封装一下呢?做一堆高大上使用率极低的东西

2.0的很好用,改成3.0真不知道咋搞,所以曲线救国。