项目有一个截图的需求,折腾了几天,发现论坛里的方案基本都是在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);
}
}