安卓开发中从系统相册打开图片并保存的流程

 

一、从系统相册加载图片

从系统相册加载图片的过程如下:(1~6的顺序就是执行顺序)

1、点击事件的触发

先给相应的按钮或者控件绑定相关的点击事件

  submitImageButton.setOnClickListener{
            openSysAlbum()
        }

 

2、openSysAlbum 函数

  1. 定义一个意图 Intent.ACTION_PICK 用于选择图片。
  2. 设置意图的类型为 "image/*",筛选图片文件。
  3. 指定意图的数据 URI 为 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,指向外部存储的图片。
  4. 使用 startActivityForResult 启动相册选择,传入意图和请求码 ALBUM_RESULT_CODE。
    // openSysAlbum 函数打开系统相册供用户选择图片
    @SuppressLint("IntentReset")
    private fun openSysAlbum() {
        val albumIntent = Intent(Intent.ACTION_PICK).apply {
            // 设置 intent 的 type 为 "image/*",这表明我们只对图片文件感兴趣
            type = "image/*"
            // 设置 intent 的 data 为 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,这指向设备上的外部图片存储
            data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        }
        // // 使用 startActivityForResult 方法启动相册选择,
        // 并通过传入的 Intent 和请求码 ALBUM_RESULT_CODE 来处理返回结果
        startActivityForResult(albumIntent, ALBUM_RESULT_CODE)
    }

 

3、onActivityResult回调

  1. 检查请求码、结果码和返回的 Intent 是否有效。
  2. 如果有效(用户选择了图片),调用 handleImageOnKitKat 函数处理返回的图片 Intent。
   // onActivityResult 回调处理从其他 Activity 返回的结果
    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        //设置了一个条件语句来检查请求码是否与预期的相册请求码 ALBUM_RESULT_CODE 匹配,结果码是否表示操作成功,以及返回的 Intent 数据是否不为空。
        if (requestCode == ALBUM_RESULT_CODE && resultCode == Activity.RESULT_OK && data != null) {
            //调用 handleImageOnKitKat 函数来处理返回的图片 Intent。
            handleImageOnKitKat(data)
        }
    }

 

4、handleImageOnKitKat 函数

从返回的 Intent 中获取图片的 URI。
判断 URI 类型:

  1. 如果是 Document 类型,使用 DocumentsContract 解析出文档 ID,并根据不同的 authority 获取实际图片路径。
  2. 如果是 content 类型,直接查询 MediaStore 获取图片路径。
  3. 如果是 file 类型,使用 URI 的路径作为文件路径。

调用 getImagePath 函数获取图片的实际存储路径。
如果获取到图片路径,调用 displayImage 函数显示图片。

    // handleImageOnKitKat 函数处理从系统相册返回的图片 URI
    private fun handleImageOnKitKat(data: Intent?) {
        var imagePath: String? = null
        // 从传递给该函数的 Intent 中获取数据(即选中图片的 URI)
        val uri = data?.data
        // 检查 uri 是否不为空,并且是 Document 类型的 URI
        if (uri != null && DocumentsContract.isDocumentUri(this, uri)) {
            // 获取 Document URI 的文档 ID
            val docId = DocumentsContract.getDocumentId(uri)
            // 获取 URI 的 authority
            val authority = uri.authority
            when (authority) {
                "com.android.providers.media.documents" -> {
                    // 解析文档 ID 来获取图片在 MediaStore 中的 ID
                    val id = docId.split(":")[1]
                    // 构建查询 MediaStore 的条件
                    val selection = "${MediaStore.Images.Media._ID} = $id"
                    // 调用 getImagePath 函数来获取图片的实际路径
                    imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection)
                }
                "com.android.providers.downloads.documents" -> {
                    // 解析文档 ID 并获取下载内容的 URI
                    val contentUri = ContentUris.withAppendedId(
                        Uri.parse("content: //downloads/public_downloads"),
                        //这行代码接收一个 String 类型的 docId,尝试将其解析为一个长整型(long)的数值,并返回该数值对应的 Long 对象。
                        //用于将字符串转换为 Long 类型的对象
                        java.lang.Long.valueOf(docId)
                    )
                    // 调用 getImagePath 函数来获取图片的实际路径
                    imagePath = getImagePath(contentUri, null)
                }
            }
        } else {
            // 如果 uri 不是 Document 类型,则根据 scheme 来获取图片路径
            if ("content" == uri?.scheme) {
                imagePath = getImagePath(uri, null)
                // 对于 file 类型的 URI,直接获取路径
            } else if ("file" == uri?.scheme)
            {
                imagePath = uri.path
            }
        }
        // 如果 imagePath 不为空,则调用 displayImage 函数来显示图片
        imagePath?.let { displayImage(it) }
    }

