意图

我们正在尝试使用本地相机应用程序,让用户采取一个新的图片。如果我们省略 EXTRA_OUTPUT extra并返回小的位图图像,那么它就可以正常工作。但是,如果我们 putExtra(EXTRA_OUTPUT,...)的意图之前启动它,一切工作,直到你试图点击“确定”按钮在相机应用程序。“确定”按钮什么都没用。摄像头应用程序保持开放,没有锁定。我们可以取消它,但文件永远不会被写入。我们到底需要做什么才能让 ACTION_IMAGE_CAPTURE把拍摄的图片写到文件中?

编辑: 这是通过 MediaStore.ACTION_IMAGE_CAPTURE的意图,只是为了清楚

189760 次浏览

您所描述的工作流应该按照您所描述的那样工作。如果你能给我们展示创建意图的代码,也许会有帮助。一般来说,下面的模式应该可以让您做您正在尝试的事情。

private void saveFullImage() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(Environment.getExternalStorageDirectory(), "test.jpg");
outputFileUri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(intent, TAKE_PICTURE);
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if ((requestCode == TAKE_PICTURE) && (resultCode == Activity.RESULT_OK)) {
// Check if the result includes a thumbnail Bitmap
if (data == null) {
// TODO Do something with the full image stored
// in outputFileUri. Perhaps copying it to the app folder
}
}
}

请注意,将创建和保存该文件的是 相机活动,而且它实际上不是应用程序的一部分,因此它不会对应用程序文件夹具有写权限。要将文件保存到应用程序文件夹中,请在 SD 卡上创建一个临时文件,并将其移动到 onActivityResult处理程序中的应用程序文件夹中。

这是一个 有详细记录的漏洞在某些版本的机器人。也就是说,在安卓系统的谷歌体验版本中,图像捕捉不能像文档中描述的那样工作。我通常在实用程序类中使用的是类似的东西。

public boolean hasImageCaptureBug() {


// list of known devices that have the bug
ArrayList<String> devices = new ArrayList<String>();
devices.add("android-devphone1/dream_devphone/dream");
devices.add("generic/sdk/generic");
devices.add("vodafone/vfpioneer/sapphire");
devices.add("tmobile/kila/dream");
devices.add("verizon/voles/sholes");
devices.add("google_ion/google_ion/sapphire");


return devices.contains(android.os.Build.BRAND + "/" + android.os.Build.PRODUCT + "/"
+ android.os.Build.DEVICE);


}

