文章目录

  • 一、调用摄像头
    • 布局文件:
    • MainActivity.java
    • AndroidManifest.xml
    • file_paths.xml
    • 注意点:
    • 效果:
  • 二、调用相册
    • 加入从相册选择图片的逻辑
    • 无法选择图片
    • 解决办法
    • 效果
    • 改进(图片压缩)

一、调用摄像头

新建项目 CameraAlbumTest 项目

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"android:orientation="vertical">// 用于发起相机<Buttonandroid:id="@+id/takePhotoBtn"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Take Photo"android:textAllCaps="false"/>// 显示拍摄的照片<ImageViewandroid:id="@+id/imageView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"/></LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {public static final int TAKE_PHOTO = 1;private ImageView picture;private Uri imageUri;private File outputImage;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button takePhoto = findViewById(R.id.takePhotoBtn);picture = findViewById(R.id.imageView);takePhoto.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// 创建 File 对象,用于存储拍照的图片,命名为 output_image.jpg// 并将图片存放在手机 SD 卡应用关联缓存目录下。// 应用关联缓存目录就是指 SD 卡中专门用于存放当前应用缓存数据的位置,// 调用 getExternalCacheDir() 方法可以得到这个目录, 具体的路径为 /sdcard/Android/data/<package name>/cache// 为什么要使用应用关联目录来存放图片呢?/*** 因为从Android 6.0系统开始,读写SD卡被列为了危险权限,* 如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步。* 另外,从Android 10.0系统开始,公有的SD卡目录已经不再允许被应用程序直接访问了,而是要使用作用域存储才行。*/outputImage = new File(getExternalCacheDir(), "output_image.jpg");try {if (outputImage.exists()) {outputImage.delete();}outputImage.createNewFile();} catch (Exception e) {e.printStackTrace();}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// 系统版本高于 7.0/*** 调用 FileProvider.getUriForFile() 方法将 File 对象转化为封装过的 Uri 对象* 之所以要进行这样一层转换,是因为从 Android 7.0系统开始,* 直接使用本地真实路径的 Uri 被认为是不安全的,会抛出一个 FileUriExposedException 异常。* 而 FileProvider 则是一种特殊的 ContentProvider,它使用了和 ContentProvider 类似的机制来对数据进行保护,* 可以选择性地将封装过的 Uri 共享给外部,从而提高了应用的安全性** param Context* param 任意唯一的字符串* param File 对象*/imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);} else {// 系统版本低于 7.0, 就调用 Uri.fromFile() 将 File 对象转化为封装过的 Uri 对象imageUri = Uri.fromFile(outputImage);}// 启动相机程序,隐式跳转Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");// 这里指定图片的输出地址,这里填入刚刚得到的Uri对象intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 启动相机,拍下的照片将会输出到 output_image.jpg 中startActivityForResult(intent, TAKE_PHOTO);}});}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case TAKE_PHOTO:// 判断是否拍照成功if (resultCode == RESULT_OK) {try {// 将拍摄照片显示出来Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(rotateIfRequired(bitmap));} catch (Exception e) {e.printStackTrace();}}break;default:break;}}/*** 需要注意的是,调用照相机程序去拍照有可能会在一些手机上发生照片旋转的情况。* 这是因为这些手机认为打开摄像头进行拍摄时手机就应该是横屏的,因此回到竖屏的情况下就会发生90度的旋转。* 为此,这里我们又加上了判断图片方向的代码,如果发现图片需要进行旋转,那么就先将图片旋转相应的角度,然后再显示到界面上。* @param bitmap* @return*/private Bitmap rotateIfRequired(Bitmap bitmap) {try {ExifInterface exif = new ExifInterface(outputImage.getPath());int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);switch (orientation) {case ExifInterface.ORIENTATION_ROTATE_90:return rotateBitmap(bitmap, 90);case ExifInterface.ORIENTATION_ROTATE_180:return rotateBitmap(bitmap, 180);case ExifInterface.ORIENTATION_ROTATE_270:return rotateBitmap(bitmap, 270);default:break;}} catch (IOException e) {e.printStackTrace();}return bitmap;}private Bitmap rotateBitmap(Bitmap bitmap, int degree) {Matrix matrix = new Matrix();matrix.postRotate(degree);Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);// 将不再需要的 bitmap 回收bitmap.recycle();return rotateBitmap;}
}

