Android与Unity交互

实现保存截图至相册、显示Toast、打开相册选择头像裁剪并保存至持久化目录、打开相机拍照裁剪并保存至持久化目录、震动等功能

创建Module库
image.png
image.png
添加unity库,在Unity安装目录下找到路径Editor\2021.3.1f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes\classes.jar导入Android工程文件夹的libs文件夹下并右键classes.jar 添加库Add Library
image.png

添加Unity的UnityPlayerActivity.java文件到工程中,在Unity安装目录下找到Editor\2021.3.1f1c1\Editor\Data\PlaybackEngines\AndroidPlayer\Source\com文件夹拷贝到Android工程中
image.png
Android工程中创建脚本AndroidNativeBridge.java
package com.packagename.aarlibrary;


import static com.unity3d.splash.services.core.misc.Utilities.runOnUiThread;



import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Environment;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.MediaStore;
import android.util.Log;
import android.widget.Toast;


import com.unity3d.player.UnityPlayer;

import java.io.File;

import java.io.FileOutputStream;
import java.io.IOException;


public class AndroidNativeBridge {

    private static AndroidNativeBridge instance;
    public String gameobject;
    public static final int PHOTO_REQUEST_CODE = 108;//打开相册
    public static final int PHOTO_CUTOUT_CODE=109;//裁剪截取 结果码
    public static final int PHOTOHRAPH = 200;// 打开拍照



    public static AndroidNativeBridge getInstance()
    {
        if(instance==null)
        {
            instance=new AndroidNativeBridge();
        }
        return  instance;
    }
    public  void Android_OnInit(String obj)
    {
        gameobject=obj;

    }

    //给U3D调用的打开相册
    public   void Android_OpenAlbum() {
        //Intent就是应用之间,应用不同Activity之间交互。ACTION_GET_CONTENT ACTION_PICK
        Intent getAlbum = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  //新建Intent,让用户选择特定类型的数据,并返回该数据的URI.
        getAlbum.setType("image/*");  //类型为图片

        UnityPlayer.currentActivity.startActivityForResult(getAlbum, PHOTO_REQUEST_CODE); //调用相册,结果码108,如果有结果会返回108,这个值随便设置,只要>=0就行
    }
    //打开相机
    public void Android_OpenCamera()
    {
        Log.i("Unity", "打开相机" );
//       String takephotoPath = Environment.getExternalStorageDirectory()+"/Android/data/com.Bitbuilding.DigitalRoomU3D/headImage.jpg";
//        File destFile = new File(takephotoPath);
//        if (destFile.exists()) {
//            destFile.delete();
//        }
//
//        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//        Uri uri = Uri.parse("file://" + takephotoPath);
//        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//        UnityPlayer.currentActivity.startActivityForResult(intent, PHOTOHRAPH);
    }
    //显示土司
    public void Android_ShowToastMessage(String msg)
    {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(UnityPlayer.currentActivity,msg,Toast.LENGTH_SHORT).show();
            }
        });
    }

    //保存到相册
    public void Android_SaveToAlbum(String filePath, String str) {
        if(str == null)
            str = "已保存到相册";//设置保存成功的提示内容.
        Toast.makeText(UnityPlayer.currentActivity, str, Toast.LENGTH_SHORT).show();
        Log.i("Unity", "------------filePath" + filePath);//打印保存文件路径日志

        SaveImageToGallery(filePath);
//        Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
//        scanIntent.setData(Uri.fromFile(new File(filePath)));
//        UnityPlayer.currentActivity.sendBroadcast(scanIntent);
    }

    //震动
    public void Android_Vibrate(long duration, float frequency)
    {
        if (duration <= 0 || frequency <= 0)
        {
           Log.e("error","Invalid parameters");
            return;
        }
        long[] pattern = { 0, (long)(duration / 2f), (long)(1000f / frequency)};

        Vibrator vibrator = (Vibrator) UnityPlayer.currentActivity.getSystemService(UnityPlayer.currentActivity.VIBRATOR_SERVICE);
        if (vibrator.hasVibrator()) {
            vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
        }

    }



    private void SaveImageToGallery(String imagePath) {
        // Create a new image file in the DCIM/Camera folder
        String timestamp = String.valueOf(System.currentTimeMillis());
        String filename="image-"+timestamp+".jpg";

        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
        values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/Camera");
        Uri externalContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        ContentResolver resolver =UnityPlayer.currentActivity.getContentResolver();
        Uri insertUri = resolver.insert(externalContentUri, values);

        // Save the image file as JPEG format
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath);

        try (OutputStream out = resolver.openOutputStream(insertUri)) {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
// Notify the MediaScanner about the new file so that it is immediately available in the Gallery app
        String[] paths = { insertUri.getPath() };
        String[] mimeTypes = { "image/jpeg" };
        MediaScannerConnection.scanFile(UnityPlayer.currentActivity, paths, mimeTypes, null);

    }


}

