When dragging an Imageview goes outside the screen
Asked Answered
E

2

2

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>
Emblazonry answered 4/4, 2016 at 12:29 Comment(0)
E
4

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 :)

Emblazonry answered 5/4, 2016 at 5:13 Comment(4)
I have changed the if statement that fixes the bottom boundary problem. if ((newX <= 0 || newX >= screenWidth - view.getWidth()) || (newY <= 0 || newY + view.getHeight() >= screenHeight ))Curd
What type of variable should be this lastAction? I am new to android and I am not sure about this. Can you kindly let me know?Saddlery
@Prahlad V Thej - Declare lastAction as an Integer(int lastAction). For reference, go through the link developer.android.com/reference/android/view/MotionEventEmblazonry
it' workign fine for top,left and right but not wotking for bottom can you please share me what i have to change in my code for all side prevention?Deathly
U
2

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
}
Urbina answered 19/3, 2021 at 18:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.