Android相机意图:如何获得全尺寸照片?

Posted

技术标签:

【中文标题】Android相机意图:如何获得全尺寸照片?【英文标题】:Android Camera Intent: how to get full sized photo? 【发布时间】:2011-09-20 21:19:33 【问题描述】:

我正在使用 Intent 启动相机:

Intent cameraIntent = new Intent(
    android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
getParent().startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST);

并使用:

Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
photoImage.setImageBitmap(thumbnail);
photoImage.setVisibility(View.VISIBLE);

但它只是一个缩略图,我如何获得完整的位图?我知道我可以使用自己的 Activity 并使用:

Camera.PictureCallback()

但是有没有办法使用 Intent 来做呢?

谢谢!

编辑:

我也试过了:

public void onActivityResult(int requestCode, int resultCode, Intent data) 
    Uri uri = data.getData();
    imageView.setImageURI(uri);

它适用于从图库中选择的照片,但对于相机意图,data.getData() 返回 null。

【问题讨论】:

【参考方案1】:

要获得完整尺寸的相机图像,您应该将相机指向临时文件中保存图片,例如:

    private URI mImageUri;

    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    File photo;
    try
    
        // place where to store camera taken picture
        photo = this.createTemporaryFile("picture", ".jpg");
        photo.delete();
    
    catch(Exception e)
    
        Log.v(TAG, "Can't create file to take picture!");
        Toast.makeText(activity, "Please check SD card! Image shot is impossible!", 10000);
        return false;
    
    mImageUri = Uri.fromFile(photo);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
    //start camera intent
    activity.startActivityForResult(this, intent, MenuShootImage);

private File createTemporaryFile(String part, String ext) throws Exception

    File tempDir= Environment.getExternalStorageDirectory();
    tempDir=new File(tempDir.getAbsolutePath()+"/.temp/");
    if(!tempDir.exists())
    
        tempDir.mkdirs();
    
    return File.createTempFile(part, ext, tempDir);

然后在图像捕获意图完成工作后 - 只需使用以下代码从 imageUri 获取您的图片:

public void grabImage(ImageView imageView)

    this.getContentResolver().notifyChange(mImageUri, null);
    ContentResolver cr = this.getContentResolver();
    Bitmap bitmap;
    try
    
        bitmap = android.provider.MediaStore.Images.Media.getBitmap(cr, mImageUri);
        imageView.setImageBitmap(bitmap);
    
    catch (Exception e)
    
        Toast.makeText(this, "Failed to load", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "Failed to load", e);
    



//called after camera intent finished
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent)

    //MenuShootImage is user defined menu option to shoot image
    if(requestCode==MenuShootImage && resultCode==RESULT_OK) 
    
       ImageView imageView;
       //... some code to inflate/create/find appropriate ImageView to place grabbed image
       this.grabImage(imageView);
    
    super.onActivityResult(requestCode, resultCode, intent);

P.S.代码需要针对 Android M 中应用的新安全限制进行修改 - FileProvider:mImageUri 必须与 FileProvider 打包在一起

【讨论】:

我试过了,uri是file:///sdcard/.temp/picture19010.jpg,我用了ImageView的setImageUri(),但是不行,是路径正确,还是我需要一些东西让 uri 可以被 imageview 识别吗? @barmaley 我很感谢你解决了 hzxu 的问题,但我对你的回答感到困惑。该代码不能按原样工作。您是否有机会重新格式化并解释“grabImage”如何适应 onActivityResult 过程?谢谢! @shanabus 看看上面的更多代码 - 我添加了 onActivityResult() 只想指出,上面的重要行是intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);,有关此行为的难以捉摸的文档,请参阅developer.android.com/reference/android/provider/…。 我已经尝试过这个解决方案,但是图像的结果是这样的错误:java.lang.RuntimeException: Failure delivery result ResultInfowho=null, request=188, result=-1, data=null 到活动 com.ketanolab.lbc/com.ketanolab.lbc.Robototal:java.lang.NullPointerException【参考方案2】:

打开相机并将图像保存到某个特定目录