然后,当我启动图像捕获时,我创建一个意图来检查 bug。

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
if (hasImageCaptureBug()) {
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File("/sdcard/tmp")));
} else {
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(i, mRequestCode);

然后在我返回的活动中,我根据设备做不同的事情。

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case GlobalConstants.IMAGE_CAPTURE:
Uri u;
if (hasImageCaptureBug()) {
File fi = new File("/sdcard/tmp");
try {
u = Uri.parse(android.provider.MediaStore.Images.Media.insertImage(getContentResolver(), fi.getAbsolutePath(), null, null));
if (!fi.delete()) {
Log.i("logMarker", "Failed to delete " + fi);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
u = intent.getData();
}
}

这节省了你必须写一个新的照相机应用程序,但这个代码也不是很好。大问题是

  1. 你永远不会得到完整的图像 装有窃听器的设备 512像素宽的图片 插入到图像内容中 设备上的提供程序 所有东西都是文档, 你会看到一幅很大的正常画面

  2. 你必须保持名单 书中写道,对于设备来说是可能的 闪现出一个版本的 安卓系统(比如: 构建 ) ,修复了 bug。 如果发生这种情况,您的代码将 补丁是使用整个 设备指纹

让相机写入 sdcard,但保存在画廊应用程序的一个新相册中,我使用这个:

 File imageDirectory = new File("/sdcard/signifio");
String path = imageDirectory.toString().toLowerCase();
String name = imageDirectory.getName().toLowerCase();




ContentValues values = new ContentValues();
values.put(Media.TITLE, "Image");
values.put(Images.Media.BUCKET_ID, path.hashCode());
values.put(Images.Media.BUCKET_DISPLAY_NAME,name);


values.put(Images.Media.MIME_TYPE, "image/jpeg");
values.put(Media.DESCRIPTION, "Image capture by camera");
values.put("_data", "/sdcard/signifio/1111.jpg");
uri = getContentResolver().insert( Media.EXTERNAL_CONTENT_URI , values);
Intent i = new Intent("android.media.action.IMAGE_CAPTURE");


i.putExtra(MediaStore.EXTRA_OUTPUT, uri);


startActivityForResult(i, 0);

请注意,您将需要生成一个唯一的文件名每次和替换1111.jpg,我写的。 这是用 Nexus One 测试的。 Uri 是在私有类中声明的,因此在活动结果中,如果需要的话,我可以将图像从 uri 加载到 imageView 进行预览。

我有同样的问题,在相机应用程序的确定按钮没有做任何事情,都在模拟器和连接一。

MediaStore.EXTRA_OUTPUT中指定了一个没有空格、没有特殊字符的安全文件名之后,问题就解决了。此外,如果您指定的文件位于一个尚未创建的目录中,那么您必须首先创建它。相机应用程序不能为你做 mkdir。

为了跟进 Yenchi 上面的评论,如果摄像头应用程序不能写入相关目录,OK 按钮也将什么都不做。

这意味着您不能在只能由应用程序写入的位置创建文件(例如,getCacheDir())下的内容,但是 getExternalFilesDir()下的内容应该可以工作)。

如果摄像头应用程序在无法写入指定的 EXTRA_OUTPUT路径时向日志打印错误消息,那就太好了,但我没有找到。

我知道以前有人回答过这个问题,但是我知道很多人在这个问题上犯了错误,所以我要加一条评论。

我的 Nexus One 也出现了同样的问题。这是来自在相机应用程序启动之前不存在于磁盘上的文件。因此,我确保在启动摄像头应用程序之前该文件已经存在。下面是我使用的一些示例代码:

String storageState = Environment.getExternalStorageState();
if(storageState.equals(Environment.MEDIA_MOUNTED)) {


String path = Environment.getExternalStorageDirectory().getName() + File.separatorChar + "Android/data/" + MainActivity.this.getPackageName() + "/files/" + md5(upc) + ".jpg";
_photoFile = new File(path);
try {
if(_photoFile.exists() == false) {
_photoFile.getParentFile().mkdirs();
_photoFile.createNewFile();
}


} catch (IOException e) {
Log.e(TAG, "Could not create file.", e);
}
Log.i(TAG, path);


_fileUri = Uri.fromFile(_photoFile);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE );
intent.putExtra( MediaStore.EXTRA_OUTPUT, _fileUri);
startActivityForResult(intent, TAKE_PICTURE);
}   else {
new AlertDialog.Builder(MainActivity.this)
.setMessage("External Storeage (SD Card) is required.\n\nCurrent state: " + storageState)
.setCancelable(true).create().show();
}

我首先使用 MD5散列创建一个惟一(多少有点)的文件名,并将其放入适当的文件夹中。然后我检查它是否存在(不应该存在,但无论如何都要检查它的良好实践)。如果它不存在,我获取父目录(一个文件夹)并创建文件夹层次结构(因此,如果导致文件位置的文件夹不存在,它们将在这一行之后。然后我创建文件。一旦文件被创建,我得到的 Uri 和传递它的意图,然后确定按钮的工作原理和所有的黄金。

现在,当在相机应用程序上按下 OK 按钮时,文件将出现在给定的位置。在本例中,它是/sdcard/Android/data/com.example.myapp/files/234asdioue23498ad。JPG

不需要像上面提到的那样复制“ onActivityResult”中的文件。

我也有同样的问题,我用以下方法解决了:

问题是,当你指定一个只有你的应用程序才能访问的文件时(例如,通过调用 getFileStreamPath("file");)

这就是为什么我只是确保给定的文件真的存在,并且每个人都有写访问它。

Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File outFile = getFileStreamPath(Config.IMAGE_FILENAME);
outFile.createNewFile();
outFile.setWritable(true, false);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,Uri.fromFile(outFile));
startActivityForResult(intent, 2);

