ChatGPT解决这个技术问题 Extra ChatGPT

Android ACTION_IMAGE_CAPTURE 意图

我们正在尝试使用本机相机应用程序让用户拍摄新照片。如果我们省略 EXTRA_OUTPUT extra 并返回小的位图图像,它就可以正常工作。但是,如果我们在启动意图之前putExtra(EXTRA_OUTPUT,...),则一切正常,直到您尝试点击相机应用程序中的“确定”按钮。 “确定”按钮什么也不做。相机应用程序保持打开状态,没有任何锁定。我们可以取消它,但文件永远不会被写入。要让 ACTION_IMAGE_CAPTURE 将拍摄的照片写入文件,我们究竟需要做什么?

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


3
3 revs, 2 users 99%

这是某些版本的 android 中的 well documented bug。也就是说,在 android 的 google 体验版本中,图像捕获不能像记录的那样工作。我通常在实用程序类中使用的是这样的东西。

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();
            }
    }

这让您不必编写新的相机应用程序,但这段代码也不是很好。最大的问题是

您永远不会从带有错误的设备中获得全尺寸图像。您将获得 512px 宽的图片,这些图片被插入到图像内容提供程序中。在没有错误的设备上,一切都像文档一样工作,你会得到一张正常的大图。您必须维护列表。如所写,设备可以使用修复了该错误的 android 版本(例如 cyanogenmod 的构建)进行刷新。如果发生这种情况,您的代码将崩溃。解决方法是使用整个设备指纹。


在 Galaxy S 上:intent.getData() 返回 null,此外,Galaxy s 上的相机应用程序会自行将新照片插入图库,但没有返回 uri。因此,如果您将文件中的照片插入图库,则会出现重复的照片。
嘿,我的设备未在此处列出,我已通过您的方式实现。但我的应用程序仍然崩溃,我不知道原因。请帮助我
在 droid-x 和 Sony xpheria 设备上使用此代码。两个设备都将意图值返回为 null。 droidx 也将结果代码返回为 0,而 xpheria 将结果代码返回为 -1。任何人都知道为什么会这样。
yanokwa 答案开头的“有据可查的错误”的链接不正确。该错误是关于在没有 putExtra(EXTRA_OUTPUT,...) 的情况下调用相机应用程序
code.google.com/p/android/issues/detail?id=1480 那么,您如何解释呢?
D
Donn Felker

我知道之前已经回答过这个问题,但我知道很多人对此感到困惑,所以我要添加评论。

我的 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 按钮按预期工作,一切都是金色的。

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

不需要复制上面发布的“onActivityResult”中的文件。


如果您使用的是比 Android 1.5 更新的版本,请确保您的清单中有 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - 花了几个小时调试相机的东西,这没有包括在内,所以我的保存总是会失败。
这对我很有用,但是每大约 10 个图像左右,空白文件不会被覆盖,我最终会得到一个 0 字节的文件,图像应该在哪里。这很烦人,而且很难追踪。
在某些设备上,例如带有果冻豆的 nexus 7,您需要更改 Environment.getExternalStorageDirectory().getName() 以使用 .getAbsolutePath() 而不是 .getName()
r
rikoona

我已经通过了许多照片捕捉策略,似乎总是有一个案例、一个平台或某些设备,其中部分或全部上述策略会以意想不到的方式失败。我能够找到一个使用下面的 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


这太棒了。我还没有在我所有的设备上测试这个,但我假设你已经做了很多。这个话题有很多噪音,这个特别的答案是如此简单和干净。到目前为止在 Nexus 5 上工作,这是我第一次收到很多报告说 ACTION_IMAGE_CAPTURE 在没有 EXTRA_OUTPUT 的情况下无法工作。我确实希望这一切都能奏效,但到目前为止还不错。谢谢
@deepwinter - 谢谢先生。我正在拉扯我的头发,但您的内容解析器解决方案在我遇到的所有有问题的情况下都能很好地工作。
参加这个聚会有点晚了,但这是为我工作的唯一解决方案。非常感谢!
我可以以某种方式检索 onActivityResult 中的 MediaStore.EXTRA_OUTPUT 还是我真的必须自己记住它?我必须坚持保存它,这样我的应用程序甚至可以记住它,如果它在拍摄照片时被破坏......
它不适用于带有棒棒糖的三星 Galaxy Note 3:ava.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getScheme()' on an null object reference
P
Praveenkumar

