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.
Related
I saw a similar question posted here: Detect touch event on a view when dragged over from other view. But that question has different behaviour compared to what I want.
If I have multiple views, I press on one and continue dragging my finger onto multiple other views, is there a way for the other views to be notified that they have been touched? It won't be just 2 views, it could be multiple views. I click on one and keep dragging my finger and go over multiple other views.
The views are dynamically made and programmatically added to a FrameLayout and positioned programmatically by adding margins around it.
I couldn't find a way to do this through the actual custom views and had to go one layer back.
The position of the views are documented on the FrameLayout that contains each view.
In the FrameLayout, I ended up overwriting onTouchEvent.
I ended up having to do something similar to this:
#Override
public boolean onTouchEvent(MotionEvent event) {
float xCoord = event.getX();
float yCoord = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Using X and Y coordinates, find out if touch event is on top of a view.
// Mark the view as touched
return true;
case MotionEvent.ACTION_UP:
// Action required once touch released.
return true;
default:
// When dragging, the default case will be called.
// Using X and Y coordinates, figure out if finger goes over a view.
// If you don't want same behaviour when going back to previous view,
// then mark it so you can ignore it if user goes back to previous view.
}
}
Once case MotionEvent.ACTION_UP gets called, you can use the fact that the views were marked to know which ones were touched. You can also get the order if required by putting the views in an array when marking them.
I'm trying to create a custom AbsListView (overriding the same stuff on ListView, GridView and HeaderGridView) that will relocate all its drawing and touching events based on an external factor (other stuff that moves on the layout).
Paddings are not an option here, because then the only way to try to control the AbsListView position is using the super crappy method smoothScrollBy(int distance, int duration) and that is giving me a whole level of different issues that I'm trying to overcome by just all together drawing in a different area of the screen.
At the moment I found out that draw on a different area of the screen is as easy as:
#Override
protected void onDraw(Canvas canvas) {
canvas.translate(0, 400);
super.onDraw(canvas);
}
but the touch events don't get translated, and all the touches are 400 pixels up. So I've tried to translate the touch as well with:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
ev.offsetLocation(0, -400);
return super.dispatchTouchEvent(ev);
}
it does relatively work, to the point that 1) on item click get's called and 2) the view changes color based on the theme android:state_pressed="true", but the Up or Cancel touch evens never get called and the view does not refresh the drawing back to the default color.
I'll still further analyse, but I'm not sure if complex custom adapter with buttons and gesture detector, if that would work or not.
So the question:
How can I translate the drawing and still properly get touch events?
I have read some different responses for this same question, so I dont know which one is right, then I decided to ask it again. Please try to not refer any other link in this answer. If you want to do so, I will be expecting a bit of extra explanation.
My question is:
I have an ImageView inside a ViewPager, my ImageView has some gestures detection(zoom and pan) and I am using the default ViewPager, which has swipe detection by default.
How does the chain of onTouch events work? The lowest view in my hierarchy will always get onTouch and if the method returns false will parent handle it? Perhaps the opposite?
I am asking that, because I have a ImageView which has gestures to left and right, and when I get in the edge of the image, I have to dispatch an event to the parent (a ViewPager) to handle the pagination event. any further tip in how to do it, it will be really appreciated.
So, I have managed that multiple touches in children views of ViewPager in a better way then onInterceptTouchEvent, I am saying that, because in my case, in order to know if I had to scroll the children views instead of the Pager, I needed the distance between the start and end points, and I didnt want to hold that variables and calculate the delta afterwards.
Anyways, After going into the source code for ViewPager, I found out that there is a really good method that I should be implementing for this reason.
Here is my implementation:
#Override
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
if (v instanceof ImageViewTouch) {
return ((ImageViewTouch) v).canScroll(dx);
} else {
return super.canScroll(v, checkV, dx, x, y);
}
}
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/support/v4/view/ViewPager.java#ViewPager.canScroll%28android.view.View%2Cboolean%2Cint%2Cint%2Cint%29
It works the opposite ViewPager eats touch events first, then ImageView will get it.
I would use onInterceptTouchEvent(MotionEvent ev) on a custom View Pager. On the MotionEvent you will want to analyze how many touch points are there by ev.getPointerCount().
If you have ev.getPointerCount() == 2, you will probably want to be doing pinch and zoom inside the ImageView.
If you have ev.getPointerCount() == 1, then you probably intent to swipe through pages of the View Pager or perform a pan in the ImageView. In order to distinguish between the two of these you will want to figure out if you are performing a Swipe Gesture with 1 finger or a simple MotionEvent.MOVE for a pan. The swipe gesture requires the user to surpass a predefined velocity. GestureDetector should take care of this for you.
Speaking of events we know that the touch screen method onTouchEvent (MotionEvent events) allows you to capture touch events on the screen and event.getX () and
event.getY () give me the coordinates of the touch as float values (ie values with a comma).
Specifically, I realized that taking logcat using the fixed point the finger in a mobile phone screen and not moving it, the event is not only perceived but MotionEvent.ACTION_DOWN MotionEvent.ACTION_MOVE and returned coordinates are manifold. This i can understand because the surface of the finger touches more points and more by reading the device coordinates believe that the finger moves while holding it.
My problem is that I would read the color of one pixel of a color image when I hold your finger still. In particular I need the central area occupied by the finger.
First, assuming you have a Bitmap object, I have to round to integer coordinates of the touch as the getPixel (intX, int y) coordinates of the entire Bitmap wants to return the pixel color.
Then how do I get the central point of touch? Be that there is a function that can help me? Keep in mind that while I stopped and read the value of a pixel on the image then I start to move slowly and even though I always record every move the center pixel. I need this because every move, if you change the color of the image in pixels, I want to vibrate or not the device.
If I could stop thinking about themselves in a dynamic array to store the coordinates of different pixels and search for the center of gravity, but moving I do not know what to do. I think however, that the research center of gravity, if not done very efficiently, can be slow during the move and then give the wrong results. If the CG is not quite enough for me to another point belonging to the environment.
I hope you can help me maybe with a few lines of code sketched.
Thanks in advance.
To get the touch position of finger, just cast the getX() and getY() to int and perform your desired tasks.
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN ) {
getPixelAt((int)event.getX(), (int)event.getY());
}
}
you can put you on main activity class when you can want to touch event occurs. let see you can put following code or not ? and return is most important as well.
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mDistanceX = 0;
}
return super.onTouchEvent(event);
}
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.