CocosCreator 调用安卓相册选择图片裁剪并传 Base64 到 ts 层


CocosCreator 调用安卓相册选择图片裁剪并传 Base64 到 ts 层

https://blog.csdn.net/W_han__/article/details/126747094

  • Java

JsbService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
public static final int REQUEST_CODE_CAMERA = 110;
public static final int REQUEST_CODE_ALBUM = 111;
public static final int REQUEST_CODE_CROP = 112;

public static Integer clipX = 0;
public static Integer clipY = 0;
public static Integer needClip = 0;
public static Integer photoType = 1;

public static Uri uriClipUri = null;
public static Uri mCameraUri = null;

public static void selectPhoto(String path, String info) {
useSystemAlbum(info);
}

/**
* 调用系统相册和裁剪
* @param info
*/
public static void useSystemAlbum(String info){
clipX = 0;
clipY = 0;
try{
JSONObject jsonObject = new JSONObject(info);
needClip = jsonObject.getInt("needClip");
clipX = jsonObject.getInt("clipX");
clipY = jsonObject.getInt("clipY");
}catch(Exception e){
e.printStackTrace();
}

pickImage(true);
}

/**
* 选择相册上传图片
*/
public static void pickImage(Boolean requestPermissionsNeeded){
photoType = 1;
String[] needPermissions = new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
List<String> permissionList = new ArrayList<>();
for (String permission : needPermissions) {
if (ActivityCompat.checkSelfPermission(appActivity, permission) != PackageManager.PERMISSION_GRANTED)
permissionList.add(permission);
}
if (permissionList.size() == 0) {
Log.i("pickImg","已经获取到所有权限");
goPhotoAlbum();

} else if (requestPermissionsNeeded) {
String[] requestPermissions = permissionList.toArray(new String[permissionList.size()]);
ActivityCompat.requestPermissions(appActivity,requestPermissions, 1001);
}
}

public static void goPhotoAlbum(){
Intent intentAlbum= new Intent(Intent.ACTION_PICK, null);
//其中External为sdcard下的多媒体文件,Internal为system下的多媒体文件。
//使用INTERNAL_CONTENT_URI只能显示存储在内部的照片
intentAlbum.setDataAndType(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
//返回结果和标识
appActivity.startActivityForResult(intentAlbum, REQUEST_CODE_ALBUM);
}

// 处理图片路径4.4之后
private static String handleImageOnKitkat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
imagePath = changeUriToPath(uri);
return imagePath;
}
private static String changeUriToPath(Uri uri){
String imagePath = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(appActivity, uri)) {
//如果是document类型的uri,则通过document id 处理
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())) {
//解析出数字格式的id
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID+ "=" +id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection );
} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse
("content://downloads/public_downloads"),Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
//如果是content类型的uri,则用普通方式处理
imagePath = getImagePath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
//如果是file类型的uri,直接获取图片路径
imagePath = uri.getPath();
}
}
return imagePath;
}
//处理图片路径4.4之前
private static String handleImageBeforeKitkat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
return imagePath;
}
private static String getImagePath(Uri uri,String selection) {
String path = null;
//通过uri 和 selection 获取真实的图片路径
Cursor cursor = appActivity.getContentResolver().query(uri,null,selection,null,null);
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}


/**
* 图片裁剪的方法
* @param uri
*/
public static void startPhotoZoom(Uri uri) {
Log.i("pickImg uri=====", "" + uri);
//com.android.camera.action.CROP,这个action是调用系统自带的图片裁切功能
Intent intent = new Intent("com.android.camera.action.CROP");

intent.setDataAndType(uri,"image/*");//裁剪的图片uri和图片类型

intent.putExtra("crop", true);//设置允许裁剪,如果不设置,就会跳过裁剪的过程,还可以设置putExtra("crop", "circle")
intent.putExtra("aspectX", 1);//裁剪框的 X 方向的比例,需要为整数
intent.putExtra("aspectY", 1);//裁剪框的 Y 方向的比例,需要为整数
if(clipX != 0 && clipY != 0){
intent.putExtra("OutputX", clipX);// 设置最终裁剪的宽和高
intent.putExtra("OutputY", clipY);// 设置最终裁剪的宽和高
Log.i("pickImg","设置裁剪输出宽和高="+clipX+"-"+clipY);
}
//uriClipUri为Uri类变量,实例化uriClipUri
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (photoType == 2) {//如果是7.0的拍照
} else if(photoType == 1) {//如果是7.0的相册
//设置裁剪的图片地址Uri
uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" +"clip.jpg");
Log.i("pickImg","android 7.0调用相册= "+uriClipUri);
}

} else {
uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg");
}
Log.i("pickImg uriClipUri=====", "" + uriClipUri);
//Android 对Intent中所包含数据的大小是有限制的,一般不能超过 1M,否则会使用缩略图 ,所以我们要指定输出裁剪的图片路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, uriClipUri);
intent.putExtra("return-data", false);//是否将数据保留在Bitmap中返回
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//输出格式,一般设为Bitmap格式及图片类型
intent.putExtra("noFaceDetection", true);//人脸识别功能
try {
grantUriPermission(intent, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
appActivity.startActivityForResult(intent, REQUEST_CODE_CROP);//裁剪完成的标识
} catch (Exception e) {
e.printStackTrace();
}

}

public static void grantUriPermission(Intent intent, Uri uri, int permissionFlags) {
List<ResolveInfo> resolvedIntentActivities = appActivity.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolvedIntentInfo : resolvedIntentActivities) {
String packageName = resolvedIntentInfo.activityInfo.packageName;
appActivity.grantUriPermission(packageName, uri, permissionFlags);
}
if (uri.getHost() != null) {
appActivity.grantUriPermission(uri.getHost(), uri, permissionFlags);
}
}


