从安卓图库中选择多张图片

142

基本上我想要实现的是在Android中打开Gallery并允许用户选择多张图片。这个问题已经被经常提出,但我对答案不满意。主要是因为我在我的IDE文档中找到了一些有趣的东西(稍后我会回顾它),因此我不想使用自定义适配器,而只想使用原始适配器。

现在我用于选择一张图片的代码是:

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);

现在,在 Stack Overflow 和其他网站上的人会告诉你有两个选择:

1)不使用 ACTION_GET_CONTENT,而是改用 ACTION_SEND_MULTIPLE。这个方法不起作用。根据文档,它是用来发送文件而不是检索文件,这正是它所做的。当我使用 ACTION_SEND_MULTIPLE 时,我的设备弹出一个窗口,让我选择要将数据发送给哪个应用程序。这不是我想要的,所以我想知道人们是如何使用这种方法实现此目标的... 我错过了什么吗?

2)实现一个自定义画廊。现在这是我考虑的最后一个选项,因为在我看来,这不是我正在寻找的,因为我必须自己设计样式,而且为什么你不能在原始画廊中选择多个图像呢?

一定有一个选项可以解决这个问题... 现在我发现的有趣的事情是:

我在对ACTION_GET_CONTENT的描述中找到了这个。

如果调用者可以处理多个返回项目(用户执行多个选择),那么它可以指定 EXTRA_ALLOW_MULTIPLE 来表示这一点。

这非常有趣。这里他们是在引用用户可以选择多个项目的情况吗?

后来他们在文档中说:

您可以使用 EXTRA_ALLOW_MULTIPLE 允许用户选择多个项目。

所以这很明显吧?这就是我需要的。但我的跟随问题是:我在哪里可以放置这个EXTRA_ALLOW_MULTIPLE?遗憾的是,我找不到这个东西在Android开发者指南中的任何位置,也没有在 INTENT 类中定义为常量。

有人能帮我解决这个EXTRA_ALLOW_MULTIPLE吗?


http://www.javacodegeeks.com/2012/10/android-select-multiple-photos-from-gallery.html - Shubham AgaRwal
http://geekonjava.blogspot.com/2015/10/easy-multiple-image-pick-android.html - Tell Me How
2
@KyleShank的解决方案对我很有用。设置EXTRA_ALLOW_MULTIPLE可以让您选择多个项目。在onActivityResult中调用返回意图的getClipData()以获取URI。 唯一的问题是,画廊小部件不允许多选。在这种情况下,单击任何图像都将完成选择器,并且您可以通过在返回的意图上调用getData来获取URI(单个项目)。 - Tanweer Alam
15个回答

157

通过Intent.putExtra()方法,在意图上设置EXTRA_ALLOW_MULTIPLE选项:

intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);

你上面的代码应该像这样:

Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Select Picture"), 1);
注意: EXTRA_ALLOW_MULTIPLE选项仅适用于Android API 18及更高版本。

intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); 使用模拟器,不支持多选。 - qinmiao
1
对我来说可以用,我不得不长按一张图片才能开始选择更多。 - Tony Wickham
18
它正在选择多张图片。但是如何从Activity结果中获取图像URL? - John
4
这会启动图片选择器并允许我选择多张图片,但是我不知道如何在onActivityResult中获取这些图片的URL。 - Tom Kincaid
8
您可以在结果的 Intent.getClipData 中获取URL。它包含了ClipData项的数组。 - Tam Huynh
显示剩余3条评论

83

在类中定义这些变量:

int PICK_IMAGE_MULTIPLE = 1; 
String imageEncoded;    
List<String> imagesEncodedList;

假设在单击按钮时,应该打开图库以选择图片。
 Intent intent = new Intent();
 intent.setType("image/*");
 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
 intent.setAction(Intent.ACTION_GET_CONTENT);
 startActivityForResult(Intent.createChooser(intent,"Select Picture"), PICK_IMAGE_MULTIPLE);

那么您需要重写onActivityResult方法。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    try {
        // When an Image is picked
        if (requestCode == PICK_IMAGE_MULTIPLE && resultCode == RESULT_OK
                    && null != data) {
            // Get the Image from data

            String[] filePathColumn = { MediaStore.Images.Media.DATA };
            imagesEncodedList = new ArrayList<String>();
            if(data.getData()!=null){

                Uri mImageUri=data.getData();

                // Get the cursor
                Cursor cursor = getContentResolver().query(mImageUri,
                            filePathColumn, null, null, null);
                // Move to first row
                cursor.moveToFirst();

                int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                imageEncoded  = cursor.getString(columnIndex);
                cursor.close();

            } else {
                if (data.getClipData() != null) {
                    ClipData mClipData = data.getClipData();
                    ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
                    for (int i = 0; i < mClipData.getItemCount(); i++) {

                        ClipData.Item item = mClipData.getItemAt(i);
                        Uri uri = item.getUri();
                        mArrayUri.add(uri);
                        // Get the cursor
                        Cursor cursor = getContentResolver().query(uri, filePathColumn, null, null, null);
                        // Move to first row
                        cursor.moveToFirst();

                        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                        imageEncoded  = cursor.getString(columnIndex);
                        imagesEncodedList.add(imageEncoded);
                        cursor.close();

                    }
                    Log.v("LOG_TAG", "Selected Images" + mArrayUri.size());
                }
            }
        } else {
            Toast.makeText(this, "You haven't picked Image",
                        Toast.LENGTH_LONG).show();
        }
    } catch (Exception e) {
        Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG)
                    .show();
    }

    super.onActivityResult(requestCode, resultCode, data);
}

