【Android Kotlin 摄像头拍照/相册选取】Uri,File,Bitmap综合运用,获取当前时间编码

1. 调用摄像头拍照

  1. 创建File对象,用于存储拍照后的照片,构造方法两个参数,第一个用getExternalCacheDir()获取SD卡本应用的关联缓存目录,6.0后读写SD卡的其他目录需权限申请。
  2. 通过Uri方法将File对象转换为本地真实路径,7.0开始直接使用本地真实路径被认为不安全,会报FileUriExposedException异常,而FileProvider则是一种特殊的内容提供器, 它使用了和容提供器类似的机制来对数据进行保护, 可以选择性地将封装过的Uri共享给外部,
  3. 构建Intent意图,先声明为图像捕捉,再指定输出地址,放入我们得到的Uri真实地址中
  4. 调用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 {
    
    

                // 1. 创建File对象,用于存储拍照后的图片
                val outputImage = File(externalCacheDir, "output_image.jpg")
                try {
    
    
                    if (outputImage.exists()) {
    
    
                        outputImage.delete()
                    }
                    outputImage.createNewFile()
                } catch (e: IOException) {
    
    
                    e.printStackTrace()
                }
                
//2.
                if (Build.VERSION.SDK_INT < 24) {
    
    
                    imageUri = Uri.fromFile(outputImage)
                } else {
    
    
                    imageUri = FileProvider.getUriForFile(
                        this@MainActivity,
                        "com.ywjh.cameraalbumtest.fileprovider",//定义唯一标识,关联后面的内容提供器
                        outputImage
                    )
                }
                // 3. 启动相机程序
                val intent = Intent("android.media.action.IMAGE_CAPTURE")
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
                startActivityForResult(intent, MainActivity.TAKE_PHOTO)

       	 }
        }
	//4. 执行回调
    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()
                }
            }

        }
    }



}
  1. 访问其他程序的数据,xml清单文件注册我们的内容提供器
    1)android:exported=“false”,当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动
    2) Content Provider 的 grantUriPermissions 属性值为“true”,则该 Provider 内所有的数据都能被授权访问。 可它如果设为“false”,则只有本元素指定的数据子集才能被授权
    3)meta-data设置共享目录,若path为空,则该app的关联目录都可被共享。
    在这里插入图片描述
    在这里插入图片描述
  2. 清单文件加入权限声明(4.4版本前仍需权限获取关联目录),前面说过了,不需动态权限申请
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  1. xmlUI一个button,一个imageview
    在这里插入图片描述

2. 提供打开相册图片的方式来获取图片

  1. 先多加一个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()
            }
        }
  1. 打开相册的意图
    fun openAlbum() {
    
    
        val intent = Intent("android.intent.action.GET_CONTENT")
        intent.type = "image/*"
        startActivityForResult(intent, CHOOSE_PHOTO) // 打开相册
    }
  1. 动态权限回调
    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()
            }

        }
    }
  1. 意图回调,增加打开相册的回调,据版本号不同以不同的方式打开
     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) {
    
    
                    // 4.4及以上系统使用这个方法处理图片
                    if (data != null) {
    
    
                        handleImageOnKitKat(data)
                    }
                } else {
    
    
                    // 4.4以下系统使用这个方法处理图片
                    if (data != null) {
    
    
                        handleImageBeforeKitKat(data)
                    }
                }
            }
            else -> {
    
    
            }
        }
    }
  1. 由于两种方法都需获取图片地址和用作展示,封装两个方法
 private fun getImagePath(uri: Uri?, selection: String?): String? {
    
    
        var path: String? = null
        // 通过Uri和selection来获取真实的图片路径
        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()
        }
    }

  1. 版本调用的两种方法,4.4版本开始, 选取相册中的图片不再返回图片真实的Uri了, 而是一个封装过的Uri, 因此如果是4.4版本以上的手机就需要对这个Uri进行解析。如果返回的Uri是document 类型的话, 那就取出document id 进行处理,如果不是的话, 那就使用普通的方式处理。 另外, 如果Uri的authority是media格式的, document id 还需要再进行一次解析, 要通过字符串分割的方式取出后半部分才能得到真正的数字id。 取出的id用于构建新的Uri和条件语句, 然后把这些值作为参数传入到getImagePath() 方法当中, 就可以获取到图片的真实路径了。
//4.4后 判断封装情况
    @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)) {
    
    
            // 如果是document类型的Uri,则通过document id处理
            val docId = DocumentsContract.getDocumentId(uri)
            if ("com.android.providers.media.documents" == uri!!.authority) {
    
    
                val id = docId.split(":".toRegex()).toTypedArray()[1] // 解析出数字格式的id
                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)) {
    
    
            // 如果是content类型的Uri,则使用普通方式处理
            imagePath = getImagePath(uri, null)
        } else if ("file".equals(uri.scheme, ignoreCase = true)) {
    
    
            // 如果是file类型的Uri,直接获取图片路径即可
            imagePath = uri.path
        }
        displayImage(imagePath) // 根据图片路径显示图片
    }

    private fun handleImageBeforeKitKat(data: Intent) {
    
    
        val uri = data.data
        val imagePath = getImagePath(uri, null)
        displayImage(imagePath)
    }

  1. 点击后会仍会报Permission deny,原因是Android 10定义了访问非私有区域也要加入声明
    android:requestLegacyExternalStorage="true"
            tools:targetApi="q"

在这里插入图片描述

3. 获取系统当前时间编码储存

  1. 获取系统当前时间
  2. 进行编码(先获取时间再固定样式)若是用数据库可以存储图片的地址,然后用Glide显示出来
    在这里插入图片描述
  3. 查看结果
    在这里插入图片描述
  4. 内存确实变大了,并且访问data文件切切实实能保存,注意路径,该路径不会被相册扫描到。注意到有几个是没图像的,原因是File先生成,若取消拍照则会变成那个。
    在这里插入图片描述
    在这里插入图片描述
  5. 若是打开相册的话可以获取路径,数据库存储起来就好了
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38304672/article/details/110846682
今日推荐