使用相机拍照并保存到相册

5

我已经查阅了多篇文档和相关技术堆栈,但是我仍然不确定如何实现这个功能...

如果有帮助或示例代码,那么我就可以更好地理解它。

以下是运行相机的一组代码,它已经完美地工作了。我的下一个问题是,如何让它自动保存到手机相册中?

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        tvCameraTiles = (TextView) findViewById(R.id.tvCameraTiles);

        tvCameraTiles.setOnClickListener(new  View.OnClickListener()
        {
             @Override
             public void onClick(View v)
             {
                 dispatchTakePictureIntent();
             }
         });
    }

    private void dispatchTakePictureIntent()
    {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        if (takePictureIntent.resolveActivity(getPackageManager()) != null)
        {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK)
        {
            //Get Camera Image File Data
//            Bundle extras = data.getExtras();
//            Bitmap imageBitmap = (Bitmap) extras.get("data");
//            mImageView.setImageBitmap(imageBitmap);
        }
    }

如果它完美地工作,那么你在哪里可以看到拍摄的照片? - greenapps
@greenapps 没有地方保存,因为它只是返回到活动结果,所以图片不会被保存。 - user6487002
它返回活动结果是正常的,这种情况总是发生。谁告诉你图片没有保存?在图库应用程序中看不到它并不意味着它没有保存在某个地方。 - greenapps
如果您关闭/打开设备,您是否能看到新图片? - greenapps
4个回答

5

你有没有阅读过这篇文章:https://developer.android.com/training/camera/photobasics.html

特别是其中的部分:

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

您似乎没有将照片保存在外部存储器中,所以应该可以正常工作。

编辑:我尝试按照文档使用galleryAddPic方法创建一个非常基本的应用程序。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button photoButton = (Button) findViewById(R.id.photobutton);
    photoButton.setOnClickListener(new  View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            dispatchTakePictureIntent();
        }
    });
}

static final int REQUEST_IMAGE_CAPTURE = 1;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
        //Bundle extras = data.getExtras();
        //Bitmap imageBitmap = (Bitmap) extras.get("data");
        //mImageView.setImageBitmap(imageBitmap);
        galleryAddPic();
    }
}

String mCurrentPhotoPath;

private File createImageFile() throws IOException {
    File storageDir = Environment.getExternalStorageDirectory();
    File image = File.createTempFile(
            "example",  /* prefix */
            ".jpg",         /* suffix */
            storageDir      /* directory */
    );
    mCurrentPhotoPath = image.getAbsolutePath();
    return image;
}

private void dispatchTakePictureIntent() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        if (photoFile != null) {
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
}

private void galleryAddPic() {
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    File f = new File(mCurrentPhotoPath);
    Uri contentUri = Uri.fromFile(f);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

这对我很有帮助!我使用相机应用程序拍照,然后在图库应用程序中显示出来。

我修改了关于FileProvider部分的代码,现在它通过Uri.fromFile(photoFile)方法传递Uri。我也将照片保存在不同的位置,请查看createImageFile方法中的代码。

在Android Studio模拟器中工作正常,请让我知道您的情况。


嗨,我确实阅读了这份文档,但我就是想不明白在哪里获取变量-> mCurrentPhotoPath。 - user6487002
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.android.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data> </provider> - user6487002
android:resource="@xml/file_paths": - user6487002
xml/file_paths通向哪里? - user6487002
我编辑了我的回答,请查看。我没有修改manifest.xml中的任何内容(如FileProvider),只需确保声明权限WRITE_EXTERNAL_STORAGE和CAMERA,并在手机设置中进行检查(如果API => 23,则应用程序需要在运行时检查权限,而不是自动检查)。 - Rex B
显示剩余4条评论

2

我尝试了Rex B的代码片段,但在Android Studio上出现了错误。

 android.os.FileUriExposedException: file:///storage/emulated/0/example6941495009290613124.jpg exposed beyond app through ClipData.Item.getUri()

如果有人遇到这个错误,我找到了一个简单的修复方法

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

我在onClick方法中添加了这两行代码

  Button photoButton = (Button) findViewById(R.id.photobutton);
    photoButton.setOnClickListener(new  View.OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
            dispatchTakePictureIntent();
        }
    });
}

