Android如何同时调用相机或相册Intent

38

如果我想从本机相机捕获图像,我可以这样做:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
        startActivityForResult(intent, IMAGE_CAPTURE);

如果我想从相册获取图片,我可以这样做:

Intent intent = new Intent();
                intent.setType("image/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent,
                        "Select Picture"), SELECT_PICTURE);
我想知道如何将上述两者结合起来。
即从相册获取图片或拍照。 选择一个操作

有没有示例代码可以实现呢? 谢谢。

8个回答

51

如果您想在Android上同时从相机和相册中获取照片,可以参考以下链接。这里也有类似的问题。

从图库和相机中捕获图像

更新代码:

请查看下面的代码。虽然它不会将所需内容显示在列表视图中,但它会提供一个对话框选项,以从相册或相机中选择图像。

public class UploadImageActivity extends Activity {
ImageView img_logo;
protected static final int CAMERA_REQUEST = 0;
protected static final int GALLERY_PICTURE = 1;
private Intent pictureActionIntent = null;
Bitmap bitmap;

    String selectedImagePath;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main1);

    img_logo= (ImageView) findViewById(R.id.imageView1);
    img_logo.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            startDialog();
        }

    });
}

private void startDialog() {
    AlertDialog.Builder myAlertDialog = new AlertDialog.Builder(
            getActivity());
    myAlertDialog.setTitle("Upload Pictures Option");
    myAlertDialog.setMessage("How do you want to set your picture?");

    myAlertDialog.setPositiveButton("Gallery",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface arg0, int arg1) {
                    Intent pictureActionIntent = null;

                    pictureActionIntent = new Intent(
                            Intent.ACTION_PICK,
                            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                     startActivityForResult(
                            pictureActionIntent,
                            GALLERY_PICTURE);

                }
            });

    myAlertDialog.setNegativeButton("Camera",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface arg0, int arg1) {

                    Intent intent = new Intent(
                            MediaStore.ACTION_IMAGE_CAPTURE);
                    File f = new File(android.os.Environment
                            .getExternalStorageDirectory(), "temp.jpg");
                    intent.putExtra(MediaStore.EXTRA_OUTPUT,
                            Uri.fromFile(f));

                     startActivityForResult(intent,
                            CAMERA_REQUEST);

                }
            });
    myAlertDialog.show();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    bitmap = null;
    selectedImagePath = null;

    if (resultCode == RESULT_OK && requestCode == CAMERA_REQUEST) {

        File f = new File(Environment.getExternalStorageDirectory()
                .toString());
        for (File temp : f.listFiles()) {
            if (temp.getName().equals("temp.jpg")) {
                f = temp;
                break;
            }
        }

        if (!f.exists()) {

            Toast.makeText(getBaseContext(),

            "Error while capturing image", Toast.LENGTH_LONG)

            .show();

            return;

        }

        try {

            bitmap = BitmapFactory.decodeFile(f.getAbsolutePath());

            bitmap = Bitmap.createScaledBitmap(bitmap, 400, 400, true);

            int rotate = 0;
            try {
                ExifInterface exif = new ExifInterface(f.getAbsolutePath());
                int orientation = exif.getAttributeInt(
                        ExifInterface.TAG_ORIENTATION,
                        ExifInterface.ORIENTATION_NORMAL);

                switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            Matrix matrix = new Matrix();
            matrix.postRotate(rotate);
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                    bitmap.getHeight(), matrix, true);



            img_logo.setImageBitmap(bitmap);
            //storeImageTosdCard(bitmap);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    } else if (resultCode == RESULT_OK && requestCode == GALLERY_PICTURE) {
        if (data != null) {

            Uri selectedImage = data.getData();
            String[] filePath = { MediaStore.Images.Media.DATA };
            Cursor c = getContentResolver().query(selectedImage, filePath,
                    null, null, null);
            c.moveToFirst();
            int columnIndex = c.getColumnIndex(filePath[0]);
            selectedImagePath = c.getString(columnIndex);
            c.close();

            if (selectedImagePath != null) {
                txt_image_path.setText(selectedImagePath);
            }

            bitmap = BitmapFactory.decodeFile(selectedImagePath); // load
            // preview image
            bitmap = Bitmap.createScaledBitmap(bitmap, 400, 400, false);



            img_logo.setImageBitmap(bitmap);

        } else {
            Toast.makeText(getApplicationContext(), "Cancelled",
                    Toast.LENGTH_SHORT).show();
        }
    }

}


}

同时添加权限:

<uses-permission android:name="android.permission.CAMERA" />

 <uses-feature
    android:name="android.hardware.camera.autofocus"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera"
    android:required="false" />

存储图像到SD卡:

private void storeImageTosdCard(Bitmap processedBitmap) {
    try {
        // TODO Auto-generated method stub

        OutputStream output;
        // Find the SD Card path
        File filepath = Environment.getExternalStorageDirectory();
        // Create a new folder in SD Card
        File dir = new File(filepath.getAbsolutePath() + "/appName/");
        dir.mkdirs();

        String imge_name = "appName" + System.currentTimeMillis()
                + ".jpg";
        // Create a name for the saved image
        File file = new File(dir, imge_name);
        if (file.exists()) {
            file.delete();
            file.createNewFile();
        } else {
            file.createNewFile();

        }

        try {

            output = new FileOutputStream(file);

            // Compress into png format image from 0% - 100%
            processedBitmap
                    .compress(Bitmap.CompressFormat.PNG, 100, output);
            output.flush();
            output.close();

            int file_size = Integer
                    .parseInt(String.valueOf(file.length() / 1024));
            System.out.println("size ===>>> " + file_size);
            System.out.println("file.length() ===>>> " + file.length());

            selectedImagePath = file.getAbsolutePath();



        }

        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

1
你非常棒。但是你是否遇到了一个问题。当从相册中选择照片后,显示在ImageView中的图像会旋转90度吗?我不知道这是我的设备问题还是其他问题?你能帮忙检查一下吗? - jjLin
我以为当你通过意图拍照时,你的应用程序不需要相机权限。 - Edward Brey
@EdwardBrey 谢谢你的建议。我会去检查一下。现在我有点忙。 - Rahul Patel
您还需要android.permission.READ_EXTERNAL_STORAGE权限。 - JakeWilson801
光标不是会返回今天点击的所有图片吗?所以这将是一个循环。相反,我们可以设置“DATE_ADDED desc limit 1”,这将限制结果为最新点击的图像。(尽管基于最后一个点击的图像选择图像本身并不是一个非常好的想法) - Darpan
显示剩余3条评论

35

假设你有两个意图(intents),一个是打开相机,一个是打开相册。我会在示例代码中称它们为cameraIntent和gallerIntent。你可以使用意图选择器(intent chooser)将这两个组合起来:

Kotlin:

val chooser = Intent.createChooser(galleryIntent, "Some text here")
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent))
startActivityForResult(chooser, requestCode)

Java:


Java
Intent chooser = Intent.createChooser(galleryIntent, "Some text here");
chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { cameraIntent });
startActivityForResult(chooser, requestCode);

这就是你要求的,如何将两者结合在一起(而无需自己制作用户界面/对话框)。


我喜欢这种方法。 - Joey Nicholas
3
当我使用 ActivityResultLauncher 时,如何区分这两个意图呢? - Pemba Tamang
@PembaTamang 没有直接的方法。我发现的是,相册总是会返回一些数据在data.data中,所以如果data为空或者data.data为空,那么它来自相机,否则它来自相册。 - undefined

14

如果您想显示在手机中安装的所有可处理照片的应用程序,例如相机、相册、Dropbox等。

您可以执行以下操作:

1. - 请求所有可用的意图:

    Intent camIntent = new Intent("android.media.action.IMAGE_CAPTURE");
    Intent gallIntent=new Intent(Intent.ACTION_GET_CONTENT);
    gallIntent.setType("image/*"); 

    // look for available intents
    List<ResolveInfo> info=new ArrayList<ResolveInfo>();
    List<Intent> yourIntentsList = new ArrayList<Intent>();
    PackageManager packageManager = context.getPackageManager();
    List<ResolveInfo> listCam = packageManager.queryIntentActivities(camIntent, 0);
    for (ResolveInfo res : listCam) {
        final Intent finalIntent = new Intent(camIntent);
        finalIntent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        yourIntentsList.add(finalIntent);
        info.add(res);
    }
    List<ResolveInfo> listGall = packageManager.queryIntentActivities(gallIntent, 0);
    for (ResolveInfo res : listGall) {
        final Intent finalIntent = new Intent(gallIntent);
        finalIntent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        yourIntentsList.add(finalIntent);
        info.add(res);
    }

2.- 显示包含项目列表的自定义对话框:

    AlertDialog.Builder dialog = new AlertDialog.Builder(context);
    dialog.setTitle(context.getResources().getString(R.string.select_an_action));
    dialog.setAdapter(buildAdapter(context, activitiesInfo),
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    Intent intent = intents.get(id);
                    context.startActivityForResult(intent,1);
                }
            });

    dialog.setNeutralButton(context.getResources().getString(R.string.cancel),
            new android.content.DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            });
    dialog.show();

