让用户拍照或从 Android 中的图库中选择图像的单一意图

Posted

技术标签:

【中文标题】让用户拍照或从 Android 中的图库中选择图像的单一意图【英文标题】:Single intent to let user take picture OR pick image from gallery in Android 【发布时间】:2011-02-12 01:57:41 【问题描述】:

我正在为 android 2.1 及更高版本开发应用程序。我想让我的用户在我的应用程序中选择个人资料图片(我没有使用联系人框架)。

理想的解决方案是触发一个意图,使用户能够从图库中选择图像,但如果没有合适的图像可用,则使用相机拍照(反之亦然,即允许用户拍摄图片,但如果他们知道他们已经有合适的图像,让他们进入画廊并选择所述图像)。

目前我可以做一个或另一个,但不能同时做。

如果我使用 MediaStore.ACTION_IMAGE_CAPTURE 直接进入相机模式,则没有选项可以放入图库。

如果我使用 Intent.ACTION_PICK 直接进入画廊,那么我可以选择一张图像,但如果我点击相机按钮(在画廊的右上角),则会触发一个新的相机意图。因此,拍摄的任何照片都不会直接返回到我的应用程序。 (当然你可以按返回按钮回到画廊并从那里选择图像,但这是一个额外的不必要的步骤,而且一点也不直观)。

那么有没有办法将两者结合起来,还是我必须提供一个菜单来在我的应用程序中执行其中一个或另一个?似乎这将是一个常见的用例......我肯定错过了什么吗?

【问题讨论】:

请检查这个答案:***.com/questions/4455558/… ***.com/questions/27874038/… 【参考方案1】:

你可以尝试做这样的事情:

// ...
// Within your enclosing Class
// ...
private static final int SELECT_PICTURE = 1;

// ... 

Intent pickIntent = new Intent();
pickIntent.setType("image/*");
pickIntent.setAction(Intent.ACTION_GET_CONTENT);

Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

String pickTitle = "Select or take a new Picture"; // Or get from strings.xml
Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
chooserIntent.putExtra
(
  Intent.EXTRA_INITIAL_INTENTS, 
  new Intent[]  takePhotoIntent 
);

startActivityForResult(chooserIntent, SELECT_PICTURE);

查看活动结果如何处理,请参考this question


注意:一个关键点是如何确定是否使用了相机或画廊。这在此代码示例中显示:https://***.com/a/12347567/294884

【讨论】:

我认为这应该是最有帮助的答案!我自己试过了,效果很好。 不幸的是,如果EXTRA_INITIAL_INTENTS 解析为多个活动,这将不起作用,因为它们未合并到原始选择器中。相反,系统项目出现在选择器中,在选择时,在选择时,打开另一个选择器,其中用户可以选择应用程序以处理EXTRA_INITIAL_INTENTS。由于您无法控制安装多少应用程序来执行某项任务,因此该解决方案只是一厢情愿,对所有实际用途都有缺陷。 @GiulioPiancastelli 即使您在实际使用中的逻辑是正确的,这个解决方案也非常有用。拥有两个相机应用程序的用户可能已经选择了一个作为默认应用程序,然后将不会获得第二个选择器。在额外意图是相机的情况下,它符合 imo 的需求。 @Macarse 你的问题链接错误伙计!灾难! :) 另一个类似的真棒答案,更详细。 显示如何确定相机或画廊 ***.com/a/12347567/294884【参考方案2】:

更新:另一个答案,使用EXTRA_INITIAL_INTENTS,在这一点上是一个更好的答案。在我写答案的时候,EXTRA_INITIAL_INTENTS 还不存在,因为它是在 API 级别 5 中添加的。

那么有没有办法将两者结合起来或 我将不得不提供一个菜单 从我的内部做一个或另一个 申请?

编写您自己的画廊,拥有您想要的功能。

我认为菜单会更简单。

这似乎是一个常见的用途 案例...我肯定遗漏了什么?

您旁边的开发人员会认为图库应该允许您从本地图库中进行选择,或者跳到 Flickr 从那里进行选择。另一位开发人员会认为相机不仅应该允许通过相机“拍照”,还应该通过从图库中选择一些东西来“拍照”,从而将事物与你想象的方式颠倒过来。另一个开发人员会认为画廊应该允许从本地画廊、Flickr、相机或网络连接的网络摄像头中进行选择。还有一个开发人员会认为图库很愚蠢,用户应该只通过文件浏览器选择文件。以此类推。

所有这些都在操作系统闪存非常重要的环境(手机)中进行。

因此,恕我直言,核心 Android 团队选择提供构建块供您按照您认为合适的方式进行组装,而不是试图适应所有可能的模式,这并不完全令人震惊。

【讨论】:

