I am using btwebview to get text selection and handle it. The problem is when I use longPress with gestureDetector the default selection is also being launched, if I override onTouchEvent and return true, the problem is solved but I cannot click on any button or highlighted link on the webview, so I cannot access footnotes or videos inserted in the webview and shouldOverrideUrlLoading stops getting called.
public void init(Context context) {
System.out.println("BTWebview init");
this.context = context;
this.getSettings().setJavaScriptEnabled(true);
gestureScanner = new GestureDetector(this);
this.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
System.out.println("event "+event.toString() );
return gestureScanner.onTouchEvent(event);
}
});
setInitialScale(100);
addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
System.out.print("on touch event "+ event.toString());
return true;
}
#Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
System.out.println("gesture listener onLongPress");
mSelection = true;
if(!this.isInSelectionMode()){
System.out.println("onLongClick in selection mode");
mSelection = true;
}
this.loadUrl("javascript:android.selection.longTouch();");
mScrolling = true;
System.out.println("onLongClick");
}
The reason that the webview has stopped responding to touches is because that functionality is implemented in the superclass' onTouchEvent.
So to get it to work again you will need to call super.onTouchEvent(event) somewhere in your onTouchEvent. Obviously just always calling it would get you back to where you started.
To achieve what you want to do you will need to call super.onTouchEvent only when you have not already detected that the event is a long press event. The simplest way to do this would be to store the pointer ID from the MotionEvent that is passed in onLongPress (you should be able to assume it will be the pointer at index 0 because a long press is by definition a single touch event).
Once you have this your onTouchEvent could look something like this
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getPointerId(0) != self.lastLongPressPointerId) {
return super.onTouchEvent(event);
}
return true;
}
You might also need to watch for the ACTION_UP and ACTION_CANCEL events relating to the pointer and stop looking for it after that just incase the system decides to reuse the pointer ID.
Related
I would like to create a RecyclerView in which a user can long click an image and preview the full sized image until they release the long click.
I have it mostly working but the issue I am having is that if I begin the long click, then drag my finger (while still holding the click down), the listener no longer waits for my ACTION_UP event and the preview image never goes away. Is there a way to sort of ignore the dragging/scrolling so that my preview image view goes away when I release the long click?
This is what I have for event listeners:
/* Long press will trigger hover previewer */
holder.thumbnailImageView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View pView) {
holder.thumbnailImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View pView, MotionEvent pEvent) {
pView.onTouchEvent(pEvent);
// We're only interested in when the button is released.
if (pEvent.getAction() == MotionEvent.ACTION_UP) {
if (isImageViewPressed) {
// Do something when the button is released.
isImageViewPressed = false;
mHoverView.setVisibility(View.GONE);
}
}
return false;
}
});
isImageViewPressed = true;
GlideApp.load(item.getUrl()).into(mHoverView);
mHoverView.setVisibility(View.VISIBLE);
return true;
}
});
/* Long press will trigger hover previewer */
holder.thumbnailImageView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View pView) {
isImageViewPressed = true;
GlideApp.load(item.getUrl()).into(mHoverView);
mHoverView.setVisibility(View.VISIBLE);
return true;
}
});
holder.thumbnailImageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View pView, MotionEvent pEvent) {
pView.onTouchEvent(pEvent);
// We're only interested in when the button is released.
if (isImageViewPressed && pEvent.getAction() == MotionEvent.ACTION_UP) {
// Do something when the button is released.
isImageViewPressed = false;
mHoverView.setVisibility(View.GONE);
}
return true;
}
});
This will work and your code is not working as longClickListener does't get's the event of action down(and neither of action down) and what you are doing currently is setting the listener for touch which never got Action_DOWN i.e by default the View's ontouch() return false on Action_Down so u have to override and return true before action down is called so that it get's action move and action up etc.
Using this
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
Only detects for single tap event i.e. quick tap and release.
If i hold down and then release, onSingleTapUp is not called.
I am looking for an motion event which is ACTION_UP after holding down.
I looked at onShowPress which is called when the user performs a down action but then I was not sure how to detect a ACTION_UP while in onShowPress.
Note this is for a recycler view to click items. At the moment, I can single tap an item which works but if I hold it down and then release, it is not invoked.
You can subclass your view and override onTouchEvent. That will let you observe the different actions before the gesture detector handles them.
#Override
public boolean onTouchEvent(MotionEvent e) {
int action = e.getActionMasked();
if (action == MotionEvent.ACTION_UP) {
// do something here
}
return mGestureDetector.onTouchEvent(e);
}
You may try the following in your onSingleTapUp method:
#Override
public boolean onSingleTapUp(MotionEvent e) {
if(e.getAction() == MotionEvent.ACTION_UP){
// Do what you want
return true;
}
return false;
}
I have a button that I would like clickable only by a stylus.
I used the method setClickable to enable or disable the click on the button, but how can I do that is clickable only with a pen?
the button should be clickable only when MotionEvent.TOOL_TYPE_STYLUS
how do i can?
you could override the Button's onTouchListener, and return immediately it the touch event is not performed through TOOL_TYPE_STYLUS. To retrieve this information, you can use
getToolType(int pointerX)
From the documentation
Gets the tool type of a pointer for the given pointer index
if it returns TOOL_TYPE_STYLUS, then you can simply check the MotionEvent for the ACTION_DOWN/ACTION_UP, and call performClick(). E.g.
b.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
if (event.getAction() == MotionEvent.ACTION_UP ) {
performClick();
return true;
}
}
return false;
}
});
When application is started I run a custom pop-up till a user touches the screen. When screen is touched I catch it with event onTouch() and cancel the pop-up. From this point I don't need the event anymore.
The problem is the event is alive and continues to jump up every time a user touches the screen.
Is there any way to unsubscribe from this event? Something like in c# -= eventName.
The code is below:
#Override
public boolean onTouch(View v, MotionEvent event) {
if (!_stopToast)
{
_hintToast.cancel();
_stopToast = true;
}
return false;
}
There's no such method (lets say removeTouchListener or similar) which will help you to remove an already defined touch listener from a view. Setting null to setOnTouchListener won't help too. What you can do is to create a new object reference of OnTouchListener class which does nothing and set it in setOnTouchListener. For example:
public final OnTouchListener dummyOnTouchListener = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent rawEvent) {
return false;
}
};
And simply use it as below:
yourView.setOnTouchListener(dummyOnTouchListener);
I'm trying to modify some code I found online to my needs. It is supposed to catch a MotionEvent (a fling in particular) and launch a separate activity. I am new to java, so I'm having some trouble understanding the code. Here's what I have so far:
public class Hypertension extends Activity {
private GestureDetector flingDetector;
View.OnTouchListener gestureListener;
private TextView redView;
private TextView greenView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
redView = (TextView)findViewById(R.id.buttonRed);
greenView = (TextView)findViewById(R.id.buttonGreen);
redView.setOnTouchListener(gestureListener);
greenView.setOnTouchListener(gestureListener);
flingDetector = new GestureDetector(new MyFlingListener());
gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (flingDetector.onTouchEvent(event)) {
//startActivity(new Intent(Hypertension.this, Green.class));
return true;
}
return false;
}
};
}
class MyFlingListener extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// TODO Auto-generated method stub
startActivity(new Intent(Hypertension.this, Green.class));
return false;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (flingDetector.onTouchEvent(event))
return true;
else
return false;
}
}
My understanding is that when the screen is touched, onTouchEvent is called. In the above code, this method is overridden to call flingDetector, which listens for a fling. If one is heard, it launches the activity.
As of now, however, nothing happens when I perform a fling in the emulator. Also, I am fairly confused as to what the return values of the various boolean methods actually represent.
You have two onTouchEvent methods in your code. One is in the GestureDetector class (not overridden), and the other is in your Hypertension activity class (which you have overridden at the bottom).
When someone triggers the TouchEvent in the main activity you explicitly calling the GestureDetector one (passing down the event) here:
if (flingDetector.onTouchEvent(event)) return true;
But if you haven't overridden the onTouchEvent method of that class then nothing is going to happen!
The purpose of overriding these "onSomething()" methods is so that they will get called automatically when an event triggers. In general the way to work with listeners is as follows:
Create a subclass of the Listener class for the event and override its "onEvent()" method to do something when the event is triggered
Call the "setListener( Listener)" method of the object you want to trigger said events after it has been initialized--passing in your previously created Listener
Sit back and watch :)
For all event listeners that return a boolean it should return true when it handles the event, so in your example if the flingDetector handles the onTouchEvent it returns true.
This question has been posed before and there are some great answers there.