手把手教你实现可滑动消失的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<ImageView>(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、效果图

在这里插入图片描述

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<ImageView>(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代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/color_E3AC62">

    <ImageView
            android:id="@+id/iv_top"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/color_E3AC62" />

    <TextView
            android:id="@+id/tv_bottom"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_marginBottom="60dp"
            android:background="@color/color_EFE7DB"
            android:gravity="center"
            android:text="滑动消失"
            android:textSize="16sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • 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代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/design_default_color_secondary_variant">

    <Button
            android:id="@+id/btn_pop_jump"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:text="showPop"
            android:visibility="visible"
            app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
6、最终效果图

在这里插入图片描述

四、小结
  • 前期对需求的分析很重要
  • 拿到需求后进行技术选型、方案设定
  • 针对做出的效果进行自测、分析、完善
  • 最终实现效果
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值