5、getImagePath函数

使用 contentResolver.query 对指定 URI 进行查询,获取图片路径。
从查询结果中提取图片路径。

    @SuppressLint("Range")
    private fun getImagePath(uri: Uri, selection: String?): String? {
        // 使用 contentResolver 执行查询操作,查询与给定 URI 相关的数据
        val cursor = contentResolver.query(uri, null, selection, null, null)
        var path: String? = null
        // 使用 cursor?.use 来确保游标在操作完成后会被关闭,即使发生异常也是如此
        cursor?.use {
            // 将游标移动到第一行数据
            if (it.moveToFirst()) {
                // 获取图片路径的列索引,使用 MediaStore.Images.Media.DATA 作为列名
                // 从游标中取出第一行的图片路径数据,并将其赋值给 path 变量
                path = it.getString(it.getColumnIndex(MediaStore.Images.Media.DATA))
            }
        }
        return path
    }

 

6、displayImage函数

使用 BitmapFactory.decodeFile 将图片路径解码成 Bitmap 对象。
将 Bitmap 设置到 ImageView 上显示图片。

    private fun displayImage(imagePath: String) {
        // 使用 BitmapFactory.decodeFile 将文件路径 imagePath 对应的图片文件解码成 Bitmap 对象
        // 并将这个 Bitmap 对象赋值给已声明的 lateinit 变量 bitmap
        bitmap = BitmapFactory.decodeFile(imagePath)
        // 并调用 ImageView 的 setBitmap 方法,将 bitmap 设置到 ImageView 上,从而显示图片
        imageView.setImageBitmap(bitmap)
    }

 

 

二、保存图片到系统相册

保存图片到系统相册的过程如下:(1~6的顺序就是执行顺序)

1、点击事件的触发

先给相应的按钮或者控件绑定相关的点击事件

  saveImageButton.setOnClickListener{
            checkAndRequestStoragePermission()
        }

2、checkAndRequestPermission函数

在需要保存图片到相册之前,此函数被调用以检查存储权限。如果权限未授予,将请求用户授权。

private fun checkAndRequestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // 如果是Android 11或更高版本,检查是否获得了管理外部存储的权限
            if (!Environment.isExternalStorageManager()) {
                // 启动一个活动,让用户授予管理所有文件的权限
                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                intent.addCategory("android.intent.category.DEFAULT")
                intent.data = Uri.parse(String.format("package:%s", packageName))
                startActivityForResult(intent, REQUEST_CODE_ASK_FOR_PERMISSIONS)
            } else {
                saveImageToGallery()
            }
        } else {
            // 对于较早的版本,请求WRITE_EXTERNAL_STORAGE权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
                requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            } else {
                saveImageToGallery()
            }
        }
    }

3、onRequestPermissionsResult函数

用户响应权限请求后,系统将调用此函数以处理权限授予的结果

   //onRequestPermissionsResult 回调处理权限请求的结果
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // 处理权限请求结果
    }

4、saveImageToGallery函数

如果用户授予了存储权限,此函数将被调用以保存图片到相册

    private fun saveImageToGallery() {
        val imageView: MatrixImageView = findViewById(R.id.miv_sample)
        val bitmap = getBitmapFromView(imageView)
        saveBitmapToExternalStorage(bitmap)
    }

 

5、getBitmapFromView函数

在saveImageToGallery函数中会调用这个函数,用于从View获取Bitmap。

