云豹相亲相册选择图片功能实现
云豹相亲交友是一款聊天交友类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