in my App I have a Custom View in an Activity.
To detect touch events I have the method onTouchEvent() in both of them.
Custom View: The onTouchEvent()-method basically just marks the object which is touched.
#Override
public boolean onTouchEvent(MotionEvent event) {
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
touchedXY[0] = event.getX();
touchedXY[1] = event.getY();
mMatrix.mapPoints(touchedXY); // this is just for
event.setLocation(touchedXY[0], touchedXY[1]); // rotation purposes
for(int i = 0; i < mPoints[i]; i++){
mPoints[i].isSelected(setIsSelected(touchedXY)); //using method to
//check if point
//is selected
}
break;
default:
return false;
}
invalidate();
return true;
}
Activity: If an Object is touched a LinearLayout should appear at the Bottom of the Activity with the number of touched Points.
int key = 0;
#Override
public boolean onTouch(View v, MotionEvent event) {
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
List<AddressPoint> selectedPoints = Arrays.asList(mPoints);
if (key == 0) {
if (selectedPoints.size() > 0) {
key = 1;
test.setText(String.valueOf(selectedPoints.size()));
popup.setVisibility(View.VISIBLE);
} else {
test.setText(String.valueOf(selectedPoints.size()));
}
} else {
if (selectedPoints.size() > 0) {
test.setText(String.valueOf(selectedPoints.size()));
} else {
key = 0;
popup.setVisibility(View.GONE);
}
}
break;
default:
return false;
}
return false;
}
So, it is working but behaves not as desired. The Activity seems only to react to the second touchevent and refreshes every second touchevent.
Like for example:
TouchEvent = 4 Points --> Activity shows no popup.
TouchEvent = 3 Points --> Activity shows popup with the number "4"
TouchEvent = 1 Point --> Activity shows popup with the number "3"
.
. and so on
.
I hope you can help me and I appreciate your help!
The problem seems to occour because your view is redrawn before the points are saved.
Therefore it might be better to save the number of points in the onTouch onTouchEvent and then invoke another function to set the text accordingly.
Related
I am trying to implement a "swipe and hold" gesture in Android where the user swipes from a point on the left to another point on the right, then continues to hold at the right point until a server-side action is complete. The idea being that if the user lets go of the hold, the server-side action will be cancelled.
I've been trying to go about this the following way but have encountered an issue when getting to the hold part of the gesture. What I'd like to have happen is that when the user reaches point B on the right, the ACTION_MOVE is somehow converted into an ACTION DOWN for the view on the right and from then on, as long as the event ACTION_DOWN continues the gesture is valid, but if the user lets go or leaves the view area, it is invalid. So the question is, is it possible to chain the ACTION_MOVE from left to right into a "hold" gesture once the user reaches point B on the right? I've tried adding an onTouch to point B after the swipe, but the only way that works is if the user lets go of the swipe, then presses point B again. Will this be possible, or should I go about this a different way, such as drag and drop?
Point A and B as found below are currently image views, but that's more placeholder than anything.
private boolean mValidGesture = false;
Rect outRect = new Rect();
int[] location = new int[2];
private boolean isViewInBounds(View view, int x, int y){
view.getDrawingRect(outRect);
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect.contains(x, y);
}
...
mPointA.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
if(event.getAction() == MotionEvent.ACTION_MOVE){
if (isViewInBounds(mPointB, x, y)) {
mPointB.dispatchTouchEvent(event);
Log.d(TAG, "onTouch ViewB");
Toast.makeText(MainActivity.this, "Swipe complete", Toast.LENGTH_SHORT).show();
//This is the part that happens only after you let go of the swipe :(
mPointB.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
mValidGesture = true;
Log.d(TAG, "Gesture: " + String.valueOf(mValidGesture));
return true;
case MotionEvent.ACTION_UP:
mValidGesture = false;
Log.d(TAG, "Gesture: " + String.valueOf(mValidGesture));
return true;
}
return false;
}
});
} else if(isViewInBounds(mPointA, x, y)){
Log.d(TAG, "onTouch ViewA");
}
}
// Further touch is not handled
return false;
}
});
You don't need to put touchListener on pointB. Try something like this:
boolean serverActionStarted = false;
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
if(event.getAction() == MotionEvent.ACTION_MOVE){
if (isViewInBounds(mPointB, x, y)) {
// TODO : swipe completed. Start server action
serverActionStarted=true;
}
}else if(event.getAction() == MotionEvent.ACTION_UP && serverActionStarted){
// TODO : cancel the server action
serverActionStarted = false;
}
So basically, when the user swipes from pointA to pointB, you start the server action and set a boolean serverActionStarted to true. After that, if ACTION_UP event occurs, with the severAction having started, you cancel the server action.
I am developing a game and I need to be able to detect that one finger is performing a MOVE while posibly another finger can TOUCH another part of the screen.
With the following code I am able to detect both the ACTION_MOVE (on certain region of the screen) and the ACTION_DOWN
public boolean onTouch(View v, MotionEvent event) {
final int dest_x = (int) event.getX();
final int dest_y = (int) event.getY();
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
The problem is that I am not able to move and fire at the same time (I need to stop moving in order to fire and viceversa)
I am aware that Android can handle multi-touch events but have not figure it how to use that to be able to process these events and the same time so that the player can move and fire at the same time
I have also try using the getActionMasked without any luck
After reading this question Android MotionEvent.getActionIndex() and MultiTouch
This is how I solved the problem
public boolean onTouch(View v, MotionEvent event) {
int dest_x ;
int dest_y ;
p = event.getActionIndex() ;
dest_x = (int) event.getX(p);
dest_y = (int) event.getY(p);
onTrackPad = dbSettings.TRACK_PAD.contains(dest_x, dest_y);
action = event.getActionMasked() ;
switch (action) {
case MotionEvent.ACTION_MOVE:
if (onTrackPad)
{
//move character
}
break;
case MotionEvent.ACTION_DOWN:
// Fire bullets
break;
}
//The event was consumed
return true;
}
MotionEvent has all information about touches that you need. You can get number of touches by executing event.getPointersCount(), and try to check MotionEvent.ACTION_POINTER_2_DOWN instead of MotionEvent.ACTION_DOWN. To get coordinates of each touch you can use event.getX(0) and event.getX(1), same is for y. If you have a case of MotionEvent.ACTION_MOVE with 2 touches, you will receive all this information in your motion event.
Try below code.
when multiple pointers touches on the screen,system generates the action events.we can keep track of individual pointers with in motion event using pointer id. Pointer id persists across touch events and also allows to track individual pointer across entire gesture.
#Override
public boolean onTouch(View v, MotionEvent event) {
int index = event.getActionIndex();
int pointerID = event.getPointerId(index);
int action = event.getActionMasked();
if(event.getPointerCount() > 1)
{
Log.i("TouchType ", "Multi Touch");
for(int i = 0; i < event.getPointerCount(); i++)
{
performAction(action);
}
}else
{
Log.i("TouchType ", "Single Touch");
performAction(action);
}
return true;
}
public void performAction(int action){
switch(action)
{
case MotionEvent.ACTION_DOWN :
Log.i("OnTouch ", "Pressed");
// Fire bullets
break;
case MotionEvent.ACTION_MOVE :
Log.i("OnTiouch", "move");
//move character
break;
case MotionEvent.ACTION_UP :
Log.i("OnTiouch", "Up");
break;
default:
Log.i("OnTiouch", "None");
}
}
I'm trying just trying to make this function work but I am having troubles. When I have touch on View, getPointerCount() method always returns 0 pointers, when I touch the screen with one finger, 1. When I touch the screen with two or more fingers, it's always return 1. Have you any ideas ?
i have trying to this code,
#Override
public boolean onTouch(View view, MotionEvent me) {
// No dragging during animation at the moment.
// TODO: Stop animation on touch event and return to drag mode.
if (me.getPointerCount() >= 2) {
mAnimate = false;
mEnableTouchPressure = false;
mRenderLeftPage = false;
startCurl(CURL_NONE);
mCurlState = CURL_NONE;
mPageRight.setFlipTexture(false);
mPageLeft.setFlipTexture(false);
return false;
} else {
if (me.getAction() == MotionEvent.ACTION_DOWN) {
Log.e("TAG_EVENT", me.getPointerCount() + "");
} else {
Log.e("TAG_EVENT - 2", me.getPointerCount() + "");
}
}
}
I think is because you should return true for the touch event to be processed.
You can read the about MotionEvent.ACTION_MASK. For catching the action of multiTouch you can use the following code :
int action = (motionEvent.getAction() & MotionEvent.ACTION_MASK) % 5
switch(action) {
case MotionEvent.ACTION_DOWN:
System.out.println(motionEvent.getPointerCount());
break;
}
I'm developing an application where I'm applying on touch listener to my layout view. I move the layout over screen but that layout doesn't respond well after 10 to 15 secs. Here is my code:
base = (LinearLayout) findViewById(R.id.load);
base.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, final MotionEvent event) {
int motion = event.getAction();
int numberOfPointers = event.getPointerCount();
if (numberOfPointers < 3) {
switch (motion & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
drag = true;
break;
case MotionEvent.ACTION_POINTER_DOWN: {
drag = false;
if (drawWaveForm != null) {
zoom = true;
}
// initial x1 and x2
BitmapDetector.prevX1 = Math.min(event.getX(0), event.getX(1));
BitmapDetector.prevX2 = Math.max(event.getX(0), event.getX(1));
BitmapDetector.prevY1 = event.getY(0);
BitmapDetector.prevY2 = event.getY(1);
}
break;
case MotionEvent.ACTION_MOVE:
if (drag && event.getActionIndex() == 0) {
// Log.d("detecting","inside the action move");
if (touching == false) {
touching = detector.isWaveformAnchor(event.getX(0), event.getY(0));
} else if (touching == true) {
float diff = event.getY(0) - loadWaveFormManager.getCh1_pos();
if ((loadWaveFormManager.getCh1_pos() + diff) < (StaticValues.screenHeight - 50)) {
int tempCh1_pos = (int) (loadWaveFormManager.getCh1_pos() + diff);
loadWaveFormManager.setCh1_pos(tempCh1_pos);
}
drawWaveForm.update(loadWaveFormManager);
}
}
break;
case MotionEvent.ACTION_UP:
// Log.d("detecting","inside the action up");
drag = false;
touching = false;
zoom = false;
break;
case MotionEvent.ACTION_POINTER_UP:
// here different function
return;
}
});
Does anyone have any idea about this? Please help me on this one.
It is not clear from your code what is your problem and when does it happen. I don't see any TimerTask or AsyncTask reference.
I guess, you're trying to do some expensive operation in UI thread, and this freezes your application. Possible long-running candidate is drawWaveForm.update(loadWaveFormManager)
If this is so, you should preform your computations asynchronously, and only then update UI.
By the way, your code will not compile at all:
public boolean onTouch(View v, final MotionEvent event)
should return true or false, instead you perform return; at the end of this method. That leads to an assumption, that you're providing irrelevant code in your question, but still want to receive a relevant answer.
Provide relevant code piece for further analyze.
Currently you are starting thread on touch event use handler instead
of thread
My class extends View and I need to get continuous touch events on it.
If I use:
public boolean onTouchEvent(MotionEvent me) {
if(me.getAction()==MotionEvent.ACTION_DOWN) {
myAction();
}
return true;
}
... the touch event is captured once.
What if I need to get continuous touches without moving the finger?
Please, tell me I don't need to use threads or timers. My app is already too much heavy.
Thanks.
Use if(me.getAction() == MotionEvent.ACTION_MOVE). It's impossible to keep a finger 100% completely still on the screen so Action_Move will get called every time the finger moves, even if it's only a pixel or two.
You could also listen for me.getAction() == MotionEvent.ACTION_UP - until that happens, the user must still have their finger on the screen.
You need to set this properties for the element
android:focusable="true"
android:clickable="true"
if not, just produce the down action.
Her is the simple code snippet which shows that how you can handle the continues touch event. When you touch the device and hold the touch and move your finder, the Touch Move action performed.
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if(isTsunami){
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Write your code to perform an action on down
break;
case MotionEvent.ACTION_MOVE:
// Write your code to perform an action on contineus touch move
break;
case MotionEvent.ACTION_UP:
// Write your code to perform an action on touch up
break;
}
}
return true;
}
Try this. It works to me:
public static OnTouchListener loadContainerOnTouchListener() {
OnTouchListener listener = new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
LinearLayout layout = (LinearLayout)v;
for(int i =0; i< layout.getChildCount(); i++)
{
View view = layout.getChildAt(i);
Rect outRect = new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
if(outRect.contains((int)event.getX(), (int)event.getY()))
{
Log.d(this.getClass().getName(), String.format("Over view.id[%d]", view.getId()));
}
}
}
Remember: the listener you´ll set must be a container layout (Grid, Relative, Linear).
LinearLayout layout = findViewById(R.id.yourlayoutid);
layout.setOnTouchListener(HelperClass.loadContainerOnTouchListener());
This might help,
requestDisallowInterceptTouchEvent(true);
on the parent view, like this -
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
view.getParent().requestDisallowInterceptTouchEvent(true);
switch(motionEvent.getAction()){
}
return false;
}
I was making a game with a custom view used as a thumb control. . . here is what I did
float x = 0, y = 0;
#Override
public boolean onTouchEvent(MotionEvent event) {
x = event.getX();
y = event.getY();
// handle touch events with
switch( event.getActionMasked() ) {
case MotionEvent.ACTION_DOWN :
if(cont)
{
// remove any previous callbacks
removeCallbacks(contin);
// post new runnable
postDelayed(contin, 10);
}
invalidate();
return true;
case MotionEvent.ACTION_MOVE :
if(!cont && thumbing != null)
{
// do non-continuous operations here
}
invalidate();
return true;
case MotionEvent.ACTION_UP :
// set runnable condition to false
x = 0;
// remove the callbacks to the thread
removeCallbacks(contin);
invalidate();
return true;
default :
return super.onTouchEvent(event);
}
}
public boolean cont = false;
// sets input to continuous
public void set_continuous(boolean b) { cont = b; }
public Runnable contin = new Runnable()
{
#Override
public void run() {
if(x != 0)
{
// do continuous operations here
postDelayed(this, 10);
}
}
};
A quick note however, make sure in your main activity that is calling this view removes the callbacks manually via the onPause method as follows
#Override
protected void onPause() {
if(left.cont) left.removeCallbacks(left.contin);
if(right.cont) right.removeCallbacks(left.contin);
super.onPause();
}
That way if you pause and come back touch events aren't being handled twice and the view is free from it's thread's overhead.
** tested on Samsung Galaxy S3 with hardware acceleration on **
All these answer are partially correct but they do not resolve in the right way the problem.
First of all, for everyone out there that decide to track when the event is ACTION_MOVE. Well that works only guess when? When user move his finger, so could if you decide to implement a custom thumb control is okay but for a normal custom button that's not the case.
Second, using a flag inside ACTION_DOWN and check it in ACTION_UP seems the logic way to do it, but as Clusterfux find out if you implement a while(!up_flag) logic you get stuck into troubles ;)
So the proper way to do it is mentioned here:
Continuous "Action_DOWN" in Android
Just keep in mind that if the logic you're going to write during the continuous press has to modify the UI in some way, you have to do it from the main thread in all the other cases it's better use another thread.
You can use the below code snippet as a reference in which I used the background to detect if the screen is held or not...
Main_Layout.setOnTouchListener(new View.OnTouchListener() {
#SuppressLint("ResourceAsColor")
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
Main_Layout.setBackgroundColor(R.color.green);
event.setAction(MotionEvent.ACTION_DOWN);
break;
default:
Main_Layout.setBackgroundColor(R.color.blue);
break;
}
return false;
}
});