Unity-Android 从本地相册选取图片截图或拍照后截图并显示到unity

新项目关于聊天时发送图片需求,要求从本地相册或拍照图片选取然后发送给玩家,百度了很多这样的帖子,参考了好几篇帖子,折腾了一天才大致ok了,本地相册中选图截图问题不大,但拍照后截图被权限问题卡住了半天,一直找不到原因。

下面是c#代码:

using UnityEngine;
using System.Collections;
using System.IO;

public class Test : MonoBehaviour
{
	public GUISkin skin;
	Texture texture;

    void Start()
    {
        //Debug.Log("Test.Start()");
    }
    void Update ()
	{
        //Debug.Log("Test.Update()");
        if (Input.GetKeyDown(KeyCode.Escape) || Input.GetKeyDown(KeyCode.Home))
  		{
   			Application.Quit();
  		}
	}
	
	void OnGUI()
	{
        //Debug.Log("Test.OnGUI()");
        GUI.skin = skin;
		
		if(GUILayout.Button("打开手机相册"))
		{
			 //调用我们制作的Android插件打开手机相册
			 AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
      	 	 AndroidJavaObject jo = jc.GetStatic("currentActivity");
        	 jo.Call("TakePhoto","takeSave");

		}
		if(GUILayout.Button("打开手机摄像机"))
		{
			 //调用我们制作的Android插件打开手机摄像机
			 AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
      	 	 AndroidJavaObject jo = jc.GetStatic("currentActivity");
        	 jo.Call("TakePhoto","takePhoto");

		}
		if(texture != null)
		{
			//注意! 我们在这里绘制Texture对象,该对象是通过
			//我们制作的Android插件得到的,当这个对象不等于空的时候
			//直接绘制。
			GUI.DrawTexture(new Rect(100,300,300,300),texture);
		}
	}
	
	void  messgae(string str)
	{
		//在Android插件中通知Unity开始去指定路径中找图片资源
		StartCoroutine(LoadTexture(str));
	
	}
	
	

  IEnumerator LoadTexture(string name)
  {
	//注解1
	string path  =  "file://" + Application.persistentDataPath +"/" + name;
	
	WWW www = new WWW(path);
	while (!www.isDone)
	{
			
	}
	yield return www;
	//为贴图赋值
	texture = www.texture;
  }  
}

这个地方没有什么问题,绘制两个按钮,分别是选择是本地相册还是打开照相机拍照,点击按钮就开始执行android代码了。

下面是android代码的执行入口了

public void TakePhoto(String str)
{
     Intent intent = new Intent(mContext,WebViewActivity.class);
     intent.putExtra("type", str);
     this.startActivity(intent);
}
这里Intent跳转Activity并将unity这边的操作关键字传递给WebViewActivity

下面是关键的WebViewActivity相关代码了

首先是跳转后执行的OnCreate()

protected void onCreate(Bundle savedInstanceState) {

		super.onCreate(savedInstanceState);
		
		
		setContentView(R.layout.main);

		
		imageView = (ImageView) this.findViewById(R.id.imageID);
		
		String type = this.getIntent().getStringExtra("type");		
		
		//在这里判断是打开本地相册还是直接照相
		if(type.equals("takePhoto"))   //打开摄像头
		{
			  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
			  String photofileName = "IMG_" + new Date() + ".jpg";
			  //将拍好的照片存储在指定文件夹中,文件名为"temp.jpg"
			  Log.i("WebViewActivity", "打开摄像头");
			  if (Build.VERSION.SDK_INT >= 24) { 
				  Log.i("WebViewActivity", "android7.0以上");
				  
				  uri = FileProvider.getUriForFile(this,"com.xys.fileprovider", new File(Environment
                            .getExternalStorageDirectory() + "/" + "DCIM/Camera", photofileName));
				  intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
				  intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
				} else {
					Log.i("WebViewActivity", "android7.0以下");
				    uri = Uri.fromFile(new File(Environment
                            .getExternalStorageDirectory() + "/" + "DCIM/Camera", photofileName)); 
				    
				} 
			  intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);//保存照片
              //开始跳转到拍照界面,当从拍照界面返回后调用onActivityResult()函数。并将requestCode = PHOTOHRAPH以参数返回
              startActivityForResult(intent, PHOTOHRAPH);   
		}else   //打开本地相册
		{
		       Intent intent = new Intent(Intent.ACTION_PICK, null);  
               intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_UNSPECIFIED);  
               startActivityForResult(intent, PHOTOZOOM);  
		}

    }