我遇到了同样的问题,相机应用程序中的 OK 按钮在模拟器和 nexus 上都没有任何作用。

MediaStore.EXTRA_OUTPUT 中指定一个没有空格、没有特殊字符的安全文件名后,问题就消失了。此外,如果您指定的文件位于尚未创建的目录中,则必须先创建它。相机应用程序不会为您执行 mkdir。


如果您的路径具有多个目录级别并且您希望创建所有目录级别,请确保使用 mkdirs() 而不是 mkdir()mkdir 只创建最后一个目录,即“叶”目录)。我遇到了一些问题,this answer 帮助了我。
我们可以使用简单的时间戳吗?或者也可以犯一些错误?请在回复时标记我:)
@user2247689 我不确定您通过“简单时间戳”创建的文件名,但只要它们不包含 SD 卡上 FAT 格式不允许的特殊字符,我认为应该没问题。
R
Reto Meier

您描述的工作流程应该像您描述的那样工作。如果您可以向我们展示有关创建 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 Activity,它实际上并不是您的应用程序的一部分,因此它没有对您的应用程序文件夹的写入权限。要将文件保存到您的应用文件夹,请在 SD 卡上创建一个临时文件并将其移动到 onActivityResult 处理程序中的应用文件夹。


这个 /should/ 工作,但相机应用程序的 ok 按钮什么也不做。我们确实让它可以写入 SD 卡,所以我怀疑这是一个文件权限问题(尽管我认为应用程序自动对其个人目录具有写入权限)。谢谢您的帮助!
您的应用程序对其个人目录具有写入权限 - 相机应用程序(正在拍照)没有。不过,您可以在 onActivityResult 中移动文件,因为它在您的应用程序中。或者,您可以自己实现一个相机活动,但这似乎有点过头了。
重做相机应用程序似乎是一种常见但乏味的方法......当我们使用 getDir("images",MODE_WORLD_WRITEABLE) 时,该权限是否不适用于我们告诉它在该目录中创建的任何文件?
不必要。权限是针对文件夹的,不一定针对其中的文件。
我使用 Android 2.2 及更高版本的 Android 设备测试了代码,所有设备都将图像保存到提供的路径。所以我想这应该是获取图像的标准行为。
P
Praveenkumar

为了跟进Yenchi 的上述评论,如果相机应用程序无法写入相关目录,OK 按钮也将不起作用。

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

如果相机应用程序无法写入指定的 EXTRA_OUTPUT 路径,那么如果相机应用程序在日志中打印一条错误消息,那就太好了,但我没有找到。


n
nabulaer

让相机写入 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。这是用连接一测试的。 uri 在私有类中声明,因此在活动结果中,如果需要,我可以将图像从 uri 加载到 imageView 以进行预览。


“_data”列来自哪里?它没有常数吗?
啊,它是 developer.android.com/reference/android/provider/… 你真的不应该在代码中使用魔术字符串。
G
Goddchen

我有同样的问题,我用以下方法修复了它:

问题在于,当您指定只有您的应用有权访问的文件时(例如,通过调用 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 按钮可以正常工作:)


AndroidRuntime (2.1 模拟器) 抛出 NoSuchMethodError: java.io.File.setWritable
P
Praveenkumar

我建议您按照 android 培训帖子拍摄照片。他们在一个例子中展示了如何拍摄大大小小的照片。您也可以从 here 下载源代码


s
svenkapudija

我创建了一个简单的库,它将管理从不同来源(画廊、相机)中选择图像,可能将其保存到某个位置(SD 卡或内部存储器)并将图像返回,因此请自由使用并改进它 - Android-ImageChooser .


你的链接失效了!!为什么,我在 2 周前看到它,我想使用它:(
C
Community

正如 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 是一个全局文件,用于引用写入的文件。然后在结果方法中返回的意图为空。那么处理意图的方法如下:

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() 是一种复制文件的方法。


W
Walery Strauch

您可以使用此代码

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;
}

T
Tunaki

用Activity Result Code解决这个问题很简单 试试这个方法

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();
   }
}

上述不完整的片段不值得它声称的简单性。当然,对于不熟悉的旅行者来说,也有隐藏的复杂性。例如扫描文件是否存在。例如,它对应用程序是公开的或私有的。例如需要提供者或仅获取缩略图大小。这些是粗心的旅行者需要注意的信息空白。
A
Ajesh Gupta
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
    }
}