(这里因为需求需要将获取的图片放大保存)

   private fun getBitmapFromView(view: View): Bitmap {
        // 创建一个和View尺寸相同的Bitmap
        val originalBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(originalBitmap)

        // 绘制背景和View内容
        val bgDrawable = view.background
        if (bgDrawable != null) {
            bgDrawable.draw(canvas)
        } else {
            canvas.drawColor(Color.WHITE)
        }
        view.draw(canvas)

        // 创建一个新的Matrix对象,并设置为缩放模式
        val matrix = Matrix()
        matrix.postScale(2f, 2f, view.width / 2f, view.height / 2f) // 放大2倍,以中心点为缩放中心

        // 创建一个新的Bitmap用于存放缩放后的图像
        val scaledBitmap = Bitmap.createBitmap(
            originalBitmap, 0, 0,
            originalBitmap.width, originalBitmap.height,
            matrix, true
        )

        // 释放原始Bitmap的内存
        originalBitmap.recycle()

        val mHeight = dp2px(this,300f)
        val mWidth = dp2px(this,200f)
        Log.e("TAG","mHeight->$mHeight mWidth->$mWidth")

        return scaledBitmap
    }

 

6、saveBitmapToExternalStorage函数

此函数在saveImageToGallery中被调用,用于实际保存Bitmap到外部存储

根据 Android 版本使用不同的方法保存 Bitmap 到外部存储:

  1. 对于 Android 10 及以上版本,使用 MediaStore API。
  2. 对于 Android 9 及以下版本,使用文件系统 API 直接写入。
 private fun saveBitmapToExternalStorage(bitmap: Bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // 对于Android 10及更高版本,使用MediaStore API
            val values = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, "New Image.jpg")
                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
            }
            val resolver = contentResolver
            val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
            uri?.let {
                val outputStream = resolver.openOutputStream(it)
                outputStream?.use {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
                    runOnUiThread{
                        Toast.makeText(this,"图片已保存到相册!",Toast.LENGTH_SHORT).show()
                    }
                }
            }
        } else {
            // 对于Android 9及更低版本,使用传统方式
            val bytes = ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
            val filePath = "${Environment.getExternalStorageDirectory()}/Pictures/New Image.jpg"
            File(filePath).outputStream().buffered().use {
                it.write(bytes.toByteArray())
                runOnUiThread{
                    Toast.makeText(this,"图片已保存到相册!",Toast.LENGTH_SHORT).show()
                }

            }
        }
    }

三、补充

1、Intent中的预定义操作

  1. Intent.ACTION_PICK:表示选择器操作,在这种情况下,Intent 是用来打开系统相册,让用户可以选择一张图片。
  2. Intent.ACTION_VIEW: 用于打开一个活动,显示数据。通常与 data URI一起使用,指向要显示的内容。
  3. Intent.ACTION_EDIT: 用于打开一个活动,编辑数据。通常与 data URI一起使用,指向要编辑的内容。
  4. Intent.ACTION_INSERT: 用于请求插入数据。例如,插入一个新的联系人或日历事件。
  5. Intent.ACTION_DELETE: 用于请求删除数据。
  6. Intent.ACTION_PICK: 用于从一组数据中选择一个条目,通常用于选择图片或视频。
  7. Intent.ACTION_GET_CONTENT: 用于打开一个内容选择器,允许用户选择或返回一个内容URI。
  8. Intent.ACTION_DIAL: 用于打开拨号器界面,拨打电话号码。
  9. Intent.ACTION_CALL: 直接拨打电话号码,不需要用户确认。
  10. Intent.ACTION_SEND: 用于发送数据,如文本、图片或文件。
  11. Intent.ACTION_SENDTO: 用于发送数据到一个特定的URI,如发送邮件或短信。
  12. Intent.ACTION_SEARCH: 用于打开搜索界面。
  13. Intent.ACTION_WEB_SEARCH: 用于打开网页搜索界面。
  14. Intent.ACTION_MAIN: 用于启动应用的主界面。
  15. Intent.ACTION_ATTACH_DATA: 用于请求附加数据,如文件。
  16. Intent.ACTION_APPLICATION_PREFERENCES: 用于打开应用的设置界面。
  17. Intent.ACTION_SYNC: 用于触发数据同步。
  18. Intent.ACTION_TIME_TICK: 每小时触发一次,用于更新时间显示。
  19. Intent.ACTION_DATE_CHANGED: 每天触发一次,用于更新日期显示。
  20. Intent.ACTION_CLOSE_SYSTEM_DIALOGS: 用于关闭系统对话框。
  21. Intent.ACTION_WALLPAPER_CHANGED: 壁纸更改时触发。