这里,当type.equals("takePhoto"),是打开照相机拍照,在这里要注意

android7.0以前是可以直接通过Uri.fromFile()创建Uri的,但7.0之后android收紧了uri的相关权限,我们需要通过FileProvider.getUriForFile()来创建,并且我们要在AndroidManifest.xml中的  . . .  标签中进行声明,如下:

       
        
            
        

其中 android:resource="@xml/provider_paths" 指明权限路径。

回到webViewActivity,在调用startActivityForResult(intent, requestCode)跳转时,在跳转到目标activity并完成操作后返回时会回调这个Intent所在的activity的onActivityResult(int requestCode, int resultCode, Intent data)方法,并返回requestCode给这个方法,其中intent可以携带一些数据进行activity之间的数据传递。这样就可以辨别是从哪个activity返回。

下面是onActivityResult(...)的代码:

protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    	Log.i("WebViewActivity", "回调onActivityResult  resultCode:"+resultCode);
        if (resultCode == NONE)  
            return;  
        // 拍照  
        if (requestCode == PHOTOHRAPH) {  
        	//设置文件保存路径这里放在跟目录下
        	startPhotoZoom(uri);  
        }  
          
        if (data == null)  
            return;  
          
        // 读取相册缩放图片  
        if (requestCode == PHOTOZOOM) {  
            startPhotoZoom(data.getData());  
        }  
         // 处理结果  
        if (requestCode == PHOTORESOULT) {  
            //Bundle extras = data.getExtras();  
            //if (extras != null) {
        	Log.i("WebViewActivity", "裁剪图片已经完成");
				try {
					Log.i("WebViewActivity", "从uritempFile中获取到裁剪后的图片");
					Bitmap photo = BitmapFactory.decodeStream(getContentResolver().openInputStream(uritempFile));
					Log.i("WebViewActivity", "imageView.setImageBitmap(photo)");
					//imageView.setImageBitmap(photo);
					Log.i("WebViewActivity", "imageView.setImageBitmap(photo)成功");
					try {
						Log.i("WebViewActivity", "成功从uritempFile中获取到了裁剪后的图片");
						SaveBitmap(photo);
					} catch (IOException e) {
						Log.i("WebViewActivity", "获取剪裁图片失败");
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} catch (FileNotFoundException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}    
       }    
       //}    
        super.onActivityResult(requestCode, resultCode, data);  
    }  

这里  requestCode == PHOTORESOULT 是将截取的图片从uritempFile中读取到图片的流数据并转化为Bitmp。

下面是最总要的裁剪代码了:

public void startPhotoZoom(Uri uri) {
		Log.i("WebViewActivity", "准备跳转到剪裁Activity");
		// 创建file文件,用于存储剪裁后的照片
		File cropImage = new File(Environment.getExternalStorageDirectory() + "/Android/data/com.xys/files/", "o_crop_image.jpg");
		String path = cropImage.getAbsolutePath();
		try {
			if (cropImage.exists()) {
				cropImage.delete();
			}
			cropImage.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}
		uritempFile = Uri.fromFile(cropImage);
    	//android图片裁剪区的
        Intent intent = new Intent("com.android.camera.action.CROP");  
        intent.setDataAndType(uri, "image/*");
        //手动给这个添加intent添加数据读取与写入权限,不然刚刚拍摄的图片将无法加载
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 0);
        intent.putExtra("aspectY", 0);
        intent.putExtra("scale", true);  
        Log.i("WebViewActivity", "设置剪裁参数");
        //裁剪后的图片Uri路径,uritempFile为Uri类变量  
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);  
        intent.putExtra("return-data", false);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        Log.i("WebViewActivity", "Uri:"+uritempFile.toString());
        Log.i("WebViewActivity", "指定Uri路径");
        intent.putExtra("noFaceDetection", true);
        startActivityForResult(intent, PHOTORESOULT);
        Log.i("WebViewActivity", "跳转activity  PHOTORESOULT");
    }

这里关于给Intent手动添加从uritempFile中读取和写入权限的代码就是卡住我的地方。。。

android的权限管理越来越收紧了,如果不添加

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

就会导致uritempFile这个Uri中无法写入裁剪图片的数据,当跳转裁剪页面是会Toast提示“图片加载失败”

但是奇怪的是,直接对本地相册中已有的图片进行裁剪是不会有任何问题,但是在照相机拍照后马上裁剪就会出现,

目前查阅资料也未发现原因。

以上。


你可能感兴趣的:(Unity-Android 从本地相册选取图片截图或拍照后截图并显示到unity)