美文网首页
仿Telegram媒体功能(一)

仿Telegram媒体功能(一)

作者: Db_z | 来源:发表于2023-07-02 14:58 被阅读0次

    先上一个Telegram的媒体样式, 后面是自定义是实现效果


    Telegram.png

    在来看一下实现的效果


    仿TG.png 仿TG1.png

    大概说一下实现的方式,目前用的是DialogFragment全屏实现,自定义滚动视图,利用pictureselector图片库 获取数据源,列表第一个是打开的相机,根据官方提供的相机库 实现绑定生命周期显示相机。
    先看滚动视图,首先你得知道你的滚动视图分为几个view,占几部分,我这里写的固定view,如果满足不了你的需求请自行修改

    DialogFragment中滚动View
     class ChatPhotoAlertNestedScrollingParent @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null,
        defStyleAttr: Int = 0,
    ) : FrameLayout(context, attrs, defStyleAttr), NestedScrollingParent3 {
    
        // 标题栏
        private var mFlTitle: View? = null
        // 滚动的间距
        private var mViewSpace: View? = null
        // 滚动底部的view
        private var mScrollBottomView: View? = null
    
        // 滚动的背景布局头
        private var mClBottomLayoutTitle: ConstraintLayout? = null
        private var mTabBgStartColor = Color.WHITE
        private var mTabBgEndColor = Color.WHITE
        private var mTabBgColor = 0
        // 背景 圆角
        private var mTabBg: GradientDrawable = GradientDrawable()
        private var mTabBgRadius: FloatArray = FloatArray(8)
        private var mMaxTabBgRadius = 0f
    
        private val mHelper: NestedScrollingParentHelper = NestedScrollingParentHelper(this)
        private var mSnapAnimator: ValueAnimator? = null
        private var mLastStartedType = 0
        private var mScrollRangeTop = 0
        private var mScrollRangeBottom = 0
        private var mCurrentScrollRange = 0
        private var mTitleChange = false
        private var isScrollTopShowTitle = false
        private var mArgbEvaluator: ArgbEvaluator = ArgbEvaluator()
        private var isDoPerformAnyCallbacks = true
        private var mOnOffsetScrollRangeListener: OnOffsetScrollRangeListener? = null
    
        init {
            mArgbEvaluator = ArgbEvaluator()
            mTabBgColor = mTabBgStartColor
            mMaxTabBgRadius = getDefaultTabBgRadius().toFloat()
            mTabBg.setColor(mTabBgStartColor)
            mTabBgRadius = FloatArray(8)
            mTabBgRadius[0] = mMaxTabBgRadius.also { mTabBgRadius[3] = it }
                .also { mTabBgRadius[2] = it }.also { mTabBgRadius[1] = it }
            mTabBgRadius[4] = 0.also { mTabBgRadius[7] = it.toFloat() }.also {
                mTabBgRadius[6] =
                    it.toFloat()
            }.also { mTabBgRadius[5] = it.toFloat() }.toFloat()
            mTabBg.cornerRadii = mTabBgRadius
        }
    
        override fun onFinishInflate() {
            super.onFinishInflate()
            mFlTitle = findViewById(R.id.fl_title)
            mViewSpace = findViewById(R.id.view_space)
            mScrollBottomView = findViewById(R.id.ll_scroll_bottom_layout)
            mClBottomLayoutTitle = findViewById(R.id.cl_bottom_top_title)
            mClBottomLayoutTitle?.background = mTabBg
        }
    
        private fun getDefaultTabBgRadius(): Int {
            return getDimen(R.dimen.dp_16)
        }
    
        private fun getDimen(@DimenRes dimenId: Int): Int {
            return resources.getDimensionPixelSize(dimenId)
        }
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
            val mViewSpaceHeight = mViewSpace!!.measuredHeight
            val mFlTitleHeight = mFlTitle!!.measuredHeight
            val mViewSpaceMarginTop = (mViewSpace!!.layoutParams as MarginLayoutParams).topMargin
            // 获取向上滑动的距离
            mScrollRangeTop = mViewSpaceHeight + mViewSpaceMarginTop + mFlTitleHeight
            val update = mCurrentScrollRange != 0 && mScrollRangeBottom == mCurrentScrollRange
            // 屏幕总高度 - 上方空间高度 - 状态栏高度
            val mViewSpaceBottom = ScreenUtils.getScreenHeight() - mViewSpaceHeight
            // 获取向下滑动的距离  布局距离下方的3分之1
            mScrollRangeBottom = -(mViewSpaceBottom - mViewSpaceBottom / 3)
            if (update) {
                mCurrentScrollRange = mScrollRangeBottom
            }
            val scrollWidthSpec = MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY)
            val scrollHeightSpec =
                MeasureSpec.makeMeasureSpec(measuredHeight - mFlTitleHeight, MeasureSpec.EXACTLY)
    //        val scrollHeightSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY);
            mScrollBottomView!!.measure(scrollWidthSpec, scrollHeightSpec)
            if (mOnOffsetScrollRangeListener != null) {
                mOnOffsetScrollRangeListener!!.offsetScroll(0f,
                    mCurrentScrollRange,
                    mScrollRangeTop,
                    mScrollRangeBottom,
                    isScrollTopShowTitle)
            }
        }
    
        override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
            super.onLayout(changed, left, top, right, bottom)
            val mViewSpaceHeight =
                ScreenUtils.getScreenHeight() - mScrollBottomView!!.measuredHeight + mViewSpace!!.measuredHeight
            mViewSpace!!.layout(left, mFlTitle!!.measuredHeight, right, mViewSpaceHeight)
            val t = mScrollRangeTop - mCurrentScrollRange + mFlTitle!!.measuredHeight
            mScrollBottomView!!.layout(left, t, right, t + mScrollBottomView!!.measuredHeight)
        }
    
        override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean {
            val started = axes and ViewCompat.SCROLL_AXIS_VERTICAL != 0
            if (started) {
                if (mSnapAnimator != null) {
                    mSnapAnimator!!.cancel()
                }
            }
            mLastStartedType = type
            return started
        }
    
        override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) {
            mHelper.onNestedScrollAccepted(child, target, axes, type)
        }
    
        override fun onStopNestedScroll(target: View, type: Int) {
            if (mLastStartedType == ViewCompat.TYPE_TOUCH || type == ViewCompat.TYPE_NON_TOUCH) {
                if (mSnapAnimator == null || !mSnapAnimator!!.isRunning) {
                    snap()
                }
            }
        }
    
        override fun onNestedScroll(
            target: View,
            dxConsumed: Int,
            dyConsumed: Int,
            dxUnconsumed: Int,
            dyUnconsumed: Int,
            type: Int,
            consumed: IntArray,
        ) {
            onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type)
            if (dyUnconsumed < 0) {
                val bottom: Int
                if (type == ViewCompat.TYPE_TOUCH) {
                    bottom = mScrollRangeBottom
                } else if (mCurrentScrollRange < 0) {
                    bottom = mScrollRangeBottom
                    if (dyUnconsumed > -10 && mCurrentScrollRange < mScrollRangeBottom * 0.25f) {
                        ViewCompat.stopNestedScroll(target, type)
                    }
                } else {
                    bottom = 0
                }
                consumed[1] = offsetScrollView(dyUnconsumed, mScrollRangeTop, bottom)
            }
        }
    
        override fun onNestedScroll(
            target: View,
            dxConsumed: Int,
            dyConsumed: Int,
            dxUnconsumed: Int,
            dyUnconsumed: Int,
            type: Int,
        ) {
        }
    
        override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
            if (dy > 0) {
                if (type == ViewCompat.TYPE_TOUCH || mCurrentScrollRange > 0) {
                    consumed[1] = offsetScrollView(dy, mScrollRangeTop, mScrollRangeBottom)
                } else {
                    offsetScrollView(dy, 0, mScrollRangeBottom)
                    consumed[1] = dy
                }
            }
        }
    
        private fun offsetScrollView(dy: Int) {
            offsetScrollView(dy, mScrollRangeTop, mScrollRangeBottom, true)
        }
    
        private fun offsetScrollView(dy: Int, top: Int, bottom: Int): Int {
            return offsetScrollView(dy, top, bottom, false)
        }
    
        private fun offsetScrollView(dy: Int, top: Int, bottom: Int, anim: Boolean): Int {
            var tempDy = dy
            if (tempDy >= 0 && mCurrentScrollRange >= top) {
                return 0
            }
            if (tempDy <= 0 && mCurrentScrollRange <= bottom) {
                return 0
            }
            val result = tempDy
            if (mCurrentScrollRange <= 0 && !anim) {
                tempDy /= 1.5f.toInt()
            }
            mCurrentScrollRange += tempDy
            if (mCurrentScrollRange > top) {
                tempDy -= mCurrentScrollRange - top
                mCurrentScrollRange = top
            } else if (mCurrentScrollRange < bottom) {
                tempDy -= mCurrentScrollRange - bottom
                mCurrentScrollRange = bottom
            }
            changeTitle(mCurrentScrollRange.toFloat())
            ViewCompat.offsetTopAndBottom(mScrollBottomView!!, -tempDy)
            return result
        }
    
        private fun changeTitle(current: Float) {
            val fraction: Float = if (current < 0) {
                0f
            } else if (current >= mScrollRangeTop) {
                1f
            } else {
                current / mScrollRangeTop
            }
    //        int titleColor = (int) mArgbEvaluator.evaluate(fraction, mTitleSearchBgStartColor, mTitleSearchBgEndColor);
    //        mFlTitle.getBackground().mutate().setAlpha((int) (fraction * 0xFF));
            val radius = mMaxTabBgRadius * (1 - fraction)
            mTabBgRadius[3] = radius
            mTabBgRadius[2] = mTabBgRadius[3]
            mTabBgRadius[1] = mTabBgRadius[2]
            mTabBgRadius[0] = mTabBgRadius[1]
            mTabBg.cornerRadii = mTabBgRadius
            val tabColor = mArgbEvaluator.evaluate(fraction, mTabBgColor, mTabBgEndColor) as Int
            mTabBg.setColor(tabColor)
            mClBottomLayoutTitle!!.background = mTabBg
            dispatchTitleChange(fraction)
            if (mOnOffsetScrollRangeListener != null && isDoPerformAnyCallbacks) {
                mOnOffsetScrollRangeListener!!.offsetScroll(fraction,
                    mCurrentScrollRange,
                    mScrollRangeTop,
                    mScrollRangeBottom,
                    isScrollTopShowTitle)
                mOnOffsetScrollRangeListener!!.isScrollTopShowTitle(mCurrentScrollRange,
                    mScrollRangeTop,
                    mCurrentScrollRange == mScrollRangeTop)
            }
        }
    
        private fun dispatchTitleChange(fraction: Float) {
            val change = fraction > 0
            if (change != mTitleChange) {
                mTitleChange = change
            }
        }
    
        fun setExpand(expand: Boolean) {
            if (expand) {
                anim(mCurrentScrollRange, mScrollRangeBottom)
            } else {
                anim(mCurrentScrollRange, 0)
            }
        }
    
        private fun snap() {
            val start = mCurrentScrollRange
            if (start != mScrollRangeTop) {
                val end: Int = if (start < mScrollRangeBottom * 0.5f) {
                    //                end = mScrollRangeBottom;
                    0
                } else if (start > mScrollRangeTop * 0.5f) {
                    mScrollRangeTop
                } else {
                    0
                }
                anim(start, end)
            }
        }
    
        private fun anim(start: Int, end: Int) {
            if (start == end) return
            isScrollTopShowTitle = end == mScrollRangeTop
            if (mSnapAnimator == null) {
                mSnapAnimator = ValueAnimator.ofInt(start, end)
                mSnapAnimator?.addUpdateListener { animation: ValueAnimator ->
                    val value = animation.animatedValue as Int
                    offsetScrollView(value - mCurrentScrollRange)
                }
            } else {
                mSnapAnimator!!.cancel()
                mSnapAnimator!!.setIntValues(start, end)
            }
            mSnapAnimator!!.duration = 250
            mSnapAnimator!!.start()
        }
    
        fun setDoPerformAnyCallbacks(doPerformAnyCallbacks: Boolean) {
            isDoPerformAnyCallbacks = doPerformAnyCallbacks
        }
    
        fun setOnOffsetScrollRangeListener(listener: OnOffsetScrollRangeListener) {
            mOnOffsetScrollRangeListener = listener
        }
    
        // 滚动的距离
        interface OnOffsetScrollRangeListener {
            fun offsetScroll(
                fraction: Float,
                offset: Int,
                scrollRangeTop: Int,
                scrollRangeBottom: Int,
                isScrollTopShowTitle: Boolean,
            )
    
            fun isScrollTopShowTitle(offset: Int, scrollRangeTop: Int, isScrollTopShowTitle: Boolean)
        }
    }
    

    这里面主要就是测量view的大小,计算出滚动的距离,还有显示的位置等;(滚动距离还有显示位置根据自己需求自行修改)

    下面才是媒体显示的页面,主要介绍一下逻辑部分(布局就不贴了,尾部给链接,自行查看)

    在滚动的时候 上面的导航标题栏显示隐藏;并且可以点击切换相册媒体文件,点击图片带有选中的序号,按照顺序显示;下方带有说明的输入框;在列表的第一个是打开的相机,这里相机是需要绑定生命周期的,直接传入DialogFragment,点击之后进入自定义拍照相机页面, 拍照介绍返回图片添加进入列表显示

    DialogFragment
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?,
        ): View? {
            initView()
            clickListener()
            return super.onCreateView(inflater, container, savedInstanceState)
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setStyle(STYLE_NORMAL, R.style.TransparentDialog)
            onCreateConfigLoader()
        }
    
        override fun onAttach(context: Context) {
            super.onAttach(context)
            mActivity = context as FragmentActivity
        }
    
        override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
            val dialog = super.onCreateDialog(savedInstanceState)
            binding = DialogChatAttachPhotoAlertBinding.inflate(layoutInflater, null, false)
            if (Build.VERSION.SDK_INT >= 30) {
                dialog.window?.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            } else {
                dialog.window?.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            }
            dialog.window?.statusBarColor = Color.TRANSPARENT
            dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
            val params = dialog.window!!.attributes
            params.gravity = Gravity.BOTTOM
            params.height = ViewGroup.LayoutParams.MATCH_PARENT
            if (Build.VERSION.SDK_INT >= 28) {
                params.layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
            }
            dialog.window?.attributes = params
            dialog.setContentView(binding.root)
            dialog.setOnKeyListener { _, keyCode, event ->
                if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP) {
                    dismissWithDelayRun()
                    true
                } else {
                    false
                }
            }
            return dialog
        }
    
        // 初始化获取数据源的状态
        private fun onCreateConfigLoader() {
            PictureSelectionConfig.imageEngine = GlideEngine.createGlideEngine()
            config = PictureSelectionConfig.getInstance()
            config.chooseMode = SelectMimeType.ofAll()
            config.isDisplayCamera = true
            config.isPageSyncAsCount = true
            config.isPageStrategy = true
            config.isGif = true
            config.isBmp = true
            config.maxSelectNum = Int.MAX_VALUE
            mLoader = if (config.isPageStrategy) LocalMediaPageLoader() else LocalMediaLoader()
            mLoader.initConfig(mActivity, config)
        }
    
        @SuppressLint("ClickableViewAccessibility", "NotifyDataSetChanged")
        private fun initView() {
            // 弹出动画之后 设置背景半透明
            mHandler.postDelayed({
                // 在显示之后 设置没有动画   否则跳转页面在返回是会有弹出动画
                dialog?.window?.setWindowAnimations(R.style.DialogNoAnimation)
                binding.nestedScrolling.setDoPerformAnyCallbacks(true)
                val animator = ValueAnimator.ofInt(0, 128)
                animator.duration = 200
                animator.addUpdateListener {
                    val animatedValue = it.animatedValue as Int
                    dialog?.window!!.statusBarColor =
                        ColorUtils.setAlphaComponent(Color.parseColor("#80222229"), animatedValue)
                    binding.nestedScrolling.setBackgroundColor(
                        ColorUtils.setAlphaComponent(Color.parseColor("#80222229"), animatedValue)
                    )
                }
                animator.start()
            }, 400)
            binding.nestedScrolling.setOnOffsetScrollRangeListener(object :
                ChatPhotoAlertNestedScrollingParent.OnOffsetScrollRangeListener {
                override fun offsetScroll(
                    fraction: Float,
                    offset: Int,
                    scrollRangeTop: Int,
                    scrollRangeBottom: Int,
                    isScrollTopShowTitle: Boolean,
                ) {
                    this@ChatAttachPhotoAlert.onScrollOffset = offset
                    this@ChatAttachPhotoAlert.scrollTop = scrollRangeTop
                    this@ChatAttachPhotoAlert.scrollBottom = scrollRangeBottom
                }
    
                override fun isScrollTopShowTitle(
                    offset: Int,
                    scrollRangeTop: Int,
                    isScrollTopShowTitle: Boolean,
                ) {
                    if (offset == scrollTop) {
                        dialog?.window!!.statusBarColor = Color.parseColor("#0096f0")
                        binding.flTitle.visible()
                    } else {
                        dialog?.window!!.statusBarColor = Color.parseColor("#80222229")
                        binding.flTitle.invisible()
                    }
                }
            })
            initAlbumListPopWindow()
    //        binding.recyclerViewPreview.setOnTouchListener { _, event ->
    //            setOnTouchListenerScrollHandler(event)
    //            return@setOnTouchListener false
    //        }
            binding.recyclerView.setOnTouchListener { _, event ->
                setOnTouchListenerScrollHandler(event)
                return@setOnTouchListener false
            }
            binding.recyclerView.setHasFixedSize(true)
            binding.recyclerView.setReachBottomRow(RecyclerPreloadView.BOTTOM_PRELOAD)
            binding.recyclerView.setOnRecyclerViewPreloadListener(this)
            binding.recyclerView.layoutManager = mLayoutManager
            binding.recyclerView.addItemDecoration(GridSpacingItemDecoration(3, 6.dp2px(), true))
            mAdapter = BasePictureAdapter(this, openCameraClick = {
                openCameraPermissions()
            }, onItemClickListener = { _, position ->
                onStartPreview(position)
            }, onItemLongClick = { _, position ->
                if (mDragSelectTouchListener != null) {
                    val vibrator = activity?.getSystemService(Service.VIBRATOR_SERVICE) as Vibrator
                    vibrator.vibrate(50)
                    mDragSelectTouchListener?.startSlideSelection(if (mAdapter.isDisplayCamera()) position - 1 else position)
                }
            }, onSelectListener = { selectedView, ivPicture, data, position ->
                val selectResultCode = confirmSelect(data, selectedView.isSelected)
                selectedMedia(selectedView, ivPicture, isSelected(data))
                mAdapter.notifyItemChanged(position)
                if (selectResultCode == SelectedManager.ADD_SUCCESS) {
                    val animation = AnimationUtils.loadAnimation(context, com.luck.picture.lib.R.anim.ps_anim_modal_in)
                    selectedView.startAnimation(animation)
                }
            })
            mAdapter.setDisplayCamera(true)
            binding.recyclerView.adapter = mAdapter
            // 图片预览
    //        binding.recyclerViewPreview.setHasFixedSize(true)
    //        binding.recyclerViewPreview.layoutManager = LinearLayoutManager(mActivity)
    //        binding.recyclerViewPreview.adapter =
    //            mAdapterPreview.apply { addItemBinder(PreviewImageGroupAdapter()) }
            SelectedManager.addAllSelectResult(mAdapter.data)
            binding.recyclerView.setOnRecyclerViewScrollStateListener(object :
                OnRecyclerViewScrollStateListener {
                override fun onScrollFast() {
                    if (PictureSelectionConfig.imageEngine != null) {
                        PictureSelectionConfig.imageEngine.pauseRequests(context)
                    }
                }
    
                override fun onScrollSlow() {
                    if (PictureSelectionConfig.imageEngine != null) {
                        PictureSelectionConfig.imageEngine.resumeRequests(context)
                    }
                }
            })
            val selectedPosition = HashSet<Int>()
            val slideSelectionHandler =
                SlideSelectionHandler(object : SlideSelectionHandler.ISelectionHandler {
                    override fun getSelection(): HashSet<Int> {
                        for (i in 0 until SelectedManager.getSelectCount()) {
                            val media = SelectedManager.getSelectedResult()[i]
                            selectedPosition.add(media.position)
                        }
                        return selectedPosition
                    }
    
                    override fun changeSelection(
                        start: Int,
                        end: Int,
                        isSelected: Boolean,
                        calledFromOnStart: Boolean,
                    ) {
                        // 下标是0的时候不处理 因为是相机
    //                    if (start == 0 || end == 0) return
                        val adapterData: ArrayList<LocalMedia> = mAdapter.data as ArrayList
                        if (adapterData.size == 0 || start > adapterData.size) return
                        val media = adapterData[start]
                        val selectResultCode: Int =
                            confirmSelect(media, SelectedManager.getSelectedResult().contains(media))
                        mDragSelectTouchListener?.setActive(selectResultCode != SelectedManager.INVALID)
                    }
                })
            mDragSelectTouchListener = SlideSelectTouchListener()
                .setRecyclerViewHeaderCount(if (mAdapter.isDisplayCamera()) 1 else 0)
                .withSelectListener(slideSelectionHandler)
            mDragSelectTouchListener?.let { binding.recyclerView.addOnItemTouchListener(it) }
            val emojiTheming = EmojiTheming(
                ContextCompat.getColor(mActivity, R.color.color_F2F3F5),
                ContextCompat.getColor(mActivity, R.color.black),
                ContextCompat.getColor(mActivity, R.color.blue_color),
                ContextCompat.getColor(mActivity, R.color.color_ECEDF1),
                ContextCompat.getColor(mActivity, R.color.blue_color),
                ContextCompat.getColor(mActivity, R.color.blue_color)
            )
            emojiPopup = EmojiPopup(rootView = binding.emotionContainerFrameLayout,
                editText = binding.layoutBottomEdit.editText,
                onEmojiPopupShownListener = {
                    binding.layoutBottomEdit.emotionImageView.setImageResource(R.drawable.icon_chat_key)
                },
                onEmojiPopupDismissListener = {
                    binding.layoutBottomEdit.emotionImageView.setImageResource(R.drawable.icon_chat_emo)
                },
                onSoftKeyboardCloseListener = {},
                onSoftKeyboardOpenListener = {},
                onEmojiBackspaceClickListener = {},
                onEmojiClickListener = {}, theming = emojiTheming
            )
            requestLoadData()
        }
    
      private fun clickListener() {
            binding.layoutBottomEdit.editText.doAfterTextChanged {
                //文字或换行导致输入框超过两行更换输入框圆角背景
                if (binding.layoutBottomEdit.editText.lineCount > 1) {
                    if (!is6dpRound) {
                        binding.layoutBottomEdit.llEditSpeak.setBackgroundResource(R.drawable.shape_white_radius_6)
                        is6dpRound = true
                    }
                } else {
                    binding.layoutBottomEdit.llEditSpeak.setBackgroundResource(R.drawable.shape_white_radius_20)
                    is6dpRound = false
                }
            }
            binding.ivBack.setOnClickListener { dismissWithDelayRun() }
            binding.llTitle.setOnClickListener {
                albumListPopWindow.showAsDropDown(binding.tvTitle)
            }
            binding.viewSpace.setOnClickListener { dismissWithDelayRun() }
            binding.layoutBottomEdit.llEditSpeak.setOnClickListener { KeyboardUtils.showSoftInput(binding.layoutBottomEdit.editText) }
            binding.layoutBottomEdit.clBottomSendEdit.setOnLongClickListener { true }
            binding.layoutBottomEdit.emotionImageView.setOnClickListener {
                emojiPopup?.toggle()
            }
            binding.tvSelectNum.setOnClickListener {
    //            switchAlbumPreviewAnimation()
            }
            binding.sendButton.setOnClickListener { dispatchTransformResult() }
        }
    
        /**
         * initAlbumListPopWindow
         */
        private fun initAlbumListPopWindow() {
            albumListPopWindow = AlbumListPopWindow.buildPopWindow(mActivity)
            albumListPopWindow.setOnPopupWindowStatusListener(object :
                AlbumListPopWindow.OnPopupWindowStatusListener {
                override fun onShowPopupWindow() {
                    if (!config.isOnlySandboxDir) {
                        AnimUtils.rotateArrow(binding.ivArrow, true)
                    }
                }
    
                override fun onDismissPopupWindow() {
                    if (!config.isOnlySandboxDir) {
                        AnimUtils.rotateArrow(binding.ivArrow, false)
                    }
                }
            })
            albumListPopWindow.setOnIBridgeAlbumWidget { position, curFolder ->
                val isDisplayCamera = config.isDisplayCamera && curFolder.bucketId == PictureConfig.ALL.toLong()
                mAdapter.setDisplayCamera(isDisplayCamera)
                if (position == 0) {
                    binding.tvTitle.text = StringUtils.getString(R.string.album)
                } else {
                    binding.tvTitle.text = curFolder.folderName
                }
                val lastFolder = SelectedManager.getCurrentLocalMediaFolder()
                val lastBucketId = lastFolder.bucketId
                if (curFolder.bucketId != lastBucketId) {
                    // 1、记录一下上一次相册数据加载到哪了,到时候切回来的时候要续上
                    val laseFolderData = ArrayList(mAdapter.data)
                    lastFolder.data = laseFolderData
                    lastFolder.currentDataPage = mPage
                    lastFolder.isHasMore = binding.recyclerView.isEnabledLoadMore
    
                    // 2、判断当前相册是否请求过,如果请求过则不从MediaStore去拉取了
                    if (curFolder.data.size > 0 && !curFolder.isHasMore) {
                        setAdapterData(curFolder.data)
                        mPage = curFolder.currentDataPage
                        binding.recyclerView.isEnabledLoadMore = curFolder.isHasMore
                        binding.recyclerView.smoothScrollToPosition(0)
                    } else {
                        // 3、从MediaStore拉取数据
                        mPage = 1
                        mLoader.loadPageMediaData(curFolder.bucketId, mPage, config.pageSize,
                            object : OnQueryDataResultListener<LocalMedia>() {
                                override fun onComplete(
                                    result: ArrayList<LocalMedia>,
                                    isHasMore: Boolean,
                                ) {
                                    handleSwitchAlbum(result, isHasMore)
                                }
                            })
                    }
                }
                SelectedManager.setCurrentLocalMediaFolder(curFolder)
                albumListPopWindow.dismiss()
                if (mDragSelectTouchListener != null && config.isFastSlidingSelect) {
                    mDragSelectTouchListener!!.setRecyclerViewHeaderCount(if (mAdapter.isDisplayCamera()) 1 else 0)
                }
            }
        }
    
    

    创建一个透明的dialog,在完全显示之后重新设置一下主题,初始化获取数据源的配置,设置列表适配器,RecyclerView也是用的pictureselector库中的RV,满足长按滑动选择功能;初始化点击事件及标题栏媒体的弹框

        // 加载数据 判断权限
        private fun requestLoadData() {
            if (XXPermissions.isGranted(mActivity, Permission.Group.STORAGE)) {
                loadAllAlbumData()
            } else {
                XXPermissions.with(mActivity)
                    // 申请多个权限
                    .permission(Permission.Group.STORAGE)
                    .request(object : OnPermissionCallback {
                        override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) {
                            if (!allGranted) {
                                val tips = StringUtils.getString(R.string.photos_media_missing_storage_permissions)
                                permissionsDialog(mActivity, permissions, tips)
                                return
                            }
                            loadAllAlbumData()
                        }
    
                        override fun onDenied(
                            permissions: MutableList<String>,
                            doNotAskAgain: Boolean,
                        ) {
                            if (doNotAskAgain) {
                                // 如果是被永久拒绝就跳转到应用权限系统设置页面
                                XXPermissions.startPermissionActivity(mActivity, permissions)
                            } else {
                                val tips =
                                    StringUtils.getString(R.string.photos_media_missing_storage_permissions)
                                permissionsDialog(mActivity, permissions, tips)
                            }
                        }
                    })
            }
        }
    
        override fun loadAllAlbumData() {
            preloadPageFirstData()
            mLoader.loadAllAlbum { localMediaFolder ->
                handleAllAlbumData(localMediaFolder)
            }
        }
    
        private fun handleAllAlbumData(result: List<LocalMediaFolder>) {
            if (ActivityCompatHelper.isDestroy(mActivity)) return
            if (result.isNotEmpty()) {
                val firstFolder = result[0]
                SelectedManager.setCurrentLocalMediaFolder(firstFolder)
                binding.tvTitle.text = StringUtils.getString(R.string.album)
                albumListPopWindow.bindAlbumData(result)
                binding.recyclerView.isEnabledLoadMore = true
            } else {
                showDataNull()
            }
        }
    
        override fun loadFirstPageMediaData(firstBucketId: Long) {
            mPage = 1
            binding.recyclerView.isEnabledLoadMore = true
            mLoader.loadPageMediaData(firstBucketId, mPage, mPage * config.pageSize,
                object : OnQueryDataResultListener<LocalMedia>() {
                    override fun onComplete(result: ArrayList<LocalMedia>, isHasMore: Boolean) {
                        handleFirstPageMedia(result, isHasMore)
                    }
                })
        }
    
        private fun handleFirstPageMedia(result: ArrayList<LocalMedia>, isHasMore: Boolean) {
            if (ActivityCompatHelper.isDestroy(mActivity)) return
            binding.recyclerView.isEnabledLoadMore = isHasMore
            if (binding.recyclerView.isEnabledLoadMore && result.size == 0) {
                // 如果isHasMore为true但result.size() = 0;
                // 那么有可能是开启了某些条件过滤,实际上是还有更多资源的再强制请求
                onRecyclerViewPreloadMore()
            } else {
                setAdapterData(result)
            }
        }
    
        override fun loadOnlyInAppDirectoryAllMediaData() {
            mLoader.loadOnlyInAppDirAllMedia { folder -> handleInAppDirAllMedia(folder) }
        }
    
        private fun handleInAppDirAllMedia(folder: LocalMediaFolder?) {
            if (!ActivityCompatHelper.isDestroy(mActivity)) {
                val sandboxDir = config.sandboxDir
                val isNonNull = folder != null
                val folderName = if (isNonNull) folder!!.folderName else File(sandboxDir).name
                binding.tvTitle.text = folderName
                if (isNonNull) {
                    SelectedManager.setCurrentLocalMediaFolder(folder)
                    setAdapterData(folder!!.data)
                } else {
                    showDataNull()
                }
            }
        }
    
        /**
         * 加载更多
         */
        override fun loadMoreMediaData() {
            if (binding.recyclerView.isEnabledLoadMore) {
                mPage++
                val localMediaFolder = SelectedManager.getCurrentLocalMediaFolder()
                val bucketId = localMediaFolder?.bucketId ?: 0
                mLoader.loadPageMediaData(bucketId, mPage, config.pageSize,
                    object : OnQueryDataResultListener<LocalMedia>() {
                        override fun onComplete(result: ArrayList<LocalMedia>, isHasMore: Boolean) {
                            handleMoreMediaData(result, isHasMore)
                        }
                    })
            }
        }
    
        /**
         * 处理加载更多的数据
         */
        @SuppressLint("NotifyDataSetChanged")
        private fun handleMoreMediaData(result: ArrayList<LocalMedia>, isHasMore: Boolean) {
            if (ActivityCompatHelper.isDestroy(mActivity)) return
            binding.recyclerView.isEnabledLoadMore = isHasMore
            if (binding.recyclerView.isEnabledLoadMore) {
                removePageCameraRepeatData(result.toMutableList())
                if (result.isNotEmpty()) {
    //                val positionStart: Int = mAdapter.data.size
                    mAdapter.data.addAll(result)
                    mAdapter.notifyDataSetChanged()
                    if (mAdapter.data.isEmpty()) {
                        showDataNull()
                    } else {
                        hideDataNull()
                    }
                } else {
                    // 如果没数据这里在强制调用一下上拉加载更多,防止是因为某些条件过滤导致的假为0的情况
                    onRecyclerViewPreloadMore()
                }
                if (result.size < PictureConfig.MIN_PAGE_SIZE) {
                    // 当数据量过少时强制触发一下上拉加载更多,防止没有自动触发加载更多
                    binding.recyclerView.onScrolled(binding.recyclerView.scrollX,
                        binding.recyclerView.scrollY)
                }
            }
        }
    
        private fun removePageCameraRepeatData(result: MutableList<LocalMedia>) {
            try {
                if (config.isPageStrategy) {
                    val iterator = result.iterator()
                    while (iterator.hasNext()) {
                        if (mAdapter.data.contains(iterator.next())) {
                            iterator.remove()
                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
    
            }
        }
    
        private fun handleSwitchAlbum(result: ArrayList<LocalMedia>, isHasMore: Boolean) {
            if (ActivityCompatHelper.isDestroy(mActivity)) return
            binding.recyclerView.isEnabledLoadMore = isHasMore
            if (result.size == 0) {
                // 如果从MediaStore拉取都没有数据了,adapter里的可能是缓存所以也清除
                mAdapter.data.clear()
            }
            setAdapterData(result)
            binding.recyclerView.onScrolled(0, 0)
            binding.recyclerView.smoothScrollToPosition(0)
        }
    
        private fun setAdapterData(result: ArrayList<LocalMedia>) {
            mAdapter.setList(result)
            SelectedManager.clearAlbumDataSource()
            SelectedManager.clearDataSource()
            if (mAdapter.data.isEmpty()) {
                showDataNull()
            } else {
                hideDataNull()
            }
            if (isFirstLoadData && mAdapter.data.isNotEmpty()) {
                isFirstLoadData = false
                val delayMills = if (RomUtils.isSamsung()) ALERT_LAYOUT_TRANSLATION_DELAY else 0L
                mHandler.postDelayed({ mAdapter.notifyItemChanged(0, NOTIFY_DATA_CHANGE) }, delayMills)
            }
        }
    

    以上就不多说了,先是权限判断,然后加载数据源,处理数据。

      /**
       * 设置选中
       *
       * @param isChecked
       */
      fun selectedMedia(view: View, ivPicture: AppCompatImageView, isChecked: Boolean) {
          if (view.isSelected != isChecked) {
              view.isSelected = isChecked
          }
          if (isChecked) {
              val selectColorFilter = StyleUtils.getColorFilter(view.context, R.color.color_80222229)
              ivPicture.colorFilter = selectColorFilter
          } else {
              val defaultColorFilter = StyleUtils.getColorFilter(view.context, R.color.transparent)
              ivPicture.colorFilter = defaultColorFilter
          }
      }
    
      /**
       * 检查LocalMedia是否被选中
       *
       * @param currentMedia
       * @return
       */
      fun isSelected(currentMedia: LocalMedia): Boolean {
          val selectedResult: List<LocalMedia> = SelectedManager.getSelectedResult()
          val isSelected = selectedResult.contains(currentMedia)
          if (isSelected) {
              val compare = currentMedia.compareLocalMedia
              if (compare != null && compare.isEditorImage) {
                  currentMedia.cutPath = compare.cutPath
                  currentMedia.isCut = !TextUtils.isEmpty(compare.cutPath)
                  currentMedia.isEditorImage = compare.isEditorImage
              }
          }
          return isSelected
      }
    
      /**
       * 对选择数量进行编号排序
       */
      fun notifySelectNumberStyle(tvNumber: AppCompatTextView, item: LocalMedia) {
          tvNumber.text = ""
          for (i in 0 until SelectedManager.getSelectCount()) {
              val media = SelectedManager.getSelectedResult()[i]
              if (TextUtils.equals(media.path, item.path) || media.id == item.id) {
                  item.num = media.num
                  media.setPosition(item.getPosition())
                  tvNumber.text = item.num.toString()
              }
          }
          tvNumber.setBackgroundResource(if (tvNumber.text == "") R.drawable.shape_photo_album_num_bg else R.drawable.shape_photo_album_text_num_select_bg)
          // 如果大于两位数  那么字体缩小 不然显示不下
          tvNumber.textSize = if (item.num > 99) 10f else 13f
      }
    
        /**
         * 计算选中数量
         */
        private fun notifySelectNumber() {
            val count = SelectedManager.getSelectCount()
            binding.tvSelectNum.text = if (count > 0) {
                StringUtils.getString(R.string.selected_picture, count)
            } else {
                StringUtils.getString(R.string.not_select)
            }
            if (count > 0) {
                binding.tvSelectNum.setCompoundDrawablesWithIntrinsicBounds(
                    0,
                    0,
                    R.drawable.icon_nav_back_black,
                    0
                )
                binding.tvSelectNum.setTextColor(
                    ContextCompat.getColor(
                        mActivity,
                        R.color.color_323233
                    )
                )
                binding.tvNumber.text = count.toString()
                binding.tvNumber.textSize = if (count > 99) 11f else 14f
                // 如果没显示的时候 才会弹出动画
                if (!binding.clBottomSendLayout.isVisible) {
                    //设置动画,从下向上滑动  布局
                    val translateAnimationLayout = TranslateAnimation(
                        TranslateAnimation.RELATIVE_TO_SELF, 0f,
                        TranslateAnimation.RELATIVE_TO_SELF, 0f,
                        TranslateAnimation.RELATIVE_TO_SELF, 1f,
                        TranslateAnimation.RELATIVE_TO_SELF, 0f
                    )
                    translateAnimationLayout.duration = 360 //设置动画的过渡时间
                    binding.clBottomSendLayout.startAnimation(translateAnimationLayout)
                    binding.clBottomSendLayout.animation.setAnimationListener(object :
                        AnimationListener {
                        override fun onAnimationStart(animation: Animation?) {
                            binding.clBottomSendLayout.visible()
                        }
    
                        override fun onAnimationEnd(animation: Animation?) {
                            binding.clBottomSendLayout.clearAnimation()
                        }
    
                        override fun onAnimationRepeat(animation: Animation?) {}
                    })
                }
            } else {
                binding.tvSelectNum.setTextColor(
                    ContextCompat.getColor(
                        mActivity,
                        R.color.color_969799
                    )
                )
                binding.tvSelectNum.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
                binding.tvNumber.text = ""
                //设置动画,从上向下滑动  布局
                val translateAnimationLayout = TranslateAnimation(
                    TranslateAnimation.RELATIVE_TO_SELF, 0f,
                    TranslateAnimation.RELATIVE_TO_SELF, 0f,
                    TranslateAnimation.RELATIVE_TO_SELF, 0f,
                    TranslateAnimation.RELATIVE_TO_SELF, 1f
                )
                translateAnimationLayout.duration = 360 //设置动画的过渡时间
                binding.clBottomSendLayout.startAnimation(translateAnimationLayout)
                binding.clBottomSendLayout.animation.setAnimationListener(object : AnimationListener {
                    override fun onAnimationStart(animation: Animation?) {
                        binding.clBottomSendLayout.invisible()
                    }
    
                    override fun onAnimationEnd(animation: Animation?) {
                        binding.clBottomSendLayout.clearAnimation()
                        binding.clBottomSendLayout.invisible()
                        binding.emotionContainerFrameLayout.gone()
                        KeyboardUtils.hideSoftInput(binding.layoutBottomEdit.editText)
                    }
    
                    override fun onAnimationRepeat(animation: Animation?) {}
                })
                binding.clBottomSendLayout.invisible()
            }
        }
    
        private fun confirmSelect(currentMedia: LocalMedia, isSelected: Boolean): Int {
    //        val checkSelectValidity = isCheckSelectValidity(currentMedia, isSelected)
    //        if (checkSelectValidity != SelectedManager.SUCCESS) {
    //            return SelectedManager.INVALID
    //        }
            // 先做选中判断逻辑  在执行以下   能否选中 在这里判断
    
            val selectedResult: MutableList<LocalMedia> = SelectedManager.getSelectedResult()
            val resultCode: Int
            if (isSelected) {
                selectedResult.remove(currentMedia)
                resultCode = SelectedManager.REMOVE
            } else {
                selectedResult.add(currentMedia)
                currentMedia.num = selectedResult.size
                resultCode = SelectedManager.ADD_SUCCESS
            }
            onSelectedChange(resultCode == SelectedManager.ADD_SUCCESS, currentMedia)
            return resultCode
        }
    
        fun onSelectedChange(isAddRemove: Boolean, currentMedia: LocalMedia) {
            notifySelectNumber()
            mAdapter.notifyItemChanged(currentMedia.position, NOTIFY_DATA_CHANGE)
            if (!isAddRemove) {
                sendChangeSubSelectPositionEvent()
            }
        }
    
    

    选中的逻辑处理,Tegegram选中之后是有一个缩放的效果的,我这目前需求没有,就只加上一层遮罩,然后处理选中的数字排序,但由于用的是pictureselector库,那里面也有已经处理完的数字排序,就没有重复造轮子了,网上也是一搜一堆的,我就直接用库自带的了;然后设置一下字体大小,如果超过两位数的话那么重新设置一下,不然显示不下了。

    //<editor-fold desc="相机事件回调处理">
    //    ***********************  相机事件回调处理  ***********************
        /**
         * 相机事件回调处理
         */
        fun dispatchHandleCamera(intent: Intent?) {
            ForegroundService.stopService(mActivity)
            PictureThreadUtils.executeByIo(object : PictureThreadUtils.SimpleTask<LocalMedia?>() {
                override fun doInBackground(): LocalMedia? {
                    val outputPath = getOutputPath(intent)
                    if (!TextUtils.isEmpty(outputPath)) {
                        config.cameraPath = outputPath
                    }
                    if (TextUtils.isEmpty(config.cameraPath)) {
                        return null
                    }
                    if (config.chooseMode == SelectMimeType.ofAudio()) {
                        copyOutputAudioToDir()
                    }
                    return buildLocalMedia(config.cameraPath)
                }
    
                override fun onSuccess(result: LocalMedia?) {
                    PictureThreadUtils.cancel(this)
                    if (result != null) {
                        onScannerScanFile(result)
                        dispatchCameraMediaResult(result)
                    }
                }
            })
        }
    
        /**
         * 尝试匹配查找自定义相机返回的路径
         *
         * @param data
         * @return
         */
        private fun getOutputPath(data: Intent?): String? {
            if (data == null) return null
    //        var outPutUri = data.getParcelableExtra<Uri>(MediaStore.EXTRA_OUTPUT)
            val url = data.getStringExtra("url")
    //        if (config.chooseMode == SelectMimeType.ofAudio() && outPutUri == null) {
    //            outPutUri = data.data
    //        }
    //        if (outPutUri == null) {
    //            return null
    //        }
            return if (PictureMimeType.isContent(url.toString())) url.toString() else Uri.parse(url)
                .toString()
    //        return if (PictureMimeType.isContent(outPutUri.toString())) outPutUri.toString() else outPutUri.path
        }
    
        /**
         * 刷新相册
         *
         * @param media 要刷新的对象
         */
        private fun onScannerScanFile(media: LocalMedia) {
            if (ActivityCompatHelper.isDestroy(mActivity)) return
            if (SdkVersionUtils.isQ()) {
                if (PictureMimeType.isHasVideo(media.mimeType) && PictureMimeType.isContent(config.cameraPath)) {
                    PictureMediaScannerConnection(mActivity, media.realPath)
                }
            } else {
                val path =
                    if (PictureMimeType.isContent(config.cameraPath)) media.realPath else config.cameraPath
                PictureMediaScannerConnection(mActivity, path)
                if (PictureMimeType.isHasImage(media.mimeType)) {
                    val dirFile = File(path)
                    val lastImageId = MediaUtils.getDCIMLastImageId(mActivity, dirFile.parent)
                    if (lastImageId != -1) {
                        MediaUtils.removeMedia(mActivity, lastImageId)
                    }
                }
            }
        }
    
        /**
         * buildLocalMedia
         *
         * @param absolutePath
         */
        private fun buildLocalMedia(absolutePath: String?): LocalMedia {
            val media: LocalMedia = LocalMedia.generateLocalMedia(mActivity, absolutePath)
            media.chooseModel = config.chooseMode
            if (SdkVersionUtils.isQ() && !PictureMimeType.isContent(absolutePath)) {
                media.sandboxPath = absolutePath
            } else {
                media.sandboxPath = null
            }
            if (config.isCameraRotateImage && PictureMimeType.isHasImage(media.mimeType)) {
                BitmapUtils.rotateImage(mActivity, absolutePath)
            }
            return media
        }
    
        /**
         * copy录音文件至指定目录
         */
        private fun copyOutputAudioToDir() {
            try {
                if (!TextUtils.isEmpty(config.outPutAudioDir) && PictureMimeType.isContent(config.cameraPath)) {
                    val inputStream =
                        PictureContentResolver.getContentResolverOpenInputStream(
                            mActivity,
                            Uri.parse(config.cameraPath)
                        )
                    val audioFileName: String = if (TextUtils.isEmpty(config.outPutAudioFileName)) {
                        ""
                    } else {
                        if (config.isOnlyCamera) config.outPutAudioFileName else System.currentTimeMillis()
                            .toString() + "_" + config.outPutAudioFileName
                    }
                    val outputFile = PictureFileUtils.createCameraFile(
                        mActivity,
                        config.chooseMode, audioFileName, "", config.outPutAudioDir
                    )
                    val outputStream = FileOutputStream(outputFile.absolutePath)
                    val isCopyStatus = PictureFileUtils.writeFileFromIS(inputStream, outputStream)
                    if (isCopyStatus) {
                        MediaUtils.deleteUri(mActivity, config.cameraPath)
                        config.cameraPath = outputFile.absolutePath
                    }
                }
            } catch (e: FileNotFoundException) {
                e.printStackTrace()
            }
        }
    
        private fun dispatchCameraMediaResult(media: LocalMedia) {
            val exitsTotalNum = albumListPopWindow.firstAlbumImageCount
            if (!isAddSameImp(exitsTotalNum)) {
                mAdapter.data.add(0, media)
            }
            // 进入下面 永远等于false
            if (config.selectionMode == SelectModeConfig.SINGLE && config.isDirectReturnSingle) {
                SelectedManager.clearSelectResult()
                val selectResultCode = confirmSelect(media, false)
                if (selectResultCode == SelectedManager.ADD_SUCCESS) {
                    dispatchTransformResult()
                }
            } else {
                confirmSelect(media, false)
            }
            mAdapter.notifyItemInserted(if (mAdapter.isDisplayCamera()) 1 else 0)
            mAdapter.notifyItemRangeChanged(if (mAdapter.isDisplayCamera()) 1 else 0,
                mAdapter.getDefItemCountDataSize())
            if (config.isOnlySandboxDir) {
                var currentLocalMediaFolder = SelectedManager.getCurrentLocalMediaFolder()
                if (currentLocalMediaFolder == null) {
                    currentLocalMediaFolder = LocalMediaFolder()
                }
                currentLocalMediaFolder.bucketId = ValueOf.toLong(media.parentFolderName.hashCode())
                currentLocalMediaFolder.folderName = media.parentFolderName
                currentLocalMediaFolder.firstMimeType = media.mimeType
                currentLocalMediaFolder.firstImagePath = media.path
                currentLocalMediaFolder.folderTotalNum = mAdapter.data.size
                currentLocalMediaFolder.currentDataPage = mPage
                currentLocalMediaFolder.isHasMore = false
                val data = ArrayList(mAdapter.data)
                currentLocalMediaFolder.data = data
                binding.recyclerView.isEnabledLoadMore = false
                SelectedManager.setCurrentLocalMediaFolder(currentLocalMediaFolder)
            } else {
                mergeFolder(media)
            }
            allFolderSize = 0
        }
    
        /**
         * 拍照出来的合并到相应的专辑目录中去
         *
         * @param media
         */
        private fun mergeFolder(media: LocalMedia) {
            val allFolder: LocalMediaFolder
            val albumList = albumListPopWindow.albumList
            if (albumListPopWindow.folderCount == 0) {
                // 1、没有相册时需要手动创建相机胶卷
                allFolder = LocalMediaFolder()
                val folderName: String = if (TextUtils.isEmpty(config.defaultAlbumName)) {
                    if (config.chooseMode == SelectMimeType.ofAudio())
                        StringUtils.getString(com.luck.picture.lib.R.string.ps_all_audio)
                    else
                        StringUtils.getString(com.luck.picture.lib.R.string.ps_camera_roll)
                } else {
                    config.defaultAlbumName
                }
                allFolder.folderName = folderName
                allFolder.firstImagePath = ""
                allFolder.bucketId = PictureConfig.ALL.toLong()
                albumList.add(0, allFolder)
            } else {
                // 2、有相册就找到对应的相册把数据加进去
                allFolder = albumListPopWindow.getFolder(0)
            }
            allFolder.firstImagePath = media.path
            allFolder.firstMimeType = media.mimeType
            val data = ArrayList(mAdapter.data)
            allFolder.data = data
            allFolder.bucketId = PictureConfig.ALL.toLong()
            allFolder.folderTotalNum =
                if (isAddSameImp(allFolder.folderTotalNum)) allFolder.folderTotalNum else allFolder.folderTotalNum + 1
            val currentLocalMediaFolder = SelectedManager.getCurrentLocalMediaFolder()
            if (currentLocalMediaFolder == null || currentLocalMediaFolder.folderTotalNum == 0) {
                SelectedManager.setCurrentLocalMediaFolder(allFolder)
            }
            // 先查找Camera目录,没有找到则创建一个Camera目录
            var cameraFolder: LocalMediaFolder? = null
            for (i in albumList.indices) {
                val exitsFolder = albumList[i]
                if (TextUtils.equals(exitsFolder.folderName, media.parentFolderName)) {
                    cameraFolder = exitsFolder
                    break
                }
            }
            if (cameraFolder == null) {
                // 还没有这个目录,创建一个
                cameraFolder = LocalMediaFolder()
                albumList.add(cameraFolder)
            }
            cameraFolder.folderName = media.parentFolderName
            if (cameraFolder.bucketId == -1L || cameraFolder.bucketId == 0L) {
                cameraFolder.bucketId = media.bucketId
            }
            // 分页模式下,切换到Camera目录下时,会直接从MediaStore拉取
            if (config.isPageStrategy) {
                cameraFolder.isHasMore = true
            } else {
                // 非分页模式数据都是存在目录的data下,所以直接添加进去就行
                if (!isAddSameImp(allFolder.folderTotalNum)
                    || !TextUtils.isEmpty(config.outPutCameraDir)
                    || !TextUtils.isEmpty(config.outPutAudioDir)
                ) {
                    cameraFolder.data.add(0, media)
                }
            }
            cameraFolder.folderTotalNum =
                if (isAddSameImp(allFolder.folderTotalNum)) cameraFolder.folderTotalNum else cameraFolder.folderTotalNum + 1
            cameraFolder.firstImagePath = config.cameraPath
            cameraFolder.firstMimeType = media.mimeType
            albumListPopWindow.bindAlbumData(albumList)
        }
    
        /**
         * 数量是否一致
         */
        private fun isAddSameImp(totalNum: Int): Boolean {
            return if (totalNum == 0) {
                false
            } else allFolderSize in 1 until totalNum
        }
        //    </editor-fold>
    
    

    也是没啥可说的,打开相机,处理相机回调数据;基本与pictureselector库一致

        /**
         * @param isDismissDialog 不管执行什么操作  如果为true 那么执行完操作 会关闭弹框  默认为false
         */
        private fun dismissWithDelayRun(isDismissDialog: Boolean = false, block: (() -> Unit)? = null) {
            if (KeyboardUtils.isSoftInputVisible(mActivity)) {
                binding.emotionContainerFrameLayout.gone()
                KeyboardUtils.hideSoftInput(binding.layoutBottomEdit.editText)
                if (isDismissDialog) {
                    dismissDelayRun(block)
                }
            } else {
                if (isDismissDialog) {
                    dismissDelayRun(block)
                } else {
                    // 如果有选中的  二次提示弹框
                    if (SelectedManager.getSelectCount() > 0) {
                        val dialog = AlertDialog.Builder(activity)
                        dialog.setTitle("是否关闭dialog")
                        dialog.setMessage("是否放弃选择关闭dialog")
                        dialog.setNegativeButton("否") { d, _ ->
                            d.cancel()
                        }
                        dialog.setPositiveButton("是") { d, _ ->
                            d.dismiss()
                            dismissDelayRun(block)
                        }
                        dialog.show()
                    } else {
                        dismissDelayRun(block)
                    }
                }
            }
        }
    
        /**
         * @param block 关闭弹框
         */
        private fun dismissDelayRun(block: (() -> Unit)? = null) {
            SelectedManager.clearDataSource()
            SelectedManager.clearAlbumDataSource()
            SelectedManager.clearSelectResult()
            binding.nestedScrolling.setDoPerformAnyCallbacks(false)
            // 在关闭消失之前 添加动画
            dialog?.window?.setWindowAnimations(R.style.DialogBottomAnim)
            // 关闭之前先把颜色设置透明 在执行关闭
            dialog?.window?.statusBarColor = Color.TRANSPARENT
            binding.nestedScrolling.setBackgroundColor(0)
            mHandler.postDelayed({
                if (block != null) {
                    block.invoke()
                } else {
                    dismissAllowingStateLoss()
                }
            }, ALERT_LAYOUT_TRANSLATION_DELAY)
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            if (mDragSelectTouchListener != null) {
                mDragSelectTouchListener!!.stopAutoScroll()
            }
        }
    
    

    关闭DialogFragment,因为图片是可以添加说明的,所以需要先判断键盘,如果显示就先关闭键盘,再则判断是否有选中的图片,如果有的话 需要再次弹出一个确认提示框(这个图方便就快速写了一个,不标准,按照自己需求写就可以),如果确认关闭那么就清除选中的数据缓冲,添加一个DialogFragment的关闭动画。在销毁的时候把初始化长按滚动选中释放掉。

    以上基本就是选中媒体图片的弹框了,我的需求查看图片跟Telegram的有点不太一样,还有查看预览的图文混排,目前注释掉了,如果后续有时间的话我会补上,会有一个图文混排和图片预览的介绍。
    git地址

    相关文章

      网友评论

          本文标题:仿Telegram媒体功能(一)

          本文链接:https://www.haomeiwen.com/subject/fkboydtx.html