Unity Android(安卓)打开相册和摄像头
需求:要实现打开手机的相册和摄像头,选择照片或者拍照后,在unity进行。
1.android插件
我使用的是AndroidStuido来写插件,下面是一步步介绍流程
(1)创建android工程
注意红框里面的东西,要修改两个地方:
1.将com.android.application 改为 com.android.library
2.将applicationId "com.niko.myunityplugin" 删除掉
删除掉这两个目录,不需要他们
(2)加入我们要依赖的unity的jar包,它的路径在:
C:\Program Files\Unity5.6.4p3\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes
然后将他放到我们的android工程下的libs目录下,如下图:
此时还要让工程知道它的存在,所以我们要:
注意:红框选择的是Compile only, 而不是Implemetation,这样选择的原因是最后我们打包出来的aar文件将不会包含这个jar包,如果使用Implemetation 将会把这个jar放到最后打出来的aar包中,我们得手动删除掉,不然我们打Apk的时候会出错,因为unity会使用自己的这个jar包。
同时我们删除掉下面红框的东西,我们不需要他们:
到这一步我们已经成功将这个jar加入到工程了,以后就可以使用它里面的接口了,接下来就开始写代码
(3)写插件代码
package com.niko.myunityplugin;import android.app.Activity;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.support.v4.content.FileProvider;import android.util.Log;import com.unity3d.player.UnityPlayer;import com.unity3d.player.UnityPlayerActivity;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION;public class MainActivity extends UnityPlayerActivity { private static final int TAKE_PHOTO = 1; private static final int OPEN_GALLERY = 2; private static final int CROP_PHOTO = 3; private Uri mPhotoUri; private Uri mCropPhotoUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public void TakePhoto(){ mPhotoUri = GetUri(CreateFile("temp.png")); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri); startActivityForResult(intent, TAKE_PHOTO); } //调用相册 public void OpenGallery() { Intent intent = new Intent(Intent.ACTION_PICK,null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*"); startActivityForResult(intent, OPEN_GALLERY); } private Uri GetUri(File file) { Uri uri; if(Build.VERSION.SDK_INT >= 24) { uri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file); } else { uri = Uri.fromFile(file); } return uri; } private File CreateFile(String name) { File file = new File(Environment.getExternalStorageDirectory(), name); try { if(file.exists()) { file.delete(); } file.createNewFile(); }catch(IOException e) { e.printStackTrace(); } return file; } private void StartCrop(Uri inputUri) { mCropPhotoUri = Uri.fromFile(CreateFile("tempCrop.png")); Intent intent = new Intent("com.android.camera.action.CROP"); intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION); intent.setDataAndType(inputUri, "image/*"); intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例 intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 300); intent.putExtra("outputY", 300); intent.putExtra("scale", true); intent.putExtra("return-data", false); intent.putExtra("noFaceDetection", true); intent.putExtra(MediaStore.EXTRA_OUTPUT, mCropPhotoUri); startActivityForResult(intent, CROP_PHOTO); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if(resultCode == Activity.RESULT_CANCELED) { Log.d("unity","user cancel operator!!"); return; } switch (requestCode) { case TAKE_PHOTO: { StartCrop(mPhotoUri); } break; case OPEN_GALLERY: { Uri uri = data.getData(); StartCrop(uri); } break; case CROP_PHOTO: { try { Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mCropPhotoUri)); FileOutputStream fOut = null; try { String path = "/mnt/sdcard/Android/data/com.niko.myunityplugin/files"; File destDir = new File(path); if(!destDir.exists()) { destDir.mkdirs(); } fOut = new FileOutputStream(path + "/" + "image.png"); } catch (FileNotFoundException e) { e.printStackTrace(); } if(bitmap != null) { bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); try { fOut.flush(); } catch (IOException e) { e.printStackTrace(); } try { fOut.close(); } catch (IOException e) { e.printStackTrace(); } UnityPlayer.UnitySendMessage("UnityPlugin","OnGetPhoto", "image.png"); } } catch(FileNotFoundException e) { e.printStackTrace(); } } break; } }}
加入上面代码后,你会发现其中有报错,如图:
意思就是缺少这个类的信息,那么我们这回就要引入对应的jar包就可以了,然后我们打开build.gradle文件,如下图添加一个新的jar引用。
添加后我们点击工程的同步按钮,让工程开始从远程仓库下载这个jar包下来,如图:
下载好后,此时我们的工程就不会报错了
(4)修改AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
(5)增加了xml文件,如图:
provider_paths.xml的内容是:
<?xml version="1.0" encoding="utf-8"?>
可以开心的开始编译工程了,编译好后如下图所示:
红框中的两个东西,是需要拷贝到unity里面去的。到这里我们插件的编写就完成了,接下来是unity那边开始调用。
2.Unity调用插件
(1)设置环境
将刚才编译出来的东西放到如图的目录下:
这里的包名要和我们设置的android插件的包名一致才可以。
(2)写代码
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.UI;public class TestPlugin : MonoBehaviour{ public Button mBtnCamera; public Button mBtnGallery; public RawImage mImage; public Text mText; private Action mPhotoAction; // Use this for initialization void Start() { mBtnCamera.onClick.AddListener(() => { TakePhoto((datas) => { }); }); mBtnGallery.onClick.AddListener(() => { OpenGallery((datas) => { }); }); } public void TakePhoto(Action callback) { AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic("currentActivity"); jo.Call("TakePhoto"); mPhotoAction = callback; } public void OpenGallery(Action callback) { AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic("currentActivity"); jo.Call("OpenGallery"); mPhotoAction = callback; } void OnGetPhoto(string name) { StartCoroutine(LoadPhoto(name)); } IEnumerator LoadPhoto(string name) { mText.text = name; string path = "file://" + Application.persistentDataPath + "/" + name; WWW www = new WWW(path); yield return www; mImage.texture = www.texture; if (mPhotoAction != null) { mPhotoAction(((Texture2D)mImage.texture).EncodeToPNG()); mPhotoAction = null; //Destroy(texture); } }}
将脚本挂到场景的对象上,然后关联上按钮和图片和文本,如图:
注意红框:由于拍照后要在unity这边显示,我们要给unity发消息,由于android代码发消息的对象名字是UnityPlugin,所以我们这里要修改为UnityPlguin。
然后开始打包apk就可以了,打包过程中我没有出现错误,打出apk后我们安装运行,恭喜你,一运行就会闪退,哈哈哈哈。报错如图:
意思就是我们缺少了android.support.v4.content.FileProvider这个类的信息, 咦?不是我们之前添加了这个库到android的工程中吗?为啥还是报错了,这个问题之前把我坑了好久,网上查了,说的是android工程打包后,是不会将这个jar包一起打包到我们的apk中的,所以出现丢失,但是我们怎么才能拿到这个jar包,让他打包到我的apk中呢?下面是解决方法:
找到这个出错的类包,然后我们按住ctrl+鼠标左键,切入到这个类中去,
然后我们将鼠标放到这个页签上,就可以知道它属于哪个包了,如图:
他的路径如图所示,这就是android studio为我们从远程仓库下载下来的依赖库,这个路径是源码的包,我们需要编译后的jar包,这个包我们可以在如图路径下找到:
ok,顺利找到了这个jar包,我们将它拷贝出来,并且改名为:support-core-utils-26.1.0-sources.jar,放到我们之前的android 工程的libs目录下去,如图:
让工程知道这个jar的存在,和之前的那个添加jar包的方法一下,结果如图:
我们删除了之前依赖远程仓库的包,避免重复,重新同步一下。此时我们重新编译这个库
可以看到这个aar包中的libs目录下有了这个需要依赖的jar包,好了,我们可以重新打包Apk了。打包后一切都好啦,可以拍照了。
最后是源码地址:源码点我啦
注意:这个源码使用的包名是另外一个,与这里我讲解的不同,这里讲解的是我重建了一个工程来给大家说明的。
更多相关文章
- Android(安卓)Studio打包时如何将版本号修改成apk名字
- 携程Android(安卓)App插件化和动态加载实践
- Android(安卓)Studio如何添加外部资源
- Android源码开发环境搭建
- arcgis for android 环境配置
- 修改Android(安卓)Studio新建工程时repositories的默认配置
- Android(安卓)PullToRefresh下拉刷新控件的简单使用
- Android(安卓)Studio 如何导出 Jar 给 Unity 使用
- 导入已有工程和更换工作空间