private String pictureImagePath = "";
private void openBackCamera() 
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = timeStamp + ".jpg";
    File storageDir = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES);
    pictureImagePath = storageDir.getAbsolutePath() + "/" + imageFileName;
    File file = new File(pictureImagePath);
    Uri outputFileUri = Uri.fromFile(file);
    Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);               
    cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
    startActivityForResult(cameraIntent, 1);

处理图片

protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    if (requestCode == 1) 
    File imgFile = new  File(pictureImagePath);
        if(imgFile.exists())        
       Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
       ImageView myImage = (ImageView) findViewById(R.id.imageviewTest);
       myImage.setImageBitmap(myBitmap);

        
    


【讨论】:

在互联网上搜索了 24 小时后,这是我想要的唯一代码。现在唯一的问题是,即使我在纵向模式下拍摄,它也会以横向模式显示图像。 也是唯一一个在工作的......我真的不明白为什么这么简单的任务会这么复杂 记住您需要 WRITE_EXTERNAL_STORAGE 权限。此答案中的代码来自developer.android.com/training/camera/photobasics.html 好一个简单易懂。别人写了这么多复杂的代码 人们真的用很长的答案把整个事情复杂化了。每次点击我回到活动后我都面临一件事,我不能一起点击多个图像。能想到怎么办?并感谢这个伟大的代码。【参考方案3】:

尽管这是一个古老的问题并且它有一个公认的答案, 我想分享我的解决方案。 在这种情况下,您不必创建临时文件。 此外,我们创建了一个选择器,为用户提供两种选择:用相机拍照或从图库中选择现有的一张。

    Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Intent chooser = new Intent(Intent.ACTION_CHOOSER);
    chooser.putExtra(Intent.EXTRA_INTENT, galleryIntent);
    chooser.putExtra(Intent.EXTRA_TITLE, getString(R.string.chooseaction));
    Intent[] intentArray = cameraIntent;
    chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
    startActivityForResult(chooser, RESULT_LOAD_IMAGE);

我们在这里检索结果:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) 
    // todo use appropriate resultCode in your case
    if (requestCode == RESULT_LOAD_IMAGE && resultCode == FragmentActivity.RESULT_OK) 
        if (data.getData() != null) 
            // this case will occur in case of picking image from the Gallery,
            // but not when taking picture with a camera
            try 
                Bitmap bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), data.getData());

                // do whatever you want with the Bitmap ....           

             catch (IOException e) 
                e.printStackTrace();
            
         else 
            // this case will occur when taking a picture with a camera
            Bitmap bitmap = null;
            Cursor cursor = getActivity().getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[]MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_ADDED,
                            MediaStore.Images.ImageColumns.ORIENTATION, MediaStore.Images.Media.DATE_ADDED,
                    null, "date_added DESC");
            if (cursor != null && cursor.moveToFirst()) 
                Uri uri = Uri.parse(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)));
                String photoPath = uri.toString();
                cursor.close();
                if (photoPath != null) 
                    bitmap = BitmapFactory.decodeFile(photoPath);
                
            

            if (bitmap == null) 
                // for safety reasons you can
                // use thumbnail if not retrieved full sized image
                bitmap = (Bitmap) data.getExtras().get("data");
            
            // do whatever you want with the Bitmap ....
        

        super.onActivityResult(requestCode, resultCode, data);
    

【讨论】:

非常好的方法,因为它不需要 readExternalStorage 权限。原生相机或图库选择器也是用户完成操作的舒适方式 这个和许多其他答案返回相机的最后一个画廊图像,而不是相机拍摄。在 API 23 模拟器和 API 21 设备中测试。 Uri 引用了与原始照片不同的路径。还有数据 == null。 试试这个:如果我选择画廊,一切都会按照这里的描述进行。但如果我选择相机,resultCode == REQUEST_CANCELLED.【参考方案4】:

我也使用了 Vicky 的答案,但我必须将 uri 保存到一个包中,以避免在方向更改时丢失它。因此,如果您在倾斜设备后没有从您的意图中得到结果,那可能是因为您的 uri 没有在方向更改后幸存下来。

