Intercept touch event SlidingPaneLayout - android

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);
}

Related

How to zoom/pan image while inside a scrollview

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;
}
}

Android Navigation Drawer Doesn't Pass onTouchEvent to Activity

I have an Activity which uses the Android NavigationDrawer.
When using only fragments (as usual), everything works perfect.
But now I want to use this drawer on other activities of my app, and for some of them,
I don't want the main view to be a fragment.
Question
The problem is, the onTouchEvent() of the activity itself (and the onItemClickedListener() of a child ListView for that matter) isn't called, because the drawer consumes it.
Of course, I want it to be called:)
Needless to say, I would hope the answer will be simple (even a XML one), and hopefully not by extending the Drawer class (unless that's what it takes of course).
More Info
The Activity's main layout is very simple, basically a ListView and the DrawerLayout on top of it (below in XML).
The Drawer has one fragment as it's childView (for fragment navigation) and of course, the ListView for the Drawer Items.
I've seen many questions regarding (not exactly) similar issues, and the frequent answer was to use onInterceptTouch(), requestDisallowInterceptTouchEvent() on the DrawerLayout, and on the Parent view (Activity's main content) and even onTouchEvent() (with False returned) on the ListView of the Drawer.
Nothing seems to do the trick.
I read this link
and it does seem like using Intercept methods somewhere could be the answer. But how?
Please let me know if you need any code. But it's a very basic code/layout for this matter.
Thanks!
Apparently the answer is somewhat easy, although it does make you extend the DrawerLayout and do some thinking, and maybe will result in some strange results (using the LAST
example, I haven't seen any, yet).
Anyway, related questions which looking backwards can help understanding the issue (will explain about the first one later on):
1. DrawerLayout prevents call of MainActivity.onTouchEvent()
2. How can I requestDisallowTouchEvents on Android DrawerLayout
3. Set drag margin for Android Navigation Drawer
Answer
First, please note that I put lots of examples here. If you just want the best one (for me), jump to the last one.
Secondly, if someone has enough reputation, please comment on the first link's question and put a link to this answer (it can help that guy).
Example 1
Well, basically, just extend Android's DrawerLayout and replace onTouchEvent() to this:
#Override
public boolean onTouchEvent(MotionEvent arg0) {
super.onTouchEvent(arg0);
return false;
}
This solution will do anything except that it won't open the Drawer on slides, only menu clicks and the like. Besides, it forwards clicks so when the Drawer is open
for instance, touching outside of it will NOT close it, but click on whatever is behind (e.g. a ListView). Le'ts try harder...
Example 2
Now, let's catch the open OR visible cases, to return true (and consume the action at the Drawer).
#Override
public boolean onTouchEvent(MotionEvent arg0) {
super.onTouchEvent(arg0);
if(isDrawerOpen(findViewById(R.id.list_slidermenu)) ||
isDrawerVisible(findViewById(R.id.list_slidermenu))){
return true;
}
return false;
}
This solution is better, as it prevents clicks on behind the Drawer when the drawer is open or even visible (slide starts...). But touch-sliding it still doesn't work.
Example 3
Ok, so let's just split cases. Touches (MotionEvent.ACTION_DOWN) inside the Drawer's margin (area that Google desided to slide Drawer when touched at)
will result in returning True to consume the action, and others will forward the event (return False).
#Override
public boolean onTouchEvent(MotionEvent arg0) {
super.onTouchEvent(arg0);
float edge = 30;//that's for a left drawer obviously. Use <parentWidth - 30> for the right one.
View mDrawerListView = findViewById(R.id.drawer_listview);
if(isDrawerOpen(mDrawerListView) ||
isDrawerVisible(mDrawerListView)){
return true;
} else if(arg0.getAction() == MotionEvent.ACTION_DOWN && arg0.getX() > edge){
return false;
}
return true;
}
Note that I used 30dp. That's what I found to be the margin (although in one of the links it is said to be 20....).
Well, the next example would of course be deciding what is, exactly, that edge (see in code above) value is, according to Android. We don't want to
use a number that could change or whatever.
New Question
So now that first link should come handy. It "hacks" the Drawer code to get that Drawer edge/megin number. BUT, it didn't work for me, as those exact Field names could not be found.
I run mDrawerLayout.getClass().getField() which returns all the fields, but without any luck finding what we want. Anyone?
Last Example - Full Code
Ok, looking on example number 3, after understanding what exactly I did, we can make it faster by extending the onFinishInflate() method and save it as a global variable
for this CustomDrawerLayout for later use. We can also put that first 'if' inside the second one to save some more work. OK here goes:
View mDrawerListView;
...
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mDrawerListView = findViewById(R.id.drawer_listview);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if(event.getX() > 30 && event.getAction() == MotionEvent.ACTION_DOWN){
if(isDrawerOpen(mDrawerListView) || isDrawerVisible(mDrawerListView)){
return true;
} else{
return false;
}
}
return true;
}
That's it for now! Hope it'll helps someone in the future beside myself, hehe....
While working on the same problem I was inspired by guy_m's answer and boiled down his proposals to the following solution.
Again it amounts to extending DrawerLayout and overriding onInterceptTouchEvent(). The logic is simple:
Whenever the touch event occurs off the drawer view (the slideable part), we return false. Then our DrawerLayout is out of the game when it comes to handling the event -- the event is handled by whatever view we put into the DrawerLayout at the respective position.
On the other hand, when the event occurs inside the drawer view, we delegate to super.onInterceptTouchEvent() to decide what to do with the event. That way the drawer will slide in and out as before on touch gestures happening on itself.
The following code sample is for a DrawerLayout whose drawer view is located on the right (android:gravity="right"). It should be obvious how to modify it to cover also the case of a left-placed drawer.
public class CustomDrawerLayout extends DrawerLayout
{
#Override
public boolean onInterceptTouchEvent( MotionEvent event )
{
final View drawerView = getChildAt( 1 );
final ViewConfiguration config = ViewConfiguration.get( getContext() );
// Calculate the area on the right border of the screen on which
// the DrawerLayout should *always* intercept touch events.
// In case the drawer is closed, we still want the DrawerLayout
// to respond to touch/drag gestures there and reopen the drawer!
final int rightBoundary = getWidth() - 2 * config.getScaledTouchSlop();
// If the drawer is opened and the event happened
// on its surface, or if the event happened on the
// right border of the layout, then we let DrawerLayout
// decide if it wants to intercept (and properly handle)
// the event.
// Otherwise we disallow DrawerLayout to intercept (return false),
// thereby letting its child views handle the event.
return ( isDrawerOpen( drawerView ) && drawerView.getLeft() <= event.getX()
|| rightBoundary <= event.getX() )
&& super.onInterceptTouchEvent( event );
}
}
With these answers, i still had some trouble. I could get the motionEvent back to the activity but I lost the onClick listener answer by fragment or everything on the screen. So I found another way to have everything work ( get answer when override OntouchEvent from activity, and answer to onClick Listener )
Extend DrawerLayout and Override this methode :
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(super.onInterceptTouchEvent(ev)) return true;
else {
Activity activity = AppContext.getCurrentActivity();
return activity.onTouchEvent(ev);
}
}
if the drawer want the motion event, let it handle it. And if not, pass the event to activity yourself. (AppContext.getCurrentActivity is something from you with current activity, you can for instance attach activity as weakreference to the drawerLayout OnCreate)
The good thing with this way, you don't care about the edge and don't care if start or end. And you don't care also if it is open or close. Everything work fine.
I have a solution:
Set OnTouchListener on the screen layout (the first childview of DrawerLayout, normally) and transmit the TouchEvent to a custom GestureDetector.
So, you can do your own things in it. One more important thing: if you want to override onSingleTapUp() or something else, you should return true in onDown() to make sure that you can get the rest MotionEvent to make onSingleTapUp() work.
private class MyGestureListener implements GestureDetector.OnGestureListener{
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public void onShowPress(MotionEvent e) {
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
// do your own things
return true;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
#Override
public void onLongPress(MotionEvent e) {
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
}
and set it :
mGestureDetector=new GestureDetector(this, new MyGestureListener());
layout_content.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return mGestureDetector.onTouchEvent(event);
}
});
To add on to guy_m 's answer, here is my implementation for a drawer that opens from the right, includes constructors so that it is viewable in the layout editor and also takes into account when a user swipes from past the edge point:
public class CustomDrawerLayout extends DrawerLayout {
View mDrawerListView;
float edge;
int holddown = 0;
static final String TAG = CustomDrawerLayout.class.getSimpleName();
public CustomDrawerLayout(#NonNull Context context) {
super(context);
setscreendimensionvals(context);
}
public CustomDrawerLayout(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setscreendimensionvals(context);
}
public CustomDrawerLayout(#NonNull Context context, #Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setscreendimensionvals(context);
}
private void setscreendimensionvals(Context context){
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
/*((Activity) context).getWindowManager()
.getDefaultDisplay()
.getMetrics(displayMetrics); */
int width = displayMetrics.widthPixels;
float density = displayMetrics.density;
edge = width - (30 * density); // 30 is the edge of the screen where the navigation drawer comes out
Log.d(TAG,"edge: " + edge);
Log.d(TAG,"width: " + width);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
mDrawerListView = findViewById(R.id.drawerconstraint_overworld);
}
#Override
public boolean onTouchEvent(MotionEvent event){
super.onTouchEvent(event); // need to add action up and a local variable to detect when lifted finger
//Log.d(TAG,"point: " + event.getX());
if(event.getX() >= edge && (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE)){
holddown = 1;
//Log.d(TAG,"hold down");
}
if(event.getAction() == MotionEvent.ACTION_UP){
holddown = 0;
//Log.d(TAG,"hold up");
}
if(holddown == 1){
return true;
}else{
if(isDrawerOpen(mDrawerListView) || isDrawerVisible(mDrawerListView)){
return true;
} else{
return false;
}
}
}
}
For anyone who might have the unfortunate luck of encountering as persistent an issue as this one, I will add onto the others' answers with my own problem case and solution in the hopes that fewer souls will face this nightmare of a headscratcher.
Due notice is that my explanation will most likely work for any swipeable view whose parent is DrawerLayout (e.g. this solution only works for views that are children of DrawerLayout), but I will regale my experience and my toils for the purpose of clarity.
In my case, I needed to have a MaterialCalendarView (3rd-party CalendarView on steroids) in a DrawerLayout with a NavigationView to the right (i.e. with "android:gravity"="end"). It wasn't long after implementing the view hierarchy that I realised there had existed a conflict between the swipe events of my NavigationView and MaterialCalendarView.
In essence, what occured was that whenever I began swiping the MaterialCalendarView to the right in order to swipe back to the next month, I wound up triggering the DrawerLayout's touch event interceptor and closing said DrawerLayout instead of swiping to the previous month.
So, the solution should be easy, shouldn't it? Set a onTouchListeneron the MaterialCalendarView, call requestDisallowInterceptTouchEvent(), and call it a day—akin to this in the view-hosting Activity:
calendar.setOnTouchListener { _, motionEvent ->
when(motionEvent.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
drawerLayout.requestDisallowInterceptTouchEvent(true)
}
}
true
}
...and you should be all set?
Well, the fact that I'm answering here is enough to infer that wasn't the case and that my onTouchListener wasn't, like the rest, being triggered.
After extensively scouring this thread and trying to follow everyone's advice, I came at a point wherein none of the solutions offered were helpful for someone who'd simply wanted to "exclude" a view from being detected by the DrawerLayout's touch event interceptor. Some ideas entirely paralysed my touch event infrastructure, while others simply gave me more of the same behaviour. I had hit a roadblock and I didn't know what to do.
Then, an epiphany.
I realised that due to my inexperience with writing custom views I'd missed the glaringly obvious: what I needed to do was simply find out where the MaterialCalendarView was, get its coordinates, and see if any touch events are inside in order to call the proper implementation (be it the Activity or default DrawerLayout one)! And, of course, since in the former, the onTouchListener disables interception of touch events by the DrawerLayout, that meant only the MaterialCalendarView could handle the swipes! It was so simple!
And fast-forward to learning about MotionEvents, reading up on what the heck a Rect was, and a muddy in-between of crashes, I finally wrote the custom DrawerLayout which responded to my swipes on the MaterialCalendarView only with the Activity implementation and ignored the ones outside, opting for the DrawerLayout touch interceptor:
class EventCalendarDrawerLayout : DrawerLayout {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0)
lateinit var calendar: MaterialCalendarView
lateinit var drawer: View
override fun onFinishInflate() {
super.onFinishInflate()
drawer = getChildAt(1)
calendar = findViewById(R.id.event_calendar)
}
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
val rect = Rect()
calendar.getGlobalVisibleRect(rect) // get the calendar rect positions
// respond to proper motions and forward events contained inside the calendar's rect only
if((event.action == MotionEvent.ACTION_MOVE ||
event.action == MotionEvent.ACTION_DOWN) && rect.contains(event.x.roundToInt(), event.y.roundToInt())) {
return (context as Activity).onTouchEvent(event)
}
// otherwise return the default intercept touch event response
return super.onInterceptTouchEvent(event)
}
}
It's not rocket science, truly, but it is something I deemed worth showing, as it was new and unexpected for me (and undoubtedly many others yet to venture here). Nevertheless, I believe that this implementation may work to ignore as many views embedded inside DrawerLayouts as one could possibly wish for.

