DrawerLayout prevents call of MainActivity.onTouchEvent() - android

I have an app that overrides the onTouchEvent(MotionEvent ev) of the MainActivity to determine Two-Finger-Swipe and Pich-Open/Pinch-Close.
Everything works fine until I add the DrawerLayout to the app (like it's described in Creating a Navigation Drawer). Problem: the DrawerLayout prevents the call of onTouchEvent() in the MainActivity.
I started to write a CustomDrawerLayout, and try to override the DrawerLayout methods onInterceptTouch() and onTouchEvent().
The only way (I found) to transmit the TouchEvent to the MainActivity:
// onTouchEvent of CustomDrawerLayout
#Override
public boolean onTouchEvent(MotionEvent ev){
// super.onTouchEvent(ev); // prevent transmission of TouchEvent
return false;
}
The problem here is that the Drawer doesn't open correctly. The Drawer stucks like described in this post:
DrawerLayout getting stuck on swipe.
Is it possible to transmit the TouchEvent to MainActivity to handle the MultiTouchDetection? Or do I have to handle this in CustomDrawerLayout?
UPDATE 1
First I have to say that the Drawer only stucks, if I swipe from the left edge. By clicking on the DrawerIcon in the ActionBar the Drawer works fine.
Transmission of TouchEvent works with following code. But only if the Drawer is opend! Otherwise Activity.onTouchEvent isn't called!
// onTouchEvent of CustomDrawerLayout
#Override
public boolean onTouchEvent(MotionEvent ev){
// super.onTouchEvent(ev); // still prevents transmission of TouchEvent
activity.onTouchEvent(ev);
return true;
}
By opening the Drawer with a swipe from the edge (-> Drawer stucks) i get a really strange behaviour :
just DrawerIcon can close "stucked Drawer" (-> I wouldn't expect something different because I override the CustomDrawerLayout.onTouchEvent)
if I close the stucked Drawer by DrawersIcon the CustomDrawerLayout.onTouchEvent is still called
Thats strange! Why Activity.onTouchEvent() isn't called? And how can I prevent the stucked Drawer?
UPDATE 2
Now i override the CustomDrawerLayout.onInterceptTouch():
#Override
public boolean onInterceptTouchEvent(MotionEvent ev){
return true;
}
This has the effect that the Drawer can't be opened by a swipe from the edge -> only the DrawersIcon can open and close the Drawer. But now the TouchEvent is always transmitted to the Activity (-> that works like expected).
But what I really want is to have the possibilty to open the Drawer by a swipe from the edge AND to have my MultiGestureDetector. Is this possible?

A bit of a late update, but after having this issue for a couple of days, the solution that worked best for me was creating a CustomDrawerLayout. Then, casting the Context from the constructor as an Activity, called the Activity onTouchEvent from onInterceptTouchEvent.
#Override public boolean onInterceptTouchEvent( MotionEvent ev )
{
getActivity().onTouchEvent( ev );
return super.onInterceptTouchEvent( ev );
}
I find the code to be a lame hack... but works for me. Good luck!

I was having some issues recognizing gestures for items within the navigation drawer itself and came across requestDisallowInterceptTouchEvent. What I found was that I was getting the MotionEvent.ACTION_DOWN, but nothing after that because the NavigationDrawer was intercepting the touches. The key would be to call requestDisallowInterceptTouchEvent(true) on the down event in your touch handler in the activity so that you can handle the touch without it being intercepted.
This presentation was also extremely useful when trying to figure out the touch system in Android.

In case you or anyone else still need it, here is my implementation.
Works perfectly with the exception of the "edge" not being taken from android code but as a constant (which I set).
Please view this link:
Android Navigation Drawer Doesn't Pass onTouchEvent to Activity

Related

Android Screen Touch Action

I am working on an android project where I need to know if user is Touch Down the screen or Touch Up the screen and I frequently need this. How can I do that?
I used on Touch Event in switch case for action Up and action down but that works for only one time. I also tried the bellow code but it gives me the the value of var(int variable) for first touch only.
my code:
int var=-1;
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
if(action==MotionEvent.ACTION_DOWN)var=1;
if(action==MotionEvent.ACTION_UP)var=0;
if(action==MotionEvent.ACTION_MOVE)var=1;
return super.onTouchEvent(event);
}
I need the value of var again and again.
Since onTouchEvent is the last point of touch event traversal, it might not be called if one of the children has consumed touch event already (of if you returned false for MotionEvent.ACTION_DOWN event indicating that you are not interested in this gesture at all).
For your problem I would rather use dispatchTouchEvent() instead of onTouchEvent() and track touches there.
For more information I highly recommend getting yourself familiar with talk from Dave Smith about Android Touch System

Android DrawerLayout.setDrawerLockMode() not working

I have a Navigation Drawer (appcompat v7) in my app which is working perfectly fine.
Now I want to disable it, until the user buys an in-app-purchase to unlock additional functionality. So in my Activity.onCreate(), after initializing the drawer and populating it, I am calling this function:
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
This function is not doing anything. The drawer continues to open and close as normal after tapping the drawer carat in the actionbar. I tried calling this function in Activity.onResume() without any difference.
What is the correct way to use this function?
(I tried looking online for answers, but couldn't find anything which addresses my issue). Any help is appreciated, as I am stuck on this issue for quite sometime now.
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
is only disabling the opening drawer layout by swiping till you click navigation drawer icon
keep a boolean variable
write mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); in onStart() and also write below lines of code
#Override
public boolean onOptionsItemSelected(android.view.MenuItem item) {
if(!disabled)
{
if (item.getItemId() == android.R.id.home) {
if (mDrawerLayout.isDrawerOpen(mDrawerLinearLayout)) {
mDrawerLayout.closeDrawer(mDrawerLinearLayout);
} else {
mDrawerLayout.openDrawer(mDrawerLinearLayout);
}
}
}
return super.onOptionsItemSelected(item);
}
this will work for sure
When you call setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) it locks opening and closing drawer only by swipes.
The drawer continues to open and close as normal after tapping the drawer carat in the action bar because your drawer will still respond to calls to openDrawer(int), closeDrawer(int) although a drawer is locked.
You need to add some logic in your action bar menu button listener and not to call openDrawer(int) when you don't want it to open.
Btw, it is okay to call setDrawerLockMode(int) in onŠ”reate
There is a bug with DrawerLayout and used gravity. I have reported it here:
https://issuetracker.google.com/issues/136738274