创建UnityActivityExtension.java文件继承UnityPlayerActivity

package com.packagename.aarlibrary;


import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;


import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;


public class UnityActivityExtension  extends UnityPlayerActivity {



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    protected void onStart() {
        super.onStart();
    }
    @Override
    protected void onPause() {
        super.onPause();
    }
    @Override
    protected void onStop() {
        super.onStop();
    }
    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    //重写Activity里的onActivityResult方法,这个方法和startActivityForResult是一对出现。
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        Log.d("onActivityResult回调","回调onActivityResult");

        // 用户没有进行有效的设置操作,返回系统固定的常量RESULT_CANCELED=0
        if (resultCode == RESULT_CANCELED) {
            Toast.makeText(UnityPlayer.currentActivity, "没有选择图片", Toast.LENGTH_LONG).show();
            return;
        }

            switch (requestCode) {
                case AndroidNativeBridge.PHOTO_REQUEST_CODE:
                    //Toast.makeText(UnityPlayer.currentActivity, "图片挑选完成", Toast.LENGTH_LONG).show();
                    //裁剪图片
                    CropPhoto(intent.getData());//获取资源的Uri,安卓Uri通用资源标识符,表示一个图片或视频
                    break;
                case AndroidNativeBridge.PHOTO_CUTOUT_CODE:
                    //Toast.makeText(UnityPlayer.currentActivity, "图片裁剪完成", Toast.LENGTH_LONG).show();
                    //保存图片
                    //可能抛出异常方法IOException方法必须用Try
                    try {
                        SaveImage(intent);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case AndroidNativeBridge.PHOTOHRAPH:
                    UnityPlayer.UnitySendMessage(AndroidNativeBridge.getInstance().gameobject, "OpenAlbum_Callback", "headImage.png");
                    break;
            }

        super.onActivityResult(requestCode, resultCode, intent);
    }
    //裁剪图片
    public void CropPhoto(Uri uri) {
        //调用裁剪器
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // 开启裁剪功能
        intent.putExtra("crop", "true");
        //设置宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        //裁剪图片宽高
        intent.putExtra("outputX", 100);
        intent.putExtra("outputY", 100);
        //请求返回数据
        intent.putExtra("return-data", true);
        UnityPlayer.currentActivity.startActivityForResult(intent, AndroidNativeBridge.PHOTO_CUTOUT_CODE);  //结果码109
    }
    //保存裁剪图片供U3D读取 使用 FileOutputStream 必须要捕获和处理错误 抛出异常IOException
    private void SaveImage(Intent intent) throws IOException {

        //获取intent传过来的数据
        //Bundle类是键值对形式,传递数据  Intent也可以传递数据,两个区别:intent是Bundle的封装
        //Intent主要数据传递用,bundle主要存取数据用。
        Log.d("b","保存裁剪后的图像SaveImage");
        Bundle extras = intent.getExtras();
//取出数据
        Bitmap bitmap = extras.getParcelable("data");
//文件路径=U3D里的Application.persistentDataPath路径 com.packagename.www为Unity工程中的的包名
        File destDir = new File("/storage/emulated/0/Android/data/com.packagename.www/files");
//如果没有这个目录,创建一个


        if (!destDir.exists()) {
            destDir.createNewFile();
        }
        try {
            Log.d("c","创建文件输出流");
            //创建图片数据输出流  com.packagename.www为Unity工程中的的包名
            FileOutputStream bitmapOut = new FileOutputStream("/storage/emulated/0/Android/data/com.packagename.www/files/headImage.png");
            Log.d("c","压缩图片");
            //压缩图片 参数1:图片格式    参数2:压缩率100表示不压缩   参数3:写入输出流
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bitmapOut);
            //讲输出流刷新写出到磁盘
            bitmapOut.flush();
            //关闭流
            bitmapOut.close();
            Log.d("c","调用Unity函数AndroidSaveHeadImageOver");
            //通知U3D可以读取图片了  参数1:U3D物体名称   参数2:方法名   参数3:要传递的参数,这里传图片名称
            UnityPlayer.UnitySendMessage(AndroidNativeBridge.getInstance().gameobject, "OpenAlbum_Callback", "headImage.png");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

选择库然后Make一下
image.png

找到Make出来的Jar包
image.png
打开jar压缩包将里面的unity3d删除
image.png
在将制作好的jar放入Unity Plugins/Android文件夹下,然后再Plugins/Android文件夹下创建AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
  <application>
    <activity android:name="com.packagename.aarlibrary.UnityActivityExtension"
                 android:configChanges="orientation|keyboardHidden|screenSize"
                android:label="@string/app_name"
              android:theme="@style/UnityThemeSelector" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    
      <meta-data
               android:name="unityplayer.UnityActivity"
               android:value="true" />
    </activity>

  </application>
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <!-- SDCard中创建与删除文件权限 -->
  <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  <!-- SDCard写入数据权限 -->
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.VIBRATE" />
</manifest>

Unity中创建脚本NativeAndroidImpl.cs


using UnityEngine;
namespace com.nativesdk.unity3d
{
#if UNITY_ANDROID && !UNITY_EDITOR
    public class NativeAndroidImpl : NativeSDKImpl
    {
        private AndroidJavaClass jc;
        private AndroidJavaObject jo;


        public NativeAndroidImpl(string obj)
        {
            jc = new AndroidJavaClass("com.bitbuilding.digitalroom.aarlibrary.AndroidNativeBridge");
            if (jc != null)
                jo = jc.CallStatic<AndroidJavaObject>("getInstance");
            if (jo != null)
                jo.Call("Android_OnInit", obj);
        }


        public override void SaveToAlbum(string filePath, string tip)
        {
            if (jo != null)
                jo.Call("Android_SaveToAlbum", filePath, tip);
        }

        public override void OpenAlbum()
        {
            if (jo != null)
                jo.Call("Android_OpenAlbum");
        }
        public override void OpenCamera()
        {
            if (jo != null)
                jo.Call("Android_OpenCamera");
        }
        public override void ShowToastMessage(string message)
        {
            if (jo != null)
                jo.Call("Android_ShowToastMessage", message);
        }
        public override void OnVibrator(long time, float hz)
        {
            jo.Call("Android_Vibrate", time, hz);
        }
    }
#endif
}
using System;
namespace com.nativesdk.unity3d
{
    public abstract class NativeSDKImpl
    {
        /// <summary>
        /// 显示Toas消息
        /// </summary>
        /// <param name="msg"></param>
        public abstract void ShowToastMessage(string message);
        /// <summary>
        /// 保存入相册
        /// </summary>
        /// <param name="filePath"></param>
        /// <param name="tip"></param>
        public abstract void SaveToAlbum(string filePath, string tip);
        /// <summary>
        /// 打开原生相册UI
        /// </summary>
        public abstract void OpenAlbum();
        /// <summary>
        /// 打开原生相机
        /// </summary>
        public abstract void OpenCamera();
        /// <summary>
        /// 震动
        /// </summary>
        /// <param name="time">持续时长</param>
        /// <param name="hz">震动频率 HZ</param>
        public abstract void OnVibrator(long time, float hz);

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。

推荐阅读更多精彩内容