1. 调用摄像头拍照
- 创建
File对象
,用于存储拍照后的照片,构造方法两个参数,第一个用getExternalCacheDir()
获取SD卡本应用的关联缓存目录
,6.0后读写SD卡的其他目录需权限申请。
- 通过
Uri方法
将File对象转换为本地真实路径,7.0开始直接使用本地真实路径被认为不安全,会报FileUriExposedException异常,而FileProvider则是一种特殊的内容提供器, 它使用了和容提供器类似的机制来对数据进行保护, 可以选择性地将封装过的Uri共享给外部,
- 构建Intent意图,先声明为图像捕捉,再指定输出地址,放入我们得到的Uri真实地址中
- 调用startActivityForResult() 来启动活动,执行回调接口,拍照成功, 就可以调用
BitmapFactory
的decodeStream() 方法将output_image.jpg这张照片解析成Bitmap 对象, 然后把它设置到ImageView中显示出来。这里用Glide也行。【Uri类型强转String类型再转回来路径就不对了】
class MainActivity : AppCompatActivity() {
companion object {
val TAKE_PHOTO = 1
val CHOOSE_PHOTO = 2
}
var imageUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
take_photo.setOnClickListener {
val outputImage = File(externalCacheDir, "output_image.jpg")
try {
if (outputImage.exists()) {
outputImage.delete()
}
outputImage.createNewFile()
} catch (e: IOException) {
e.printStackTrace()
}
if (Build.VERSION.SDK_INT < 24) {
imageUri = Uri.fromFile(outputImage)
} else {
imageUri = FileProvider.getUriForFile(
this@MainActivity,
"com.ywjh.cameraalbumtest.fileprovider",
outputImage
)
}
val intent = Intent("android.media.action.IMAGE_CAPTURE")
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, MainActivity.TAKE_PHOTO)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
TAKE_PHOTO -> if (resultCode == RESULT_OK) {
try {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri!!))
picture.setImageBitmap(bitmap)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
}
访问其他程序的数据
,xml清单文件注册我们的内容提供器
1)android:exported=“false”,当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动
2) Content Provider 的 grantUriPermissions 属性值为“true”,则该 Provider 内所有的数据都能被授权访问。 可它如果设为“false”,则只有本元素指定的数据子集才能被授权
3)meta-data设置共享目录,若path为空,则该app的关联目录都可被共享。
- 清单文件加入权限声明(4.4版本前仍需权限获取关联目录),前面说过了,不需动态权限申请
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- xmlUI一个button,一个imageview
2. 提供打开相册图片的方式来获取图片
- 先多加一个button提供点击,加入权限申请+调用方法体
choose_from_album.setOnClickListener {
if (ContextCompat.checkSelfPermission(this@MainActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) !== PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
} else {
openAlbum()
}
}
- 打开相册的意图
fun openAlbum() {
val intent = Intent("android.intent.action.GET_CONTENT")
intent.type = "image/*"
startActivityForResult(intent, CHOOSE_PHOTO)
}
- 动态权限回调
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) {
1 -> if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAlbum()
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show()
}
}
}
- 意图回调,增加打开相册的回调,据版本号不同以不同的方式打开
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
TAKE_PHOTO -> if (resultCode == Activity.RESULT_OK) {
try {
val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri!!))
picture.setImageBitmap(bitmap)
} catch (e: Exception) {
e.printStackTrace()
}
}
CHOOSE_PHOTO -> if (resultCode == Activity.RESULT_OK) {
if (Build.VERSION.SDK_INT >= 19) {
if (data != null) {
handleImageOnKitKat(data)
}
} else {
if (data != null) {
handleImageBeforeKitKat(data)
}
}
}
else -> {
}
}
}
- 由于两种方法都需获取图片地址和用作展示,封装两个方法
private fun getImagePath(uri: Uri?, selection: String?): String? {
var path: String? = null
val cursor = contentResolver.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
}
private fun displayImage(imagePath: String?) {
if (imagePath != null) {
val bitmap = BitmapFactory.decodeFile(imagePath)
picture.setImageBitmap(bitmap)
} else {
Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show()
}
}
- 版本调用的两种方法,4.4版本开始, 选取相册中的图片不再返回图片真实的Uri了, 而是一个封装过的Uri, 因此如果是4.4版本以上的手机就需要对这个Uri进行解析。如果返回的Uri是document 类型的话, 那就取出document id 进行处理,如果不是的话, 那就使用普通的方式处理。 另外, 如果Uri的authority是media格式的, document id 还需要再进行一次解析, 要通过字符串分割的方式取出后半部分才能得到真正的数字id。 取出的id用于构建新的Uri和条件语句, 然后把这些值作为参数传入到getImagePath() 方法当中, 就可以获取到图片的真实路径了。
@TargetApi(19)
private fun handleImageOnKitKat(data: Intent) {
var imagePath: String? = null
val uri = data.data
Log.d("TAG", "handleImageOnKitKat: uri is $uri")
if (DocumentsContract.isDocumentUri(this, uri)) {
val docId = DocumentsContract.getDocumentId(uri)
if ("com.android.providers.media.documents" == uri!!.authority) {
val id = docId.split(":".toRegex()).toTypedArray()[1]
val selection = MediaStore.Images.Media._ID + "=" + id
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection)
} else if ("com.android.providers.downloads.documents" == uri.authority) {
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(docId))
imagePath = getImagePath(contentUri, null)
}
} else if ("content".equals(uri!!.scheme, ignoreCase = true)) {
imagePath = getImagePath(uri, null)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
imagePath = uri.path
}
displayImage(imagePath)
}
private fun handleImageBeforeKitKat(data: Intent) {
val uri = data.data
val imagePath = getImagePath(uri, null)
displayImage(imagePath)
}
- 点击后会仍会报Permission deny,原因是Android 10定义了访问非私有区域也要加入声明
android:requestLegacyExternalStorage="true"
tools:targetApi="q"
3. 获取系统当前时间编码储存
- 获取系统当前时间
- 进行编码(先获取时间再固定样式)若是用数据库可以存储图片的地址,然后用Glide显示出来
- 查看结果
- 内存确实变大了,并且访问data文件切切实实能保存,注意路径,该路径不会被相册扫描到。注意到有几个是没图像的,原因是File先生成,若取消拍照则会变成那个。
- 若是打开相册的话可以获取路径,数据库存储起来就好了