static final int CAMERA_CAPTURE_REQUEST = 1;
static final String ARG_CURRENT_PIC_URI = "CURRENT_PIC_URI";


String pictureImagePath = folderName + "/" + imageFileName;
File file = new File(Environment.getExternalStorageDirectory(), pictureImagePath);
Uri outputFileUri = Uri.fromFile(file);

mCurrentPicUri = outputFileUri.getPath();

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(cameraIntent, CAMERA_CAPTURE_REQUEST);

活动结果代码:

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

  if (requestCode == CAMERA_CAPTURE_REQUEST && resultCode == Activity.RESULT_OK) 
  
    File imgFile = new  File(mCurrentPicUri);
    // do something with your image
    // delete uri
    mCurrentPicUri = "";
  

将 uri 保存到包中:

@Override
public void onSaveInstanceState(Bundle outState) 
  super.onSaveInstanceState(outState);
  // save uri to bundle
  outState.putString(ARG_CURRENT_PIC_URI, mCurrentPicUri);

在创建过程中从您保存的包中检索它:

if (bundle.containsKey(ARG_CURRENT_PIC_URI))
  mCurrentPicUri = bundle.getString(ARG_CURRENT_PIC_URI);

【讨论】:

【参考方案5】:

不要使用onActivityResult 的数据。我花了很多时间来测试不同的解决方案。摄像头保存一张图片(即使您没有在 AndroidManifest 中设置摄像头和读卡权限),但随后 onActivityResult 返回 data == nullMediaStore 返回错误路径。在这些解决方案中,您只需获取最后一张画廊图片,而不是您的照片。

private Uri photoUri;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) 
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    ...


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) 
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == CAMERA_RESULT) 
        if (resultCode == RESULT_OK) 
            if (photoUri != null) 
                image.setImageURI(photoUri);
            
        
    


private void showCamera() 
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intent.resolveActivity(getContext().getPackageManager()) != null) 
        File file = null;
        try 
            file = createImageFile();
         catch (IOException e) 
            e.printStackTrace();
        
        photoUri = null;
        if (file != null) 
            photoUri = Uri.fromFile(file);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            startActivityForResult(intent, CAMERA_REQUEST);
        
    


private File createImageFile() throws IOException 
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File storageDir = getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    // File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    return File.createTempFile(timeStamp, ".jpg", storageDir);

【讨论】:

真的没有点赞吗?与所有...相比,这是最好的解决方案... @DIRTYDAVE,非常感谢!很高兴这个解决方案对您有所帮助!【参考方案6】:

为了从相机捕捉最大的图片尺寸,我希望这些简单的步骤会非常有用

 public static Camera mCamera;

Camera.Parameters parameters = mCamera.getParameters();
  parameters.getSupportedPictureSizes();
List<Camera.Size> supportedSizes = parameters.getSupportedPictureSizes();
  mSizePicture1 = supportedSizes.get(0);
  int cameraSize = supportedSizes.size();
  mSizePicture2 = supportedSizes.get(cameraSize - 1);

    if (mSizePicture1.height < mSizePicture2.height)
       mSizePicture = supportedSizes.get(cameraSize - 1);
    else
       mSizePicture = supportedSizes.get(0);

parameters.setPictureSize(mSizePicture.width, mSizePicture.height);

这里,每个手机支持的尺寸是从固定为要捕获的图片尺寸的最大尺寸中获取的。

【讨论】:

【参考方案7】:

API 级别 29

我尝试了接受的答案,但在接受的答案中使用的 Environment.getExternalStorageDirectory() 和在后续答案中使用的 Environment.getExternalStoragePublicDirectory() 在 API 级别 29 中已被弃用。在第三个答案中使用的 MediaStore.Images.Media.DATA 在 API 级别中已弃用29. 以下代码(在 Kotlin 中)将完整图像存储在 MediaStore 中,如 this *** answer 所示,用于不同的问题,并且不依赖于 FileProvider

    var imageUri: Uri? = null

    fun takePhoto(view: View?) 
        val values = ContentValues()
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "myimage.jpg")
        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
        values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
        imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
        startActivityForResult(intent, TAKE_PHOTO_REQUEST)
    