public static String bitmapToBase64(Bitmap bitmap) {
String result = null;
ByteArrayOutputStream baos = null;
try {
if (bitmap != null) {
baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 30, baos);
baos.flush();
baos.close();

byte[] bitmapBytes = baos.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.NO_WRAP);
}
} catch (IOException e) {
e.printStackTrace();
Log.i("pickImg","io exception111");
} finally {
try {
if (baos != null) {
baos.flush();
baos.close();
}
} catch (IOException e) {
Log.i("pickImg","io exception222");
e.printStackTrace();
}
}
return result;
}

public static void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_CODE_ALBUM && resultCode == appActivity.RESULT_OK){
Log.i("pickImg ","调用相册回调");
if(data != null){
if(needClip == 1){
startPhotoZoom(data.getData());
return;
}
String filePath = null;
if (resultCode == appActivity.RESULT_OK) {
//判断手机的系统版本号
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//4.4系统的及以上用此方法处理照片
filePath = handleImageOnKitkat(data);
} else {
// 4.4以下的使用这个方法处理照片
filePath = handleImageBeforeKitkat(data);
}
}
Bitmap bitmap = compressImage(BitmapFactory.decodeFile(filePath));

String base64Bitmap = bitmapToBase64(bitmap);

send2JsCommonUtils(String.format("window.GameJsb.onPickImageArrive('%s', '%s')", filePath, base64Bitmap));
}
}
else if(requestCode == REQUEST_CODE_CROP && resultCode == appActivity.RESULT_OK){
Log.i("pickImg","裁剪完成"+data.getData());
if(data != null){
if(photoType == 1){
String filePath = null;
if (resultCode == appActivity.RESULT_OK) {
//判断手机的系统版本号
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//4.4系统的及以上用此方法处理照片
filePath = changeUriToPath(uriClipUri);
} else {
// 4.4以下的使用这个方法处理照片
filePath = getImagePath(uriClipUri, null);
}
}

Bitmap bitmap = compressImage(BitmapFactory.decodeFile(filePath));
String base64Bitmap = bitmapToBase64(bitmap);

send2JsCommonUtils(String.format("window.GameJsb.onPickImageArrive('%s', '%s')", filePath, base64Bitmap));
}else if(photoType == 2){

}
}
}
}
private static Bitmap compressImage(Bitmap beforeBitmap) {

// 可以捕获内存缓冲区的数据,转换成字节数组。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (beforeBitmap != null) {
// 第一个参数:图片压缩的格式;第二个参数:压缩的比率;第三个参数:压缩的数据存放到bos中
beforeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, bos);

// 循环判断压缩后的图片大小是否满足要求,这里限制100kb,若不满足则继续压缩,每次递减10%压缩
int options = 100;
while (bos.toByteArray().length / 1024 > 100) {
bos.reset();// 置为空
beforeBitmap.compress(Bitmap.CompressFormat.JPEG, options, bos);
options -= 10;
}

// 从bos中将数据读出来 转换成图片
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
Bitmap afterBitmap = BitmapFactory.decodeStream(bis);
return afterBitmap;
}
return null;
}

private static void send2JsCommonUtils(String callJsFunc) {
appActivity.runOnGLThread(new Runnable() {
@Override
public void run() {
Cocos2dxJavascriptJavaBridge.evalString(callJsFunc);
Log.i("pickImg","crop返回path,base64,callFuncStr = "+callJsFunc);
}
});
}

public static void onNewIntent(Intent intent) {
Log.d("RtmSdk", "=========== onNewIntent ");
}

AppActivity.java

1
2
3
4
5
6
7
8
9
10
// 请求权限结束的回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1001) {
Log.d(TAG, "onRequestPermissionsResult");
// 请求权限结束,调起相册获取图片
JsbService.pickImage(false);
}
}
  • TS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//选择相册图片    
selectPhoto(clipSize: cc.Size = new cc.Size(150, 150), needClip = 1) {
if (!cc.sys.isNative) {
return;
}
var clipX = 0;
var clipY = 0;
if(clipSize){
clipX = clipSize.width;
clipY = clipSize.height;
}
var dict = {
needClip: needClip, //1是裁剪,2是不裁剪
clipX:clipX, //裁剪x尺寸
clipY:clipY, //裁剪y尺寸
}

let tmpPath = jsb.fileUtils.getWritablePath() + 'tmpPhoto.jpg';
if (cc.sys.os == cc.sys.OS_ANDROID) {
jsb.reflection.callStaticMethod(
GameJsb._androidJSBPath,
"selectPhoto",
"(Ljava/lang/String;Ljava/lang/String;)V",
tmpPath,
JSON.stringify(dict));
}
}

/** base64图片回调 */
onPickImageArrive(filePath: string, filedata) {
// GameEvent.instance.dispatchEvent(GameEvent.JSB_ALBUM_PHOTO_SELECTED, { filePath: filePath, filedata: filedata});
data.filedata; // 临时保存base64图片数据
cc.loader.load(data.filePath, (err,img) => {
node.getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(img);
});
}
  • 其他

在 gradle.properrties 里添加

1
2
android.useAndroidX=true
android.enableJetifier=true

在 AndoridMainfest 里添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 相册权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="包名.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

然后再 res 下面创建 xml/file_path.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Offer access to files under Context.getCacheDir() -->
<cache-path name="my_cache" />
<files-path name="my_files" path = "." />
<external-path name="my_external" path = "." />
<external-path path="." name="camera_photos" />
</paths>