摘要:利用相机意图拍照,并以正确方向显示照片(适用于大多数设备)。

25

似乎这是世界上最简单的事情之一:使用默认相机活动在您的Android应用程序中拍照。然而,有许多陷阱,在StackOverflow和网络上有几篇文章对其进行了覆盖,例如传回空意图、图片方向不正确或OutOfMemoryErrors等。

我正在寻找一种解决方案,允许我

  1. 通过相机意图启动相机活动,
  2. 检索照片的Uri,并且
  3. 检索照片的正确方向。

此外,尽可能避免设备配置(制造商、型号、操作系统版本)的具体实现。因此,我想知道:最好的方法是什么?


我们可以把这个放到Github上吗? - Kleptine
是的,我很快就会。我会让你知道。 - Ralf
+1 为总结和解决方案。但这并不能消除瘙痒感。原因之一是用户可能选择了自定义默认相机;代码查看的是制造_phone_的厂商,而不是当前默认相机应用程序。我将研究使用包名称来切换行为的可能性。此外,我更喜欢根据 API 调用意图并在调用后掩盖错误行为。这会有点奖励那些修复其实现的制造商。您认为这可能吗,考虑到方向等问题? - volley
这很酷,但请将其放在GitHub上作为某种库,我刚刚尝试实现您的代码,但您有一种奇怪的风格,我不太理解。例如,为什么不使用setContentView而是在onCreate中进行一些奇怪的膨胀? - A. Steenbergen
1
这个问题似乎不是一个问题,而是一个提供解决方案的帖子。如果将其分成一个问题和一个答案,此主题可能适用于 Stack Overflow。 - bummi
显示剩余11条评论
4个回答

22

更新:2014年1月2日: 我尽力避免根据设备制造商实施不同的策略。不幸的是,我无法回避它。在阅读了数百篇帖子并与几位开发人员交谈后,没有人找到一种可在所有设备上运行且无需实施特定于设备制造商的代码的解决方案。

在我在StackOverflow上发布了我的解决方案后,一些开发人员要求我将我的代码发布到github上。所以现在它在这里:AndroidCameraUtil on github

该代码已在Android API-Level >= 8的各种设备上成功测试。有关完整列表,请参见github上的Readme文件。

CameraIntentHelperActivity提供了主要功能,以下内容也对其进行了更详细的描述。

调用默认相机活动:

  • 对于三星和索尼设备:我使用方法调用startActivityForResult调用相机活动。我只设置恒定的CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE。我不设置任何其他intent extras。
  • 对于所有其他设备:与之前一样,我使用方法调用startActivityForResult调用相机活动。但是,这次我另外设置了intent extra MediaStore.EXTRA_OUTPUT并提供一个URI,指定图片要存储的位置。

在两种情况下,我都记住了相机活动开始的时间。


在相机活动结果上:

  1. Mediastore:首先,我尝试从MediaStore中读取正在捕获的照片。使用MediaStore内容上的mangedQuery,我检索最新拍摄的图像以及其方向属性和时间戳。如果我找到了一张图片并且它在调用相机意图之前没有被拍摄过,则它就是我要寻找的图片。否则,我会放弃该结果并尝试以下方法之一。
  2. 意图额外信息: 其次,我尝试从返回的意图中的intent.getData()获取图像Uri。如果这也不成功,我将继续执行第3步。
  3. 默认照片Uri: 如果以上所有步骤都不起作用,我将使用传递给相机活动的图像Uri。

此时,我已经获取了照片Uri及其方向,并将它们传递给我的UploadPhotoActivity。


图像处理

请仔细查看我的BitmapHelper类。 它基于该教程中详细描述的代码。

此外,shrinkBitmap方法还根据之前提取的方向信息旋转图像(如果需要)。


希望对你们中的一些人有所帮助。


