云豹相亲相册选择图片功能实现
云豹相亲交友是一款聊天交友类app,主打相亲直播和聊天交友,用户可以结识各种优质的异性朋友,进行直播视频等互动,也可以类似微信一样进行文字语音聊天。其中有很多选择图片的场景,比如设置个人头像,聊天发送图片,发布动态等,都用到了图片选择功能。下面介绍一下云豹相亲交友中图片选择功能的具体实现方式。



如上图所示,在选择图片的时候,会搜索相册中的全部图片,以网格列表呈现图片,可进行多选图片,选中的图片以选中的顺序进行标记。点击网格上的小图片时候,可以放大预览,底部有被选中的图片列表,预览的大图可进行拖拽,向下拖拽并且放手后,大图自动缩小返回到对应的小图的位置。
部分代码如下:
class ChooseImageVM : BaseViewModel() {
private val mChooseImageFlowUtil = RefreshFlowUtil<ChooseImageBean>()
private val mPreviewCheckListFlowUtil = RefreshFlowUtil<ChooseImageBean>()
private val mCheckedCountFlow = MutableStateFlow("")
private val mStringDone: String = WordUtil.getString(R.string.完成)
fun activityCallback(
onImageList: suspend (MutableList<ChooseImageBean>) -> Unit,
onCheckedCount: (String) -> Unit
) = Callback(
on(mChooseImageFlowUtil.mFlow) {
onImageList(it.mList)
},
on(mCheckedCountFlow) {
onCheckedCount(it)
}
)
/**
* 预览弹窗下方选中列表回调
*/
fun previewDialogCallback(
onPreviewCheckList: suspend (MutableList<ChooseImageBean>) -> Unit,
onCheckedCount: (String) -> Unit
) = Callback(
on(mPreviewCheckListFlowUtil.mFlow) {
onPreviewCheckList(it.mList)
},
on(mCheckedCountFlow) {
onCheckedCount(it)
}
)
/**
* 查询本地图片
*/
fun queryImages(context: Context) {
viewModelScope.launch {
val permissions = applyPermission(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
if (!permissions.isAllGranted()) return@launch
val uriList = withContext(Dispatchers.Default) {
val tempList = mutableListOf<Uri>()
val cursor = context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null,
"mime_type=? or mime_type=? or mime_type=?",
arrayOf("image/jpeg", "image/png", "image/webp"),
"${MediaStore.MediaColumns.DATE_ADDED} desc"
)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
if (id > 0) {
val uri =
ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
tempList.add(uri)
}
}
cursor.close()
}
tempList
}
val imageBeanList = uriList.mapIndexed { index, uri ->
ChooseImageBean(mIndex = index, mUri = uri)
} as MutableList<ChooseImageBean>
mChooseImageFlowUtil.refresh(imageBeanList)
mCheckedCountFlow.update {
"(0/${mChooseImageFlowUtil.mData.size})$mStringDone"
}
}
}
fun getDataList(): MutableList<ChooseImageBean> {
return mChooseImageFlowUtil.mData
}
fun getListItem(position: Int) = mChooseImageFlowUtil.getItem(position)
/**
* 切换选中和取消
*/
fun toggleCheck(toggleBean: ChooseImageBean, maxCount: Int) {
val choosed = if (toggleBean.mCheckedNumber > 0) {
if (maxCount > 1) {
mChooseImageFlowUtil.mData.forEach {
if (it.mCheckedNumber > toggleBean.mCheckedNumber) {
mChooseImageFlowUtil.setItem(
it.mIndex,
it.copy(mCheckedNumber = it.mCheckedNumber - 1)
)
}
}
}
mChooseImageFlowUtil.setItem(
toggleBean.mIndex,
toggleBean.copy(mCheckedNumber = 0)
)
false
} else {
if (maxCount > 1) {
var checkedCount = 0
mChooseImageFlowUtil.mData.forEach {
if (it.mCheckedNumber > 0) {
checkedCount++
}
}
if (checkedCount >= maxCount) {
toast(R.string.已达到最大数量)
return
}
mChooseImageFlowUtil.setItem(
toggleBean.mIndex,
toggleBean.copy(mCheckedNumber = checkedCount + 1)
)
} else {
for (bean in mChooseImageFlowUtil.mData) {
if (bean.mCheckedNumber > 0) {
mChooseImageFlowUtil.setItem(
bean.mIndex,
bean.copy(mCheckedNumber = 0)
)
break
}
}
mChooseImageFlowUtil.setItem(
toggleBean.mIndex,
toggleBean.copy(mCheckedNumber = 1)
)
}
true
}
mChooseImageFlowUtil.update()
val previewCheckList = mChooseImageFlowUtil.mData.filter {
it.mCheckedNumber > 0
} as MutableList<ChooseImageBean>
if (maxCount > 1) {
previewCheckList.sortBy { it.mCheckedNumber }
}
if (choosed) {
setPreviewListChecked(previewCheckList, toggleBean.mIndex)
}
mPreviewCheckListFlowUtil.refresh(previewCheckList)
mCheckedCountFlow.update {
"(${previewCheckList.size}/$maxCount)$mStringDone"
}
}
private fun setPreviewListChecked(
previewCheckList: MutableList<ChooseImageBean>,
chooseIndex: Int
) {
for (i in 0 until previewCheckList.size) {
val bean = previewCheckList[i]
if (bean.mIndex == chooseIndex) {
if (!bean.mPreviewListChecked) {
previewCheckList[i] = bean.copy(mPreviewListChecked = true)
}
} else {
if (bean.mPreviewListChecked) {
previewCheckList[i] = bean.copy(mPreviewListChecked = false)
}
}
}
}
fun setPreviewListChecked(chooseIndex: Int) {
setPreviewListChecked(mPreviewCheckListFlowUtil.mData, chooseIndex)
mPreviewCheckListFlowUtil.update()
}
}
class ChooseImagePreviewDialog :
BaseDialog<ChooseImagePreviewDialogBinding>() {
val mVM by activityViewModels<ChooseImageVM>()
var mGetBackTargetView: ((Any?) -> View?)? = null
var mEnterPageIndex = 0
var mMaxCount = 1
private var mPageSize = 0
private val mTopHeight = ScreenDimenUtil.sIntance.statusBarHeight + DpUtil.dp2px(40)
private val mBottomHeight = DpUtil.dp2px(126)
private var mDrawableCheckOutline: Drawable? = null
private var mDrawableCheckFill: Drawable? = null
override fun getStyle() = R.style.dialog
override fun cancelable() = true
override fun setWindowAttributes(window: Window) {
val params = window.attributes
window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
params.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
params.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
params.width = WindowManager.LayoutParams.MATCH_PARENT
params.height = WindowManager.LayoutParams.MATCH_PARENT
window.attributes = params
}
override fun setUp() {
mPageSize = mVM.getDataList().size
mDrawableCheckOutline =
ContextCompat.getDrawable(mContext, R.drawable.choose_image_check_outline)
mDrawableCheckFill = ContextCompat.getDrawable(mContext, R.drawable.choose_image_check_fill)
val viewPagerAdapter = ChooseImagePreviewPageAdapter(
mContext,
mVM.getDataList(),
mEnterPageIndex,
object : DragBackImageView.Callback {
override fun onEnterAnimCompleted() {
mVB.apply {
groupTop.animate().apply {
duration = 200
translationY(0f)
}.start()
groupBottom.animate().apply {
duration = 200
translationY(0f)
}.start()
}
}
override fun onMoveStart() {
mVB.apply {
groupTop.animate().apply {
duration = 200
translationY(-mTopHeight)
}.start()
groupBottom.animate().apply {
duration = 200
translationY(mBottomHeight)
}.start()
}
}
override fun onMoveCancel() {
mVB.apply {
groupTop.animate().apply {
duration = 200
translationY(0f)
}.start()
groupBottom.animate().apply {
duration = 200
translationY(0f)
}.start()
}
}
override fun onBackAnimCompleted() {
dismiss()
}
override fun getBackTargetView(backTag: Any?): View? {
return mGetBackTargetView?.invoke(backTag)
}
}
)
val previewListAdapter = ChooseImagePreviewListAdapter(mContext) {
mVB.viewPager.setCurrentItem(it.mIndex, false)
}
mVB.apply {
btnChoose.setOnClickListener {
mVM.getListItem(viewPager.currentItem)?.let {
mVM.toggleCheck(it,mMaxCount)
}
val bean = mVM.getListItem(viewPager.currentItem)
imgCheck.setImageDrawable(
if (bean != null && bean.mCheckedNumber > 0) {
mDrawableCheckFill
} else {
mDrawableCheckOutline
}
)
}
groupTop.translationY = -mTopHeight
groupBottom.translationY = mBottomHeight
title.setText("${mEnterPageIndex + 1}/$mPageSize")
viewPager.apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
title.setText("${position + 1}/$mPageSize")
val bean = mVM.getListItem(viewPager.currentItem)
imgCheck.setImageDrawable(
if (bean != null && bean.mCheckedNumber > 0) {
mDrawableCheckFill
} else {
mDrawableCheckOutline
}
)
mVM.setPreviewListChecked(position)
}
})
offscreenPageLimit = 1
adapter = viewPagerAdapter
setCurrentItem(mEnterPageIndex, false)
}
recyclerView.apply {
layoutManager = LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)
adapter = previewListAdapter
}
}
mVM.previewDialogCallback(
onPreviewCheckList = {
previewListAdapter.refreshData(it)
val checkedPosition = previewListAdapter.getCheckedPosition()
if (checkedPosition > -1) {
mVB.recyclerView.smoothScrollToPosition(checkedPosition)
}
},
onCheckedCount = {
mVB.btnDone.text = it
}
).register(this)
}
}这样就实现了图片选择的功能。
声明:以上内容为云豹科技作者本人原创,未经作者本人同意,禁止转载,否则将追究相关法律责任www.yunbaokj.com






鲁公网安备 37090202000844号

