Reputation: 958
I want to implement a movable imageView when it is drag. The dragging is working perfectly alright. But when i drag to the end , the imageView goes out of the screen. How can i make the movable of the imageview only inside the screen ?
This is my OnTouch listerner :
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
lastAction = MotionEvent.ACTION_DOWN;
break;
case MotionEvent.ACTION_MOVE:
view.setY(event.getRawY() + dY);
view.setX(event.getRawX() + dX);
lastAction = MotionEvent.ACTION_MOVE;
break;
case MotionEvent.ACTION_UP:
if (lastAction == MotionEvent.ACTION_DOWN)
Toast.makeText(DraggableView.this, "Clicked!", Toast.LENGTH_SHORT).show();
break;
default:
return false;
}
return true;
}
and this is my layout. the layout contain a scrollview and their child, and the imageview which i want to drag.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#ff0000"
android:gravity="center"
android:text="View 1"
android:textColor="#000"
android:textSize="100dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="400dp"
android:background="#00ff00"
android:text="View 2"
android:textColor="#000"
android:textSize="100dp" />
</LinearLayout>
</ScrollView>
<ImageView
android:id="@+id/draggable_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/icon" />
</FrameLayout>
Upvotes: 2
Views: 4007
Reputation: 143
After a lot of research, I found the best robust solution. The following code works perfectly for me keeping the ImageView or any View within Screen bounds. Use the code in ACTION_MOVE
val pointerIndex = event.findPointerIndex(activePointerId)
if (pointerIndex == -1) return false
val (x, y) = event.getX(pointerIndex) to event.getY(pointerIndex)
posX += x - lastTouchX
posY += y - lastTouchY
// The BOUNDS_PADDING can be any value you like (Ex: 50)
val leftBound = -screenWidth + BOUNDS_PADDING
val rightBound = screenWidth - BOUNDS_PADDING
val downBound = -screenHeight + BOUNDS_PADDING
val upBound = screenHeight - BOUNDS_PADDING
if (posX <= leftBound) {
// Left Bound Reached while Dragging. So reset view position to be within bounds
posX = leftBound.toFloat()
}
if (posX >= rightBound) {
// Right Bound Reached while Dragging. So reset view position to be within bounds
posX = rightBound.toFloat()
}
if (posY <= downBound) {
// Bottom Bound Reached while Dragging. So reset view position to be within bounds
posY = downBound.toFloat()
}
if (posY >= upBound) {
// Top Bound Reached while Dragging. So reset view position to be within bounds
posY = upBound.toFloat()
}
binding.imageView.x = posX
binding.imageView.y = posY
lastTouchX = x
lastTouchY = y
You can obtain the screen width and height using the following
// Get Device Display Height and Width
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val wm = windowManager.currentWindowMetrics
val insets = wm.windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
screenWidth = wm.bounds.width() - insets.left - insets.right
screenHeight = wm.bounds.height() - insets.bottom - insets.top
} else {
val dm = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(dm)
screenWidth = dm.widthPixels
screenHeight = dm.heightPixels
}
Upvotes: 2
Reputation: 958
After spend more time on it, finally find the best solution.
Modify my OnTouch listerner section as :
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
screenHight = displaymetrics.heightPixels;
screenWidth = displaymetrics.widthPixels;
@SuppressLint("NewApi") @Override
public boolean onTouch(View view, MotionEvent event) {
float newX, newY;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
lastAction = MotionEvent.ACTION_DOWN;
break;
case MotionEvent.ACTION_MOVE:
newX = event.getRawX() + dX;
newY = event.getRawY() + dY;
// check if the view out of screen
if ((newX <= 0 || newX >= screenWidth-view.getWidth()) || (newY <= 0 || newY >= screenHight-view.getHeight()))
{
lastAction = MotionEvent.ACTION_MOVE;
break;
}
view.setX(newX);
view.setY(newY);
lastAction = MotionEvent.ACTION_MOVE;
break;
case MotionEvent.ACTION_UP:
if (lastAction == MotionEvent.ACTION_DOWN)
Toast.makeText(DraggableView.this, "Clicked!", Toast.LENGTH_SHORT).show();
break;
default:
return false;
}
return true;
}
It's working perfectly :)
Upvotes: 4