这是一个完整的示例:https://gist.github.com/felixgborrego/7943560


干得好,@Felix!我有一个问题,我该如何从片段中调用这个静态方法?问题是onActivityResult在活动中被触发。 - Evgeniy Mishustin

2
实际上,您的对话框标题是“选择操作”,这意味着该对话框实际上是一个Intent选择器,而不是用户自定义的对话框。每个单独的项目代表一个Intent。翻译后的内容为:

实际上,你的对话框标题是"选择一个操作",这意味着该对话框实际上是一个Intent选择器,而不是用户自定义的对话框。每个单独的项目代表一个Intent。

public void click(View view) {
        File file = getExternalFilesDir(Environment.DIRECTORY_DCIM);
        Uri cameraOutputUri = Uri.fromFile(file);
        Intent intent = getPickIntent(cameraOutputUri);
        startActivityForResult(intent, -1);
    }

    private Intent getPickIntent(Uri cameraOutputUri) {
        final List<Intent> intents = new ArrayList<Intent>();

        if (true) {
            intents.add(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI));
        }

        if (true) {
            setCameraIntents(intents, cameraOutputUri);
        }

        if (intents.isEmpty()) return null;
        Intent result = Intent.createChooser(intents.remove(0), null);
        if (!intents.isEmpty()) {
            result.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toArray(new Parcelable[] {}));
        }
        return result;


    }

    private void setCameraIntents(List<Intent> cameraIntents, Uri output) {
        final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        final PackageManager packageManager = getPackageManager();
        final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
        for (ResolveInfo res : listCam) {
            final String packageName = res.activityInfo.packageName;
            final Intent intent = new Intent(captureIntent);
            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
            intent.setPackage(packageName);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, output);
            cameraIntents.add(intent);
        }
    }

如果在操作系统版本大于等于23的设备上运行,您可能需要自己解决权限问题。

这是我的演示:(由于操作系统不同,外观会有所不同)
在此输入图片描述

最初的回答:


这张图片帮助我更好地理解了它。 - ofundefined

1
我想我之前遇到过你的情况。一个想法是我们将创建一个带有可选择项目的单项列表警告对话框,每个项目都将执行由您自己意图定义的独特功能。如果您想要在项目列表中的每个元素上添加图标,则需要更多的工作来完成。希望这会有所帮助。
    String title = "Open Photo";
    CharSequence[] itemlist ={"Take a Photo",
                  "Pick from Gallery",
                  "Open from File"};

    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setIcon(R.drawable.icon_app);
    builder.setTitle(title);
    builder.setItems(itemlist, new DialogInterface.OnClickListener() {

        @Override
        public void onClick(DialogInterface dialog, int which) {
            switch (which) {
            case 0:// Take Photo
                // Do Take Photo task here
                break;
            case 1:// Choose Existing Photo
                // Do Pick Photo task here
                break;
            case 2:// Choose Existing File
                // Do Pick file here
                break;
            default:
                break;
            }
        }
    });
    AlertDialog alert = builder.create();
    alert.setCancelable(true);
    alert.show();

0
在您的XML布局中创建一个按钮,然后添加属性android:onClick="takeAPicture",接着在您的主活动中创建一个与onClick属性相同名称的方法。
public void takeAPicture(View view){
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
        startActivityForResult(intent, IMAGE_CAPTURE);
}

当你想从图库获取图片时,只需编写另一个方法:

public void getImageFromGallery(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent,
                    "Select Picture"), SELECT_PICTURE);
} 

我想把它们像上面的截图一样放在一起。 - jjLin
你能否只使用图像按钮来创建它? - 0gravity

0

public static Intent getPickImageIntent(Context context) {
    Intent chooserIntent = null;

    List<Intent> intentList = new ArrayList<>();

    Intent pickIntent = new Intent(Intent.ACTION_PICK,
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    takePhotoIntent.putExtra("return-data", true);
    takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(context)));
    intentList = addIntentsToList(context, intentList, pickIntent);
    intentList = addIntentsToList(context, intentList, takePhotoIntent);

    if (intentList.size() > 0) {
        chooserIntent = Intent.createChooser(intentList.remove(intentList.size() - 1),
                context.getString(R.string.pick_image_intent_text));
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toArray(new Parcelable[]{}));
    }

    return chooserIntent;
}