FileProvider 是一种特殊的ContentProvider,它使用了和ContentProvider类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。所以我们这里要注册 ContentProvider

AndroidManifest.xml

    <applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.CameraAlbumTest"><provider// name 属性固定android:name="androidx.core.content.FileProvider"// authority 必须和 FileProvider.getUriForFile()方法中的第二个参数相同android:authorities="com.example.cameraalbumtest.fileprovider"android:exported="false"android:grantUriPermissions="true">// 指定 Uri 的共享路径<meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths"/></provider></application>

file_paths.xml

1、在 res 下新建 xml 文件夹


2、创建 file_paths.xml,填入以下代码

<?xml version="1.0" encoding="UTF-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
/**external-path就是用来指定Uri共享路径的,name属性的值可以随便填,path属性的值表示共享的具体路径。这里使用一个单斜线表示将整个SD卡进行共享,当然你也可以仅共享存放output_image.jpg这张图片的路径。**/<external-pathname="my_images"path="/"/>
</paths>

注意点:

如果你将项目中的appcompat库升级到 1.3.0 或更高的版本,你会发现startActivityForResult()方法已经被废弃了。

官方现在更加建议使用Activity Result API来实现在两个Activity之间交换数据的功能。
参考文章:使用 Activity Result API 代替 startActivityForResult

使用 Activity Result API 代替 startActivityForResult,我们不使用 startActivityForResult()方法来实现活动的跳转,新建一个ActivityResultLauncher对象,使用该对象的launch方法来实现跳转。并且另一个活动结束时会回调该对象registerForActivityResult()方法,我们在回调的地方获取并执行数据。

例:
1、注册对 Activity 结果的监听

