安卓调用手机摄像头和相册
Posted z啵唧啵唧
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓调用手机摄像头和相册相关的知识,希望对你有一定的参考价值。
文章目录
调用摄像头和相册
调用摄像头进行拍照
- 新建一个CameraAlbumTest项目,修改activity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/takePhotoBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="take photo" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
- 可以看到在布局文件当中,有一个Button和一个ImageView.Button是用于打开摄像头进行拍照的,而ImageView则是用于将拍到图片显示出来.
- 在MainActivity中编写调用摄像头的代码逻辑
package com.zb.cameraalbumtest
import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.core.content.FileProvider
import kotlinx.android.synthetic.main.activity_main.*
import java.io.File
/**
* @Description:
* @author zb~
* @date 2022/12/21
*/
class MainActivity : AppCompatActivity()
val takePhoto = 1
lateinit var imageUri: Uri
lateinit var outputImage: File
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
takePhotoBtn.setOnClickListener
//创建File对象,用于存储拍照后的图片
outputImage = File(externalCacheDir, "output_image.jpg")
if (outputImage.exists())
outputImage.delete()
outputImage.createNewFile()
imageUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
FileProvider.getUriForFile(
this, "com.zb.cameraalbumtest.fileprovider",
outputImage
)
else
Uri.fromFile(outputImage)
//启动相机程序
val intent = Intent("android.media.action.IMAGE_CAPTURE")
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, takePhoto)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
super.onActivityResult(requestCode, resultCode, data)
when (requestCode)
takePhoto ->
if (resultCode == Activity.RESULT_OK)
//将拍摄的照片显示出来
Log.d("MainActivity", "进入")
val bitmap = BitmapFactory.decodeStream(
contentResolver.openInputStream(imageUri)
)
imageView.setImageBitmap(rotateIfRequire(bitmap))
private fun rotateIfRequire(bitmap: Bitmap): Bitmap
val exif = ExifInterface(outputImage.path)
val orientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
return when (orientation)
ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitmap(bitmap, 90)
ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitmap(bitmap, 180)
ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitmap(bitmap, 270)
else -> bitmap
private fun rotateBitmap(bitmap: Bitmap, degree: Int): Bitmap
val matrix = Matrix()
matrix.postRotate(degree.toFloat())
val rotateBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height,
matrix, true
)
bitmap.recycle() //将不需要的Bitmap对象进行回收
return rotateBitmap
- 首先创建了一个File对象,用于存储摄像头拍下的图片,这里将图片命名为output_image.jpg
- 并且存放在手机SD卡的应用关联缓存目录下,所谓关联缓存目录就是SD卡中专门用于存放当前缓存数据的位置,调用getExternalCacheDir()方法可以得到这个目录.其具体的路径是:/sdcard/Android/data//cache
- 至于为什么要使用这个关联缓存来存放图片是因为,从Android6.0系统开始,读写SD卡别列为了危险权限,如果将如片放在SD卡任何目录,都要进行运行时权限处理才行,而使用应用关联目录则可以跳过这一步
- 另外,从Android10.0系统开始,公有的SD目录已经不再允许被应用程序直接访问了,而是要使用作用域存储才可以.
- 接着会进行一个判断,如果运行设备的系统版本低于Android7.0就调用Uri的fromFile()方法将File对象转换成为Uri对象.
- 这个Uri对象标识着output_image.jpg这张图片的本地真是路径
- 否者就调用FileProvider的getUriForFile()方法将File对象转换成为一个封装过的Uri对象.
- getUriForFile()方法接受三个参数:第一个参数要求传入Context对象,第二个参数可以是任意唯一的字符串,第三个参数则是我们刚刚创建的File对象
- 之所以要进行这样一层转换是因为从Android7.0开始,直接使用本地真实的Uri被认为是不安全的,会抛出一个FileUriExposedException异常,而FileProvider则是一种特殊的ContentProvider,它使用了和ContentProvider类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高应用的安全性.
- 接下来构建了一个Intent对象,并将这个Intent得action指定为android.media.action.IMAGE_GAPTURE,在调用Intent.putExtra()方法来指定图片输出得地址,这里填入刚刚得到得Uri对象系统会找出能够响应这个Intent的Activity去启动.
- 这样相机程序就会被打开,拍下的照片会被存放到output_image.jpg当中
- 由于刚才使用的是startActivityForResult()启动Activity的,因此拍照完之后会有结果返回到onActivityResult()方法当中,如果发现拍照成功,就可以调用BitmapFactory的decodeStream()方法将output_image.jpg这张照片解析成为Bitmap对象,然后把它设置到ImageView中显示出来.
- 需要注意的是,调用照相机程序去拍照可能会在一些手机上发生照片旋转的情况,这是因为这些手机认为打开摄像头进行拍摄是手机就应该是横屏的,因此回到竖屏的情况下就会发生90°旋转
- 为此在代码当中加上了判断图片方向的代码,如果发现图片需要进行翻转,那么就先将图片旋转相应的角度,然后再显示到界面上.
- 还没有完,因为FileProvider是一种特殊的ContentProvider,所以需要在AndroidManifest.xml当中对他进行注册才行.
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.zb.cameraalbumtest.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDES_PATHS"
android:resource="@xml/file_paths" />
</provider>
- android:name属性的值是固定
- android:authorities属性的值,必须和刚才FileProvider.getUriFile()方法第二参数值是一致的
- 另外还需要在标签当中使用标签来指定Uri的共享路径,并引用了一个@xml/file_paths资源
- 在res目录下面创建这个资源
<?xml version="1.0" encoding="utf-8" ?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/" />
</paths>
-
external-path就是用来指定Uri共享路径的,name属性的值可以随便填写
-
path属性的值就表示共享的具体路径,这里使用了一个单斜线便是将整个SD卡进行共享,当然也可以仅仅共享存放output_image.jpg这张图片的路径.
-
这样代码就变写完了,运行项目,点击take photo就可以进行拍照了
从相册当中选取图片
- 编辑activity_main.xml文件,在布局当中添加一个按钮用于,从相册当中选择图片
<Button
android:id="@+id/fromAlbumBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="from album"/>
- 添加一个全局变量
val takePhoto = 1
- 编写该按钮的点击事件
fromAlbumBtn.setOnClickListener
//打开文件选择器
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//指定只显示图片
intent.type = "image/*"
startActivityForResult(intent, fromAlbum)
- 在fromAlbum按钮的点击事件当中,先构建了一个Intent对象,并将它的action指定为Intent.ACTION_OPEN_DOCUMENT,表示打开系统的文件选择器,接着给这个Intent对象设置一些条件过滤,只允许可打开的图片显示出来,接着调用startActivityForResult()方法,我们给startActivityForResult()方法的第二个参数传入值为我们新定义的全局变量,这样当选择完图片之后会回到onActivityResult()方法是,就会进入fromAlbum的条件下处理图片
- 编写onActivityResult()方法当中的逻辑
fromAlbum ->
if (resultCode == Activity.RESULT_OK && data != null)
data.data?.let uri ->
//选择将图片进行显示
val bitmap = getBitmapFromUri(uri)
imageView.setImageBitmap(bitmap)
- 调用了返回Intent的getData()方法来获取Uri,然后调用getBitmapFromUri()方法将Uri转换成为Bitmap对象,最终将图片显示到界面上
- 编写getBitmapFromUri()方法
private fun getBitmapFromUri(uri: Uri) = contentResolver.openFileDescriptor(uri, "r")?.use
BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
以上是关于安卓调用手机摄像头和相册的主要内容,如果未能解决你的问题,请参考以下文章