zoukankan      html  css  js  c++  java
  • Unity Android 打开相册和摄像头

    来源:https://blog.csdn.net/qweewqpkn/article/details/84141614

    需求:要实现打开手机的相册和摄像头,选择照片或者拍照后,在unity进行。

    1.android插件
    我使用的是AndroidStuido来写插件,下面是一步步介绍流程

    (1)创建android工程

    注意红框里面的东西,要修改两个地方:

    1.将com.android.application 改为 com.android.library

    2.将applicationId "com.niko.myunityplugin" 删除掉

    删除掉这两个目录,不需要他们

    (2)加入我们要依赖的unity的jar包,它的路径在:

    C:Program FilesUnity5.6.4p3EditorDataPlaybackEnginesAndroidPlayerVariationsmonoReleaseClasses

    然后将他放到我们的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引用。

    引用改为:

    implementation 'com.android.support:support-v4:28.0.0'

    添加后我们点击工程的同步按钮,让工程开始从远程仓库下载这个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了。打包后一切都好啦,可以拍照了。

    最后是源码地址:源码点我啦

    注意:这个源码使用的包名是另外一个,与这里我讲解的不同,这里讲解的是我重建了一个工程来给大家说明的。
    ————————————————
    版权声明:本文为CSDN博主「monk_CD」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qweewqpkn/article/details/84141614

  • 相关阅读:
    java—数组乘积输入: 一个长度为n的整数数组input 输出: 一个长度为n的数组result,满足result[i] = input数组中,除了input[i] 之外的所有数的乘积,不用考虑溢出例如 input {2, 3, 4, 5} output: {60, 40, 30, 24}
    深入理解按位异或运算符
    针对数组的三中排序方式:冒泡排序,选择排序,插入排序
    笔试题目整理
    Android中AIDL通信机制分析
    android消息处理机制之2handler与looper,MessageQueue:的关系
    Android消息处理机制(Handler 与Message)---01
    vim 批处理
    React 组件条件渲染的几种方式
    vim命令行模式
  • 原文地址:https://www.cnblogs.com/LiTZen/p/11899508.html
Copyright © 2011-2022 走看看