您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Android事件沖突怎么解決懸浮窗拖拽問題”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Android事件沖突怎么解決懸浮窗拖拽問題”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
最近項目中要做一個音樂播放懸浮按鈕的功能,最終實現效果如下:
懸浮窗布局文件就不放了,就是水平LinearLayout
里面放幾個ImageView
。
做的過程當中遇到一個問題,就是懸浮窗是可以任意拖拽的,懸浮窗里面的按鈕是可以點擊的,比如暫停,下一曲,關閉懸浮窗等。
按常規思路,先給整個懸浮窗setOnTouchListener()
,然后再給你里面的按鈕setOnClickListener()
,點擊運行,結果發現,點擊事件是可以響應,拖拽也沒問題,但是當手指放在ImageView
上拖拽時,onTouchListener
事件無法響應。
此時第一感覺就是setOnTouchListener()
和setOnClickListener()
沖突了,需要解決一下沖突。無奈自己對Android事件分發消費機制一直都是一知半解的,一般都是出了問題需要解決,第一時間先百度,沒有解決方案就只能去研究Android事件分發消費機制了,但是研究完也都是懵懵懂懂的,今天就決定把這個難點徹底消化掉。
主要研究了這篇文章Android事件分發消費機制,然后對照著寫了個demo,一一去驗證,加深了自己的理解,最后終于解決了我的問題。
先說下解決思路,自定義LinearLayout
,當手指處于滑動時,直接攔截事件,交給自己的onTouchEvent
處理即可,核心代碼如下:
/** * @author:Jason * @date:2021/8/24 19:49 * @email:1129847330@qq.com * @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 * @email:1129847330@qq.com * @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
區域時會拖不動。
讀到這里,這篇“Android事件沖突怎么解決懸浮窗拖拽問題”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。