好的,谢谢马克。作为您的常用软件书籍的热心读者,我将把它作为一个明确的答案。现在,我将在我的应用程序中实现一个菜单,并希望 G 为我提供一些东西,作为预期的下个月 Froyo 版本的一部分。 Gallery 应用程序在 2.1 上非常好,我希望能够针对这种情况重用/自定义它。 是的,您的想法(ACTION_PICK 后跟相机图标会拍照、添加并返回图片)当然并不疯狂。你可能想看看 Gallery 的 pre-Froyo 代码,看看他们在那里做了什么。 一般来说,如果您想说“我希望用户为我选择某种类型的 X 种内容”,您会使用 GET_CONTENT。例如,在这里您可以使用类型为 image/* 的 GET_CONTENT,这将首先向用户显示一个他们可以从中选择图像的事物列表(画廊、相机以及任何其他应用程序可以提供的图像)。 @hackbod... 除了这样做不提供使用相机的选项。拍照需要 MediaStore.ACTION_IMAGE_CAPTURE 动作,而选择现有图像需要 Intent.ACTION_PICK 动作......因此,如果不按照 CommonsWare 的建议,它们本质上就会相互排斥【参考方案3】:

您可以在 Activity 中以这种方式进行:

private static final int REQUEST_CODE_PICTURE= 1;

    /**
     * Click on View to change photo. Sets into View of your layout, android:onClick="clickOnPhoto"
     * @param view View
     */
    public void clickOnPhoto(View view) 
        Intent pickIntent = new Intent();
        pickIntent.setType("image/*");
        pickIntent.setAction(Intent.ACTION_GET_CONTENT);
        Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        String pickTitle = "Take or select a photo";
        Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]  takePhotoIntent );
        startActivityForResult(chooserIntent, REQUEST_CODE_PICTURE);
    

然后,总是在你的 Activity 中添加 onActivityResult 方法:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) 
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) 
            if (data == null) 
                return;
            
            try 
                InputStream inputStream = getContentResolver().openInputStream(data.getData());
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                imgPhoto.setImageBitmap(bitmap);
             catch (FileNotFoundException e) 
                e.printStackTrace();
            
        
    

【讨论】:

onActivityResult,只对pickIntent有效,对takePhotoIntent无效 使用这个,我怎样才能让照片以纵向模式显示?如果我以纵向模式拍照,它会以横向模式显示。 使用 Glide 或 Picasso 以正确的方式显示图像【参考方案4】:

我的答案与@Macarse 解决方案几乎相同,但我还添加了一个额外的意图来显示画廊应用程序(例如:谷歌照片)并且是用 Kotlin 编写的:

val REQUEST_CODE_GET_IMAGE = 101

private fun addProfileImage() 
    val pickImageFileIntent = Intent()
    pickImageFileIntent.type = "image/*"
    pickImageFileIntent.action = Intent.ACTION_GET_CONTENT

    val pickGalleryImageIntent = Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

    val captureCameraImageIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    val pickTitle = "Capture from camera or Select from gallery the Profile photo"
    val chooserIntent = Intent.createChooser(pickImageFileIntent, pickTitle)
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(captureCameraImageIntent, pickGalleryImageIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE_GET_IMAGE)

从结果意图中提取图像:

private var imageTempFile: File? = null
private var imageMimeType: String? = null

private fun extractImage(intent: Intent?) 
    val imageUri = intent?.data
    imageUri?.let 
        Glide.with(this)
                .load(imageUri)
                .into(profileImageCiv)
        imageTempFile = MediaUtils.copyContentFromUriToCacheFile(this, imageUri, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
        imageMimeType = MediaUtils.getMimeType(this, imageUri)
     ?: run 
        intent?.extras?.get("data")?.let  bitmap ->    // Bitmap was returned as raw bitmap
            Glide.with(this)
                    .load(bitmap)
                    .into(profileImageCiv)
            imageTempFile = MediaUtils.writeBitmapToCacheFile(this, bitmap as Bitmap, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
            imageMimeType = "image/jpeg"    // The bitmap was compressed as JPEG format. The bitmap itself doesn't have any format associated to it
         ?: run 
            imageTempFile = null
            imageMimeType = null
            Log.e("Intent data is null.")
            Log.d("Error during photo selection")
        
    

【讨论】:

以上是关于让用户拍照或从 Android 中的图库中选择图像的单一意图的主要内容,如果未能解决你的问题,请参考以下文章

Android:在捕获或从图库中挑选后裁剪图像

如何在 ImageView 中显示图库中的图像?

如何让用户选择在保存之前裁剪从相机拍摄的照片?

Android - 如何从相机捕获图像或从库中添加

Android“拍照” - 单击的图像被保存为损坏的图像

Android 相机和图库