说说 Android 中如何使用摄像头和相册
发布日期:2021-06-29 21:11:16
浏览次数:2
分类:技术文章
本文共 8046 字,大约阅读时间需要 26 分钟。
很多 APP 应用都有用户头像功能,用户既可以调用摄像头马上拍一张美美的自拍,也可以打开相册选取一张心仪的照片作为头像。
1 调用摄像头
布局文件:
活动类代码:
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; public static final int OPEN_PHOTO_REQUEST_CODE = 1; private Uri imgUrl = null; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //为按钮添加【打开摄像头】事件 findViewById(R.id.open_photo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File file = new File(getExternalCacheDir(), "imageView.jpg");//存储照片 if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= 24) { //版本高于 7.0 imgUrl = FileProvider.getUriForFile(MainActivity.this, "net.deniro.camera.fileProvider", file); } else { imgUrl = Uri.fromFile(file); } //打开摄像头 Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUrl);//指定图片输出地址 startActivityForResult(intent, OPEN_PHOTO_REQUEST_CODE); } }); //显示拍摄的照片 imageView = (ImageView) findViewById(R.id.img); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try { //解析图片并显示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; default: break; } }}
- getExternalCacheDir() 可以获取 SD 卡中专门用于存放当前应用缓存数据的位置。Android6.0+ 开始,读取存放在 SD 卡中的任何其它目录都被列为危险权限,因此需要设定运行时权限才可以操作,这里使用了与应用关联的目录,所以就可以跳过这一步。
- getUriForFile() 方法接收三个参数:Context对象、任意唯一的字符串与 File对象。从 android 7.0+ 系统开始,直接使用本地真实的路径被认为是不安全的,会抛出一个 FileExposedException 异常,而 FileProvider 是一种特殊的内容提供器,它使用与内容提供器类似的机制对数据进行保护。
在 AndroidManifest.xml 中注册刚才定义的内容提供器:
- android:authorities 就是我们在 FileProvider.getUriForFile() 方法中传入的第二个参数。
- 使用
<meta-data>
指定了 Uri 的共享路径,在此引用了 xml 资源。
在 IDEA 中可以通过快捷键 ctrl + enter 直接在 xml 文件夹下创建文件:
file_paths.xml:
Android 4.4 之前,还需要设置访问 SD 卡应用的关联目录权限:
拍照后效果:
2 从相册中选取照片
直接从相册中选取一张现有的照片比打开摄像头拍一张照片更加常用,因此,一个好的 app,应该将这两种方式都实现。
修改布局文件,加入【打开相册】按钮:
在活动类中加入打开相册选取照片的处理逻辑:
/** * 打开相册请求码 */public static final int CHOOSE_PHOTO_REQUEST_CODE = 2;...@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... //打开相册 findViewById(R.id.choose_photo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, CHOOSE_PHOTO_REQUEST_CODE); } else { openAlbum(); } }});/** * 打开相册 */private void openAlbum() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOOSE_PHOTO_REQUEST_CODE);}/** * 请求 WRITE_EXTERNAL_STORAGE 权限 * * 相册中的照片一般是存放在 SD 卡上的,所以从 SD 卡中读取照片需要申请权限 * * WRITE_EXTERNAL_STORAGE 表示读写 SD 卡的能力权限 * @param requestCode * @param permissions * @param grantResults */@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case CHOOSE_PHOTO_REQUEST_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openAlbum(); } else { Toast.makeText(this, "被拒绝啦", Toast.LENGTH_SHORT).show(); } break; default: break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "requestCode: " + requestCode); Log.d(TAG, "imgUrl: " + imgUrl); switch (requestCode) { case OPEN_PHOTO_REQUEST_CODE: if (resultCode == RESULT_OK) { try { //解析图片并显示 Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imgUrl)); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; case CHOOSE_PHOTO_REQUEST_CODE: if (Build.VERSION.SDK_INT >= 19) { //4.4 及以上系统 handleFor4_4(data); } else { handleForBefore4_4(data); } break; default: break; }}/** * 处理相片 * android 4.4- * * @param data */private void handleForBefore4_4(Intent data) { display(getImagePath(data.getData(),null));}/** * 处理相片 * android 4.4+ * * @param data */@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void handleFor4_4(Intent data) { String imagePath = null; Uri uri = data.getData();//需要解析 if (DocumentsContract.isDocumentUri(this, uri)) { //是 Document 类型,使用 document id 来处理 String documentId = DocumentsContract.getDocumentId(uri); String mediaDocumentAuthority = "com.android.providers.media.documents"; String downloadDocumentAuthority = "com.android.providers.downloads.documents"; if (mediaDocumentAuthority.equals(uri.getAuthority())) { //media 格式 String id = documentId.split(":")[1];//数字格式的 ID String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if (downloadDocumentAuthority.equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); imagePath = getImagePath(contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { //content 类型 uri imagePath = getImagePath(uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { //file 类型,则直接获取路径 imagePath = uri.getPath(); } display(imagePath);}/** * 显示 * @param path */private void display(String path) { if(path==null){ Toast.makeText(this, "无法获取图片", Toast.LENGTH_SHORT).show(); }else{ imageView.setImageBitmap(BitmapFactory.decodeFile(path)); }}/** * 获取图片路径 * * @param uri * @param selection * @return */private String getImagePath(Uri uri, String selection) { String path = null; Cursor cursor = getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path;}
- 这里请求了 WRITE_EXTERNAL_STORAGE 权限,以为相册中的照片一般是存放在 SD 卡上的,所以从 SD 卡中读取照片需要申请权限。WRITE_EXTERNAL_STORAGE 表示读写 SD 卡的能力权限。
- 为了兼容新老版本的手机(以 Android 4.4 为分水岭),因为 Android 4.4+ 的版本返回的 Uri 需要解析才可以使用。
点击【打开相册】按钮,会弹出读取 SD 卡的权限申请:
选取照片后的效果:
转载地址:https://deniro.blog.csdn.net/article/details/80462448 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月28日 08时38分57秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
在 Android Studio 2.2 中愉快地使用 C/C++
2021-07-03
C++和JNI的数据转换
2021-07-03
JNI 传递结构体参数
2021-07-03
JNI中枚举类型作为参数
2021-07-03
qlineedit tab焦点处无法输入问题
2021-07-03
android精确绘制文字位置的方法
2021-07-03
Android中UI线程与后台线程交互设计的5种方法
2021-07-03
[Android]调用字符串资源的几种方法
2021-07-03
Android更新UI的两种方法——handler与runOnUiThread()
2021-07-03
Java中new Thread的弊端及Java四种线程池的使用
2021-07-03
android线程与UI消息传递
2021-07-03
java枚举定义
2021-07-03
[翻译][Java]ExecutorService的正确关闭方法
2021-07-03
QT父子窗口事件传递与事件过滤器
2021-07-03
Qt到Cortex-A8的移植
2021-07-03
Qt4.7.3交叉编译移植
2021-07-03
luvcview摄像头程序到Cortex A8的安装移植
2021-07-03
Android开发书籍书籍介绍:
2021-07-03
QT 的信号与槽机制介绍
2021-07-03
Qt信号槽机制与Q_OBJECT
2021-07-03