这样,摄像头应用程序就可以对给定的 Uri 进行写访问,OK 按钮也可以正常工作:)

我建议你跟随机器人的培训帖子来捕捉照片。他们举例说明如何拍摄大小照片。您还可以从 给你下载源代码

我创建了一个简单的库,可以管理从不同来源(画廊,相机)选择图像,也许保存到某个位置(SD 卡或内存) ,然后返回图像,所以请免费使用和改进它-Android-ImageChooser Android-图像选择器

我已经通过了一些照片捕捉策略,似乎总是有一个情况下,一个平台或某些设备,其中一些或所有上述策略将失败在意想不到的方式。我能够找到一个策略,使用下面的 URI 生成代码,它似乎在大多数情况下,如果不是所有的情况下工作。

mPhotoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new ContentValues());
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoUri);
startActivityForResult(intent,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE_CONTENT_RESOLVER);

为了进一步参与讨论并帮助新手,我创建了一个示例/测试应用程序,它显示了几种不同的照片捕捉实现策略。我们绝对鼓励其他实现的贡献加入讨论。

Https://github.com/deepwinter/androidcameratester

正如 普雷文指出的那样,文件需要由摄像头来写。

在我的使用中,我希望将文件存储在内部存储器中:

Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (i.resolveActivity(getPackageManager()!=null)){
try{
cacheFile = createTempFile("img",".jpg",getCacheDir());
cacheFile.setWritavle(true,false);
}catch(IOException e){}
if(cacheFile != null){
i.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(cacheFile));
startActivityForResult(i,REQUEST_IMAGE_CAPTURE);
}
}

这里的 cacheFile是一个全局文件,用于引用写入的文件。 然后在 result 方法中返回的意图为 null。 然后处理意图的方法如下:

protected void onActivityResult(int requestCode,int resultCode,Intent data){
if(requestCode != RESULT_OK){
return;
}
if(requestCode == REQUEST_IMAGE_CAPTURE){
try{
File output = getImageFile();
if(output != null && cacheFile != null){
copyFile(cacheFile,output);


//Process image file stored at output


cacheFile.delete();
cacheFile=null;
}
}catch(IOException e){}
}
}

这里的 getImageFile()是一个实用的方法来命名和创建应该存储图像的文件,而 copyFile()是一个复制文件的方法。

你可以用这个代码

private Intent getCameraIntent() {


PackageManager packageManager = mContext.getPackageManager();
List<ApplicationInfo> list = packageManager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Intent main = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
List<ResolveInfo> launchables = packageManager.queryIntentActivities(main, 0);
if (launchables.size() == 1)
return packageManager.getLaunchIntentForPackage(launchables.get(0).activityInfo.packageName);
else
for (int n = 0; n < list.size(); n++) {
if ((list.get(n).flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
Log.d("TAG", "Installed Applications  : " + list.get(n).loadLabel(packageManager).toString());
Log.d("TAG", "package name  : " + list.get(n).packageName);
String defaultCameraPackage = list.get(n).packageName;




if (launchables.size() > 1)
for (int i = 0; i < launchables.size(); i++) {
if (defaultCameraPackage.equals(launchables.get(i).activityInfo.packageName)) {
return packageManager.getLaunchIntentForPackage(defaultCameraPackage);
}
}
}
}
return null;
}

这是非常简单的解决这个问题与活动结果代码简单的尝试这种方法

if (reqCode == RECORD_VIDEO) {
if(resCode == RESULT_OK) {
if (uri != null) {
compress();
}
} else if(resCode == RESULT_CANCELED && data!=null){
Toast.makeText(MainActivity.this,"No Video Recorded",Toast.LENGTH_SHORT).show();
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_CANCELED)
{
//do not process data, I use return; to resume activity calling camera intent
enter code here
}
}