вид перетаскивания android гладкий

Мне нужно перетащить некоторые виды на экран. Я изменяю их положение, изменяя левый и верхний параметры их макета из события движения на ACTION_MOVE из сенсорного прослушивателя. Есть ли способ «перетаскивать» элементы более плавно? Потому что такое «перетаскивание» совсем не гладкое... Вот код

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;
}

person Buda Gavril    schedule 16.12.2011    source источник
comment
попробуйте использовать мой код здесь: stackoverflow.com/a/18806475/878126 я думаю, что это довольно гладко.   -  person android developer    schedule 15.09.2013
comment
Я только что загрузил видеоурок на Youtube о том, как сделать любое представление перетаскиваемым с помощью всего одного простого класса помощника. Проверьте это: youtu.be/licui_5_iLk   -  person Jeffrey Chen    schedule 06.04.2016


Ответы (6)


Попробуйте использовать motionEvent.getRawX() и motionEvent.getRawY() вместо getY и getX

person Marylia Gutierrez    schedule 08.04.2013

Вам не нужно использовать 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;
  }
person Ajit Singh    schedule 02.04.2016
comment
классное видео хорошая работа - person Mightian; 13.03.2017
comment
но почему мы должны добавлять дельту, если представление не должно перемещаться в новую позицию указателя без этого пробела. - person Mightian; 13.03.2017
comment
Спасибо, идеальный ответ!! - person Fidan Bacaj; 03.05.2019

Причиной неплавного перемещения является целочисленное значение leftMargin и topMargin.
Для плавного перемещения позиция должна быть плавающей.
Это может помочь.

person Andrei Aulaska    schedule 08.10.2012
comment
LayoutParams требует целых чисел. - person ; 21.03.2013

введите здесь описание изображения

Один из самых простых и изящных способов добиться этого — создать собственное представление и реализовать 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)
}
person Jossy Paul    schedule 05.08.2020
comment
Очень хорошее решение - person james04; 15.05.2021

Было бы полезно посмотреть, как вы обрабатываете свои события ACTION_MOVE. Используете ли вы все точки, используя метод event.getHistorical()? Если это не дает вам более плавного перетаскивания, другой идеей может быть интерполяция точек на пути. Я считаю, что будет компромисс между достижением плавности движения и быстрой реакцией на прикосновение пользователя. ХТН.

person Sameer    schedule 16.12.2011
comment
Вы используете перетаскивание внутри ScrollView? @gabi - person MGDroid; 06.10.2012

Вы должны округлить свои целые числа, также использование полей не будет очень гладким, используйте другой макет и установите координаты X и Y в соответствии с размерами экрана.

person Gerard    schedule 09.06.2013