一、前言
- 日常开发过程中经常使用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、最终效果图
四、小结
- 前期对需求的分析很重要
- 拿到需求后进行技术选型、方案设定
- 针对做出的效果进行自测、分析、完善
- 最终实现效果