private ActivityResultLauncher requestDataLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {@Overridepublic void onActivityResult(ActivityResult result) {if (result.getResultCode() == RESULT_OK) {try {// 将拍摄照片显示出来Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(rotateIfRequired(bitmap));} catch (Exception e) {e.printStackTrace();}}}});

2、实现跳转

requestDataLauncher.launch(intent);

实现:

public class MainActivity extends AppCompatActivity {public static final int TAKE_PHOTO = 1;private ImageView picture;private Uri imageUri;private File outputImage;// 使用 Activity Result API 实现private ActivityResultLauncher requestDataLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {@Overridepublic void onActivityResult(ActivityResult result) {if (result.getResultCode() == RESULT_OK) {try {// 将拍摄照片显示出来Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(rotateIfRequired(bitmap));} catch (Exception e) {e.printStackTrace();}}}});@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button takePhoto = findViewById(R.id.takePhotoBtn);picture = findViewById(R.id.imageView);takePhoto.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// 创建 File 对象,用于存储拍照的图片,命名为 output_image.jpg// 并将图片存放在手机 SD 卡应用关联缓存目录下。// 应用关联缓存目录就是指 SD 卡中专门用于存放当前应用缓存数据的位置,// 调用 getExternalCacheDir() 方法可以得到这个目录, 具体的路径为 /sdcard/Android/data/<package name>/cache// 为什么要使用应用关联目录来存放图片呢?/*** 因为从Android 6.0系统开始,读写SD卡被列为了危险权限,* 如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步。* 另外,从Android 10.0系统开始,公有的SD卡目录已经不再允许被应用程序直接访问了,而是要使用作用域存储才行。*/outputImage = new File(getExternalCacheDir(), "output_image.jpg");try {if (outputImage.exists()) {outputImage.delete();}outputImage.createNewFile();} catch (Exception e) {e.printStackTrace();}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// 系统版本高于 7.0/*** 调用 FileProvider.getUriForFile() 方法将 File 对象转化为封装过的 Uri 对象* 之所以要进行这样一层转换,是因为从 Android 7.0系统开始,* 直接使用本地真实路径的 Uri 被认为是不安全的,会抛出一个 FileUriExposedException 异常。* 而 FileProvider 则是一种特殊的 ContentProvider,它使用了和 ContentProvider 类似的机制来对数据进行保护,* 可以选择性地将封装过的 Uri 共享给外部,从而提高了应用的安全性** param Context* param 任意唯一的字符串* param File 对象*/imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);} else {// 系统版本低于 7.0, 就调用 Uri.fromFile() 将 File 对象转化为封装过的 Uri 对象imageUri = Uri.fromFile(outputImage);}// 启动相机程序,隐式跳转Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");// 这里指定图片的输出地址,这里填入刚刚得到的Uri对象intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 启动相机,拍下的照片将会输出到 output_image.jpg 中// 一、使用废弃的 startActivityForResult() 方法//startActivityForResult(intent, TAKE_PHOTO);// 二、使用 Activity Result API 实现requestDataLauncher.launch(intent);}});}/*** 需要注意的是,调用照相机程序去拍照有可能会在一些手机上发生照片旋转的情况。* 这是因为这些手机认为打开摄像头进行拍摄时手机就应该是横屏的,因此回到竖屏的情况下就会发生90度的旋转。* 为此,这里我们又加上了判断图片方向的代码,如果发现图片需要进行旋转,那么就先将图片旋转相应的角度,然后再显示到界面上。* @param bitmap* @return*/private Bitmap rotateIfRequired(Bitmap bitmap) {try {ExifInterface exif = new ExifInterface(outputImage.getPath());int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);switch (orientation) {case ExifInterface.ORIENTATION_ROTATE_90:return rotateBitmap(bitmap, 90);case ExifInterface.ORIENTATION_ROTATE_180:return rotateBitmap(bitmap, 180);case ExifInterface.ORIENTATION_ROTATE_270:return rotateBitmap(bitmap, 270);default:break;}} catch (IOException e) {e.printStackTrace();}return bitmap;}private Bitmap rotateBitmap(Bitmap bitmap, int degree) {Matrix matrix = new Matrix();matrix.postRotate(degree);Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);// 将不再需要的 bitmap 回收bitmap.recycle();return rotateBitmap;}
}

效果:

二、调用相册

新加一个 Button 用于从相册选取图片

<Buttonandroid:id="@+id/fromAlbumBtn"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="From Album"android:textAllCaps="false" />

加入从相册选择图片的逻辑

这里用了 ViewBinding 的知识
ViewBindind

public class MainActivity extends AppCompatActivity {public static final int TAKE_PHOTO = 1;public static final int CHOOSE_PHOTO = 2;private ImageView picture;private Uri imageUri;private File outputImage;// ViewBindingActivityMainBinding mBinding;// 使用 Activity Result API 实现拍照回调private ActivityResultLauncher takePhotoDataLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {@Overridepublic void onActivityResult(ActivityResult result) {if (result.getResultCode() == RESULT_OK) {try {// 将拍摄照片显示出来Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(rotateIfRequired(bitmap));} catch (Exception e) {e.printStackTrace();}}}});// 使用 Activity Result API 实现选择相册照片回调private  ActivityResultLauncher fromAlbumDataLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {@Overridepublic void onActivityResult(ActivityResult result) {Log.d("MainActivity", "fromAlbumDataLauncher");if (result.getResultCode() == RESULT_OK && result.getData() != null) {Uri uri = result.getData().getData();if (uri != null) {// 将选择的图片显示Bitmap bitmap = getBitmapFromUri(uri);if (bitmap != null) {picture.setImageBitmap(bitmap);}}}}});private Bitmap getBitmapFromUri(Uri uri) {Bitmap bitmap = null;try {ParcelFileDescriptor fileDescriptor = getContentResolver().openFileDescriptor(uri, "r");if (fileDescriptor != null) {bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor());fileDescriptor.close();}} catch (Exception e) {e.printStackTrace();}return bitmap;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 初始化 ViewBindingmBinding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(mBinding.getRoot());//picture = findViewById(R.id.imageView);picture = mBinding.imageView;// 使用相机获取图片takePhoto();// 使用相册获得图片fromAlbum();}private void fromAlbum() {//Button fromAlbum = findViewById(R.id.fromAlbumBtn);mBinding.fromAlbumBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Log.d("MainActivity", "click");// 打开文件选择器Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);intent.addCategory(Intent.CATEGORY_OPENABLE);// 指定只显示图片intent.setType("image/*");fromAlbumDataLauncher.launch(intent);}});}private void takePhoto() {//Button takePhoto = findViewById(R.id.takePhotoBtn);mBinding.takePhotoBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {// 创建 File 对象,用于存储拍照的图片,命名为 output_image.jpg// 并将图片存放在手机 SD 卡应用关联缓存目录下。// 应用关联缓存目录就是指 SD 卡中专门用于存放当前应用缓存数据的位置,// 调用 getExternalCacheDir() 方法可以得到这个目录, 具体的路径为 /sdcard/Android/data/<package name>/cache// 为什么要使用应用关联目录来存放图片呢?/*** 因为从Android 6.0系统开始,读写SD卡被列为了危险权限,* 如果将图片存放在SD卡的任何其他目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步。* 另外,从Android 10.0系统开始,公有的SD卡目录已经不再允许被应用程序直接访问了,而是要使用作用域存储才行。*/outputImage = new File(getExternalCacheDir(), "output_image.jpg");try {if (outputImage.exists()) {outputImage.delete();}outputImage.createNewFile();} catch (Exception e) {e.printStackTrace();}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// 系统版本高于 7.0/*** 调用 FileProvider.getUriForFile() 方法将 File 对象转化为封装过的 Uri 对象* 之所以要进行这样一层转换,是因为从 Android 7.0系统开始,* 直接使用本地真实路径的 Uri 被认为是不安全的,会抛出一个 FileUriExposedException 异常。* 而 FileProvider 则是一种特殊的 ContentProvider,它使用了和 ContentProvider 类似的机制来对数据进行保护,* 可以选择性地将封装过的 Uri 共享给外部,从而提高了应用的安全性** param Context* param 任意唯一的字符串* param File 对象*/imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);} else {// 系统版本低于 7.0, 就调用 Uri.fromFile() 将 File 对象转化为封装过的 Uri 对象imageUri = Uri.fromFile(outputImage);}// 启动相机程序,隐式跳转Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");// 这里指定图片的输出地址,这里填入刚刚得到的Uri对象intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 启动相机,拍下的照片将会输出到 output_image.jpg 中// 一、使用废弃的 startActivityForResult() 方法//startActivityForResult(intent, TAKE_PHOTO);// 二、使用 Activity Result API 实现takePhotoDataLauncher.launch(intent);}});}//    @Override
//    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {//        super.onActivityResult(requestCode, resultCode, data);
//        switch (requestCode) {//            case TAKE_PHOTO:
//                // 判断是否拍照成功
//                if (resultCode == RESULT_OK) {//                    try {//                        // 将拍摄照片显示出来
//                        Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
//                        picture.setImageBitmap(rotateIfRequired(bitmap));
//                    } catch (Exception e) {//                        e.printStackTrace();
//                    }
//                }
//                break;
//            default:
//                break;
//        }
//    }/*** 需要注意的是,调用照相机程序去拍照有可能会在一些手机上发生照片旋转的情况。* 这是因为这些手机认为打开摄像头进行拍摄时手机就应该是横屏的,因此回到竖屏的情况下就会发生90度的旋转。* 为此,这里我们又加上了判断图片方向的代码,如果发现图片需要进行旋转,那么就先将图片旋转相应的角度,然后再显示到界面上。* @param bitmap* @return*/private Bitmap rotateIfRequired(Bitmap bitmap) {try {ExifInterface exif = new ExifInterface(outputImage.getPath());int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);switch (orientation) {case ExifInterface.ORIENTATION_ROTATE_90:return rotateBitmap(bitmap, 90);case ExifInterface.ORIENTATION_ROTATE_180:return rotateBitmap(bitmap, 180);case ExifInterface.ORIENTATION_ROTATE_270:return rotateBitmap(bitmap, 270);default:break;}} catch (IOException e) {e.printStackTrace();}return bitmap;}private Bitmap rotateBitmap(Bitmap bitmap, int degree) {Matrix matrix = new Matrix();matrix.postRotate(degree);Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);// 将不再需要的 bitmap 回收bitmap.recycle();return rotateBitmap;}
}

无法选择图片

运行程序,发现打开了文件管理器,但是无法选择图片。
查阅资料发现,Android 10 中引入了作用域存储的概念,Android系统对SD卡的使用做了很大的限制。从Android 10开始,每个应用程序只能有权在自己的外置存储空间关联目录下读取和创建文件。

原文:
Android 10 作用域存储
Android长久以来都支持外置存储空间这个功能,也就是我们常说的SD卡存储。这个功能使用得极其广泛,几乎所有的App都喜欢在SD卡的根目录下建立一个自己专属的目录,用来存放各类文件和数据。

那么这么做有什么好处吗?我想了一下,大概有两点吧。第一,存储在SD卡的文件不会计入到应用程序的占用空间当中,也就是说即使你在SD卡存放了1G的文件,你的应用程序在设置中显示的占用空间仍然可能只有几十K。第二,存储在SD卡的文件,即使应用程序被卸载了,这些文件仍然会被保留下来,这有助于实现一些需要数据被永久保留的功能。

然而,这些“好处”真的是好处吗?或许对于开发者而言这算是好处吧,但对于用户而言,上述好处无异于一些流氓行为。因为这会将用户的SD卡空间搞得乱糟糟的,而且即使我卸载了一个完全不再使用的程序,它所产生的垃圾文件却可能会一直保留在我的手机上。

另外,存储在SD卡上的文件属于公有文件,所有的应用程序都有权随意访问,这也对数据的安全性带来了很大的挑战。

为了解决上述问题,Google在Android 10当中加入了作用域存储功能。

那么到底什么是作用域存储呢?简单来讲,就是Android系统对SD卡的使用做了很大的限制。从Android 10开始,每个应用程序只能有权在自己的外置存储空间关联目录下读取和创建文件,获取该关联目录的代码是:context.getExternalFilesDir()。关联目录对应的路径大致如下:

/storage/emulated/0/Android/data/<包名>/files

将数据存放到这个目录下,你将可以完全使用之前的写法来对文件进行读写,不需要做任何变更和适配。但同时,刚才提到的那两个“好处”也就不存在了。这个目录中的文件会被计入到应用程序的占用空间当中,同时也会随着应用程序的卸载而被删除。

另外,存储在SD卡上的文件属于公有文件,所有的应用程序都有权随意访问,这也对数据的安全性带来了很大的挑战。

那么有些朋友可能会问了,我就是需要访问其他目录该怎么办呢?比如读取手机相册中的图片,或者向手机相册中添加一张图片。为此,Android系统针对文件类型进行了分类,图片、音频、视频这三类文件将可以通过MediaStore API来进行访问,而其他类型的文件则需要使用系统的文件选择器来进行访问。

另外,我们的应用程序向媒体库贡献的图片、音频或视频,将会自动拥有其读写权限,不需要额外申请 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 权限。而如果你要读取其他应用程序向媒体库贡献的图片、音频或视频,则必须要申请 READ_EXTERNAL_STORAGE 权限才行。 WRITE_EXTERNAL_STORAGE 权限将会在未来的 Android 版本中废弃。

总结

  • 引入作用域存储使得应用不能再随意访问 SD 卡中的数据。
  • 每个应用只能在自己的外置存储关联目录下读取和创建文件。
  • 应用向媒体库贡献的图片、视频、音频会自动拥有读写权限,如果要读取其他应用提供的这些数据,则要申请 READ_EXTERNAL_STORAGE 权限。

** 补充**

  • WRITE_EXTERNAL_STORAGE 权限在 Android 11 中开始已经被废弃了
  • 但是如果要获取写入权限的话还是有办法的,比如 ES文件管理器这类软件需要获取SD 卡全局数据,美图秀秀等修图软件需要修改其他软件贡献的图片。具体做法访问给出的参考文章。Android 10 作用域存储

解决办法

基于上述知识,我们就可以知道打开文件管理器却无法选择图片的原因,我们只需在 AndroidManifest.xml文件中声明 READ_EXTERNAL_STORAGE 权限就可以了。

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

效果

改进(图片压缩)

不过,目前我们的实现还不算完美,因为如果某些图片的像素很高,直接加载到内存中就有可能会导致程序崩溃。更好的做法是根据项目的需求先对图片进行适当的压缩,然后再加载到内存中。至于如何对图片进行压缩,就要考验你查阅资料的能力了,这里就不再展开进行讲解了。

8-2 Android 摄像头和相册相关推荐

  1. android调用相册和摄像头,Android8.3调用摄像头和相册

    我们平时在使用QQ或微信的时候经常要和别人分享图片,这些图片可以是用手机摄像头拍的,也可以是从相册中选取的.类似这样的功能实在是太常见了,几乎在每个应用程序中都会有,那么本节我们就学习一下调用摄像头和 ...

  2. Android基础实战之调用摄像头与相册 | 带实例

    调用摄像头与相册 调用摄像头拍照/从相册选择照片 xml 设置了两个按钮,分别是打开摄像头的按钮,以及打开相册的按钮 <Buttonandroid:id="@+id/take_phot ...

  3. Android摄像头相册使用+上传照片至服务器(上篇)

    这篇教程主要介绍如何使用摄像头还有相册,还介绍如何将获取到的照片上传至服务器保存(这里主要是用到了阿里云的对象存储OSS服务) 应用场景:APP中"我的"界面或者"个人中 ...

  4. android调用相册和摄像头,调用Android摄像头与打开相册

    以下为代码块: package com.example.demo; import java.io.File; import android.annotation.TargetApi; import a ...

  5. Android调用摄像头和相册

    点击打开链接 http://blog.csdn.net/u012796139/article/details/50084517 //layout <?xml version="1.0& ...

  6. Android学习(15)-摄像头和相册

    如何调用自己手机的拍照功能以及从手机相册选择图片? 比如找个小例子: 首先,还是弄一个layout先: <RelativeLayout xmlns:android="http://sc ...

  7. iOS摄像头和相册-UIImagePickerController-浅析(转)

    iOS摄像头和相册-UIImagePickerController-浅析(转) 转自: http://blog.sina.com.cn/s/blog_7b9d64af0101cfd9.html 在一些 ...

  8. 使用HTML5+调用手机摄像头和相册

    前言: 前端时间使用HTML5做了一个WEB端APP,其中用到了H5页面调用手机摄像头的功能,当时也是花了不少时间去研究.最终是采用了HTML5plus(HTML5+)的方式完成了该功能,现将具体方法 ...

  9. html调起苹果手机摄像头_使用HTML5+调用手机摄像头和相册

    前言: 前端时间使用HTML5作了一个WEB端APP,其中用到了H5页面调用手机摄像头的功能,当时也是花了很多时间去研究.最终是采用了HTML5plus(HTML5+)的方式完成了该功能,现将具体方法 ...

最新文章

  1. 基于linuxunix高性能web服务器架构思路分析
  2. paddleocr ‘bytes‘ object has no attribute ‘shape‘
  3. JavaScript发布订阅者模式
  4. 键盘流的逆袭- Idea 中使用 VIM mode 提高生成效率
  5. 在JDK 9中将InputStream传输到OutputStream
  6. MVC View 中 html 属性名与关键字冲突问题的分析与解决
  7. 随想录(嵌入式工程师的出路)
  8. 台式计算机怎么连手机热点,台式电脑怎么连接手机热点进行上网
  9. 解决Sublime Text 3在GB2312编码下的中文乱码问题
  10. H5标签datalist
  11. Thinkpad E430c 16GB内存安装成功
  12. 【五校联考2015 8.20】宝藏
  13. C#计算wgs84大地坐标转换为空间直角坐标
  14. 〖Python 数据库开发实战 - MySQL篇⑦〗- MySQL的用户管理与重设用户密码
  15. 微软亚洲研究院的软件工程课程
  16. GPS定位系统源码,通用系统源码包含GPS行业核心功能可轻松开发出各类行业应用
  17. C语言课程设计报告税,第十周—C语言 个人所得税的计算
  18. 移动端android和ios解码器自适应软硬解方案
  19. OpenCV - C++实战(06) — Grabcut图像分割
  20. 线下餐饮受挫,狂奔的自热速食

热门文章

  1. 宇宙中的“创造之柱”
  2. QLU—新生训练赛002补题
  3. 在centos8环境下用asterisk18配置pjsip和webrtc音视频通话教程(一)
  4. 影视广告创意与制作(二)
  5. ie9 java小程序设置_小程序 自定义导航栏
  6. C#重载函数与可选参数简单实例
  7. 7-28 | 猴子选大王
  8. windows10出现扬声器小红叉问题的解决方法
  9. office2016 Excel 打开“宏”分三步
  10. html链接外部样式表、链接网站图标