Android remove onInterceptTouchEvent when tapping

Hello right now I'm developing a custom view where there'll be 2 listview that the header can be stacked each other like this picture :
||
||
\/
I already succeed to create this view, by overriding overscrolled and intercept all touch and playing with the margin of the header contents. but the problem now is because I intercept all touch now I can't tap the content of the listview, I know the flow of the touch is from ACTION_DOWN -> ACTION_MOVE -> ACTION_UP, and then when I dispatch the event to the child the tap is working but the scroll isn't working at all.
So for now the flow for my view is like this onInterceptTouchEvent -> onTouch -> gestureListener (or with overscroll)
this is some part of my code
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
onTouchEvent(ev);
return true;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
topListViewHeight = lvFirstListView.getTotalHeight() - rlFirstHeader.getHeight();
if(isBottomScrollable) {
listGestureDetector.onTouchEvent(event);
lvSecondListView.dispatchTouchEvent(event);
} else {
gestureDetector.onTouchEvent(event);
}
return true;
}
Is there anybody here have an idea about this? Any suggestion is very appreciated and if you need more question about this question please ask me.
Thanks before

Let parent View assume MotionEvents if child returns false

I have a application that need event handling on a unusual way.
For my question, let me first explain a simple case that the current event handling system of Android don't fits for me.
Supposing that I have a FrameLayout (that I'll call ViewSwiper since now) that all Views added on it are MATCH_PARENT X MATCH_PARENT (that I'll call PageView), it's handles events by translating the actual View and replace it based on the direction of moving.
This component I already have done and work properly ( Using Animation to swipe views ).
But the problem is on that PageView I add on top of it, in case of ImageViews that return false on it's onTouchEvent, the ViewSwiper will handle the events and let another PageView enter the screen, but if I add a ScrollView on that, all the events will be consumed by the Scroll and the ViewSwiper will not have chance to replace the PageView.
So I figured out that returning false onTouchEvent of the ScrollView the parent can assume it's events, I wrote this sub-class of ScrollView to test it:
public class ScrollViewVertical extends ScrollView {
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
}
public boolean onTouchEvent(MotionEvent evt) {
super.onTouchEvent(evt);
return false;
}
}
But returning false make any further events to get dispatched to the parent, but I need these events for VERTICAL scrolling, so I have the idea to return falses only if the user are moving HORIZONTAL, that's what my code looks like:
public class ScrollViewVertical extends ScrollView {
private MovementTracker moveTracker;
public ScrollViewVertical(Context context) {
super(context);
setOverScrollMode(OVER_SCROLL_ALWAYS);
setVerticalScrollBarEnabled(false);
moveTracker = new MovementTracker();
}
public boolean onTouchEvent(MotionEvent evt) {
if (moveTracker.track(evt))
if (moveTracker.getDirection() == Direction.HORIZONTAL)
return false;
return super.onTouchEvent(evt);
}
}
PS: MovementTracker will returns true on track() after some events and tell on which direction the user is moving.
But in that case, the ScrollView keep receiving events since it's returns true on the first events.
Any ideas on how can I handle the events on ViewSwiper when it's child returns false (even if some trues are returned).
PS: I can give more info about this if needed, and accept different solutions also.
Based on answers I tried the following:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
return intercept;
}
public boolean onTouchEvent(MotionEvent evt) {
boolean x = super.onTouchEvent(evt);
if (moveTracker.track(evt)) {
intercept = moveTracker.getDirection() != Direction.VERTICAL;
if (!intercept)
getParent().requestDisallowInterceptTouchEvent(false);
}
return x;
}
Still nothing.
try this in onTouchEvent() of the scrollview
//if (evt.getAction() == MotionEvent.ACTION_MOVE) {
if (moveTracker.track(evt)){
if (moveTracker.getDirection() == Direction.VERTICAL){
//Or the direction you want the scrollview keep moving
getParent().requestDisallowInterceptTouchEvent(true);
}
}
return true;
Update
Please try the following to the custom Scrollview
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
super.dispatchTouchEvent(ev);
return false;
}
And nothing else
This way i assume the MotionEvent will perform on both views. And since they don't conflict (One is vertical the other one is Horizontal) this could work
Based on the answer from weakwire, I came to the following solution:
On ViewSwiper
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(!super.dispatchTouchEvent(ev))
onTouchEvent(ev);
return true;
}
And on ScrollHorizontal I return false on dispatchTouchEvent when I don't need then anymore.