2、onActivityResult回调函数

onActivityResult 是 Android 中的一个生命周期回调方法,属于 Activity 组件。它在 Activity 启动另一个 Activity 并请求代码 (requestCode) 后被调用,用于接收返回结果。

源码:

package com.example.myadjustcontrast

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
import android.graphics.Matrix
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.Settings
import android.util.Log
import android.util.TypedValue
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.SeekBar
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.example.myadjustcontrast.CustomView.MatrixImageView.MatrixImageView
import java.io.ByteArrayOutputStream
import java.io.File

class MainActivity : AppCompatActivity() {
    companion object {
        const val REQUEST_CODE_ASK_FOR_PERMISSIONS = 123 // 选择一个未被使用的整数作为请求码
        const val ALBUM_RESULT_CODE = 0x999
    }

    private lateinit var imageView: MatrixImageView
//    private lateinit var imageView: ImageView
    private lateinit var contrastSeekBar: SeekBar
    private lateinit var brightnessSeekBar: SeekBar
    private lateinit var saturabilitySeekBar: SeekBar
    private lateinit var saveImageButton :Button
    private lateinit var submitImageButton:Button
    private lateinit var editText:EditText
    private lateinit var getText:Button

    private lateinit var bitmap:Bitmap


    @SuppressLint("MissingInflatedId")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        imageView = findViewById(R.id.miv_sample)
        contrastSeekBar = findViewById(R.id.contrastSeekBar)
        brightnessSeekBar = findViewById(R.id.brightnessSeekBar)
        saturabilitySeekBar = findViewById(R.id.saturabilitySeekBar)
        saveImageButton = findViewById(R.id.saveImageButton)
        submitImageButton = findViewById(R.id.submitImageButton)
//        getText = findViewById(R.id.getText)
//        editText = findViewById(R.id.editText)

        contrastSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                applyAdjustments(progress, brightnessSeekBar.progress, saturabilitySeekBar.progress)
            }
            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
        })

        brightnessSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                applyAdjustments(contrastSeekBar.progress, progress, saturabilitySeekBar.progress)
            }
            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
        })

        saturabilitySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                applyAdjustments(contrastSeekBar.progress, brightnessSeekBar.progress, progress)
            }
            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
        })

        saveImageButton.setOnClickListener{
            checkAndRequestPermission()
        }

        submitImageButton.setOnClickListener{
            openSysAlbum()
        }
//        getText.setOnClickListener{
//            val text = editText.text.toString()
//
//        }
    }

    private fun applyAdjustments(contrastValue: Int, brightnessValue: Int, saturabilityValue: Int) {
        val contrastMatrix = adjustContrast(contrastValue)
        val brightnessMatrix = adjustBrightness(brightnessValue)
        val saturationMatrix = adjustSaturability(saturabilityValue)

        val combinedMatrix = ColorMatrix()
        combinedMatrix.postConcat(contrastMatrix)
        combinedMatrix.postConcat(brightnessMatrix)
        combinedMatrix.postConcat(saturationMatrix)

        imageView.colorFilter = ColorMatrixColorFilter(combinedMatrix)
    }

