Android调用相册、相机(兼容6.0、7.0、8.0)
又好久没有写博客了,好习惯不能断,该写点就得写点,今天带来的笔记是关于Android 项目调用系统相机 与调用系统相册的之后拿到照片的基本操作,我感觉好多人还是不太熟悉的哈。项目兼容 Android 5.0设备、Android 6.0设备、Android 7.0、Android 8.0等设备,下面请开始欣赏我的表演,先上动画,给大家看一下效果哈。
Android 5.0设备效果:
Android 6.0设备效果:
Android 8.0 设备效果:
看了三个小动画,感觉就是相机效果越来越清楚了(说人话),好的回归正题
一、界面上定义了三个按钮,一个imageview代码还是扔上来吧,
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn_get_pic_form_photo_album" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_margin="10dp" android:text="调用相册" /> <Button android:id="@+id/btn_get_Permission" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_margin="10dp" android:text="动态权限申请" /> <Button android:id="@+id/btn_get_pic_from_camera" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_margin="10dp" android:text="调用相机" /> <ImageView android:id="@+id/iv_test" android:layout_width="200dp" android:layout_height="200dp" android:layout_centerInParent="true" android:contentDescription="@string/app_name" android:scaleType="fitXY" tools:src="@mipmap/ic_launcher" /> </RelativeLayout>
这里没什么好说的,
二、引入框架
我这个人很懒,引入了 glide
图片加载框架和easypermissions动态权限申请框架,两个十分好用的框。
//glide 图片加载 implementation 'com.github.bumptech.glide:glide:4.8.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' //动态权限申请库 implementation 'pub.devrel:easypermissions:1.3.0'
三、简单说一下easypermissions框架使用
众所周知,Android 6.0 开始 google 爸爸引入了动态权限机制,所谓来保护用户隐私(其实就是对开发者坑爹,个人见解)。但是我们必须处理啊,毕竟是google 爸爸。
以下仅仅以本项目所申请权限为例进行讲解。
1、AndroidManifest.xml 内操作
<!--读写内存块权限--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--调用相机权限--> <uses-permission android:name="android.permission.CAMERA" />
2、初始化集合装载权限
private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
3、检查权限、申请权限
//获取权限 private void getPermission() { if (EasyPermissions.hasPermissions(this, permissions)) { //已经打开权限 Toast.makeText(this, "已经申请相关权限", Toast.LENGTH_SHORT).show(); } else { //没有打开相关权限、申请权限 EasyPermissions.requestPermissions(this, "需要获取您的相册、照相使用权限", 1, permissions); } }
4、实现接口
在所运用权限申请的界面实现EasyPermissions.PermissionCallbacks
接口
5、处理回调
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //框架要求必须这么写 EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } //成功打开权限 @Override public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) { Toast.makeText(this, "相关权限获取成功", Toast.LENGTH_SHORT).show(); } //用户未同意权限 @Override public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) { Toast.makeText(this, "请同意相关权限,否则功能无法使用", Toast.LENGTH_SHORT).show(); }
四、调用相机 适配>=6.0以上设备
1、AndroidManifest.xml 内操作
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.hxd.pictest.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
其中,android:authorities="com.example.hxd.pictest.fileprovider"
的值其实是:项目包名.fileprovider
。
2、创建file_paths.xml文件
在 项目 res 下面创建 xml 文件夹,xml文件夹下创建 file_paths.xml 文件,文件内写如下:内容
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
文件创建,如下图:
3、点击按钮拍照
private File cameraSavePath;//拍照照片路径 private Uri uri;//照片uri //激活相机操作 private void goCamera() { cameraSavePath = new File(Environment.getExternalStorageDirectory().getPath() + "/" + System.currentTimeMillis() + ".jpg"); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //第二个参数为 包名.fileprovider uri = FileProvider.getUriForFile(MainActivity.this, "com.example.hxd.pictest.fileprovider", cameraSavePath); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { uri = Uri.fromFile(cameraSavePath); } intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); MainActivity.this.startActivityForResult(intent, 1); }
4、获取照片并且展示
Android 6.0以上设备的照片路径 就是你声明的uri。
if (requestCode == 1 && resultCode == RESULT_OK) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { photoPath = String.valueOf(cameraSavePath); } else { photoPath = uri.getEncodedPath(); } Log.d("拍照返回图片路径:", photoPath); Glide.with(MainActivity.this).load(photoPath).into(ivTest); }
总结,截止到这里,你拍摄的照片就可以拿到并且进行显示了,这里使用到了强大的Glide 图片加载器省去了很多自己要写的代码。很方便。
五、相册内选照片
1、调去系统相册
//激活相册操作 private void goPhotoAlbum() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_PICK); intent.setType("image/*"); startActivityForResult(intent, 2); }
2、获取相册返回的uri
这里封装了一个工具类,根据不同版本系统返回不同类型的uri ,这个工具类保留简单好用
import android.annotation.SuppressLint; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.provider.MediaStore; /** * Content:从相册内获取照片转化工具类 * Actor:韩小呆 ヾ(゚▽゚)ノ * Time: 2018/08/18 18:54 * Update: * Time: */ public class getPhotoFromPhotoAlbum { /** * 根据Uri获取图片的绝对路径 * * @param context 上下文对象 * @param uri 图片的Uri * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null */ public static String getRealPathFromUri(Context context, Uri uri) { int sdkVersion = Build.VERSION.SDK_INT; if (sdkVersion >= 19) { return getRealPathFromUriAboveApi19(context, uri); } else { return getRealPathFromUriBelowAPI19(context, uri); } } /** * 适配api19以下(不包括api19),根据uri获取图片的绝对路径 * * @param context 上下文对象 * @param uri 图片的Uri * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null */ private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) { return getDataColumn(context, uri, null, null); } /** * 适配api19及以上,根据uri获取图片的绝对路径 * * @param context 上下文对象 * @param uri 图片的Uri * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null */ @SuppressLint("NewApi") private static String getRealPathFromUriAboveApi19(Context context, Uri uri) { String filePath = null; if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是document类型的 uri, 则通过document id来进行处理 String documentId = DocumentsContract.getDocumentId(uri); if (isMediaDocument(uri)) { // 使用':'分割 String id = documentId.split(":")[1]; String selection = MediaStore.Images.Media._ID + "=?"; String[] selectionArgs = {id}; filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs); } else if (isDownloadsDocument(uri)) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); filePath = getDataColumn(context, contentUri, null, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是 content 类型的 Uri filePath = getDataColumn(context, uri, null, null); } else if ("file".equals(uri.getScheme())) { // 如果是 file 类型的 Uri,直接获取图片对应的路径 filePath = uri.getPath(); } return filePath; } /** * 获取数据库表中的 _data 列,即返回Uri对应的文件路径 * */ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { String path = null; String[] projection = new String[]{MediaStore.Images.Media.DATA}; Cursor cursor = null; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); path = cursor.getString(columnIndex); } } catch (Exception e) { if (cursor != null) { cursor.close(); } } return path; } /** * @param uri the Uri to check * @return Whether the Uri authority is MediaProvider */ private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * @param uri the Uri to check * @return Whether the Uri authority is DownloadsProvider */ private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } }
3、拿到图片,展示图片
if (requestCode == 2 && resultCode == RESULT_OK) { photoPath = getPhotoFromPhotoAlbum.getRealPathFromUri(this, data.getData()); Glide.with(MainActivity.this).load(photoPath).into(ivTest); }
六、贴出 Activity 内完整代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks { private ImageView ivTest; private File cameraSavePath;//拍照照片路径 private Uri uri; private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnGetPicFromCamera = findViewById(R.id.btn_get_pic_from_camera); Button btnGetPicFromPhotoAlbum = findViewById(R.id.btn_get_pic_form_photo_album); Button btnGetPermission = findViewById(R.id.btn_get_Permission); ivTest = findViewById(R.id.iv_test); btnGetPicFromCamera.setOnClickListener(this); btnGetPicFromPhotoAlbum.setOnClickListener(this); btnGetPermission.setOnClickListener(this); cameraSavePath = new File(Environment.getExternalStorageDirectory().getPath() + "/" + System.currentTimeMillis() + ".jpg"); } @Override public void onClick(View v) { int id = v.getId(); switch (id) { case R.id.btn_get_pic_from_camera: goCamera(); break; case R.id.btn_get_pic_form_photo_album: goPhotoAlbum(); break; case R.id.btn_get_Permission: getPermission(); break; } } //获取权限 private void getPermission() { if (EasyPermissions.hasPermissions(this, permissions)) { //已经打开权限 Toast.makeText(this, "已经申请相关权限", Toast.LENGTH_SHORT).show(); } else { //没有打开相关权限、申请权限 EasyPermissions.requestPermissions(this, "需要获取您的相册、照相使用权限", 1, permissions); } } //激活相册操作 private void goPhotoAlbum() { Intent intent = new Intent(); intent.setAction(Intent.ACTION_PICK); intent.setType("image/*"); startActivityForResult(intent, 2); } //激活相机操作 private void goCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { uri = FileProvider.getUriForFile(MainActivity.this, "com.example.hxd.pictest.fileprovider", cameraSavePath); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else { uri = Uri.fromFile(cameraSavePath); } intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); MainActivity.this.startActivityForResult(intent, 1); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //框架要求必须这么写 EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } //成功打开权限 @Override public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) { Toast.makeText(this, "相关权限获取成功", Toast.LENGTH_SHORT).show(); } //用户未同意权限 @Override public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) { Toast.makeText(this, "请同意相关权限,否则功能无法使用", Toast.LENGTH_SHORT).show(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { String photoPath; if (requestCode == 1 && resultCode == RESULT_OK) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { photoPath = String.valueOf(cameraSavePath); } else { photoPath = uri.getEncodedPath(); } Log.d("拍照返回图片路径:", photoPath); Glide.with(MainActivity.this).load(photoPath).into(ivTest); } else if (requestCode == 2 && resultCode == RESULT_OK) { photoPath = getPhotoFromPhotoAlbum.getRealPathFromUri(this, data.getData()); Glide.with(MainActivity.this).load(photoPath).into(ivTest); } super.onActivityResult(requestCode, resultCode, data); } }
总结:没做事情之前,永远不要把事情想的特别难;每天进步一点点,不要嫌少,日久天长,也是满满的收获。
demo源码:PicTest
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【Android】仿淘宝商品详情页面的下拉渐变
最近需要做一个和最新版淘宝相似的商品详情页。先看原版: 淘宝的版本 我实现的效果: 我的版本 因为单纯实现渐变功能,所以背景只用了一张尺寸较大的imageview。 先说实现效果要求: 沉浸式状态栏:当整个页面下滑到一定高度时,变为白色;banner出现时,变为透明,期间颜色是渐变的; toolbar的返回键和后边的“更多”键始终显示,不受渐变影响,toolbar中间的商品图标原始状态为隐藏,banner开始隐藏时慢慢渐变显示; tablayout整体原始为隐藏,banner开始隐藏时,tablayout开始显示,直至无透明度。 思路:简单说就是检测用户手指的纵向滑动长度,就是Y方向,整体用一个NestedScrollView包裹着,而NestedScrollView有一个滚动监听方法:addOnScrollChangedListener。 于是乎,动手吧! 先看布局: <com.zhy.autolayout.AutoRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ap...
- 下一篇
Android中的设计模式之责任链模式
参考 《设计模式:可复用面向对象软件的基础 》5.1 Chain of responsibility 职责链 对象行为型模式 《Android源码设计模式解析与实战》第9章 使编程更有灵活性--责任链模式 意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 适用场景 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。 想在不明确指定接收者的情况下,像多个对象中的一个提交一个请求。 可处理一个请求的对象集合应被动态指定。 结构 结构 Handler 抽象处理角色,声明一个请求处理的方法,并在其中保持一个对下一个处理节点Handler对象的引用。 ConcreteHandler 具体处理者角色,对请求进行处理,如果不能处理则将该请求转发给下一个节点上的处理对象。 进一步抽象 对于请求来说,其形式是固定的,就是一个字符串,而判断一个节点上的对象是否能够处理该请求的标志,则是该字符串是否与之匹配。然而在大多数情况下,责任链中的请求和对应的处理规则是不尽相同的,在这种情况下可以将请...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS6,CentOS7官方镜像安装Oracle11G
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS6,7,8上安装Nginx,支持https2.0的开启