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;
}
Try to use motionEvent.getRawX() and motionEvent.getRawY() instead of getY and getX
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;
}
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)
}
The reason of non-smooth move is integer value of leftMargin and topMargin.
For smooth move position should be float.
This could help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With