//---------------------------------------------------------------------------------------------------


    //onRequestPermissionsResult 回调处理权限请求的结果
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // 处理权限请求结果
    }

    // 在你的Activity或Fragment中注册ActivityResultLauncher
    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            saveImageToGallery()
        }
    }

    private fun checkAndRequestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // 如果是Android 11或更高版本,检查是否获得了管理外部存储的权限
            if (!Environment.isExternalStorageManager()) {
                // 启动一个活动,让用户授予管理所有文件的权限
                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
                intent.addCategory("android.intent.category.DEFAULT")
                intent.data = Uri.parse(String.format("package:%s", packageName))
                startActivityForResult(intent, REQUEST_CODE_ASK_FOR_PERMISSIONS)
            } else {
                saveImageToGallery()
            }
        } else {
            // 对于较早的版本,请求WRITE_EXTERNAL_STORAGE权限
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
                requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            } else {
                saveImageToGallery()
            }
        }
    }

    private fun saveImageToGallery() {
        val imageView: MatrixImageView = findViewById(R.id.miv_sample)
        val bitmap = getBitmapFromView(imageView)
        saveBitmapToExternalStorage(bitmap)
    }

    private fun getBitmapFromView(view: View): Bitmap {
        // 创建一个和View尺寸相同的Bitmap
        val originalBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(originalBitmap)

        // 绘制背景和View内容
        val bgDrawable = view.background
        if (bgDrawable != null) {
            bgDrawable.draw(canvas)
        } else {
            canvas.drawColor(Color.WHITE)
        }
        view.draw(canvas)

        // 创建一个新的Matrix对象,并设置为缩放模式
        val matrix = Matrix()
        matrix.postScale(2f, 2f, view.width / 2f, view.height / 2f) // 放大2倍,以中心点为缩放中心

        // 创建一个新的Bitmap用于存放缩放后的图像
        val scaledBitmap = Bitmap.createBitmap(
            originalBitmap, 0, 0,
            originalBitmap.width, originalBitmap.height,
            matrix, true
        )

        // 释放原始Bitmap的内存
        originalBitmap.recycle()

        val mHeight = dp2px(this,300f)
        val mWidth = dp2px(this,200f)
        Log.e("TAG","mHeight->$mHeight mWidth->$mWidth")

        return scaledBitmap
    }


    private fun saveBitmapToExternalStorage(bitmap: Bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // 对于Android 10及更高版本,使用MediaStore API
            val values = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, "New Image.jpg")
                put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
            }
            val resolver = contentResolver
            val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
            uri?.let {
                val outputStream = resolver.openOutputStream(it)
                outputStream?.use {
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
                    runOnUiThread{
                        Toast.makeText(this,"图片已保存到相册!",Toast.LENGTH_SHORT).show()
                    }
                }
            }
        } else {
            // 对于Android 9及更低版本,使用传统方式
            val bytes = ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
            val filePath = "${Environment.getExternalStorageDirectory()}/Pictures/New Image.jpg"
            File(filePath).outputStream().buffered().use {
                it.write(bytes.toByteArray())
                runOnUiThread{
                    Toast.makeText(this,"图片已保存到相册!",Toast.LENGTH_SHORT).show()
                }

            }
        }
    }

