新项目关于聊天时发送图片需求,要求从本地相册或拍照图片选取然后发送给玩家,百度了很多这样的帖子,参考了好几篇帖子,折腾了一天才大致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提示“图片加载失败”
但是奇怪的是,直接对本地相册中已有的图片进行裁剪是不会有任何问题,但是在照相机拍照后马上裁剪就会出现,
目前查阅资料也未发现原因。
以上。