clicking on the fragment invokes fragment behind

I have a really strange problem in my android app. The app contains several fragments and one of them consists a surfaceView. The most time changing to the fragment with the surfaceview works good but sometimes the replacement is unsuccessful. Then if I touch the surfaceview the fragment behind receives the touchevents and not the actual fragment.
But this only happens sometimes and not always. So it's really hard for me finding the reason.
Had anybody already the same or similar problem?
for replacing the fragment I use following code:
Fragment fragment = new Fragment();
fragment.setArguments(bundle);
ft.replace(R.id.frame_container, fragment).addToBackStack(null).commit();
It sounds like your surface view has not been told to respond to touch events.
Try making it clickable, either with android:clickable="true" or setClickable(true). That should prevent touch events from passing through the surface view to the fragment below.
Another trick I've used, for views that don't need to respond to touch, but do need to prevent events from going through them, is to add a touch listener and eat the event:
surfaceView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return true; // true means the event has been processed
}
});

Cancel Touch Eventbutt

I am writing an very simple application with following scenario:
1) Screen A have 3 button to move on other screen's.
2) Now if I hold one button(say Button 1) and perform rapid click on other button then it launch multiple instance of other screen. Which I think should not be happened. How can prevent this.
3) and it's more weird. After move on other screen if I don't release Button 1 which was on Screen A then it still allow to perform click for rest of two button of screen A even I can see second screen.
Here it's clear launch second screen but still first screen button event working.
Any idea how can avoid such scenario.
How you are going to disable other buttons while having 1 enabled, that's an algorhytmic problem. You can try creating a boolean or control variable in your activity (and then pass the final reference of the activity to wherever you need it), or in a static context. But to answer the title of the question - you can "Cancel Touch Event" either by adding an OnTouchListener, or if you're extending class Button, you can override onTouchEvent(MotionEvent ev) method.
Using OnTouchListener will disable any previously defined touch-event behavior. You can call the actual click event from the inside by calling performClick method from your button.
//in order to use button inside OnTouchEvent, its reference must be final
//if it's not, create a new final reference to your button, like this:
final finalButton = button;
button.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouchEvent(MotionEvent ev) {
// ... additional code if necessary
if(canBeClicked) {
finalButton.performClick();
return true;
}
else return false;
}
}
Overriding onTouchEvent in a class extending Button should look something like this.
#Override
public boolean onTouchEvent(MotionEvent ev) {
// ... additional code if necessary
//here we don't really need to call performClick(), although API recommends it
//we just send the touch event to the super-class and let it handle the situation.
if(activity.canBeClicked) return super.onTouchEvent(ev);
else return false;
}
One solution that I found is to disable click listener in onPause() and enable it in onResume() . Can we have better support for this?

How to open the SlidingPanelLayout, only through button?

I'm developing an Android app with GoogleMap and I need to implement a SlidingPanelLayout in this Activity.
I want to open the SlidingPanelLayout only when a certain button is clicked, because if I drag the finger
on the GoogleMap the SlindingPanelLayout appears. So if I fix the open SlidingPanelLayout only when the button is clicked, the problem will be solved.
Are there any way to block the SlidingPanelLayout opening when I drag on the map, or something like this?
If you really want the panel to open only through a button press, you should create a class that extends SlidingPaneLayout and override the onInterceptTouchEvent() method.
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!isOpen()){
//The map panel is being shown. We don't want the SlidingPaneLayout to handle the MotionEvent.
return false;
}
else{
//The other panel is being shown... Let the SlidingPaneLayout handle the MotionEvent as normal.
return super.onInterceptTouchEvent(ev);
}
}
Remember to use the custom SlidingPaneLayout class in your code or layout and not the regular one. Also, you should, obviously, place a button that will call the openPane() method of your custom class somewhere.
====
THE APPROACH BELOW IS NOT THE ANSWER TO THE QUESTION, BUT AN ALTERNATIVE SOLUTION
Now, if you want to let the user to freely use the GoogleMap object and let the SlidingPaneLayout open if a drag event occur in a certain region of the screen/map, you can use this approach:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//Get the user touch event position in dp units
float xTouchPosDp = ev.getX()/getResources().getDisplayMetrics().density;
if(!isOpen()){
if(xTouchPosDp < 30){
//If the panel is closed (map pane being entirely shown)
//and the touch event occur on the first 30 horizontal dp's
//Let the SlidingPaneLayout onTouchEvent() method handle the
//motion event alone (the GoogleMap object won't receive the event
//and depending on the movement, the panel will open)
return true;
}else{
//Now, if the panel is closed, but the touch event occur
//on the rest of the screen, let the GoogleMap object handle
//the motion event.
return false;
}
}
else{
//If the panel is opened, let the SlidingPaneLayout handle the
//motion event normally.
return super.onInterceptTouchEvent(ev);
}
}
Again, remember to use your custom SlidingPaneLayout class in your code/layout.
The problem with this solution is that if both panels are opened (they together fit the entire screen), you won't be able to move the map laterally.

Categories

Resources