//    --------------------------------------------------------------------------------------------

    // onActivityResult 回调处理从其他 Activity 返回的结果
    @Deprecated("Deprecated in Java")
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        //设置了一个条件语句来检查请求码是否与预期的相册请求码 ALBUM_RESULT_CODE 匹配,结果码是否表示操作成功,以及返回的 Intent 数据是否不为空。
        if (requestCode == ALBUM_RESULT_CODE && resultCode == Activity.RESULT_OK && data != null) {
            //调用 handleImageOnKitKat 函数来处理返回的图片 Intent。
            handleImageOnKitKat(data)
        }
    }

    // openSysAlbum 函数打开系统相册供用户选择图片
    @SuppressLint("IntentReset")
    private fun openSysAlbum() {
        val albumIntent = Intent(Intent.ACTION_PICK).apply {
            // 设置 intent 的 type 为 "image/*",这表明我们只对图片文件感兴趣
            type = "image/*"
            // 设置 intent 的 data 为 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,这指向设备上的外部图片存储
            data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        }
        // // 使用 startActivityForResult 方法启动相册选择,
        // 并通过传入的 Intent 和请求码 ALBUM_RESULT_CODE 来处理返回结果
        startActivityForResult(albumIntent, ALBUM_RESULT_CODE)
    }

    /**
     * 这个函数主要处理两种类型的 URI:
     *
     * Document 类型的 URI:
     * 这是 Android 4.4(KitKat)引入的一种新的 URI 形式,
     * 用于访问存储在不同存储提供商中的文件。
     * 函数通过 DocumentsContract 来解析这些 URI,
     * 并根据不同的 authority 来获取实际的图片路径。
     *
     * Content 和 File 类型的 URI:
     * 对于 content 类型的 URI,函数直接查询 MediaStore 来获取图片路径;
     * 对于 file 类型的 URI,函数直接使用 URI 的 path 作为文件路径。
     */
    // handleImageOnKitKat 函数处理从系统相册返回的图片 URI
    private fun handleImageOnKitKat(data: Intent?) {
        var imagePath: String? = null
        // 从传递给该函数的 Intent 中获取数据(即选中图片的 URI)
        val uri = data?.data
        // 检查 uri 是否不为空,并且是 Document 类型的 URI
        if (uri != null && DocumentsContract.isDocumentUri(this, uri)) {
            // 获取 Document URI 的文档 ID
            val docId = DocumentsContract.getDocumentId(uri)
            // 获取 URI 的 authority
            val authority = uri.authority
            when (authority) {
                "com.android.providers.media.documents" -> {
                    // 解析文档 ID 来获取图片在 MediaStore 中的 ID
                    val id = docId.split(":")[1]
                    // 构建查询 MediaStore 的条件
                    val selection = "${MediaStore.Images.Media._ID} = $id"
                    // 调用 getImagePath 函数来获取图片的实际路径
                    imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection)
                }
                "com.android.providers.downloads.documents" -> {
                    // 解析文档 ID 并获取下载内容的 URI
                    val contentUri = ContentUris.withAppendedId(
                        Uri.parse("content: //downloads/public_downloads"),
                        //这行代码接收一个 String 类型的 docId,尝试将其解析为一个长整型(long)的数值,并返回该数值对应的 Long 对象。
                        //用于将字符串转换为 Long 类型的对象
                        java.lang.Long.valueOf(docId)
                    )
                    // 调用 getImagePath 函数来获取图片的实际路径
                    imagePath = getImagePath(contentUri, null)
                }
            }
        } else {
            // 如果 uri 不是 Document 类型,则根据 scheme 来获取图片路径
            if ("content" == uri?.scheme) {
                imagePath = getImagePath(uri, null)
                // 对于 file 类型的 URI,直接获取路径
            } else if ("file" == uri?.scheme)
            {
                imagePath = uri.path
            }
        }
        // 如果 imagePath 不为空,则调用 displayImage 函数来显示图片
        imagePath?.let { displayImage(it) }
    }

    @SuppressLint("Range")
    private fun getImagePath(uri: Uri, selection: String?): String? {
        // 使用 contentResolver 执行查询操作,查询与给定 URI 相关的数据
        val cursor = contentResolver.query(uri, null, selection, null, null)
        var path: String? = null
        // 使用 cursor?.use 来确保游标在操作完成后会被关闭,即使发生异常也是如此
        cursor?.use {
            // 将游标移动到第一行数据
            if (it.moveToFirst()) {
                // 获取图片路径的列索引,使用 MediaStore.Images.Media.DATA 作为列名
                // 从游标中取出第一行的图片路径数据,并将其赋值给 path 变量
                path = it.getString(it.getColumnIndex(MediaStore.Images.Media.DATA))
            }
        }
        return path
    }

    private fun displayImage(imagePath: String) {
        // 使用 BitmapFactory.decodeFile 将文件路径 imagePath 对应的图片文件解码成 Bitmap 对象
        // 并将这个 Bitmap 对象赋值给已声明的 lateinit 变量 bitmap
        bitmap = BitmapFactory.decodeFile(imagePath)
        // 并调用 ImageView 的 setBitmap 方法,将 bitmap 设置到 ImageView 上,从而显示图片
        imageView.setImageBitmap(bitmap)
//        adjustImageScale()
//        imageView.setImageBitmap(bitmap)

    }

    private fun adjustImageScale() {

        val bitmapWidth = bitmap.width
        val bitmapHeight = bitmap.height
        Log.e("TAG","bitmapWidth -> $bitmapWidth  bitmapHeight -> $bitmapHeight")

        val screenWidth = resources.displayMetrics.widthPixels
        val screenHeight = resources.displayMetrics.heightPixels
        Log.e("TAG","screenWidth -> $screenWidth  screenHeight -> $screenHeight")

        // 计算缩放比例
        val scaleWidth = screenWidth / bitmapWidth.toFloat()
        val scaleHeight = screenHeight / bitmapHeight.toFloat()
        val scale = Math.min(scaleWidth, scaleHeight)

        // 创建一个新的 Bitmap 并应用缩放
        val newBitmap = Bitmap.createScaledBitmap(bitmap, (bitmapWidth * scale).toInt(), (bitmapHeight * scale).toInt(), true)
        imageView.setImageBitmap(newBitmap)
    }

    
    private fun dp2px(context: Context, dp: Float): Float {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.resources.displayMetrics)
    }
}

  • 29
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值