I am creating a rectangular box and putting an imageView inside it which can be moved. While doing onMove I noticed that imageView is going out of boundries of it's parent view which I don't want. I want to restrict the imageView within the boundaries of it's parent view. How to do that.
Here is the xml:
<FrameLayout
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:id="#+id/objContainer"
android:background="#android:color/holo_blue_bright" >
<ImageView
android:id="#+id/iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#android:drawable/btn_default" />
</FrameLayout>
</RelativeLayout>
And code to handletouch event:
#Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
final float x = event.getRawX();
final float y = event.getRawY();
// Remember where we started
mLastTouchX = x;
mLastTouchY = y;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
final float x1 = event.getRawX();
final float y1 = event.getRawY();
// Calculate the distance moved
final float dx = x1 - mLastTouchX;
final float dy = y1 - mLastTouchY;
// Move the object
mPosX += dx;
mPosY += dy;
view.setX(mPosX);
view.setY(mPosY);
// Remember this touch position for the next move event
mLastTouchX = x1;
mLastTouchY = y1;
// Invalidate to request a redraw
root.invalidate();
break;
}
return true;
}}
Inside of your overridden onTouch you need to check if the following move will keep the ImageView inside its container.
In other words you need to check if ImageView's rectangle will stay inside the parent after being moved by dx, dy.
Here's an example:
case MotionEvent.ACTION_MOVE:
...
// Calculate the distance moved
final float dx = x1 - mLastTouchX;
final float dy = y1 - mLastTouchY;
// Make sure we will still be the in parent's container
Rect parent = new Rect(0, 0, root.getWidth(), root.getHeight());
int newLeft = (int) (iv.getX() + dx),
newTop = (int) (iv.getY() + dy),
newRight = newLeft + iv.getWidth(),
newBottom = newTop + iv.getHeight();
if (!parent.contains(newLeft, newTop, newRight, newBottom)) {
Log.e(TAG, String.format("OOB # %d, %d - %d, %d",
newLeft, newTop, newRight, newBottom));
break;
}
...
Note also that you don't need to invalidate the container, as setX/setY invalidates the ImageView itself.
Related
I need to click on particular item on the canvas while zooming and moving functionalities also enable for canvas. I can calculate the rectangle position while moving the canvas. There I just calculate the touch movement distance by (CurrenTouchXPosition - StartXPosition).
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
float moveOffsetX = (event.getX() - start.x);
float moveOffsetY = (event.getY() - start.y);
Then,
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "action up");
secondRectUpperX = secondRectUpperX + moveOffsetX;
secondRectBottomX = secondRectBottomX + moveOffsetX;
secondRectUpperY = secondRectUpperY + moveOffsetY;
secondRectBottomY = secondRectBottomY + moveOffsetY;
This can identify the new canvas position of the rectangle. This works perfectly. I can identify the touch event of particular item while moving the canvas by this logic.
But now i need to calculate the rectangle position relative to the canvas, after zoom the canvas. Whats the maths behind the zooming. If anyone knows please help in this.
Thank you.
Finally I come up with a solution.
I translate my touch position screen coordinates to canvas coordinates.
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_POINTER_DOWN:
...
break;
case MotionEvent.ACTION_UP:
float []m = new float[9];
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X] * -1;
float transY = m[Matrix.MTRANS_Y] * -1;
float scaleX = m[Matrix.MSCALE_X];
float scaleY = m[Matrix.MSCALE_Y];
lastTouchX = (int) ((event.getX() + transX) / scaleX);
lastTouchY = (int) ((event.getY() + transY) / scaleY);
lastTouchX = Math.abs(lastTouchX);
lastTouchY = Math.abs(lastTouchY);
Thanks for Andres Cardenas Pardo's answer here
I could able to get the touch position coordinates according to the canvas coordinates. Since i know the coordinates of my drawn object, i check whether the touch position is within the range of drawn object.
if((lastTouchX>=firstRectUpperX && firstRectBottomX>=lastTouchX) && (lastTouchY>=firstRectUpperY && firstRectBottomY>=lastTouchY)) {
isbtn1Clicked = true;
}
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>
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 :)
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
}
I am try to implement the vertical slider of image control. The image is inside the ScrollView. When it comes to vertical dragging of ImageView, the top margin of relative layout always provide different reading.
If it is greater than 600 something while dragging down, the background image of the relative layout stretch vertically together with the image position I have dragged.
<ScrollView
android:id="#+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:fillViewport="true" >
...
<RelativeLayout
android:id="#+id/relativeLayouyt6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#+id/rTop"
android:background="#drawable/plain" >
<ImageView
android:id="#+id/dragImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/arrow_one"
android:visibility="gone" />
</RelativeLayout>
Would you please tell me how to
get the offset I have scrolled in my scrollview ?
I know it is get touch position minus scrolled position and plus image Y position, how to implement this parameter after finishing dragging ?
How to set relative boundary for imageview inside the relative layout ? is it wiser to take this background image out as the imageview?
If I programmatically repositioning of 6 relativelayouts but coming up to the same width , would it affect the scrolling position and the scollview total Height ? If so , How to calculate the offset Y for the repositioning ?
The below is my code as of February 11:
dragImage.setOnTouchListener(new OnTouchListener(){
//private int _xDelta;
private int _yDelta;
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
final float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//_yDelta = Y - lParams.topMargin;
mOldY2 = y;
break;
case MotionEvent.ACTION_UP:
mViewPager.setPagingEnabled(true);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
btn4.setBackgroundResource(R.drawable.fbl02);
final float dy = y - mOldY2;
mNewY2 += dy;
mOldY2 = y;
System.out.println(mNewY2);
while(mNewY2 > 224){
mNewY2 -= 224;
}
while(mNewY2 < 157){
mNewY2 += 157;
}
if(mNewY2 < 157 || mNewY2 > 224)
break;
v.setTranslationY((int)mNewY2);
v.invalidate();
float power = (float) ( 51.5/67 -(0.2/67) * mNewY2) ;
System.out.println(power);
Float roundF = new Float( Math.round(power));
midBandStick = roundF;
btn4.setText(String.valueOf(midBandStick) );
//}
//break;
}
return true;
}
The below is my code :
public static void setRLBelowAnother(RelativeLayout rA , RelativeLayout rB ){
RelativeLayout.LayoutParams rparam4 =
(android.widget.RelativeLayout.LayoutParams) rB.getLayoutParams();
rparam4.addRule(RelativeLayout.BELOW, rA.getId());
rB.setLayoutParams(rparam4);
}
setRLBelowAnother(rTop , r1);
setRLBelowAnother(r1 , r2);
setRLBelowAnother(r2 , r6 );
setRLBelowAnother(r6 , r3 );
setRLBelowAnother(r3 , r4 );
setRLBelowAnother(r4 , r5 );
dragImage.setVisibility(View.VISIBLE);
dragImage.setImageResource(R.drawable.slide_lshort);
dragImage.setX((float) (0.15*screenWidth));
dragImage.setY((float) (0.05*screenHeight));
dragImage.setOnTouchListener(new OnTouchListener(){
//private int _xDelta;
private int _yDelta;
#Override
public boolean onTouch(View v, MotionEvent event) {
v.getParent().requestDisallowInterceptTouchEvent(true);
final int X = (int) event.getX();
final int Y = (int) event.getY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) dragImage
.getLayoutParams();
//_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
mViewPager.setPagingEnabled(true);
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
midStick = 0.2f;
btn4.setBackgroundResource(R.drawable.fbl02);
RelativeLayout.LayoutParams ParamsA = (RelativeLayout.LayoutParams) dragImage
.getLayoutParams();
//ParamsA.leftMargin = X - _xDelta;
ParamsA.topMargin = Y - _yDelta;
//ParamsA.rightMargin = -250;
ParamsA.bottomMargin = -250;
mViewPager.setPagingEnabled(false);
int offYb = 0;
int pos = ParamsA.topMargin + offYb ;
if(pos > -52 && pos < 582 ){
dragImage.setLayoutParams(ParamsA);
System.out.println(ParamsA.topMargin);
float power = (float) (100 + (900/634) * ParamsA.topMargin) ;
Float roundF = new Float( Math.round(power));
midStick = roundF;
btn4.setText(String.valueOf(midStick));
}
break;
}
return true;
}
});
Call the getScrollY() method from the ScrollView to get the Y index (scrolled index)
If I understood (otherwise please correct me) you could get a frame (boundary) between the ImageView and the RelativeLayout adding padding or margin to the ImageView. You just need to call android:padding="" or android:margin=""
The height of the ScrollView will change and also the scrollY if the new added RelativeLayout/ImageView doesn't completely fit on the available space. When you finish adding the new layout you could get the scrollY from the ScrollView and see where the ScrollView has scrolled to.
Can you improve the questions 2, 3 and 4? It's quite confusing.
I have a circle and triangle images inside a view pager.
I am trying to move the triangle image along the circle image using on-touch listener.
But when i move the triangle, the screen/fragment also moves to the next as i am using a view pager.
What do i do so that when the on-touch listener of the triangle or circle is called then view pager on touch listener is not called.
UPDATE: ADDING CODE
mCurrTempIndicator.setOnTouchListener(new MyOnTouchListener());
private class MyOnTouchListener implements OnTouchListener {
private double startAngle;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mThermostatCentreXOnScreen == 0 || mThermostatCentreYOnScreen == 0){
int centerXOnImage=mThermostatBgrd.getWidth()/2;
int centerYOnImage=mThermostatBgrd.getHeight()/2;
mThermostatCentreXOnScreen=mThermostatBgrd.getLeft()+centerXOnImage;
mThermostatCentreYOnScreen=mThermostatBgrd.getTop()+centerYOnImage;
mStartPosX = event.getRawX();
mStartPosY = event.getRawY();
float dx = event.getX() - mThermostatCentreXOnScreen;
float dy = event.getY() - mThermostatCentreYOnScreen;
float r = FloatMath.sqrt((dx * dx) + (dy * dy));
////
break;
case MotionEvent.ACTION_MOVE:
dx = event.getX() - mThermostatCentreXOnScreen;
dy = event.getY() - mThermostatCentreYOnScreen;
r = FloatMath.sqrt((dx * dx) + (dy * dy));
mEndPosX = event.getX();
mEndPosY = event.getY();
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
return true;
}
Thanks and regards,
Sunny
If you return true in your onTouchListener for the triangle and circle it will consume the event and the parent won't receive it.
I am trying to move an ImageView (not rotate). The movement is supposed to be on the edge of a circle. This circle is also an image view.
based on the onTouch, ACTION_MOVE event, I am trying to move it.
Noe the Dilema is that the use may not move the finger in a perfectly circular fashion but I would like to make sure that the image still moves around edge of this circle.
I am currently using the following inside ACTION_MOVE:
mCurrTempIndicator.setTranslationX(event.getX());
mCurrTempIndicator.setTranslationY(event.getY());
But this will not move in a perfect circle.
could someone please help.
UPDATE: code
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = event.getX();
mEndY = event.getY();
float deltaX = mEndX - mInitialX;
float deltaY = mEndY - mInitialY;
double angleInDegrees = Math.atan(deltaY / deltaX) * 180 / Math.PI;
mInitialX = mEndX;
mInitialY = mEndY;
mCurrTempIndicator.setRotation((float)angleInDegrees);
mCurrTempIndicator.setTranslationX((float)(310*(Math.cos(angleInDegrees))));
mCurrTempIndicator.setTranslationY((float)(310*(Math.sin(angleInDegrees))));
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
return true;
}
calculate the center Point of the Circle
get the current touch point
calculate the angle between center and new touch point
Calculate the point on the circle using angle and radius of circle (x = r * cos(angle), y = r * sin(angle)).
Reset the image position to the new point.
To get the angle use the below equation
deltaY = P2_y - P1_y
deltaX = P2_x - P1_x
angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
//Code inside ACTION_MOVE case
mInitialX = event.getX();
mInitialY = event.getY();
float deltaX = circleCenter.x - mInitialX;
float deltaY = circleCenter.y - mInitialY;
double angleInRadian = Math.atan2(yDiff, xDiff);
PointF pointOnCircle = new PointF();
pointOnCircle.x = circleCenter.x + ((float)(circleRadius*(Math.cos(angleInRadian))));
pointOnCircle.y = circleCenter.y + ((float)(circleRadius*(Math.cos(angleInRadian))));