创建一个项目,用户可以选择拍照或者是从文件中选择图片,然后显示在ImageView中。
首先修改activity_main.xml中代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/takePhotoBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Photo" />
<Button
android:id="@+id/fromAlbumBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="From Album" />
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
一、调用摄像头拍照
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()
// 用户版本是否大于Android 7.0
// 从7.0开始,直接使用本地真实路径Uri是不安全的。
imageUri = if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(this,
"com.example.test.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) {
// 将拍摄的照片显示出来
val bitmap = BitmapFactory.decodeStream(contentResolver.
openInputStream(imageUri))
imageView.setImageBitmap(rotateIfRequired(bitmap))
}
}
}
}
// 拍照可能在一些手机上发生照片旋转的情况
// 因为手机认为摄像头拍摄时手机就应该是横屏的
// 因此回到竖屏就会发生90度旋转,这里就判断图片方向
// 判断图片是否需要旋转。
private fun rotateIfRequired(bitmap: Bitmap): Bitmap {
val exif = ExifInterface(outputImage.path)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL)
return when (orientation) {
ExifInterface.ORIENTATAION_ROTATE_90 ->
rotateBitmap(bitmap, 90)
ExifInterface.ORIENTATAION_ROTATE_180 ->
rotateBitmap(bitmap, 180)
ExifInterface.ORIENTATAION_ROTATE_270 ->
rotateBitmap(bitmap, 270)
}
}
private fun rotateBitmap(bitmap: Bitmap, degree: Int): Bitmap {
val matrix = Matrix()
matrix.postRotate(degree.toFloat())
val rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
bitmap.width, bitmap.height, matrix, true)
// 将不再需要的Bitmap对象回收
bitmap.recycle()
return rotatedBitmap
}
}
FileProvider是一种特殊的ContentProvider,它使用了和ContentProvider类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。
不过现在还没结束,刚才提到了ContentProvider,那么我们自然要在AndroidManifest.xml中对它进行注册才行:
<applicaion ...>
...
<provider
<!-- 固定值 -->
android:name="androidx.core.content.FileProvider"
<!-- 对应FileProvider.getUriForFile()第二个参数 -->
android:authorities="com.example.test.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
<!-- 指定Uri的共享路径 -->
android:name="android.support.FILE_PROVIDER_PATHS"
<!-- 引用资源,需要新建 -->
android:resource="@xml/file_paths" />
</provider>
</application>
新建file_paths.xml文件,在res目录下新建xml目录,并新建一个file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 指定Uri共享,name可以随便填,path表示共享的具体路径 -->
<!-- 这里的单斜线表示将整个SD卡进行共享,也可以仅共享output_image.jpg图片路径 -->
<external-path name="my_images" path="/" />
</paths>
二、从相册中选择图片
class MainActivity : AppCompatActivity() {
...
val fromAlbum = 2
override fun onCreate(sacedInstanceState: Bundle?) {
...
fromAlbumBtn.setOnClickListener {
// 打开文件选择器
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
// 指定只显示图片
intent.type = "image/*"
startActivityForResult(intent, fromAlbum)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode,resultCode,data)
when(requestCode) {
...
fromAlbum -> {
if(resultCode == Activity.RESULT_OK && data != null) {
// 调用intent的getData()方法来获取选中图片uri
data.data?.let { uri ->
// 将选择的图片显示
val bitmap = getBitmapFromUri(uri)
imageView.setImageBitmap(bitmap)
}
}
}
}
}
private fun getBitmapFromUri(uri: Uri) = contentResolver
.openFileDescriptor(uri, "r")?.use {
BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
}
...
}