我知道这是一篇旧帖子,但我相信总有人会看到它的。


0

首先,文件将被创建并将字节流写入文件并保存。您无需为此文件存在于画廊中做任何事情。这将由画廊自动刷新。


嗯,我正在使用红米Note 4来测试我的产品,但它似乎没有出现在我的相册中。 - user6487002
请将图像保存的路径发送给我。如果您将其保存在某个临时文件夹中,相册将无法显示。请尝试发布图像文件的路径。 - Noorul

0

我在过去的一周里尝试了大部分stackoverflow线程,但没有找到一个全面的解决方案,但所有的线程都对我有所帮助。这是我测试过的工作代码,在Android 11、12和13(Google Pixex 6a)中都可以运行。

AndroidManifest.xml将包含以下内容

   <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}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
   </provider>

这是我在provider_paths.xml中提到的内容

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

在 onclick 事件上调用以下方法

dispatchTakePictureIntent();

在onActivityResult中

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch(requestCode) {
        case STApplication.SHOW_TAKEPICTURE:
            //Uri selectedImage=null;
            if (resultCode == Activity.RESULT_OK) {
                try{
                    //Bundle extras = data.getExtras();
                    Bitmap imageBitmap =  MediaStore.Images.Media.getBitmap(getContentResolver(), OldURI);
                    //galleryAddPic();
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        try {
                            saveImageInAndroidApi29AndAbove(imageBitmap);
                        } catch (Exception e) {
                            //show error to user that operatoin failed
                        }
                    } else {
                        saveImageInAndroidApi28AndBelow(imageBitmap);
                    }
                    
                    btnPicture.setText(getString(R.string.picture_taken));
                } catch (Exception e) {
                    e.printStackTrace();
                }
                break;
            }
        }
}   

最后,在您的活动类中包含以下所有方法和变量

    String currentPhotoPath;
    Uri ImageURI;
    public static final int SHOW_TAKEPICTURE = 99;
    

    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // Ensure that there's a camera activity to handle the intent
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // Create the File where the photo should go
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException ex) {
                // Error occurred while creating the File
            }
            // Continue only if the File was successfully created
            if (photoFile != null) {
                Uri photoURI = FileProvider.getUriForFile(this,
                        BuildConfig.APPLICATION_ID + ".provider",
                        photoFile);
                ImageURI=photoURI;
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, SHOW_TAKEPICTURE);
            }
        }
    }


    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",         /* suffix */
                storageDir      /* directory */
        );

        // Save a file: path for use with ACTION_VIEW intents
        currentPhotoPath = image.getAbsolutePath();
        return image;
    }

    private boolean saveImageInAndroidApi28AndBelow(Bitmap bitmap) {
        OutputStream fos;
        String imagesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
        File image = new File(imagesDir, "IMG_" + System.currentTimeMillis() + ".png");
        try {
            fos = new FileOutputStream(image);
            bitmap.compress(Bitmap.CompressFormat.PNG, 95, fos);
            Objects.requireNonNull(fos).close();
        } catch (IOException e) {
            e.printStackTrace();
            //isSuccess = false;
            return false;
        }
        //isSuccess = true;
        return true;
    }

    @NonNull
    public Uri saveImageInAndroidApi29AndAbove(@NonNull final Bitmap bitmap) throws IOException {
        final ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "IMG_" + System.currentTimeMillis());
        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
        }
        final ContentResolver resolver = getApplicationContext().getContentResolver();
        Uri uri = null;
        try {
            final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            uri = resolver.insert(contentUri, values);
            if (uri == null) {
                //isSuccess = false;
                throw new IOException("Failed to create new MediaStore record.");
            }
            try (final OutputStream stream = resolver.openOutputStream(uri)) {
                if (stream == null) {
                    //isSuccess = false;
                    throw new IOException("Failed to open output stream.");
                }
                if (!bitmap.compress(Bitmap.CompressFormat.PNG, 95, stream)) {
                    //isSuccess = false;
                    throw new IOException("Failed to save bitmap.");
                }
            }
            //isSuccess = true;
            return uri;
        } catch (IOException e) {
            if (uri != null) {
                resolver.delete(uri, null, null);
            }
            throw e;
        }
    }

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