注意: 相册没有提供选择多张图片的功能,所以我们在这里开启了所有图片工具,您可以从中选择多张图片。 不要忘记在清单文件中添加权限。

非常重要: 使用 getData() 方法可获取一张单独的图片,并将其保存在 imageEncoded 字符串中。 如果用户选择多个图像,则应将它们存储在列表中。

因此,您需要检查哪个对象为空,以便使用另一个对象。

祝您成功,也祝其他人成功。


1
只需使用data.getClipData()部分即可,无需检查data.getData()。 - truongnm
11
uri = content://com.android.providers.media.documents/document/image%3A772 该uri中包含数据,但是用cursor.getString返回null。imageEncoded = cursor.getString(columnIndex); - Muhammad Zubair Ashraf
3
这很有用,但我必须使用这些函数来补充getPath:https://dev59.com/questions/YmIj5IYBdhLWcg3wnmXA#20559175 - Geynen
它在照片上运行正常,但如果我选择Google Drive,那么getClipData为空,如果我选择DropBox,则游标为空。 - temirbek
1
你使用 null != data 而不是 data != null 让我脑子短路了。 - stavros.3p
显示剩余6条评论

57

这些答案有很多相似之处,但都缺少最重要的部分,就是在onActivityResult中检查data.getClipData是否为null,然后再检查data.getData

调用文件选择器的代码:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*"); //allows any image file type. Change * to specific extension to limit it
//**The following line is the important one!
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURES); //SELECT_PICTURES is simply a global int used to check the calling intent in onActivityResult

获取所选所有图片的代码:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == SELECT_PICTURES) {
        if(resultCode == Activity.RESULT_OK) {
            if(data.getClipData() != null) {
                int count = data.getClipData().getItemCount(); //evaluate the count before the for loop --- otherwise, the count is evaluated every loop.
                for(int i = 0; i < count; i++) {
                    Uri imageUri = data.getClipData().getItemAt(i).getUri();
                    //do something with the image (save it to some directory or whatever you need to do with it here)
                } 
            }
        } else if(data.getData() != null) {
            String imagePath = data.getData().getPath();
            //do something with the image (save it to some directory or whatever you need to do with it here)
        }
    }
}

请注意,某些设备上Android的选择器中有“照片”和“图库”可用。“照片”允许选择多个图像,“图库”一次只能选择一个。


getClipData()是什么?data.getData不够用吗? - adi
6
在一些三星设备上,结果将与非三星设备不同。如果用户选择了多个文件,则 getData() 有时不会为空,但只会有 一个 Uri。如果您想处理用户选择 多个 文件的情况,请在处理 getData() 之前检查 getClipData() -- 如果剪贴板数据不为空,则用户可能已选择多个图像。在处理 getData() 之前处理 getClipData(),但同时处理 两种 情况,这对于支持不同设备且仍允许多个Uri非常重要。 - Mira_Cole
@Mira_Code 我该如何将所选图像设置到不同的图像视图中。 - Hasnain Ghias
官方的androidx GetMultipleContents 实现在首先检查'getData()',然后再检查'getClipData()'。 它使用Set来确保每个Uri都是唯一的。但是,如果数据中包含一个不包含在clipData中的Uri,则结果可能包含更多的Uri,而不仅仅是clip data uris,虽然我不确定这是否真的会发生,可能取决于外部应用程序。 - Dwagner

20

我希望这个回答不会太晚。因为画廊小部件默认不支持多选,但您可以自定义网格视图以接受您的多选意图。另一个选择是扩展画廊视图并添加自己的代码以允许多选。
这个简单的库可以做到:https://github.com/luminousman/MultipleImagePick

更新:
根据 @ilsy 的评论,此库中的CustomGalleryActivity使用了manageQuery,该方法已过时,需要改为getContentResolver().query()cursor.close(),可以参考这个答案


@R4j 是的,我写过关于这个问题的文章:该库还没有准备好用于项目中。需要进行许多更新才能开始使用它。 至于你的更新:不要在UI线程中使用getContentResolver().query()。请阅读有关Loaders和Support Library的内容。 - mbelsky
.cacheOnDisc() 也已经被弃用,所以请将其更改为带有布尔参数的 .cacheOnDisc(true) - Pratik Butani

