Unity头像上传功能实现 一(接在《Unity调用Andriod相册,细节分享》后)
来源:https://blog.csdn.net/cscscs007/article/details/79633004
2018-06-01 后面由于要接入微信登录/支付、支付宝支付这些东西,原来在MainActivity中实现这些逻辑明显不太好了,于是将这个类重新整理了一下,方便后续添加其他接口
GitHub地址: https://github.com/alexchenc/unityplugins
前段时间需要做一个头像上传功能,参考了一些资料后总算是弄了出来。
首先感谢其他博主们的分享:
Unity3D研究院之打开照相机与本地相册进行裁剪显示(三十三)
Unity3d本地上传并且裁剪图片—–Android平台
1. 新建安卓项目
新建一个Android项目,我这里用的是Idea(Eclipse的安卓插件一直弄不好然后放弃了),第一次建要好长时间下载插件,耐心等一下。
Application name: 自己随意命名一个。
Company Domain: 也是自己命名。
Package name: 点击右边Edit,自己修改包名。此处包名需要和Unity项目中打包的包名相同
1
2
3
这是Unity项目中的设置。包名与AndroidAPI版本
下一步,选择最低版本API。版本越低兼容越好,这里我选的是4.1,与Unity中保持的一致。
下一步,创建Activity。只需要创建一个空的Activity即可,选择EmptyActivity。
这个Activity就命名为MainActivity吧,勾选“Generate Layout File”,生成Layout文件;
不用勾选下面那个框,点击Finish创建完成。
2. Unity环境集成
首先需要把Unity的jar包拷贝过来,放在libs目录下。jar包在Unity软件目录下找:
/Applications/Unity/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar
如果找不到就用Spotlight搜索 classes.jar,去与Unity有关的路径查找。
将jar包拷贝至libs目录下,右键->Add as Library, 确定,导入jar包。
3. 打开相册和相机,裁剪功能实现
让MainActivity继承自UnityPlayerActivity(从Unity导入的jar包中包含的类)
这个MainActivity将作为App启动的第一个Activity
由于后期可能需要接入多个Api,所以只把这里当做一个Controller,具体逻辑放在各自的类中实现。这里我将相机、相册逻辑放在PhotoService.java中。
MainActivity.java代码如下:
package com.cclovers.demo;
import android.content.Intent;
import android.os.Bundle;
import com.unity3d.player.UnityPlayerActivity;
public class MainActivity extends UnityPlayerActivity {
public static final String UNITY_GAMEOBJECT = "AndroidManager";
public static MainActivity Instance;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//直接打包apk时需要设置下面这一行
// setContentView(R.layout.activity_main);
Instance = this;
}
/**
* 调用相机
*/
public void openCamera() {
PhotoService.GetInstance().openCamera();
}
/**
* 调用相册
*/
public void openAlbum() {
PhotoService.GetInstance().openAlbum();
}
/**
* 调用支付宝API
*/
public void openAlipay(String orderStr) {
}
/**
* 调用微信API
*/
public void openWechat() {
}
/**
* 微信登录接口
*/
public void openWechatLogin() {
}
/**
* 回调
* @param requestCode
* @param resultCode
* @param data
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//调用相机/相册的返回结果
PhotoService.GetInstance().onActivityResult(requestCode, resultCode, data);
}
}
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
PhotoService.java 用来处理相机、相册逻辑,使用的单例模式。
代码如下:
package com.cclovers.demo;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import com.unity3d.player.UnityPlayer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class PhotoService {
private static final int NONE = 0;
private static final int ALBUM = 1; //相册
private static final int CAMERA = 2; //拍照
private static final int RESULT = 3; //结果
public static final String IMAGE_UNSPECIFIED = "image/*";
public final static String PHOTO_NAME = "temp.jpg";
public final static String CROP_NAME = "temp.png";
public final static String PERSISTANT_PATH = "/Android/data/com.cclovers.demo/files/images/";
private PhotoService() {
// 1. 根目录 Environment.getExternalStorageDirectory() = storage/emulated/0
photoPath = Environment.getExternalStorageDirectory().toString() + "/" + PHOTO_NAME;
// 2. 程序读写目录Applicatio.persistantDataPath = /Android/data/com.xxx.xxx/files/
cropPath = Environment.getExternalStorageDirectory().getAbsolutePath() + PERSISTANT_PATH;
}
private static PhotoService instance;
public static PhotoService GetInstance() {
if (instance == null) {
instance = new PhotoService();
}
return instance;
}
public static boolean HasInstance() {
return instance != null;
}
private String photoPath; //相机拍照图片路径
private String cropPath; //裁剪结果路径
/**
* 调用相机
*/
public void openCamera() {
try {
File file = new File(photoPath);
//先删除该文件再创建
if (file.exists()) {
file.delete();
}
//图片路径
Uri imageUrl = Uri.fromFile(file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUrl);
MainActivity.Instance.startActivityForResult(intent, CAMERA);
} catch (Exception ex) {
LogUtil.logError(ex.getMessage());
}
}
/**
* 调用相册
*/
public void openAlbum() {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED);
MainActivity.Instance.startActivityForResult(intent, ALBUM);
}
/**
* 调用裁剪
*
* @param uri
*/
public void startPhotoZoom(Uri uri) {
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, IMAGE_UNSPECIFIED);
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 100);
intent.putExtra("outputY", 100);
intent.putExtra("return-data", true);
MainActivity.Instance.startActivityForResult(intent, RESULT);
}
/**
* 将裁剪结果保存到本地(只能是Unity具有访问权限的路径)
*/
public void SaveBitmap(Bitmap bitmap) throws IOException {
FileOutputStream stream = null;
try {
if (bitmap != null) {
//检查路径是否存在,如果没有则创建
File destDir = new File(cropPath);
if (!destDir.exists()) {
destDir.mkdirs();
}
//获取文件流
stream = new FileOutputStream(cropPath + CROP_NAME);
//写入本地
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
stream.flush();
}
} catch (Exception e) {
LogUtil.logError(e.getMessage());
} finally {
if (stream != null) {
stream.close();
}
}
}
/**
* 回调
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
try {
//相册
if (requestCode == ALBUM) {
if (data != null) {
//跳转到裁剪
Uri uri = data.getData();
if (uri != null) {
startPhotoZoom(uri);
}
}
}
//相机
else if (requestCode == CAMERA) {
File file = new File(photoPath);
if (file != null && file.exists()) {
//跳转到裁剪
Uri uri = Uri.fromFile(file);
if (uri != null) {
startPhotoZoom(uri);
}
}
}
//裁剪结果
else if (requestCode == RESULT) {
if (data != null) {
Bundle extras = data.getExtras();
if (extras != null) {
Bitmap photo = extras.getParcelable("data");
if (photo != null) {
SaveBitmap(photo);
//调用unity中方法 GetImage
UnityPlayer.UnitySendMessage(MainActivity.UNITY_GAMEOBJECT, "GetImage", CROP_NAME);
}
}
}
}
} catch (Exception e) {
LogUtil.logError("Error:" + e.getMessage());
}
}
}
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
工具类 LogUtil.java, 用于向Unity发送log信息:
package com.cclovers.demo;
import com.unity3d.player.UnityPlayer;
/**
* Created by Alex on 18/5/30.
*/
public class LogUtil {
public static void logError(String message) {
try {
UnityPlayer.UnitySendMessage(MainActivity.UNITY_GAMEOBJECT, "LogError", message);
}
catch (Exception e) {
}
}
public static void logDebug(String message) {
try {
UnityPlayer.UnitySendMessage(MainActivity.UNITY_GAMEOBJECT, "LogDebug", message);
}
catch (Exception e) {
}
}
}
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
3. 小结
核心功能其实就在PhotoService中了,具体实现也没有什么逻辑,大多是控制跳转
稍微需要注意一下的可能是图片储存的路径了,Unity程序的文件夹权限只有根据包名生成的文件夹下,所以图片只能存在在这里,然后在Unity中去读取这个图片
4. android项目打包配置,导出jar包和资源
设置AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cclovers.demo">
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name="com.cclovers.demo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<!-- 连接互联网的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
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
导出jar包了,Idea需要设置一下才能导出jar包,File->Project Structure->Artifacts
如果输出文件中有与class.jar 和 res有关的文件,就全部去掉吧,只留一个app的。不然在Unity打包apk的时候会失败,原因是插件资源重复,具体错误代码就不贴了。
勾选中Include in project build, 在项目build的时候就会生成jar包了,然后到输出目录去找.
这里的jar包还要再处理一下,用压缩软件打开
- 删除所有以**R$**开头的文件
- 将**BuildConfig.class**删除掉
- 删除Android文件夹(如果有)
- 只留下自己眼熟的文件,如图,然后保存-_-。
1
2
3
4
需要注意的是,由于API版本的原因,有些地方可能会导致在Unity中打包失败,具体原因可根据错误代码去搜索一下。
以下是我遇到的问题,打包报错安卓主题找不到,解决办法如下:
需要修改一下src/res/values/styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
**改为下面**
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:colorPrimary">@color/colorPrimary</item>
<item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:colorAccent">@color/colorAccent</item>
</style>
</resources>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
5. 导入到Unity
最后就是导入到Unity中集成了,在Unity项目中新建目录:Assets/Plugins/Android
新建bin目录,将导出的jar包拷入其中;
把res文件从android项目全部拷贝过来
AndroidManifest.xml拷贝至Android目录下
项目结构如下:
5. 总结
以上就是头像上传功能java部分的实现了,看起来内容不多,但实际操作起来还是会遇到许多问题的,一边参考其他的资料一边尝试,花了几天的时间才把这些问题解决。
至于后面在Unity中如何使用和读取图片功能,等有时间再写一下
————————————————
版权声明:本文为CSDN博主「丶淺忆」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cscscs007/article/details/79633004