I have a custom View, which inherits from GestureOverlayView, and I want to log all the MotionEvent passed to this view.
It works well, but I can't get the MotionEvent when my gesture starts on an interactive layout widget (Button, TextEdit....)
Is there a way to bypass this behaviour?
Since you didn't mention any snipped code of yours,I make 2 different solution hopefully is help you
One way will be passing your widget to as View to method and call OnTouchListener
button.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
break;
}
case MotionEvent.ACTION_UP: {
break;
}
case MotionEvent.ACTION_CANCEL:{
break;
}
}
return false;
}
});
Another way could be manually creating a MotionEvent using custom constructor
like this:
static public CustomMotionEvent obtain(long downTime, long eventTime, int action,
float x, float y, int State) {
}
According to API
Create a new MotionEvent, filling in a subset of the basic motion
values. Those not specified here are: device id (always 0), pressure
and size (always 1), x and y precision (always 1), and edgeFlags
(always 0).
API
Related
I'm kinda trying to clone the ripple effect found in Google's material design. Now, I've noticed that the ripple always starts from the position that was touched. For this, I've created a drawable which will be initially invisible and but will become visible once the view is touched. The ImageView containing the drawable will be then moved to the position that was touched and the ripple animation will start thereafter. Now, while trying to do this, I'm stumbling upon one thing which is I can't figure out how to find out the position that was touched. I came across several questions on stackoverflow which said that the touch position can be found out by using the following code.
#Override
public boolean onTouch(View v, MotionEvent e) {
int X = (int)(e.getX());
int Y = (int)(e.getY());
return false;
}
I did try to implement this code in my app but I ended up with a NullPointerException. Please tell me how to find the exact location of the screen that was touched and move the view there?
Use like this:
#Override
public boolean onTouch(View v, MotionEvent e) {
if(e.getAction() == MotionEvent.ACTION_DOWN) { //ACTION_DOWN for the position touched.
int X = (int)(e.getX());
int Y = (int)(e.getY());
}
return false;
}
You should get position for ACTION_DOWN event and then move your view
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float x = event.getX();
float y = event.getY();
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
I'm developing an Android 2.2.2 application for a client and he wants to do the following:
Now I have a button with an onClick event but he doesn't like, he wants to dectect when user release the button.
I've found View.OnTouchListener which I think this is what I need to use but, is there any posibility to add this event to xml like I did with onClick?
<ImageButton
android:id="#+id/btnSaveNewGate"
android:layout_width="#dimen/btnSaveNewGate_width"
android:layout_height="#dimen/btnSaveNewGate_height"
android:layout_below="#+id/radioGrGateType"
android:layout_centerHorizontal="true"
android:layout_marginTop="#dimen/btnSaveNewGate_marginTop"
android:background="#null"
android:contentDescription="#string/layout_empty"
android:onClick="onSaveNewGateClick"
android:scaleType="fitXY"
android:src="#drawable/save_gate_selector" />
I have two questions more:
Which is the event associated when user releases his finger?
Is there any guidelines which prohibit using View.OnTouchListener instead of onClick?
The event when user releases his finger is MotionEvent.ACTION_UP. I'm not aware if there are any guidelines which prohibit using View.OnTouchListener instead of onClick(), most probably it depends of situation.
Here's a sample code:
imageButton.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
// Do what you want
return true;
}
return false;
}
});
Presumably, if one wants to use an OnTouchListener rather than an OnClickListener, then the extra functionality of the OnTouchListener is needed. This is a supplemental answer to show more detail of how an OnTouchListener can be used.
Define the listener
Put this somewhere in your activity or fragment.
private View.OnTouchListener handleTouch = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("TAG", "touched down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("TAG", "moving: (" + x + ", " + y + ")");
break;
case MotionEvent.ACTION_UP:
Log.i("TAG", "touched up");
break;
}
return true;
}
};
Set the listener
Set the listener in onCreate (for an Activity) or onCreateView (for a Fragment).
myView.setOnTouchListener(handleTouch);
Notes
getX and getY give you the coordinates relative to the view (that is, the top left corner of the view). They will be negative when moving above or to the left of your view. Use getRawX and getRawY if you want the absolute screen coordinates.
You can use the x and y values to determine things like swipe direction.
OnClick is triggered when the user releases the button. But if you still want to use the TouchListener you need to add it in code. It's just:
myView.setOnTouchListener(new View.OnTouchListener()
{
// Implementation;
});
for use sample touch listener just you need this code
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
ClipData data = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
view.startDrag(data, shadowBuilder, null, 0);
return true;
}
I am a newbie in Andoird.
In my case, I have a scenario that when click certain part of an image it will trigger onclick events. I tried to detecte the position when the onTouch is fired, it works, but I think it's not a standard implementation, so what is the best practice for such case?
thanks.
here is codes like:
imgView.setOnTouchListener((OnTouchListener) new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event) {
if(isIn(event.getX(), event.getY(), 124,3,221,36)){
ShowMemberInfo(R.string.app_m01);
} else if(isIn(event.getX(), event.getY(), 8,155,72,181)){
..
}
return true;
}
private boolean isIn(float x, float y, int fx, int fy, int tx, int ty) {
return x<tx && x > fx && y<ty && y>fy;
}
Try to use ImageButton
Simply implement onClickListener() for your ImageView.
Easiest way to implement onClick event is to include android:onClickMe="methodName" inside your <ImageView> in XML layout and define that method inside your activity file.
For example:
public void methodName(View v)
{
....
....
// do whatever you want for click even ton imageview
}
If you want to detect the position where user touched(Relative to the ImageView user touched), you can get the touch point from MotionEvent object.
Try to register a touch event listener for the ImageView and get the touched point position from MotionEvent object's getX() and getY() methods when touch event is triggered. And then define a rectangular area and using contains() to check whether the touch point is inside the area or not.
ImageView img = new ImageView(this);
img.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.w("Hit test", "is hit? "+isIn(event.getX(),event.getY(),0,0,120,120));
return false;
}
});
private boolean isIn(float x, float y, int fx, int fy, int tx, int ty){
return new Rect(fx,fy,tx,ty).contains((int)x,(int)y);
}
Hope this will be helpful to you. :)
I have two activities A and B. I would like to have one touch event MotionEvent.ACTION_DOWN caught in A, while still holding down, launch B, then having the release event MotionEvent.ACTION_UP be caught in B.
In A there is a View v that has an OnTouchListener with the following callback:
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
startActivity(new Intent(A.this, B.class));
break;
case MotionEvent.ACTION_UP:
// not called
break;
}
// false doesn't work either
return true;
}
In B there is an overlapping View v2 (over the original v) with the same kind of OnTouchListener but B's "onTouch" is not getting called when activity starts unless I move my finger (regenerating touch events).
Simply put, I am doing an application that would cause a new activity to appear when holding down on the screen and finish when I release the finger.
Is it not possible to have a MotionEvent.ACTION_DOWNed state transfer from one view to another? Or does the new activity B clear any current "on screen touch listeners" only available to A because it was initiated there?
Thanks for any explanation of how these MotionEvents get dispatched to activities and/or any solution/hack to my problem.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
startActivity(new Intent(A.this, B.class));
break;
case MotionEvent.ACTION_UP:
// Obtain MotionEvent object
long downTime = SystemClock.uptimeMillis();
long eventTime = SystemClock.uptimeMillis() + 100;
float x = 0.0f;
float y = 0.0f;
// List of meta states found here: developer.android.com/reference/android/view/KeyEvent.html#getMetaState()
int metaState = 0;
MotionEvent motionEvent = MotionEvent.obtain(
downTime,
eventTime,
MotionEvent.ACTION_UP,
x,
y,
metaState
);
// Dispatch touch event to activity (make B static or get the activity var some other way)
B.OnTouchEvent(motionEvent);
break;
}
// false doesn't work either
return true;
}
And in B Activity Override OnTouchEvent (make B implements OnTouchListener) like this:
#Override
public bool OnTouchEvent( MotionEvent e )
{
return someview.OnTouchEvent( e );
}
And remember , someview must be a view in order to catch the ontouchevent , cause the activitys doesn't really knows what to do with it.
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;
}
});