手把手教你实现可滑动消失的PopupWindow

一、前言
  • 日常开发过程中经常使用PopupWindow作为弹层,系统提供的方法都是通过点击消失
    ,那能不能通过滑动实现消失呢?
  • 今天我们就通过添加手势,实现可滑动消失的PopupWindow
二、分析
  • 1、既然是实现可滑动的PopupWindow,我们就继承系统的PopupWindow来实现
  • 2、需要添加滑动手势,重写onTouch事件,分别处理点击、滑动、抬起操作
  • 3、定义向上滑动消失,调用系统update方法来动态改变PopupWindow位置
三、代码实现
  • 1、代码版本一
class TouchablePopUpWindow constructor(
    context: Context
) : PopupWindow(context) {

    private var mContext = context
    private var mStartY: Int = 0
    private var mDiffY: Int = 0

    init {
        width = WindowManager.LayoutParams.MATCH_PARENT
        height = WindowManager.LayoutParams.MATCH_PARENT
        val mContentView = LayoutInflater.from(context).inflate(R.layout.touchable_pop, null)
        val mIvTop = mContentView.findViewById(R.id.iv_top)
        animationStyle = R.style.PopupWindowAnimation
        contentView = mContentView
        isClippingEnabled = false
        mIvTop.setOnTouchListener { _, event ->
            event.run {
                when (action) {
                    MotionEvent.ACTION_DOWN -> {
                        mStartY = rawY.toInt()
                    }
                    MotionEvent.ACTION_MOVE -> {
                        mDiffY = rawY.toInt() - mStartY
                        update(0, -mDiffY, -1, -1, true)
                    }
                    MotionEvent.ACTION_UP -> {
                            dismiss()
                        }

                    }
                }
            }
            true
        }
    }

}
2、效果图

手把手教你实现可滑动消失的PopupWindow_第1张图片

3、效果分析

通过查看版本一代码效果,发现距离存在以下几个问题:

  • 显示的PopupWindow在屏幕手机边沿存在缝隙
  • 点击事件和Touch事件冲突,导致不能点击事件被Touch事件消费掉了
  • 未限制滑动方向,可上下滑动
4、问题解决方案
  • 问题1

在init中为PopupWindow设置背景

setBackgroundDrawable(null)
  • 问题2

修改ACTION_UP事件代码,通过检测移动距离来手动区分touch和click事件

 MotionEvent.ACTION_UP -> {
                        if (abs(rawY.toInt() - mStartY) < 10) {
                            Toast.makeText(mContext, "clicked", Toast.LENGTH_SHORT).show()
                        } else {
                            dismiss()
                        }

                    }
  • 问题3

修改ACTION_MOVE事件,限制滑动方向

 MotionEvent.ACTION_MOVE -> {
                        mDiffY = rawY.toInt() - mStartY
                        // 限制方向
                        if (mDiffY > 0) mDiffY = 0
                        update(0, -mDiffY, -1, -1, true)
                    }
5、完整代码
  • PopupWindow代码
/**
 * @date 2020/12/15
 * @author qipeng
 * @desc
 */
class TouchablePopUpWindow constructor(
    context: Context
) : PopupWindow(context) {

    private var mContext = context
    private var mStartY: Int = 0
    private var mDiffY: Int = 0

    init {
        width = WindowManager.LayoutParams.MATCH_PARENT
        height = WindowManager.LayoutParams.MATCH_PARENT
        setBackgroundDrawable(null)
        val mContentView = LayoutInflater.from(context).inflate(R.layout.touchable_pop, null)
        val mIvTop = mContentView.findViewById(R.id.iv_top)
        animationStyle = R.style.PopupWindowAnimation
        contentView = mContentView
        isClippingEnabled = false

        mIvTop.setOnTouchListener { _, event ->
            event.run {
                when (action) {
                    MotionEvent.ACTION_DOWN -> {
                        mStartY = rawY.toInt()
                    }
                    MotionEvent.ACTION_MOVE -> {
                        mDiffY = rawY.toInt() - mStartY
                        // 限制方向
                        if (mDiffY > 0) mDiffY = 0
                        update(0, -mDiffY, -1, -1, true)
                    }
                    MotionEvent.ACTION_UP -> {
                        // click
                        if (abs(rawY.toInt() - mStartY) < 10) {
                            Toast.makeText(mContext, "clicked", Toast.LENGTH_SHORT).show()
                        } else {
                            dismiss()
                        }

                    }
                }
            }
            true
        }
    }

}
  • PopupWindow layout代码



    

    


  • activity代码
class CustomPopActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_custom_pop)
        showPop()
    }

    private fun showPop() {
        btn_pop_jump.setOnClickListener {
            val mPop = TouchablePopUpWindow(this)
            mPop.showAtLocation(
                window.decorView, Gravity.BOTTOM, WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT
            )
        }
    }
}
  • activity layout代码



    
6、最终效果图

手把手教你实现可滑动消失的PopupWindow_第2张图片

四、小结
  • 前期对需求的分析很重要
  • 拿到需求后进行技术选型、方案设定
  • 针对做出的效果进行自测、分析、完善
  • 最终实现效果

你可能感兴趣的:(Kotlin,自定义View,android,移动开发,安卓,app)