安卓拖动视图平滑实现

21

我需要在屏幕上拖动一些视图。我通过从触摸监听器的运动事件中更改它们的布局参数的左侧和顶部来修改它们的位置。有没有办法使这种“拖动”更加平滑?因为目前这种方式非常不流畅... 下面是代码:

public boolean onTouch(View view, MotionEvent motionEvent) {
    switch (motionEvent.getAction()) {
        case MotionEvent.ACTION_DOWN:
            dx = (int) motionEvent.getX();
            dy = (int) motionEvent.getY();
            break;

        case MotionEvent.ACTION_MOVE:
            int x = (int) motionEvent.getX();
            int y = (int) motionEvent.getY();
            RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) view.getLayoutParams();
            int left = lp.leftMargin + (x - dx);
            int top = lp.topMargin + (y - dy);
            lp.leftMargin = left;
            lp.topMargin = top;
            view.setLayoutParams(lp);
            break;
    }
    return true;
}

1
尝试使用我的代码:https://dev59.com/ZFzUa4cB1Zd3GeqP4ZPD#18806475我认为它非常流畅。 - android developer
我刚刚在Youtube上发布了一个视频教程,讲述如何使用一个简单的助手类使任何视图可拖动。快去看看吧:https://youtu.be/licui_5_iLk - Jeffrey Chen
6个回答

26

尝试使用motionEvent.getRawX()和motionEvent.getRawY()代替getY和getX。


9

您不需要使用LayoutParameters来进行拖动。您只需设置视图的X和Y坐标即可完成。您可以在此处阅读更多内容

您可以像这样做。

  @Override
  public boolean onTouch(View view, MotionEvent event) {
    switch (event.getActionMasked()) {
      case MotionEvent.ACTION_DOWN:
        dX = view.getX() - event.getRawX();
        dY = view.getY() - event.getRawY();
        break;

      case MotionEvent.ACTION_MOVE:
        view.setY(event.getRawY() + dY);
        view.setX(event.getRawX() + dX);
        break;

      default:
        return false;
    }
    return true;
  }

了不起的视频,干得好! - Mightian
但是为什么我们要添加delta呢?难道视图不应该在没有那个空间的情况下移动到新的指针位置吗? - Mightian
谢谢,完美的答案!! - Fidan Bacaj

2

实现这个的最简单、最整洁的方法之一是创建一个自定义视图,并在该自定义视图中实现onTouchListener。在下面的代码片段中,我们扩展了一个AppCompatImageView。但是我们可以使用任何类型的视图。

输入图像描述

class DraggableImageView(context: Context, attrs: AttributeSet) :
    AppCompatImageView(context, attrs) {

    private var draggableListener: DraggableListener? = null
    private var widgetInitialX: Float = 0F
    private var widgetDX: Float = 0F
    private var widgetInitialY: Float = 0F
    private var widgetDY: Float = 0F

    init {
        draggableSetup()
    }

    private fun draggableSetup() {
        this.setOnTouchListener { v, event ->
            val viewParent = v.parent as View
            val parentHeight = viewParent.height
            val parentWidth = viewParent.width
            val xMax = parentWidth - v.width
            val xMiddle = parentWidth / 2
            val yMax = parentHeight - v.height

            when (event.actionMasked) {
                MotionEvent.ACTION_DOWN -> {
                    widgetDX = v.x - event.rawX
                    widgetDY = v.y - event.rawY
                    widgetInitialX = v.x
                    widgetInitialY = v.y
                }
                MotionEvent.ACTION_MOVE -> {
                    var newX = event.rawX + widgetDX
                    newX = max(0F, newX)
                    newX = min(xMax.toFloat(), newX)
                    v.x = newX

                    var newY = event.rawY + widgetDY
                    newY = max(0F, newY)
                    newY = min(yMax.toFloat(), newY)
                    v.y = newY

                    draggableListener?.onPositionChanged(v)
                }
                MotionEvent.ACTION_UP -> {
                    if (event.rawX >= xMiddle) {
                        v.animate().x(xMax.toFloat())
                            .setDuration(Draggable.DURATION_MILLIS)
                            .setUpdateListener { draggableListener?.onPositionChanged(v) }
                            .start()
                    } else {
                        v.animate().x(0F).setDuration(Draggable.DURATION_MILLIS)
                            .setUpdateListener { draggableListener?.onPositionChanged(v) }
                            .start()
                    }
                    if (abs(v.x - widgetInitialX) <= DRAG_TOLERANCE && abs(v.y - widgetInitialY) <= DRAG_TOLERANCE) {
                        performClick()
                    } else draggableListener?.xAxisChanged(event.rawX >= xMiddle)
                }
                else -> return@setOnTouchListener false
            }
            true
        }
    }

    override fun performClick(): Boolean {
        Log.d("DraggableImageView", "click")
        return super.performClick()
    }

    fun setListener(draggableListener: DraggableListener?) {
        this.draggableListener = draggableListener
    }
}

object Draggable {
    const val DRAG_TOLERANCE = 16
    const val DURATION_MILLIS = 250L
}

interface DraggableListener {
    fun onPositionChanged(view: View)
    fun xAxisChanged(isInRightSide: Boolean)
}

非常好的解决方案。 - james04

1
非平滑移动的原因是leftMargin和topMargin的整数值。
为了平滑移动,位置应该是浮点数。
这里 可以帮助解决问题。

2
LayoutParams需要整数。 - user153275

0

看到您如何处理ACTION_MOVE事件会很有用。 您是否使用event.getHistorical()方法利用所有点? 如果这不能使拖动更平滑,另一个想法可能是在路径上插值点。我相信在实现运动平滑度和快速响应用户触摸之间会有一个权衡。希望对您有所帮助。


你在ScrollView中使用拖动了吗?@gabi - MGDroid

0

你应该对整数进行四舍五入,同时使用边距不会很平滑,使用不同的布局并根据屏幕尺寸设置X和Y坐标。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接