private static List<Intent> addIntentsToList(Context context, List<Intent> list, Intent intent) {
    List<ResolveInfo> resInfo = context.getPackageManager().queryIntentActivities(intent, 0);
    for (ResolveInfo resolveInfo : resInfo) {
        String packageName = resolveInfo.activityInfo.packageName;
        Intent targetedIntent = new Intent(intent);
        targetedIntent.setPackage(packageName);
        list.add(targetedIntent);
    }
    return list;
}


0
使用ActivityResultsContract的方式是2023年的方法,您可以创建自己的自定义ActivityResultContract,类似于这样。
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.MediaStore
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts

class ImageChooserActivityResultContract: ActivityResultContract<Triple<Uri?, PickVisualMediaRequest, String>, Pair<Boolean, Uri?>>() {
override fun createIntent(
    context: Context,
    input: Triple<Uri?, PickVisualMediaRequest, String>
): Intent {
    if (input.first == null) return getImageSelectIntent(input.second)
    val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        .putExtra(MediaStore.EXTRA_OUTPUT, input.first)
    val imageSelectIntent = getImageSelectIntent(input.second)
    val chooserIntent = Intent.createChooser(cameraIntent, input.third)
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(imageSelectIntent))
    return chooserIntent
}

private fun getImageSelectIntent(pickVisualMediaRequest: PickVisualMediaRequest): Intent {
    return if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable()) {
        Intent(MediaStore.ACTION_PICK_IMAGES).apply {
            type = getVisualMimeType(pickVisualMediaRequest.mediaType)
        }
    } else {
        // For older devices running KitKat and higher and devices running Android 12
        // and 13 without the SDK extension that includes the Photo Picker, rely on the
        // ACTION_OPEN_DOCUMENT intent
        Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
            type = getVisualMimeType(pickVisualMediaRequest.mediaType)

            if (type == null) {
                // ACTION_OPEN_DOCUMENT requires to set this parameter when launching the
                // intent with multiple mime types
                type = "*/*"
                putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
            }
        }
    }
}

private fun getVisualMimeType(input: ActivityResultContracts.PickVisualMedia.VisualMediaType): String? {
    return when (input) {
        is ActivityResultContracts.PickVisualMedia.ImageOnly -> "image/*"
        is ActivityResultContracts.PickVisualMedia.VideoOnly -> "video/*"
        is ActivityResultContracts.PickVisualMedia.SingleMimeType -> input.mimeType
        is ActivityResultContracts.PickVisualMedia.ImageAndVideo -> null
    }
}

override fun parseResult(resultCode: Int, intent: Intent?): Pair<Boolean, Uri?> {
    val isSuccessful = resultCode == Activity.RESULT_OK
    val uri = intent.takeIf { isSuccessful }?.data
    return Pair(isSuccessful, uri)
    }
}

下一步,为相同的操作创建一个ActivityResultLauncher。
private val imageChooser =
    registerForActivityResult(ImageChooserActivityResultContract()) { pair ->
        if (pair.first) {
            pair.second?.let { imageUri ->
                //Do image capture part here
                mCameraUri?.let { cameraImageUri ->
                    cameraImageUri.path?.let { path ->
                        val file = File(path)
                        if (file.exists()) file.delete()
                    }
                }
            }
                ?: kotlin.run { mCameraUri?.let { /*Do camera image select here */ } }
        } else {
            //Handle cancel result
        }
    }

相机权限启动器
private val cameraPermissionLauncher =
    registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
        if (isGranted) {
            createFileForCameraImageCapture()
            launchImageChooser(mCameraUri)
        } else if (ActivityCompat.shouldShowRequestPermissionRationale(
                this,
                android.Manifest.permission.CAMERA
            )
        ) {
            showPermissionExplainerDialog(getString(R.string.camera_permission_explainer))
        } else {
            launchImageChooser(null)
        }
    }

发起
private fun createFileForCameraImageCapture() {
    val imageFile: File? = try {
        createImageFile()
    } catch (e: Exception) {
        Log.e(TAG, "createFileForCameraImageCapture: Unalble to create image file", e)
        null
    }

    imageFile?.let {
        mCameraUri =
            FileProvider.getUriForFile(this, BuildConfig.FILE_PROVIDER_AUTHORITY, it)
    } ?: kotlin.run { mCameraUri = null }
}

private fun launchImageChooser(cameraUri: Uri?) {
    imageChooser.launch(
        Triple(
            cameraUri,
            PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly),
            "Image Chooser"
        )
    )
}

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