[Android Kotlin camera photo/album selection] Uri, File, Bitmap integrated use, get the current time code

1. Call the camera to take pictures

  1. Created File对象, used to store the photo after taking the photo, the construction method has two parameters, the first one is to getExternalCacheDir()obtain the SD card 本应用的关联缓存目录, and after 6.0, the permission application is required to read and write other directories of the SD card.
  2. By Uri方法converting the File object to the local real path, it is considered unsafe to directly use the local real path since 7.0, and FileUriExposedException will be reported. FileProvider is a special content provider that uses a mechanism similar to that of the content provider. To protect data, you can selectively share the encapsulated Uri with the outside,
  3. To construct the Intent, first declare it as image capture, then specify the output address, and put it into the Uri real address we got
  4. Call startActivityForResult() to start the activity, execute the callback interface, and take the picture successfully, you can call BitmapFactorythe decodeStream() method to parse the output_image.jpg picture into a Bitmap object, and then set it to the ImageView for display. Glide also works here. [Uri type forced to String type and then back to the path is wrong]

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 manifest file to register our 内容提供器
    1) android:exported="false", whether the current Activity can be started by another Application component: true is allowed to be started; false is not allowed to be started
    2) Content Provider's grantUriPermissions attribute value is "true ", all data in the Provider can be authorized to access. But if it is set to "false", only the subset of data specified by this element can be authorized.
    3) Meta-data sets the shared directory. If the path is empty, all the associated directories of the app can be shared.
    Insert picture description here
    Insert picture description here
  2. The manifest file is added to the permission statement (the permission is still required to obtain the associated directory before version 4.4), as mentioned before, no dynamic permission application is required
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  1. xmlUI one button, one imageview
    Insert picture description here

2. Provide a way to open the album picture to get the picture

  1. First add a button to provide click, add permission application + call method body
    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. Intent to open album
    fun openAlbum() {
    
    
        val intent = Intent("android.intent.action.GET_CONTENT")
        intent.type = "image/*"
        startActivityForResult(intent, CHOOSE_PHOTO) // 打开相册
    }
  1. Dynamic permission callback
    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. Intention callback, add callback to open the album, open in different ways according to the version number
     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. Since both methods need to obtain the picture address and use it for display, encapsulate the two methods
 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. The two methods of version calling, starting from version 4.4, the selected picture in the album no longer returns the real Uri of the picture, but a packaged Uri, so if it is a mobile phone above version 4.4, this Uri needs to be parsed. If the returned Uri is of the document type, then take out the document id for processing, if not, then use the normal way to deal with it. In addition, if the authority of Uri is in media format, the document id needs to be parsed again, and the second half must be extracted by string splitting to get the real digital id. The retrieved id is used to construct a new Uri and conditional statement, and then these values ​​are passed as parameters to the getImagePath() method, and the real path of the image can be obtained.
//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. After clicking, it will still report Permission deny. The reason is that Android 10 defines access to non-private areas and also adds a statement
    android:requestLegacyExternalStorage="true"
            tools:targetApi="q"

Insert picture description here

3. Get the current system time code storage

  1. Get the current time of the system
  2. Encode (get the time first and then fix the style) If you use the database to store the address of the picture, then use Glide to display it
    Insert picture description here
  3. View Results
    Insert picture description here
  4. The memory has indeed become larger, and access to the data file can be saved tangibly, pay attention to the path, the path will not be scanned by the album. Note that some of them have no images. The reason is that the File is generated first. If you cancel the photo, it will become that.
    Insert picture description here
    Insert picture description here
  5. If you open the album, you can get the path, and the database is just fine.
    Insert picture description here

Guess you like

Origin blog.csdn.net/qq_38304672/article/details/110846682