【讨论】:

【参考方案8】:

好的,是时候回报了。 所以你有 Mainfest 的权限

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-feature android:name="android.hardware.camera" android:required="true" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

你也有你的提供者元数据

 <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="$applicationId.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"></meta-data>
    </provider>

一个遗漏的细节是 (android:authorities applicationId) 你需要添加你自己的应用程序包名称。 所以你在res 文件夹下有xml 文件,正如我们在manifest 中提到的那样,你在它下面创建了file_paths ;

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path
        name="my_images"
        path="Pictures" />
</paths>

我们完成了第 1 部分的复制粘贴。现在在onCreate(savedInstanceState: Bundle?) 上方的活动中定义这些美女

    val REQUEST_IMAGE_CAPTURE = 1
    lateinit var currentPhotoPath: String
    var cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

你可能想看看原始资源,但像往常一样缺少细节 Android 开发人员:link

另一个缺失的细节是packageName + ".fileprovider", 注意如果你有方法,你需要提供自己的包名。

// org android developers
    private fun dispatchTakePictureIntent() 
        Intent(MediaStore.ACTION_IMAGE_CAPTURE).also  takePictureIntent ->
            // Ensure that there's a camera activity to handle the intent
            takePictureIntent.resolveActivity(packageManager)?.also 
                // Create the File where the photo should go
                val photoFile: File? = try 
                    createImageFile()
                 catch (ex: IOException) 
                    // Error occurred while creating the File
                    ex.message
                    null
                
                // Continue only if the File was successfully created
                photoFile?.also 
                    val photoURI: Uri = FileProvider.getUriForFile(
                        this,
                        packageName + ".fileprovider",
                        it
                    )
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
                
            
        
    

createImageFile函数

 // org android developers
    @Throws(IOException::class)
    private fun createImageFile(): File 
        // Create an image file name
        val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        val storageDir: File = this!!.getExternalFilesDir(Environment.DIRECTORY_PICTURES)!!
        return File.createTempFile(
            "JPEG_$timeStamp_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
        ).apply 
            // Save a file: path for use with ACTION_VIEW intents
            currentPhotoPath = absolutePath
        

    

测试。 使用 onClick 事件调用您的 dispatchTakePictureIntent() 方法,确保允许权限

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) 
        super.onActivityResult(requestCode, resultCode, data);
  if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_IMAGE_CAPTURE) 
var  mBitmap_org = MediaStore.Images.Media.getBitmap(
                this.getContentResolver(),
                //Uri.parse(currentPhotoPath)
                Uri.fromFile(File(currentPhotoPath))
            );
        //mImg_display?.setImageBitmap(mBitmap_org)

    

不检查数据,我们将通过 imagePath 获取。如果您正在检查Uri.parse(currentPhotoPath),请确保它是Uri.fromFile(File(currentPhotoPath))

现在你有了你的位图,有时间花其他几个小时/天来调整解码大小,保存。

还有一种保存tokken图像的方法,如果我需要tokken图像保存在画廊中,也许你可以帮我看看我应该把它放在哪里

  // org android developers
    private fun galleryAddPic() 
        Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also  mediaScanIntent ->
            val f = File(currentPhotoPath)
            mediaScanIntent.data = Uri.fromFile(f)
            sendBroadcast(mediaScanIntent)
        
    

【讨论】:

以上是关于Android相机意图:如何获得全尺寸照片?的主要内容,如果未能解决你的问题,请参考以下文章

用相机意图拍照以肖像模式android旋转图片

Android:BroadcastReceiver 意图检测拍摄的相机照片?

Android中的相机,如何获得最佳尺寸,预览尺寸,图片尺寸,视图尺寸,图像失真

打开相机意图时如何防止android应用程序方向

Android中的相机意图和优先级队列

Android相机录制时间限制和最大尺寸