I have a custom WebView and I want to simulate a click when I get a MotionEvent.ACTION_DOWN. I don't want to have any kind of input on the WebView , I must not even make it clickable(.setClickable(false)). So what i have done is override the onTouchEvent() in my custom WebView to return false.
this works fine but I'm missing the click. I have seen in the source code of WebView that it send messages to class called WebViewCore but this communication is done differently in every version of android.
Does anyone knows how can I programmatically send a click to a webView?
Here is what I'm trying to do:
public class VoidWebView extends WebView {
public VoidWebView(Context context) {
super(context);
}
public VoidWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
//tell the webview that a click has been performed , it doesn't matter where the click happened
}
return false;
}
}
I think you are looking for the performClick() method of the View.
You can get some more information here.
Pass up and down event to super class.
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP){
super.onTouchEvent(event);
return true;
}
return false;
}
Related
I am using a ViewPager with a TouchImageView inside it and it works great, (I have used this solution in many of my Android apps).
However I have an app for which there are many other controls on the same screen so they are all inside a scrollview control.
In this scenario I see the scrollview does not play nice and I am not able to pan within the zoomed image. When I use my finger to pan upward or downward the entire page scrolls instead of the image panning.
So here is what I am trying to do....
Inside the TouchImageView I detect Zoom Begin and Zoom End and have created an interface to make a callback to my Activity onZoomBegin() and onZoomEnd() methods.
In the onZoomBegin() method I want to disable the scrollview from responding to any touch events and in onZoomEnd() I can re-enable it.
So far here are the things I have tried doing in the onZoomBegin() method for which none are working....
scrollView.setEnabled(false);
scrollView.requestDisallowInterceptTouchEvent(true);
also I have tried the answer to a similar question which was to takeover the onTouchListener like such:
scrollView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
This does stop the scrollview from scrolling but the scrollview is still intercepting the touch events cause the image still will not pan up or down.
I've tried checking nestedScrollingEnabled in the layout designer, no joy....
I just want to know is there a way to totally disable a scrollview and then re-enable it from responding to touch events?
I found this answer on another question somewhere but by the time I realized it was the solution to my problem (answer to my question) then I lost reference to it. I will keep looking so I can edit this post to give credit where credit is due.
public class CustomScrollView extends ScrollView {
// true if we can scroll the ScrollView
// false if we cannot scroll
private boolean scrollable = true;
public CustomScrollView(Context context) {
super(context);
}
public CustomScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setScrollingEnabled(boolean scrollable) {
this.scrollable = scrollable;
}
public boolean isScrollable() {
return scrollable;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
if (scrollable)
return super.onTouchEvent(ev);
// only continue to handle the touch event if scrolling enabled
return false; // scrollable is always false at this point
default:
return super.onTouchEvent(ev);
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
if (!scrollable)
return false;
else
return super.onInterceptTouchEvent(ev);
}
}
This part I just figured out for myself.... In the TouchImageView I added a callback interface which is called when a zoom begins and ends so in my Activity I only had to do this:
private class OnZoomListener implements TouchImageView.OnZoomListener {
#Override
public void onZoomBegin() {
isZoomed = true;
scrollView.scrollTo(0, 0);
scrollView.setScrollingEnabled(false); // <-- disables scrollview
hideImageControls();
sizeViewPager();
}
#Override
public void onZoomEnd() {
scrollView.setScrollingEnabled(true); // <-- enables scrollview
showImageControls();
isZoomed = false;
}
}
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.
I have an Activity with a SlidingPaneLayout, and inside there are two fragments: a ListView on the left, and a MapFragment on the right.
How is it possible to intercept the touch event generated so that the user can move the map without close the panel?
The only area that I would like to use to close/open the right panel is the first fourth. On the right of that area I would like to move the map.
Thanks
EDIT2:
Ok, now I figured out how to properly subclass SlidingPaneLayout, now the problem is how to capture correctly the touch event:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN
&& event.getX() > (getWidth() / 6)) {
return false;
}
return super.onTouchEvent(event);
}
With this code I'm not able to slide the map, it remains fixed.
the problem is that I want to intercept the touch ONLY when the right panel is selected (in other words, only when map is displayed).
SlidingPaneLayout have it's own touch listener, so when you reset it by calling setOnTouchListener (which is a method from the super class View) you are loosing all the onTouch behaviour specific to a SlidingPaneLayout.
-------------------------------
Here is a try : make your own SlidingPaneLayout :
the constructor should be this way in order to use your view in an xml layout
public class MySlidingPaneLayout extends SlidingPaneLayout{
public MySlidingPaneLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event){
if (event.getX() < widthPIX / 6) {
return super.onTouchEvent(event);// here it works as a normal SlidingPaneLayout
}
return false; // here it returns false so that another event's listener should be called, in your case the MapFragment listener
}
}
and in your code add MySlidingPaneLayout instead
I finally solved the problem:
simply override this method and control if the SlidingPaneLayout is closed or open (in my case I have a boolean field value "open")
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (!homeActivity.open && event.getX() > (getWidth() / 5)) {
return false;
}
return super.onInterceptTouchEvent(event);
}
I am using google maps API and I have some code that tries to capture the position of the center of the map after the user has dragged it.
MapView mv = ...;
mv.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
GeoPoint pt = mv.getMapCenter();
//do something with the point
return A;
}
return B;
}
});
Now my problem is with the return values:
if B is false, the map gets dragged but I only see the ACTION_DOWN event and ACTION_UP is never triggered - which I understand
if B is true, I receive the ACTION_UP event, but the map is not dragged
it seems that whether A is true or false does not make a difference
What I want is to receive the ACTION_UP event AND to have the map dragged.
What am I missing here?
Here's one solution: Instead of using the default MapView, use a custom MapView which has a GestureDetector, basically, this lets you create a custom event listener for your map which helps you avoid the issue of messing up the dragging etc. and moreover gives you are vast number of interaction options compared to the default MapView. A couple of months ago I'd faced a similar problem and so I decided to implement the solution I just mentioned. Here's the code for the custom MapView called TapControlledMapView and the code for the interface for the custom listener is provided at the bottom : http://pastebin.com/kUrm9zFg.
So to implement the listener, all you need to do is use the following code in your mapactivity class (Oh and in case you didn't know this, you have to declare the following in your MapActivity layout XML file since you are using a custom MapView:
<?xml version="1.0" encoding="utf-8"?>
//The bottom line was the tricky business (You get ClassCastExceptions and whatnot)
<NAMEOFYOURPROJECT.TapControlledMapView (ex: com.android.googlemapsapp.TapControlledMapView)
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="true"
android:apiKey="API_KEY_HERE"
/>
and use the following code in your MapActivity class.
mapView.setOnSingleTapListener(new OnSingleTapListener() {
#Override
public boolean onSingleTap(MotionEvent arg1) {
if(arg1.getAction() == MotionEvent.ACTION_UP)
{
GeoPoint pt = mv.getMapCenter();
// do something with the point.
return ***true***;
}
return true;
});
Let me know how it goes.
Following #ViswaPatel's idea, I create a new MapView class, extending MapView, which overrides the onTouchEvent method and manages its own listeners:
public class CustomMapView extends MapView {
private OnTouchListener lst;
public CustomMapView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomMapView(Context context, String apiKey) {
super(context, apiKey);
}
public void setOnActionUpListener(OnTouchListener lst) {
this.lst = lst;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if (lst != null && event.getAction() == MotionEvent.ACTION_UP) {
lst.onTouch(this, event); // return value ignored
}
return true;
}
}
The calling code is:
mv.setOnActionUpListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
final GeoPoint pt = mv.getMapView().getMapCenter();
//doing my stuff here
return true; //ignored anyway
}
});
}
I have an onKeyDown Event which is supposed to display an image once triggered, but I have noticed that despite pressing the corresponding key several times, the image does not appear until I click anywhere on the canvas with my mouse. Any suggestions on the actual problem and how to proceed? Pretty new to the concept so not quite sure what may be missing.
*Edited and pasted class in its entirety.
Thanks
public class BuccaneerView extends TileView {
public static final int PLAYER = 1;
public static final int GREEN_STAR = 2;
Coordinate P_Location;
public BuccaneerView(Context context, AttributeSet attrs) {
super(context, attrs);
initBucc();
}
private void initBucc() {
this.setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(PLAYER, r.getDrawable(R.drawable.aerialplayer));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
/**/
P_Location = new Coordinate(5,5);
setTile(PLAYER, P_Location.x, P_Location.y);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
if (keyCode == KeyEvent.KEYCODE_SPACE)
{
setTile(GREEN_STAR, 1, 0);
}
return super.onKeyDown(keyCode, msg);
}
public void update()
{
}
}
You seem to be treating onKeyDown as one of your on methods.
return super.onKeyDown(keyCode, msg);
Is a bad thing to do, its as if you have called this function and want to return what key has been pressed. Change it to simply be return false which will mean you are handling what they keyboard is doing.
EDIT
Is there any reason you use onKeyDown and not onKey? Here is some extra code which I use, it uses an array of booleans (pressedKeys, which is 128 in length) and you can later use it to check the array to see if a key is pressed
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (event.getAction() == android.view.KeyEvent.ACTION_DOWN)
{
if(keyCode > 0 && keyCode < 127)
pressedKeys[keyCode] = true;
}
if (event.getAction() == android.view.KeyEvent.ACTION_UP)
{
if(keyCode > 0 && keyCode < 127)
pressedKeys[keyCode] = false;
}
keyEventsBuffer.add(keyEvent);
}
return false;
}
So with this you can then say
If(pressedKeys[KeyYouWantToCheck])
{
//Do some stuff if that key is down
}
At a guess, the key event is not being delivered to whatever you have set the key listener on. This can happen if there is another view in between which is a listener and which stops the propagation of the key event (i.e. returning true from this method). There are some views which do this by default (e.g. EditText for most keys). It would be helpful if you could edit your question and include more code, or describe how your activity is setup.
By 'clicking on the canvas' you are probably changing the focus and making the key event being delivered to a different view. This could explain why you suddenly see the key listener working after clicking.