You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确创建可拖动悬浮视图?解决首次触摸偏移问题

修复浮动视图首次触摸跳转问题

问题根源在于你在ACTION_DOWN时错误使用了moveIcon的相对坐标(view.x/view.y)计算触摸偏移,而浮动视图的实际位置由WindowManager.LayoutParams的x/y控制,二者并不等价,导致首次触摸时计算出的目标位置错误。

以下是修复后的完整代码:

private lateinit var windowManager: WindowManager
private var floatingView: View? = null
private var moveIcon: View? = null
// 新增变量记录初始触摸坐标和视图当前位置
private var initialRawX = 0f
private var initialRawY = 0f
private var currentLayoutX = 0
private var currentLayoutY = 0

// 初始化浮动视图的代码
fun initFloatingView() {
    windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val layoutInflater = LayoutInflater.from(this)
    floatingView = layoutInflater.inflate(R.layout.floating_layout, null)

    val params = WindowManager.LayoutParams(
        WindowManager.LayoutParams.WRAP_CONTENT,
        WindowManager.LayoutParams.WRAP_CONTENT,
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
        } else {
            @Suppress("DEPRECATION")
            WindowManager.LayoutParams.TYPE_PHONE
        },
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT
    )
    // 可选:设置初始位置,比如屏幕右下角(需先测量视图尺寸)
    // floatingView?.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
    // params.x = resources.displayMetrics.widthPixels - floatingView!!.measuredWidth
    // params.y = resources.displayMetrics.heightPixels - floatingView!!.measuredHeight

    windowManager.addView(floatingView, params)

    moveIcon = floatingView!!.findViewById(R.id.moveIcon)

    moveIcon.setOnTouchListener { _, event ->
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                // 获取浮动视图当前的布局参数
                val layoutParams = floatingView!!.layoutParams as WindowManager.LayoutParams
                // 记录视图当前的实际位置
                currentLayoutX = layoutParams.x
                currentLayoutY = layoutParams.y
                // 记录触摸点的绝对屏幕坐标
                initialRawX = event.rawX
                initialRawY = event.rawY
            }
            MotionEvent.ACTION_MOVE -> {
                // 计算触摸移动的偏移量
                val deltaX = (event.rawX - initialRawX).toInt()
                val deltaY = (event.rawY - initialRawY).toInt()
                // 计算新位置:当前位置 + 触摸偏移量
                val newX = currentLayoutX + deltaX
                val newY = currentLayoutY + deltaY
                updatePosition(newX, newY)
            }
        }
        true
    }
}

private fun updatePosition(x: Int, y: Int) {
    val layoutParams = floatingView!!.layoutParams as WindowManager.LayoutParams
    layoutParams.x = x
    layoutParams.y = y
    windowManager.updateViewLayout(floatingView, layoutParams)
}

关键修复点:

  • 移除基于moveIcon相对坐标的偏移计算,改为直接记录浮动视图的实际位置(layoutParams.x/y)和触摸点的绝对屏幕坐标
  • 在ACTION_MOVE时,通过触摸点的偏移量叠加到视图当前位置,确保拖动过程平滑无跳转

内容的提问来源于stack exchange,提问作者user19763422

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:24:53