Android事件沖突解決懸浮窗拖拽處理方案
需求場(chǎng)景
最近項(xiàng)目中要做一個(gè)音樂播放懸浮按鈕的功能,最終實(shí)現(xiàn)效果如下:
問題暴露
懸浮窗布局文件就不放了,就是水平LinearLayout
里面放幾個(gè)ImageView
。
做的過程當(dāng)中遇到一個(gè)問題,就是懸浮窗是可以任意拖拽的,懸浮窗里面的按鈕是可以點(diǎn)擊的,比如暫停,下一曲,關(guān)閉懸浮窗等。
按常規(guī)思路,先給整個(gè)懸浮窗setOnTouchListener()
,然后再給你里面的按鈕setOnClickListener()
,點(diǎn)擊運(yùn)行,結(jié)果發(fā)現(xiàn),點(diǎn)擊事件是可以響應(yīng),拖拽也沒問題,但是當(dāng)手指放在ImageView
上拖拽時(shí),onTouchListener
事件無法響應(yīng)。
此時(shí)第一感覺就是setOnTouchListener()
和setOnClickListener()
沖突了,需要解決一下沖突。無奈自己對(duì)Android事件分發(fā)消費(fèi)機(jī)制一直都是一知半解的,一般都是出了問題需要解決,第一時(shí)間先百度,沒有解決方案就只能去研究Android事件分發(fā)消費(fèi)機(jī)制了,但是研究完也都是懵懵懂懂的,今天就決定把這個(gè)難點(diǎn)徹底消化掉。
主要研究了這篇文章Android事件分發(fā)消費(fèi)機(jī)制,然后對(duì)照著寫了個(gè)demo,一一去驗(yàn)證,加深了自己的理解,最后終于解決了我的問題。
解決思路
先說下解決思路,自定義LinearLayout
,當(dāng)手指處于滑動(dòng)時(shí),直接攔截事件,交給自己的onTouchEvent
處理即可,核心代碼如下:
/** * @author:Jason * @date:2021/8/24 19:49 * @description:可拖拽的LinearLayout,解決子View設(shè)置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()
,在里面實(shí)現(xiàn)拖拽功能,但是這樣就固定死了,所以我選擇在外面setOnTouchListener()
,需要拖拽功能時(shí)才去實(shí)現(xiàn)
/** * 創(chuàng)建懸浮窗 */ @SuppressLint("ClickableViewAccessibility") private fun showWindow() { //獲取WindowManager windowManager = getSystemService(WINDOW_SERVICE) as WindowManager val outMetrics = DisplayMetrics() windowManager.defaultDisplay.getMetrics(outMetrics) var layoutParam = WindowManager.LayoutParams().apply { /** * 設(shè)置type 這里進(jìn)行了兼容 */ 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 //位置大小設(shè)置 width = WRAP_CONTENT height = WRAP_CONTENT gravity = Gravity.LEFT or Gravity.TOP //設(shè)置劇中屏幕顯示 x = outMetrics.widthPixels / 2 - width / 2 y = outMetrics.heightPixels / 2 - height / 2 } //在這里設(shè)置觸摸監(jiān)聽,以實(shí)現(xiàn)拖拽功能 floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager)) // 將懸浮窗控件添加到WindowManager windowManager.addView(floatRootView, layoutParam) isAdded = true }
主要是這行代碼
//在這里設(shè)置觸摸監(jiān)聽,以實(shí)現(xiàn)拖拽功能 floatRootView?.setOnTouchListener(ItemViewTouchListener(layoutParam, windowManager))
ItemViewTouchListener.kt
文件內(nèi)容
/** * @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事件會(huì)接收不到,所以初始位置需要在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 } }
這里有一點(diǎn)需要注意的是,重寫了LinearLayout
的onInterceptTouchEvent()
后會(huì)導(dǎo)致setOnTouchListener()
里面的ACTION_DOWN
事件接收不到,所以不要在down事件里面寫邏輯。然后onTouch
一定要返回true,表示要消費(fèi)事件,否則當(dāng)拖拽非ImageView
區(qū)域時(shí)會(huì)拖不動(dòng)。
好了,花了一整天,就解決了這個(gè)小問題。
以上就是Android事件沖突解決懸浮窗拖拽處理方案的詳細(xì)內(nèi)容,更多關(guān)于Android事件沖突懸浮窗拖拽的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android編程程序?qū)崿F(xiàn)一鍵鎖屏的方法講解
今天小編就為大家分享一篇關(guān)于Android編程程序?qū)崿F(xiàn)一鍵鎖屏的方法講解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03Android實(shí)現(xiàn)讀寫JSON數(shù)據(jù)的方法
這篇文章主要介紹了Android實(shí)現(xiàn)讀寫JSON數(shù)據(jù)的方法,以完整實(shí)例形式分析了Android解析及生成json數(shù)據(jù)的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10android為ListView每個(gè)Item上面的按鈕添加事件
本篇文章主要介紹了android為ListView每個(gè)Item上面的按鈕添加事件,有興趣的同學(xué)可以了解一下。2016-11-11NestScrollView嵌套R(shí)ecyclerView實(shí)現(xiàn)淘寶首頁滑動(dòng)效果
這篇文章主要介紹了NestScrollView嵌套R(shí)ecyclerView實(shí)現(xiàn)淘寶首頁滑動(dòng)效果,主要實(shí)現(xiàn)淘寶首頁嵌套滑動(dòng),中間tab吸頂效果,以及介紹NestScrollView嵌套R(shí)ecyclerView處理滑動(dòng)沖突的方法,需要的朋友可以參考下2021-12-12Android中AutoCompleteTextView與MultiAutoCompleteTextView的用法
這篇文章主要介紹了Android中AutoCompleteTextView與MultiAutoCompleteTextView的用法,需要的朋友可以參考下2014-07-07Android中TextView顯示圓圈背景或設(shè)置圓角的方法
TextView顯示文本給用戶,并允許他們選擇編輯。TextView是一個(gè)完整的文本編輯器,但是其基本類配置為不允許編輯。下面這篇文章主要給大家介紹了關(guān)于Android中TextView顯示圓圈背景或設(shè)置圓角的方法,需要的朋友可以參考借鑒,下面來一起看看吧。2017-05-05Android編程實(shí)現(xiàn)使用webView打開本地html文件的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)使用webView打開本地html文件的方法,結(jié)合實(shí)例形式分析了Android中webview布局及打開HTML文件的功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-02-02Android自定義扇形倒計(jì)時(shí)實(shí)例代碼
最近工作中需要做一個(gè)倒計(jì)時(shí),是那種一個(gè)圓,慢慢的被吃掉的動(dòng)畫倒計(jì)時(shí),由于自己是android小白,效果還不是多滿意,先給大家分享實(shí)例代碼,僅供大家參考2017-03-03