Android事件冲突解决悬浮窗拖拽处理方案
作者:小迪vs同学 发布时间:2023-12-17 01:51:17
需求场景
最近项目中要做一个音乐播放悬浮按钮的功能,最终实现效果如下:
问题暴露
悬浮窗布局文件就不放了,就是水平LinearLayout
里面放几个ImageView
。
做的过程当中遇到一个问题,就是悬浮窗是可以任意拖拽的,悬浮窗里面的按钮是可以点击的,比如暂停,下一曲,关闭悬浮窗等。
按常规思路,先给整个悬浮窗setOnTouchListener()
,然后再给你里面的按钮setOnClickListener()
,点击运行,结果发现,点击事件是可以响应,拖拽也没问题,但是当手指放在ImageView
上拖拽时,onTouchListener
事件无法响应。
此时第一感觉就是setOnTouchListener()
和setOnClickListener()
冲突了,需要解决一下冲突。无奈自己对Android事件分发消费机制一直都是一知半解的,一般都是出了问题需要解决,第一时间先百度,没有解决方案就只能去研究Android事件分发消费机制了,但是研究完也都是懵懵懂懂的,今天就决定把这个难点彻底消化掉。
主要研究了这篇文章Android事件分发消费机制,然后对照着写了个demo,一一去验证,加深了自己的理解,最后终于解决了我的问题。
解决思路
先说下解决思路,自定义LinearLayout
,当手指处于滑动时,直接拦截事件,交给自己的onTouchEvent
处理即可,核心代码如下:
/**
* @author:Jason
* @date:2021/8/24 19:49
* @description:可拖拽的LinearLayout,解决子View设置OnClickListener之后无法拖拽的问题
*/
class DraggerbleLinearLayout : LinearLayout {
constructor(context: Context, attr: AttributeSet) : this(context, attr, 0)
constructor(context: Context, attr: AttributeSet, defStyleAttr: Int) : this(context, attr, defStyleAttr, 0)
constructor(context: Context, attr: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attr, defStyleAttr, defStyleRes)
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_MOVE -> {
return true
}
}
return super.onInterceptTouchEvent(ev)
}
}
很简单,就是在onInterceptTouchEvent()
里面拦截move
事件即可,这里你也可以重写onTouchEvent()
,在里面实现拖拽功能,但是这样就固定死了,所以我选择在外面setOnTouchListener()
,需要拖拽功能时才去实现
/**
* 创建悬浮窗
*/
@SuppressLint("ClickableViewAccessibility")
private fun showWindow() {
//获取WindowManager
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val outMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(outMetrics)
var layoutParam = WindowManager.LayoutParams().apply {
/**
* 设置type 这里进行了兼容
*/
type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_PHONE
}
format = PixelFormat.RGBA_8888
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
//位置大小设置
width = WRAP_CONTENT
height = WRAP_CONTENT
gravity = Gravity.LEFT or Gravity.TOP
//设置剧中屏幕显示
x = outMetrics.widthPixels / 2 - width / 2
y = outMetrics.heightPixels / 2 - height / 2
}
//在这里设置触摸监听,以实现拖拽功能
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
// 将悬浮窗控件添加到WindowManager
windowManager.addView(floatRootView, layoutParam)
isAdded = true
}
主要是这行代码
//在这里设置触摸监听,以实现拖拽功能
floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
ItemViewTouchListener.kt
文件内容
/**
* @author:Jason
* @date: 2021/8/23 19:27
* @description:
*/
class ItemViewTouchListener(val layoutParams: WindowManager.LayoutParams, val windowManager: WindowManager) : View.OnTouchListener {
private var lastX = 0.0f
private var lastY = 0.0f
override fun onTouch(view: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
//这里接收不到Down事件,不要在这里写逻辑
}
MotionEvent.ACTION_MOVE -> {
//重写LinearLayout的OnInterceptTouchEvent之后,这里的Down事件会接收不到,所以初始位置需要在Move事件里赋值
if (lastX == 0.0f || lastY == 0.0f) {
lastX = event.rawX
lastY = event.rawY
}
val nowX: Float = event.rawX
val nowY: Float = event.rawY
val movedX: Float = nowX - lastX
val movedY: Float = nowY - lastY
layoutParams.apply {
x += movedX.toInt()
y += movedY.toInt()
}
//更新悬浮球控件位置
windowManager?.updateViewLayout(view, layoutParams)
lastX = nowX
lastY = nowY
}
MotionEvent.ACTION_UP -> {
lastX = 0.0f
lastY = 0.0f
}
}
return true
}
}
这里有一点需要注意的是,重写了LinearLayout
的onInterceptTouchEvent()
后会导致setOnTouchListener()
里面的ACTION_DOWN
事件接收不到,所以不要在down事件里面写逻辑。然后onTouch
一定要返回true,表示要消费事件,否则当拖拽非ImageView
区域时会拖不动。
好了,花了一整天,就解决了这个小问题。
来源:https://juejin.cn/post/7205023631168028730


猜你喜欢
- TransactionScope是.Net Framework 2.0滞后,新增了一个名称空间。它的用途是为数据库访问提供了一个“轻量级”[
- 新建类Product:class Product{ public string name; &
- 前言本章是关于Java数组的最全汇总,本篇为汇总上篇,主要讲了一维数组的相关内容。数组是最常见的一种数据结构,它是相同类型的用一个标识符封装
- 实现的功能:默认情况下将扫描整个项目的文件可以使用@ComponentScan注解配置扫描路径只将被@Component注解修饰的类装载到容
- 茫茫人海千千万万,感谢这一秒你看到这里。希望我的能对你的有所帮助!共勉!愿你在未来的日子,保持热爱,奔赴山海!Java基础知识(多态)多态因
- springboot项目不配置数据源启动报错spring boot默认会加载org.springframework.boot.autocon
- 这个进度条可以反映真实进度,并且完成百分比的文字时随着进度增加而移动的,所在位置也恰好是真实完成的百分比位置,效果如下:思路如下:第一部分是
- 这篇文章主要介绍了Mybatis Plugin * 开发过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 一直写过数组全排列的算法,当时接触的是使用回溯的方法,这样可以保证生成的全排列一定是按照字典序的,但是今天在做leetcode上的一道题时,
- 本文实例讲述了WinForm实现仿视频播放器左下角滚动新闻效果的方法。分享给大家供大家参考。具体实现方法如下:using System;us
- 在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多关于多线程的
- 一、概述UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.NET框架通过UdpClie
- 本文解析了C# KeyUp事件中MessageBox的回车(Enter)键出现回调问题的解决办法。具体问题如下:在一个窗体上有一个名为txt
- 做Android开发的童靴们肯定对系统自带的控件使用的都非常熟悉,比如Button、TextView、ImageView等。如果你问我具体使
- 在Android开发中;Activity之间传递参数是常见的事;如果我们要在Activity之间传递图片;1。MainActivity中包括
- 首先,定义TabHost的布局文件:<?xml version="1.0" encoding="utf-
- Android listview的滑动冲突解决方法在Android开发的过程中,有时候会遇到子控件和父控件都要滑动的情况,尤其是当子控件为l
- currentThread的详解currentThread方法是Thread类的一个静态方法,用来获取当前运行的代码段,正在被哪个线程调用。
- 由于项目这种类型的图片按钮比较多,所以重写了ImageButton类。package me.henji.widget;import andr
- 前言周六在公司写Reactor模型,一女同事问我为啥都2023年了还在学习Reactor模型呀,我问她为啥快30的年纪了,周六还在公司看我写