I am adding some ImageViews to a relative layout dynamically (on touch points)
Any ImageView has an OnTouchListener. In Action Move I move the ImageView(sth like Drag and Drop).
My problem is that ImageViews may overlap each other when they are moving. I want to prevent them from overlap in any way! (best is stop moving when collision happen)
Look at this image
All ImageViews are saved in a link list. I get the x & y of moving view and compare it with other views and if they had collision don't let them moving.
But I want that user can make them separate again. How can I do this work?
In brief when views collision happen I don't let them move any more but I want if the move was making them far from each other it happens.
Try this
Rect rc1 = new Rect();
imageView1.getDrawingRect(rc1);
Rect rc2 = new Rect();
imageView2.getDrawingRect(rc2);
if (Rect.intersects(rc1, rc2) {
// intersection is detected
// here is your method call
}
Related
I have a 4X4 grid of textviews inside a constraintlayout. I also have an imageview that moves around the textviews upon touch input.
I override onGlobalLayout in order to record the tag id of each textview and its global location.
//inside onGlobalLayout
Rect rect = new Rect();
textviewMap.put(view.getTag().toString() , viewgetGlobalVisibleRect(rect));
So, the location of each textview is recorded in the map.
When user touches and moves the gamepiece I create a rect which computes if it intersects one of the rects inside the textviewMap!
I am able to detect the exact rect that it intersects and therefore move the gamepiece(imageview) to the centerX() and centerY() position of the rect that belongs to the textview.
Everything works wonderfully when moving horizontally. But vertically somehow the viewproperty animation does not work!
this is how I call the viewproperty animation:
if(direction.equals("horizontal")){ //works perfectly
gamepiece.animate().x(centerX()).setInterpolator(new
OverShootInterpolator).setDuration(600);
}
if(direction.equals("vertical")){ //not working good
gamepiece.animate().y(centerY()).setInterpolator(new
OverShootInterpolator).setDuration(600);
}
on the vertical movement the gamepiece jumps the textview below it and then if I try to move it up one textview it does not go. If I move two textviews vertically the gamepiece jumps only one textview NOT two.
The animations have a Animator.AnimatorListener and in the onAnimationEnd() I have tried calling:
gamepiece.clearAnimation();
animator.removeAllListeners();
Seems like this animation needs to be reset but on the vertical movement it does not work only in the horizontal movement!
see the picture I have attached!
Please help
i have a button which rotates in the middle of the screen using an ObjectAnimator and another button (which will be a square or a circle) moving back and forth, which sometimes does overlap on the first.
I need to know when the first button is covered by the second one, however the first button does rotate, so i can't just compare the two rectangles.
Does anybody know a solution?
edit
I've been trying pturner's solution but there have been a few issues that i can't fix anyhow:
The rotating button's rectangle's values seem to be generated from the origin of the axis, rather than the origin of the button, thus the moving button will believe he's intersecting the rotating button in the top-left side of the screen.
When the rotating button rotates, the rectangles' values will eventually invert, becoming negative and thus flipping left/right and top/bottom. As a result at certain angles the moving button believes to always be overlapping, while at some other it believes to never overlap.
I really have no clue how to fix those issues, i'll just leave this question here hoping for someone to know a solution.
I think you should be able to get the rects for both views, and then transform them with the matrix, something along the lines of:
Matrix matrix1 = button1.getMatrix();
Rect box1 = new Rect();
button1.getDrawingRect(box1);
Rectf boxf1 = new RectF(box1);
matrix1.mapRect(boxf1);
Matrix matrix2 = button2.getMatrix();
Rect box2 = new Rect();
button2.getDrawingRect(box2);
Rectf boxf2 = new RectF(box2);
matrix2.mapRect(boxf2);
boolean intersect = boxf1.intersect(boxf2);
My parent view is a RelativeLayout, I added a triangle to it extending the View class. I draw the triangle using canvas and paint. The problem i am facing is that, when i touch on the relative layout, both touch listeners , the relative layout and triangle are being triggered. I just want the triangle to take the exact space as it requires. How can i limit my customview , triangle in this case to take it's space, rather than occupying the entire parent layout.
Actually my requirement is: i have one relative layout on the layout i am adding some custom views dynamically. those have touch listeners for for dragging points in the triangle,but those are taking full screen of my parent view, because of that i am unable to trigger touch listeners separately for parent and child view's.
I've spent countless hours developing solutions for this problem and I just couldn't get my head around it. Any help would be greatly appreciated.
From what I am gathering you are saying,you have a triangle drawn within a RelativeLayout in a CustomView. The actual View size is FILL_PARENT for both width and height. So the square representing the touchable area of the triangle is the same as the square representing the touchable area of the parent RelativeLayout.
You would like touch events inside the triangle's drawn area to be received by the triangle View and anything outside to be received by the parent. It is not possible to define a custom touch area from what I understand.
That doesn't stop you manipulating drawing bounds and touch focus to accomplish what you want though.
How it can be done
Ensure that your CustomView with the triange always receives the touch event. When it receives the touch event, it perform basic maths to figure out if the touch was within your triangle area (you must have your triangle bounds you use to draw your triangle saved as global variables for the CustomView).
Then a simple conditional statement to determine whether to act on and consume or pass the event on (do you even want a TouchEvent on your RelativeLayout to do anything? If not that makes this a lot easier).
Example code skeleton:
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean inTriangle = false;
float x = event.getX();
float y = event.getY();
// This really depends on what behaviour you want, if a drag extends outside the
// triangle bounds, do you want this to end the touch event? This code could be done
// more efficiently depending on your choices / needs
// Do maths to figure out if the point is actually inside the triangle. Once again,
// many solutions and some much easier than others depending on type of triangle
// This flow will only register events in the triangle, and do nothing when outside
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (inTriangle) {
// Do whatever it is you want to do
return true; // to say you consumed the event if necessary
} else {
return false; // parent RelativeLayout should now get touch event
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (inTriangle) {
// Do whatever it is you want
return true;
} else {
// Do nothing
return true; // So is stil consumed
}
}
return true;
}
To be honest, the question is way to open-ended / unclear to give anything more specific. But this is how you would accomplish what you asked. Just apply it to your specific scenario.
Note: I have encountered issues with TouchEvent passing, so if you really do want to pass them to specific places, you may have to investigate toying with View.dispatchTouchEvent() if the order your Touches are being processed becomes an issue.
You have an issue to handle the touch events of the triangle because you are getting touch on both the view RalativeLayout and Your customview.
To solve this issue you have to do some changes in the customview's onTouchEvent.
Like If you are touching on the customview and if you want to handle the event then from onTouchEvent you will have to return true instead of false so that your parent view will not get the touch events.
I have a activity where I have two imageViews. In onTouch I recognize which imageView was clicked. Now I have a question. How I can draw new very small image in place where I clicked. I know that onTouch i have getX() and getY() and I save this position when I touch the screen, but I don't know how I can draw image in place where I clicked. My image is in drawable resources. I need to use canvas or is better way. I need draw this picture and after that I need run animation this image.
edit1: How can I set positionX and positionY my image. I have position where I click but I don't know how I can set image in this coordinates.
you should have a image view with visible attribute of FALSE and after touch (or any event you want) set three attribute : visible = true; x = getX() and y = getY();
is it enough or explain more?
I see the following approach.
Replace the ImageView by a custom view derived from ImageView.
Override onDraw.
call super.onDraw to draw the image
paint your image using onDraw's canvas at the last touch position.
each time you get a touch, you need to invalidate the custom view
I would like to produce an android user interface which allows the user to move added components/widgets around the screen by selecting them and then dragging them around.
Is this possible using the standard android apis?
Yes. It depends what you are trying to achieve.
It can be done using the standard APIs, but this functionality is not part of the standard APIs. That is, there is no widget.DragOverHere() method unless you write one.
That said, it would not be terribly complicated to do. At a minimum, you would need to write a custom subclass of View and implement two methods: onDraw(Canvas c) and onTouch(MotionEvent e). A rough sketch:
class MyView extends View {
int x, y; //the x-y coordinates of the icon (top-left corner)
Bitmap bitmap; //the icon you are dragging around
onDraw(Canvas c) {
canvas.drawBitmap(x, y, bitmap);
}
onTouch(MotionEvent e) {
switch(e.getAction()) {
case MotionEvent.ACTION_DOWN:
//maybe use a different bitmap to indicate 'selected'
break;
case MotionEvent.ACTION_MOVE:
x = (int)e.getX();
y = (int)e.getY();
break;
case MotionEvent.ACTION_UP:
//switch back to 'unselected' bitmap
break;
}
invalidate(); //redraw the view
}
}
To some degree. It depends on what degree of freedom you want to give the user. One solution that comes to mind is a TableLayout with predefined cells. A user can then tap and drag components. When dragging a component you'll just want to draw an image of it under the user's finger, but when they release, remove the component from it's previous parent and add it to the new parent using the ViewGroup add/remove methods.
While you can programmatically shift Views around, it would be difficult/impossible to switch layouts on the fly as far as I can see.
The way I have done this is to have an absolute layout and then just update the object position as it is dragged. You have to implement dragging yourself. If the widgets have a natural order (a toolbar where you want to be able to drag buttons around) you can stack an absolute layout on top of the toolbar and when the drag starts, you add it to the absolute layout and when it finishes, you add it back to the original layout in the new position.