Android更换用户头像开发记录

490 阅读1分钟

一、使用DialogFragment开发头像来源选择弹框

在Activity或者Fragment中监听按钮点击事件:

fun setOnBtnClickListener(listener: OnBtnClickListener): ChangePortraitDialog {    
    mClickListener = listener    
    return this
}
interface OnBtnClickListener { 
   fun onAlbumClick(v: View)    
    fun onCameraClick(v: View)
}


changePortraitDialog.setOnBtnClickListener(
    object : ChangePortraitDialog.OnBtnClickListener{
        override fun onAlbumClick(v: View) {
            openAlbum()
        }

        override fun onCameraClick(v: View) {
            openCamera()
        }
}).show(childFragmentManager,"changePortraitDialog")


二、使用startActivityForResult跳转到相册或相机获得头像 1、跳转到系统相机需要传入拍照保存路径,并申请临时读写权限:



private fun openCamera(){
    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE, null).apply {
        val imageFile = File(FileUtils.getPhotoDir(),
            "xxx_${getNowDate("yyyyMMdd_HHmmss")}.jpg")
        imageCaptureUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            FileProvider.getUriForFile(requireContext(),
                "xxx.file.provider", imageFile)
        }else {
            Uri.fromFile(imageFile)
        }
        putExtra(
            MediaStore.EXTRA_OUTPUT,
            imageCaptureUri
        )
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE)
}

Android7.0以上系统需要在manifest文件中注册provider,然后通过FileProvider取得需要传入的imgeUri:

<provider
            android:authorities="xxx.file.provider"
            android:name="androidx.core.content.FileProvider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
</provider>

新建file_paths.xml文件,因为要保存在外部存储中,所以使用external-path标签


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path name="camera_photos" path=""/>
    </paths>
</resources>

2、相册是自定义实现的,所以需要通过ContentResolver查询设备上保存有的图片,并加载到网格列表当中


fun queryImages(resolver: ContentResolver){
        uiScope.launch {
            var imageLists = ArrayList<String>()
            val cursor = resolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,
                MediaStore.Images.Media.MIME_TYPE + "=? or "
                        + MediaStore.Images.Media.MIME_TYPE + "=? or "
                        + MediaStore.Images.Media.MIME_TYPE + "=?",
                arrayOf("image/jpeg", "image/png", "image/jpg"),
                MediaStore.Images.Media.DATE_MODIFIED + " DESC")
            cursor?.run {
                use {
                    while (it.moveToNext()){
                        val  path = it.getString(it.getColumnIndex(MediaStore.Images.Media.DATA))
                        if (path.contains(".")&&(
                             path.substring(path.lastIndexOf("."), path.length) == ".jpg"
                             || path.substring(path.lastIndexOf("."), path.length) == ".png")){
                            imageLists.add(path)
                        }
                    }
                }
            }
            cursor?.close()
            imagePaths.postValue(imageLists)
        }
    }

在网格列表当中,使用fresco框架实现每张图片的加载与缓存:



    <com.facebook.drawee.view.SimpleDraweeView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        fresco:placeholderImage="@drawable/ic_bzl_logo"
        fresco:placeholderImageScaleType="fitCenter"
        fresco:failureImage="@drawable/ic_bzl_logo"
        fresco:failureImageScaleType="fitCenter"
        app:layout_constraintDimensionRatio="16:9"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tool:ignore="ContentDescription"
        tool:background="@drawable/ic_sina"/>
val request = ImageRequestBuilder.newBuilderWithSource(Uri.parse("file://${imagePaths[position]}"))
                .setResizeOptions(ResizeOptions(320, 180))
                .build()
            image.controller = Fresco.newDraweeControllerBuilder()
                .setOldController(image.controller)
                .setImageRequest(request)
                .build()

因为本地的图片有可能很大,我们要限制其尺寸并对已加载过的图片缓存到内存中,这样子相册显示和操作会流畅很多。

三、使用系统自带的页面裁剪头像

    private fun cutPhoto(uri: Uri) {
        val intent = Intent("com.android.camera.action.CROP").apply {
            setDataAndType(uri, "image/*")
            putExtra("crop", true)
            // 裁剪框的比例,1:1
            putExtra("aspectX", 1)
            putExtra("aspectY", 1)
            // 裁剪后输出图片的尺寸大小
            putExtra("outputX", 250)
            putExtra("outputY", 250)
            putExtra("scale", true)
            putExtra("outputFormat", "JPEG") // 图片格式
            putExtra("noFaceDetection", true) // 取消人脸识别
            putExtra("return-data", false)
            putExtra(MediaStore.EXTRA_OUTPUT, imageCutUri)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        }
        startActivityForResult(intent, REQUEST_CODE_CUT_IMAGE)
    }

同样的,需要传入裁剪头像的保存路径,并申请临时读写权限,不同的是Android7.0以上系统也不需要通过FileProvider取得需要传入的imgeUri,直接取即可:

val imageFile = FileUtils.getPortraitFile()
        imageCutUri = Uri.fromFile(imageFile)

四、最后在onActivityResult中取得最终的头像并展示出来

  
  override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == RESULT_OK){
            when(requestCode){
                REQUEST_CODE_IMAGE_CAPTURE ->{
                    imageCaptureUri?.let {
                        cutPhoto(it)
                    }
                }
                REQUEST_CODE_OPEN_ALBUM ->{
                    data?.data?.let {
                        cutPhoto(it)
                    }
                }
                REQUEST_CODE_CUT_IMAGE ->{
                    imageCutUri?.let {
                        clearFrescoCaches(it)
                        ivPortrait.setImageURI(it)
                        EventBus.getDefault().post(it)
                    }
                }
            }
        }
    }