8

初始化实例:

private String imagePath;
private List<String> imagePathList;

onActivityResult 中,您需要编写 If-else 2 块。一个用于单个图像,另一个用于多个图像。
if (requestCode == GALLERY_CODE && resultCode == RESULT_OK  && data != null) {

    imagePathList = new ArrayList<>();

    if (data.getClipData() != null) {

        int count = data.getClipData().getItemCount();
        for (int i=0; i<count; i++) {
            Uri imageUri = data.getClipData().getItemAt(i).getUri();
            getImageFilePath(imageUri);
        }
    }
    else if (data.getData() != null) {
        Uri imgUri = data.getData();
        getImageFilePath(imgUri);
    }
}

最重要的部分,从 URI 中获取图像路径:

public void getImageFilePath(Uri uri) {

    File file = new File(uri.getPath());
    String[] filePath = file.getPath().split(":");
    String image_id = filePath[filePath.length - 1];

    Cursor cursor = getContentResolver().query(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Images.Media._ID + " = ? ", new String[]{image_id}, null);
    if (cursor!=null) {
        cursor.moveToFirst();
        imagePath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        imagePathList.add(imagePath);
        cursor.close();
    }
}

希望这能对你有所帮助。


6

2022年 - Android Jetpack Compose的方式

使用Android Jetpack Compose在图库中选择多个图像。

val launcherMultipleImages = rememberLauncherForActivityResult(
    contract = ActivityResultContracts.GetMultipleContents(),
) { uriList: List<Uri> ->
    // TODO
}

然后使用 launcherMultipleImages.launch("image/*") 以启动图片选择。

例如:

Button(onClick = { launcherMultipleImages.launch("image/*") }) {
    Text(text = "Select images")
}

我们如何限制GetMultipleContents的选择数量?例如,最多50张照片。 - KyawLinnThant
我们如何限制GetMultipleContents的选择数量?例如,最多50张照片。 - KyawLinnThant
我们如何限制GetMultipleContents的选择数量?例如,最多50张照片。 - KyawLinnThant

3
这适用于多个图片选择。在Google相册中API 29、30已经测试通过。
private static final int PICK_IMAGE = 2;

Intent intent = new Intent(Intent.ACTION_PICK, 
    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
                startActivityForResult(Intent.createChooser(intent, "Select 
    images"),PICK_IMAGE);

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMAGE && resultCode == RESULT_OK) {
        if(data.getClipData() != null) {
            int count = data.getClipData().getItemCount();
            for(int i = 0; i < count; i++) {
            Uri imageUri = data.getClipData().getItemAt(i).getUri();
            //do what do you want to do
            }
        }
        else if(data.getData() != null) {
            Uri selectedImageUri = data.getData();
            //do what do you want to do
        }
}

3
将getContent定义如下;
val getContent = registerForActivityResult(ActivityResultContracts.GetMultipleContents()) 
{ uriList ->
    // todo
}

在您授予相关权限之后,运行以下代码。
getContent.launch("image/*")

这是选择任何数据的最简单方法之一。

1
我从游标中得到了null。然后找到了一个完美地将Uri转换为Bitmap的解决方案。
这是适用于我的解决方案:
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
{

    if (resultCode == Activity.RESULT_OK) {

        if (requestCode == YOUR_REQUEST_CODE) {

            if (data != null) {

                if (data.getData() != null) {

                    Uri contentURI = data.getData();
                    ex_one.setImageURI(contentURI);

                    Log.d(TAG, "onActivityResult: " + contentURI.toString());
                    try {

                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), contentURI);

                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                } else {

                    if (data.getClipData() != null) {
                        ClipData mClipData = data.getClipData();
                        ArrayList<Uri> mArrayUri = new ArrayList<Uri>();
                        for (int i = 0; i < mClipData.getItemCount(); i++) {

                            ClipData.Item item = mClipData.getItemAt(i);
                            Uri uri = item.getUri();
                            try {
                                Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }

                        }
                    }

                }

            }

        }

    }

}

0

试试这个 IntentChooser。只需添加几行代码,我已经为您完成了其余部分。

private void startImageChooserActivity() {
    Intent intent = ImageChooserMaker.newChooser(MainActivity.this)
            .add(new ImageChooser(true))
            .create("Select Image");
    startActivityForResult(intent, REQUEST_IMAGE_CHOOSER);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_IMAGE_CHOOSER && resultCode == RESULT_OK) {
        List<Uri> imageUris = ImageChooserMaker.getPickMultipleImageResultUris(this, data);
    }
}

PS:如上面的答案所提到的,EXTRA_ALLOW_MULTIPLE 仅适用于 API >= 18。而一些相册应用程序并不提供此功能(Google 相册和文档 (com.android.documentsui) 可以使用)。


即使添加了 intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);,也无法让我选择多个图像。 - Scorpion

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