如何在数据文件夹中将相机结果作为uri使用?
我正在创build一个应用程序,我想要捕获一个图像,然后我想发送该图像的电子邮件作为附件。
我打开一个使用android.provider.MediaStore.ACTION_IMAGE_CAPTURE
意图行动的相机,我传递文件的Uri作为参数EXTRA_OUTPUT
获取图像回文件。 这是完美的工作,我能够得到捕获的图像,如果我使用external storage uri
作为EXTRA_OUTPUT
但如果我使用数据文件夹uri它不工作,相机不闭合,其所有的button不工作。
这是我的代码在外部存储目录中获取结果
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); File out = Environment.getExternalStorageDirectory(); out = new File(out, imagename); i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out)); startActivityForResult(i, CAMERA_RESULT);
而这个代码是为了获取数据文件夹中的图像
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); File out = getFilesDir(); out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME); i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out)); startActivityForResult(i, CAMERA_RESULT);
我知道第三个应用程序无法访问数据文件夹,因此可能会导致问题,所以我创build了一个内容提供程序来共享文件。
这是我的内容提供类
public class MyContentProvider extends ContentProvider { private static final String Tag = RingtonContentProvider.class.getName(); public static final Uri CONTENT_URI = Uri .parse("content://xyz/"); private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>(); static { MIME_TYPES.put(".mp3", "audio/mp3"); MIME_TYPES.put(".wav", "audio/mp3"); MIME_TYPES.put(".jpg", "image/jpeg"); } @Override public boolean onCreate() { return true; } @Override public String getType(Uri uri) { String path = uri.toString(); for (String extension : MIME_TYPES.keySet()) { if (path.endsWith(extension)) { return (MIME_TYPES.get(extension)); } } return (null); } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { File f = new File(getContext().getFilesDir(), uri.getPath()); if (f.exists()) { return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY)); } throw new FileNotFoundException(uri.getPath()); } @Override public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort) { throw new RuntimeException("Operation not supported"); } @Override public Uri insert(Uri uri, ContentValues initialValues) { File file = new File(getContext().getFilesDir(), uri.getPath()); if(file.exists()) file.delete(); try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return Uri.fromFile(file); } @Override public int update(Uri uri, ContentValues values, String where, String[] whereArgs) { throw new RuntimeException("Operation not supported"); } @Override public int delete(Uri uri, String where, String[] whereArgs) { File f = new File(getContext().getFilesDir(), "image1.jpg"); if(f.exists()) f.delete(); f = new File(getContext().getFilesDir(), "image2.jpg"); if(f.exists()) f.delete(); getContext().getContentResolver().notifyChange(CONTENT_URI, null); } }
所以要使用这个内容提供我使用下面的代码将uri传递到相机的活动
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); Uri uri = MyContentProvider.CONTENT_URI; uri = Uri.withAppendedPath(uri, imagename); getContentResolver().insert(uri, null); getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null); Log.d(Tag, uri.toString()); i.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(i, CAMERA_RESULT);
现在,如果我通过url以外的其他外部存储目录相机正在打开,但它不是在模拟器中closures,但在设备相机将closures,但我没有得到结果。
我已经声明这个内容在清单文件中提供
<provider android:name=".contentproviders.MyContentProvider" android:authorities="xyz" />
此外,我已经授予了写入外部存储器的权限,并且还使用了相机 。
我能够使用外部存储器捕获映像,但是我想将映像存储在数据目录中而不是外部存储器中,因为如果外部存储器不可用,我想要捕获映像并想发送邮件。
如果我创build内容提供,那么我也可以将我的图像分享给电子邮件应用程序。
如果我们不提供额外的照相机意图,它将在活动结果中以字节[]forms返回图像作为数据额外的,但是这是为了缩略图的目的,所以我不能使用这种方式获得高分辨率图像。
请看看我的问题,并提出你的build议。 谢谢。
有两种方法可以解决这个问题。
1.保存您从onActivityResult方法收到的位图
您可以通过意图使用下面的代码捕捉照片启动相机
Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST);
捕捉照片后,您将获得onActivityResult方法的位图
if (requestCode == CAMERA_REQUEST) { Bitmap photo = (Bitmap) data.getExtras().get("data"); }
现在你可以简单地把这个位图保存到内部存储器中
注意:这里的位图对象由拇指图像组成,它不会有全分辨率的图像
2.使用内容提供者将位图直接保存到内部存储器
在这里我们将创build内容提供者类来允许本地存储目录对相机活动的许可
示例提供程序示例如下所示
public class MyFileContentProvider extends ContentProvider { public static final Uri CONTENT_URI = Uri.parse ("content://com.example.camerademo/"); private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>(); static { MIME_TYPES.put(".jpg", "image/jpeg"); MIME_TYPES.put(".jpeg", "image/jpeg"); } @Override public boolean onCreate() { try { File mFile = new File(getContext().getFilesDir(), "newImage.jpg"); if(!mFile.exists()) { mFile.createNewFile(); } getContext().getContentResolver().notifyChange(CONTENT_URI, null); return (true); } catch (Exception e) { e.printStackTrace(); return false; } } @Override public String getType(Uri uri) { String path = uri.toString(); for (String extension : MIME_TYPES.keySet()) { if (path.endsWith(extension)) { return (MIME_TYPES.get(extension)); } } return (null); } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { File f = new File(getContext().getFilesDir(), "newImage.jpg"); if (f.exists()) { return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_WRITE)); } throw new FileNotFoundException(uri.getPath()); } }
之后,您可以使用下面的代码简单地使用URI传递到相机活动
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI); startActivityForResult(i, CAMERA_RESULT);
如果您不想创build自己的提供程序,则可以使用support-library-v4中的FileProvider 。 有关详细的帮助,你可以看看这个post
我find的最佳解决scheme是:FileProvider(需要support-library-v4)它使用内部存储! https://developer.android.com/reference/android/support/v4/content/FileProvider.html
-
在应用程序元素清单中定义您的FileProvider:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="your.package.name.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/image_path" /> </provider>
-
在清单根元素中添加权限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
-
在例如res / xml / image_path.xml中定义图像path:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="captured_image" path="your/path/"/> </paths>
-
Java的:
private static final int IMAGE_REQUEST_CODE = 1; // your authority, must be the same as in your manifest file private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
4.1捕获意图:
File path = new File(activity.getFilesDir(), "your/path"); if (!path.exists()) path.mkdirs(); File image = new File(path, "image.jpg"); Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, IMAGE_REQUEST_CODE);
4.2 onActivityResult():
@Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == IMAGE_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { File path = new File(getFilesDir(), "your/path"); if (!path.exists()) path.mkdirs(); File imageFile = new File(path, "image.jpg"); // use imageFile to open your image } } super.onActivityResult(requestCode, resultCode, intent); }
意图调用捕捉照片,
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST);
然后在ActivityResult
获取位图
if (requestCode == CAMERA_REQUEST) { Bitmap photo = (Bitmap) data.getExtras().get("data"); }
然后写入内部存储器, 看到这个
// The openfileOutput() method creates a file on the phone/internal storage in the context of your application final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE); // Use the compress method on the BitMap object to write image to the OutputStream bm.compress(CompressFormat.JPEG, 90, fos);
然后下次阅读该文件,
Bitmap bitmap = BitmapFactory.decodeFile(file);
首先保存在您的外部存储传来的照片,并尝试它 –
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); this.imageView = (ImageView)this.findViewById(R.id.imageView1); Button photoButton = (Button) this.findViewById(R.id.button1); photoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST); } }); } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == CAMERA_REQUEST) { Bitmap bmp = intent.getExtras().get("data"); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bmp.compress(Bitmap.CompressFormat.PNG, 100, stream); byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array // save it in your external storage. FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png")); fo.write(byteArray); fo.flush(); fo.close(); } }
下一个目标 –
File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png"); startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND) .setType("image/jpg") .putExtra(Intent.EXTRA_SUBJECT, "Subject") .putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile)) .putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
您也可以在不需要内容提供商的情况下执行此操作,因为无论如何您都需要SD卡才能打开相机图像捕获目的。 你当然可以破解SD卡的存在,但不是相机意图捕获….所以你正在检查外部存储,但在这一点上需要它….只要你也应该检查出一个作物库裁剪图像,而不是使用本地,因为它不是很好的跨设备支持。
File mediaStorageDir; String photoFileName = "photo.jpg"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // page = getArguments().getInt("someInt", 0); // title = getArguments().getString("someTitle"); // Get safe storage directory for photos mediaStorageDir = new File( Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), APP_TAG); // Create the storage directory if it does not exist if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) { Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory()); Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath()); Log.d(APP_TAG, "Directory exists: " + Environment.getExternalStorageState()); Log.d(APP_TAG, "failed to create directory"); } }
在你的“拍照”代码中:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));
…
public Uri getPhotoFileUri(String fileName) { return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator + fileName)); }
在研究这个bug一段时间后,我注意到,只有当手机内存不足时,才会重新启动称为相机意图的活动。 所以因为活动重新开始,持有path或Uri的对象被刷新(消零)
所以我build议你捕获/检测onActivityResult中的空对象,并提示用户释放设备上的空间或重新启动手机以进行快速临时修复。