我们正在尝试使用原生相机应用让用户拍摄新照片。如果我们省略EXTRA_OUTPUT extra
并返回小的Bitmap图像,它就可以正常工作。但是,如果在启动它之前我们putExtra(EXTRA_OUTPUT,...)
有意图,那么一切都会有效,直到您尝试点击相机应用中的“确定”按钮。 “确定”按钮无效。相机应用程序保持打开状态,没有任何锁定。我们可以取消它,但文件永远不会被写入。我们到底要做什么才能让ACTION_IMAGE_CAPTURE
将拍摄的照片写入文件?
编辑:这是通过MediaStore.ACTION_IMAGE_CAPTURE
意图完成的,只是为了清楚
答案 0 :(得分:144)
在某些版本的android中,这是well documented bug。也就是说,在google体验构建的android上,图像捕获不能像记录的那样工作。在公用事业类中,我通常使用的是这样的东西。
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);
}
然后当我启动图像捕获时,我创建了一个检查错误的意图。
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();
}
}
这可以节省你必须编写一个新的相机应用程序,但这个代码也不是很好。最大的问题是
你永远不会从中获得全尺寸的图像 有bug的设备。你得到 那张512px宽的照片 被插入到图像内容中 供应商。在没有的设备上 bug,一切都作为文档, 你会得到一个很正常的画面。
你必须维护清单。如 写的,有可能是设备 用一个版本闪现 修复了错误的android(说cyanogenmod's builds)。 如果发生这种情况,您的代码将会 崩溃。修复是使用整个 设备指纹。
答案 1 :(得分:50)
我知道这已经得到了回答,但我知道有很多人因此而被绊倒,所以我要添加一条评论。
我在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并将其传递给intent,然后OK按钮按预期工作,并且都是黄金。
现在,当在相机应用程序上按下确定按钮时,该文件将出现在给定位置。在这个例子中,它将是/sdcard/Android/data/com.example.myapp/files/234asdioue23498ad.jpg
无需复制上面发布的“onActivityResult”中的文件。
答案 2 :(得分:32)
我已经完成了许多照片捕捉策略,并且似乎总是存在一个案例,一个平台或某些设备,其中上述部分或全部策略将以意想不到的方式失败。我能够找到一种使用下面的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);
为了进一步为讨论做出贡献并帮助新手,我创建了一个示例/测试应用程序,其中显示了几种不同的照片捕获实现策略。绝对鼓励其他实现的贡献添加到讨论中。
答案 3 :(得分:30)
我遇到了同样的问题,即相机应用程序中的OK按钮在模拟器和nexus上都没有做任何事情。
在MediaStore.EXTRA_OUTPUT
中指定一个没有空格但没有特殊字符的安全文件名后,问题就消失了。另外,如果您指定的文件位于尚未创建的目录中,则表示首先创建它。相机应用程序不会为你做mkdir。
答案 4 :(得分:28)
您描述的工作流程应该像您所描述的那样工作。如果您可以向我们展示创建Intent的代码,这可能会有所帮助。一般来说,下面的模式可以让你做你正在尝试的事情。
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
}
}
}
请注意, Camera 活动将创建并保存文件,并且它实际上不是您应用程序的一部分,因此它不具有您的应用程序文件夹的写入权限。要将文件保存到app文件夹,请在SD卡上创建一个临时文件,然后将其移至onActivityResult
处理程序中的app文件夹。
答案 5 :(得分:7)
要跟进Yenchi上面的评论,如果相机应用程序无法写入相关目录,则OK按钮也不会执行任何操作。
这意味着您无法在只能由应用程序编写的位置创建文件(例如,getCacheDir())
getExternalFilesDir()
下的某些内容应该可以正常工作。
如果相机应用程序如果无法写入指定的EXTRA_OUTPUT
路径,则会向日志显示错误消息,但我找不到。
答案 6 :(得分:4)
让相机写入SD卡,但保留在图库应用程序的新相册中我使用此:
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测试的。 uri在私有类中声明,因此在活动结果中,我可以将图像从uri加载到imageView以供预览(如果需要)。
答案 7 :(得分:2)
我建议你按照Android训练帖来拍摄照片。他们在一个例子中展示了如何拍摄小图片和大图片。您也可以从here
下载源代码答案 8 :(得分:1)
我遇到了同样的问题,我用以下方法解决了这个问题:
问题在于,当您指定只有您的应用有权访问的文件时(例如通过调用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按钮可以正常工作:)
答案 9 :(得分:1)
使用活动结果代码解决此问题非常简单。请尝试使用此方法
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();
}
}
答案 10 :(得分:0)
我创建了一个简单的库,可以管理从不同来源(图库,相机)选择图像,也可以将其保存到某个位置(SD卡或内部存储器)并将图像返回,以便免费使用并改进它 - Android-ImageChooser
答案 11 :(得分:0)
相机可以写文件,Praveen指出。
在我的使用中,我想将文件存储在内部存储中。我是这样做的:
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
是一个全局文件,用于指代写入的文件。
然后在结果方法中返回的intent为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()
是复制文件的方法。
答案 12 :(得分:0)
您可以使用此代码
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;
}
答案 13 :(得分:0)
.reflected