android drag view smooth
Asked Answered
A

6

21

I have to drag some views on screen. I am modifying their position by changing left and top of their layout parameters from motion event on ACTION_MOVE from touch listener. Is there a way to "drag" items more smooth? Because tis kind of "dragging" is no smooth at all... Here is the code

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;
}
Amortize answered 16/12, 2011 at 14:31 Comment(2)
try using my code here: https://mcmap.net/q/659809/-how-to-use-correct-dragging-of-a-view-on-android i think it's quite smooth.Dodona
I just upload a video tutorial on Youtube about how to make any view draggable with only one simple assistant class. Check it out: youtu.be/licui_5_iLkPluri
E
26

Try to use motionEvent.getRawX() and motionEvent.getRawY() instead of getY and getX

Etherify answered 8/4, 2013 at 14:6 Comment(0)
S
9

You need not to use LayoutParameters to drag. You can do it by just setting the X and Y coordinates of the view. You can read more on this here.

You can do something like this.

  @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;
  }
Selfdiscipline answered 2/4, 2016 at 8:6 Comment(3)
awesome video nice jobBuccal
but why should we add the delta isn't the view supposed to move to the new pointer position without that space.Buccal
Thank you, perfect answer !!Deem
S
2

enter image description here

One of the simplest and neat way to achieve this is to create a custom view and implement the onTouchListener in that custom view. In the below code snippet we are extending an AppCompatImageView. Instead we can use any kind of views.

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)
}
Sarraute answered 5/8, 2020 at 16:10 Comment(1)
Very nice solutionPled
N
1

The reason of non-smooth move is integer value of leftMargin and topMargin.
For smooth move position should be float.
This could help.

Nihi answered 8/10, 2012 at 18:44 Comment(1)
LayoutParams requires ints there.Scallop
P
0

It would be useful to see how you are processing your ACTION_MOVE events. Are you utilizing all the points using event.getHistorical() method? If that does not give you a smoother drag, other idea might be to interpolate points on the path. I believe there will be a trade-off between achieving smoothness of movement and quick response to user's touch. HTH.

Presumptuous answered 16/12, 2011 at 15:20 Comment(1)
Are you using dragging inside a ScrollView?@gabiRearm
P
0

You should round your ints, also using margins is not going to be very smooth, use a different layout and set X and Y coordinates according to screen dimensions.

Prescott answered 9/6, 2013 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.