1
请问您可以提供更多信息吗?例如您正在运行哪个Android版本,是否安装了自定义ROM,是否使用第三方相机应用程序等等。 - Ralf
我20天前购买了索尼Xperia C2305 Android 4.2.2,版本号为16.0.B.2.13。我没有安装任何东西,使用的是索尼默认的相机应用程序。你能帮我解决一下吗?相机应用程序已经打开,但在点击“拍照按钮”后会出现闪光灯,然后回到呼叫屏幕...照片没有保存...这个应用程序在三星和一些本地供应商的手机上运行得很完美。 - Sharp Edge
我想你需要实现一个特定设备的“策略”。你可以通过在CameraIntentHelperActivity类的startCameraIntent方法中添加以下几行代码来实现。请确保model字段实际上保存了值“C2305”:if (manufacturer.contains("sony") && model.contains("C2305")) { setPreDefinedCameraUri = true; } - Ralf
我可以使用相机API制作自定义相机应用程序,具有基本功能。目前来看,与其使用意图,自己创建相机应用程序似乎是更好的方法。感谢@Ralf的快速回复。 - Sharp Edge
1
你们有没有计划简化添加项目的步骤,例如 compile 'de.ecostatic:camerautil:1.0.0'?这将会非常非常方便 :) - jean d'arme

0

我在使用Sony Xperia Z5时遇到了一些问题。

我添加了这个,效果好多了。

if (buildType.contains("sony")&& buildDevice.contains("e5823")) {
                setPreDefinedCameraUri = true;}

但在22次测试中,有4次它重新启动了相机,还有一次它重新启动了两次。我每次测试都重启了应用程序。 有没有什么方法可以避免这种情况,或者我应该接受这个结果?

问题是,如果相机重新启动,我可以按两次返回按钮,瞬间就能在我的ImageView中看到图像并保存。


0

我已经在Sony Xperia Go、Samsung Galaxy SII、Samsung Galaxy SIII mini和Samsung Galaxy Y上测试了这段代码,它可以在所有设备上运行!

但是在LG E400(2.3.6)上它无法工作,你会在图库中看到重复的图片。因此,我在void startCameraIntent()中添加了manufacturer.contains("lge")以解决问题。

if(!(manufacturer.contains("samsung")) && !(manufacturer.contains("sony")) && !(manufacturer.contains("lge"))) {
    String filename = System.currentTimeMillis() + ".jpg";
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.TITLE, filename);
    cameraPicUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraPicUri);
}

0
在安装了CM 10.1的Galaxy S3上,我在BitmapHelper中遇到了空指针异常:

bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);

随后我的UploadPhotoActivity在以下位置失败:

    try {
        photo = BitmapHelper.readBitmap(this, cameraPicUri);
        if (photo != null) {
            photo = BitmapHelper.shrinkBitmap(photo, 600, rotateXDegrees);
            thumbnail = BitmapHelper.shrinkBitmap(photo, 100);
            ImageView imageView = (ImageView) findViewById(R.id.sustainable_action_photo);
            imageView.setImageBitmap(photo);                
        } else {
            Log.e(TAG,"IMAGE ERROR 1");
        }
    } catch (Exception e) {
            Log.e(TAG,"IMAGE ERROR 2");
        e.printStackTrace();
    }
在第二个日志(图像错误2)处。 尝试了几次后,我的相机坏了,并出现了“无法连接到相机”的错误。
在Nexus 7上测试过,完美运行。
编辑:将其缩小到以下内容:
fileDescriptor = context.getContentResolver().openAssetFileDescriptor(selectedImage,“r”);
虽然selectedImage包含此内容:

file:///storage/emulated/0/DCIM/Camera/IMG_20131023_183343.jpg

fileDescriptor返回FileNotFoundException。我检查了文件系统,发现图像未保存在此位置。TakePhotoActivity中的cameraPicUri指向一个不存在的图像。我正在检查出错的地方。

编辑2:我找到了错误:由于设备是三星设备,并告诉应用程序它是三星设备,因此应用了三星特定的修复程序。但Cyanogenmod不需要这些修复程序,最终代码会崩溃。一旦你删除

(manufacturer.contains("samsung")) &&

它就可以工作了。当然,由于这是自定义ROM,你无法计划这种情况。我正在尝试找出一种方法来检测设备是否运行Cyanogenmod,然后将其包含在你的代码中。

感谢你提供的良好相机修复!

编辑3:我已经将它修复为在Galaxy S3上运行Cyanogenmod,将你的代码更改为:好吧,现在有时候它能工作,有时候它不能。奇怪。

if (getPackageManager().hasSystemFeature("com.cyanogenmod.android") || (!(manufacturer.contains("samsung")) && !(manufacturer.contains("sony")) && !(manufacturer.contains("lge"))))


嘿,我更新了我的代码并修复了一些错误。也许这个更新可以解决你的问题。 - Ralf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接