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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.niko.myunityplugin">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.niko.myunityplugin.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
</application>
<!-- 連線網際網路的許可權 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- SDCard寫入資料許可權 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>
(5)增加了xml檔案,如圖:
provider_paths.xml的內容是:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
可以開心的開始編譯工程了,編譯好後如下圖所示:
紅框中的兩個東西,是需要拷貝到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<byte[]> mPhotoAction;
// Use this for initialization
void Start()
{
mBtnCamera.onClick.AddListener(() =>
{
TakePhoto((datas) =>
{
});
});
mBtnGallery.onClick.AddListener(() =>
{
OpenGallery((datas) =>
{
});
});
}
public void TakePhoto(Action<byte[]> callback)
{
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
jo.Call("TakePhoto");
mPhotoAction = callback;
}
public void OpenGallery(Action<byte[]> callback)
{
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("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了。打包後一切都好啦,可以拍照了。
最後是原始碼地址:原始碼點我啦
注意:這個原始碼使用的包名是另外一個,與這裡我講解的不同,這裡講解的是我重建了一個工程來給大家說明的。