Android. How to get layout values from views in TabHost?

I have a TabHost hosting 3 Activities. In addition to the tabs, I want support for swipe gestures to change the current tab. My issue is that one of the views holds a horizontal scrollview, and I cannot figure out how to prevent touches in the horizontal scrollview from changing the current tab.
plateView is the horizontal scrollview that needs to be handled. Finding it's bottom with plateView.getBottom() and not counting touches above that works without the TabHost, but now it returns null and crashes, regardless of where it's called.
onTouchEvent counts touches everywhere and dispatchTouchEvent doesn't count touches on any widget. It seems some combination of them would be great, but together they yield the same functionality as onTouchEvent alone. Swipes anywhere count to change the Activity. My understanding of these is a little fuzzy though.
Why does getBottom() return null? How can I get this to work?
gestureDetector = new GestureDetector(new CalcGestureDetector());
gestureListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG,"onTOUCH");
if(gestureDetector.onTouchEvent(event)){
return true;
}
return false;
}
};
//Takes the touch and interprets it. Handles it. Changes tabs on fling.
class CalcGestureDetector extends SimpleOnGestureListener{
#Override
public boolean onFling(MotionEvent eOne, MotionEvent eTwo, float velocityX, float velocityY){
Log.d(TAG,"WTF "+plateView.getBottom());
if(false){
}
else{
try{
if (Math.abs(eOne.getY() - eTwo.getY()) > flingMaxOffPath)
return false; //Too much of an arc in the fling.
// right to left swipe
if(eOne.getX() - eTwo.getX() > flingMinDistance && Math.abs(velocityX) > flingMinVelocity) {
tabHost.setAnimation(slideLeftIn);
tabHost.setAnimation(slideLeftOut);
tabHost.setCurrentTab((tabHost.getCurrentTab()+1)%3);
} else if (eTwo.getX() - eOne.getX() > flingMinDistance && Math.abs(velocityX) > flingMinVelocity) {
tabHost.setAnimation(slideRightIn);
tabHost.setAnimation(slideRightOut);
tabHost.setCurrentTab((tabHost.getCurrentTab()+2)%3);
}
}
catch(Exception e){
}
}
return false;
}
}
//This method alone keeps a touch in the weights from changing tabs, but won't register
//touches on ANY widget on any screen (like text views).
public boolean dispatchTouchEvent(MotionEvent event){
super.dispatchTouchEvent(event);
return gestureDetector.onTouchEvent(event);
}
//This method alone counts touches for swipes anywhere and everywhere.
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}

Categories

Resources