一、使用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)
}
}
}
}
}