android onscroll gesture detector - android

In my android app I've got an Activity with a ViewFlipper. Inside the Viewflipper I've got 3 Linear Layouts.
<ViewFlipper
android:id="#+id/viewFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dip"
android:text="#string/publicas"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="#+id/listViewDemandasPublicas"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dip"
android:text="#string/pendientes"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="#+id/listViewDemandasPendientes"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dip"
android:text="#string/pendientes"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ListView
android:id="#+id/listViewDemandasFinalizadas"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ViewFlipper>
So I want to change the ViewFlippers containing when I scroll horizontaly. So I've got a gesture detector and a gesture listener implemented. It works fine even if I have a ListView inside (I've done in in another activity). My onScroll methos is the next one:
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
ViewFlipper vf = (ViewFlipper) getWindow().findViewById(R.id.viewFlipper);
View view = null;
if ((e1.getX() < e2.getX()) && (distanceX < -20)) { // dedo a la derecha
Log.i("ListaDemandas", "Muevo a la derecha SCROLL");
if (esPendientes) {
esPendientes = false;
esEntrada = true;
verPublicas(view);
vf.setInAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_right_in));
vf.setOutAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_right_out));
vf.showPrevious();
} else if (esFinalizadas) {
esFinalizadas = false;
esPendientes = true;
verPendientes(view);
vf.setInAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_right_in));
vf.setOutAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_right_out));
vf.showPrevious();
}
} else if ((e1.getX() > e2.getX()) && (distanceX > 20)) {
Log.i("ListaDemandas", "Muevo a la izquierda SCROLL");
if (esPendientes) {
esPendientes = false;
esFinalizadas = true;
verFinalizadas(view);
vf.setInAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_left_in));
vf.setOutAnimation(AnimationUtils.loadAnimation(getParent(), .anim.push_left_out));
vf.showNext();
} else if (esEntrada) {
esEntrada = false;
esPendientes = true;
verPendientes(view);
vf.setInAnimation(AnimationUtils.loadAnimation(getParent(), R.anim.push_left_in));
vf.setOutAnimation(AnimationUtils.loadAnimation(getParent(), .anim.push_left_out));
vf.showNext();
}
}
return true;
}
My problem is that onScroll is being called (of course) each time a scroll distance is bigger than 20. So, if I make a long scroll, each time my scroll movement is bigger than 20 the method is being called. So if my Scroll have a distance of 50, the method is being called in 21, 22, 23....
How can I manage this to get the method being called only once? I've been reading the API and I've been trying controlling the MotionEvent times, but it isn't a good solution because I can make two fast scrolls. I've seen addBatch method too. This is my solution? Anybody knows how can I batch all calls in one? Any "artisanal" way?
Thank you
=============================================================================================================
EDIT:
I've solved it in an artisanal way. A boolean initializated with true newScroll. So when I make a Scroll I put it to false and onDown() I put it to true again. So:
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
ViewFlipper vf = (ViewFlipper) getWindow().findViewById(R.id.viewFlipper);
if (scrollNuevo) {
if ((e1.getX() < e2.getX()) && (distanceX < -20)) {
Log.i("ListaDemandas", "Muevo a la derecha SCROLL");
scrollNuevo = false; Log.i("ListaDemandas","ScrollNuevo=false");
if (esPendientes) {
...
And the method onDown:
#Override
public boolean onDown(MotionEvent e) {
scrollNuevo = true;Log.i("ListaDemandas","ScrollNuevo=true");
return true;
}

I've solved it in an artisanal way. A boolean initializated with true newScroll. So when I make a Scroll I put it to false and onDown() I put it to true again. So:
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
ViewFlipper vf = (ViewFlipper) getWindow().findViewById(R.id.viewFlipper);
if (scrollNuevo) {
if ((e1.getX() < e2.getX()) && (distanceX < -20)) {
Log.i("ListaDemandas", "Muevo a la derecha SCROLL");
scrollNuevo = false; Log.i("ListaDemandas","ScrollNuevo=false");
if (esPendientes) {
...
And the method onDown:
#Override
public boolean onDown(MotionEvent e) {
scrollNuevo = true;Log.i("ListaDemandas","ScrollNuevo=true");
return true;
}

Related

Android viewflipper not working with drawer

I am trying to integrate viewflipper in activity which already have navigation drawer, but viewflipper not working, although navigation drawer is working. theme is to swipe a layout and show data. Below is my code
xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context="com.inabia.dailyayat.Activities.MainActivity">
<ViewFlipper
android:id="#+id/viewflipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="6dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="#+id/txtayat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:shadowColor="#ffffff"
android:shadowDx="3"
android:shadowDy="-3"
android:shadowRadius="1.5"
android:textColor="#ffd700"
android:textSize="40sp"
android:textStyle="bold" />
</LinearLayout>
</ViewFlipper>
</RelativeLayout>
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#d3d3d3"
android:cacheColorHint="#00000000"
android:choiceMode="singleChoice"
android:divider="#ffffff"
android:dividerHeight="1px" />
</android.support.v4.widget.DrawerLayout>
and java code
private ViewFlipper viewFlipper;
private float lastX;
viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper);
public boolean onTouchEvent(MotionEvent touchevent) {
switch (touchevent.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = touchevent.getX();
break;
case MotionEvent.ACTION_UP:
float currentX = touchevent.getX();
// Handling left to right screen swap.
if (lastX < currentX) {
// If there aren't any other children, just break.
if (viewFlipper.getDisplayedChild() == 1)
break;
// c.add(Calendar.DAY_OF_MONTH,-1);
// formattedDate = df.format(c.getTime());
// txt1.setText(formattedDate);
// Next screen comes in from left.
viewFlipper.setInAnimation(this, R.anim.slide_in_from_left);
// Current screen goes out from right.
viewFlipper.setOutAnimation(this, R.anim.slide_out_to_right);
// Display next screen.
viewFlipper.showNext();
}
// Handling right to left screen swap.
if (lastX > currentX) {
// If there is a child (to the left), kust break.
if (viewFlipper.getDisplayedChild() == 1)
break;
// c.add(Calendar.DAY_OF_MONTH,1);
// formattedDate = df.format(c.getTime());
// txt1.setText(formattedDate);
// Next screen comes in from right.
viewFlipper.setInAnimation(this, R.anim.slide_in_from_right);
// Current screen goes out from left.
viewFlipper.setOutAnimation(this, R.anim.slide_out_to_left);
// Display previous screen.
viewFlipper.showPrevious();
}
break;
}
return false;
}
Try to overide dispatchTouchEvent rather then onTouchEvent and use GestureDetector.SimpleOnGestureListener to detect all touch events on device screen hope it will work for you.
Code :
CustomGestureDetector.java
class CustomGestureDetector extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// Swipe left (next)
if (e1.getX() > e2.getX()) {
viewFlipper.setInAnimation(context, R.anim.left_in);
viewFlipper.setOutAnimation(context, R.anim.left_out);
viewFlipper.showNext();
}
// Swipe right (previous)
if (e1.getX() < e2.getX()) {
viewFlipper.setInAnimation(context, R.anim.right_in);
viewFlipper.setOutAnimation(context, R.anim.right_out);
viewFlipper.showPrevious();
}
return super.onFling(e1, e2, velocityX, velocityY);
}
#Override
public boolean onDoubleTap(MotionEvent e) {
Log.d("Tap", "Double tap");
return super.onDoubleTap(e);
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
}
dispatchTouchEvent :
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mGestureDetector.onTouchEvent(ev);
return super.dispatchTouchEvent(ev);
}
and in your onCreate method of activity call GestureDetector like this :
CustomGestureDetector customGestureDetector = new CustomGestureDetector();
mGestureDetector = new GestureDetector(this, customGestureDetector);

Android onTouchListener for entire dialog

I have a dialog to choose files from. This dialog contains a listview to show directories and files and has an onItemClickListener for navigation purposes.
How can I react to fling events to go up in the directory structure? I tried setting an OnTouchListener on the root RelativeLayout to dispatches touch events to my GestureListener:
final GestureDetector detector = new GestureDetector(new MyGestureDetector(tvCurrentPath));
dialog.findViewById(R.id.rlFilelist).setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View view, MotionEvent e) {
detector.onTouchEvent(e);
return false;
}
});
The events get registered in onTouch(), but the onFling() method is never called in the MyGestureDetector. If, however, I attach the OnTouchListener to the listview, it works as expected. The problem with this approach is that the listview is not always filled extensively with data and when it is empty or when clicking below items, no onTouch() events are triggered.
Dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rlFilelist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:focusable="true" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/llButtons" >
<TextView
android:id="#+id/tvCurrentPath"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip" >
</TextView>
<ListView
android:id="#+id/lvFileListing"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="#id/llButtons"
android:layout_below="#id/tvCurrentPath" >
</ListView>
</RelativeLayout>
<RelativeLayout
android:id="#+id/llButtons"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5dip"
android:layout_marginTop="5dip" >
<Button
android:id="#+id/btAddDirectory"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="10px"
android:text="#string/host_tv_dg_add_directory" />
<Button
android:id="#+id/btUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10px"
android:layout_toRightOf="#id/btAddDirectory"
android:text="#string/host_tv_dg_navigate_up" />
<Button
android:id="#+id/btClose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/btUp"
android:text="#string/_tv_close" />
</RelativeLayout>
</RelativeLayout>
Screenshot for dialog with not completely filled lisview:
http://imageshack.us/photo/my-images/802/dialog.png/
How can I make the onFling() events trigger on the whole dialog or on the "whole" listview even when it's not completely filled with items?
Thank you!
You should create a class which extends the dialog class and then you can create a gesture detector and attach to the ontouchevent of the class.
Here is a code example:
public class GestureDialog extends Dialog {
public GestureDialog (Context context, int theme) {
super(context, theme);
gestDetec = new MyGestureDetector(); //inital setup
gestureDetector = new GestureDetector(gestDetec);
gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return false;
}
};
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
System.out.println( "Help im being touched!" );
return false;
}
}
}
You can Take one activity and set it's theme in manifest to dialog.
Then implements onTouchListener to that activty
and write one onTouch() event in that Activity.
Now you got Touch event for entire dialog :)

Navigate only one View by gesture on Gallery

I have a Gallery that each current item matchs the display dimensions. I want when the user try to "throw" the views to any side, instead of the automatic scrolling, I want to go just to the next View.
How can I do that?
Check this snippet. It uses no scrolling constants, instead it relies on scolling key event to handle for different resolutions.
private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2){
return e2.getX() > e1.getX();
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY){
int kEvent;
if(isScrollingLeft(e1, e2)){ //Check if scrolling left
kEvent = KeyEvent.KEYCODE_DPAD_LEFT;
}
else{ //Otherwise scrolling right
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;
}
onKeyDown(kEvent, null);
return true;
}
This piece of code belongs to a galleryView model which extends Gallery and its layout looks like this
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="180dp">
<com.example.android.GalleryView
android:id="#+id/gallery"
android:layout_width="fill_parent"
android:layout_height="180dp"
android:gravity="bottom"
android:fadingEdge="none"/>
</LinearLayout>

stop touch from passing to lower surfaceview

I have a SurfaceView "over" a GLSurfaceView in Android.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<my.project.ActionSurface
android:id="#+id/actionSurface"
android:layout_marginBottom="30dip"
android:layout_marginLeft="20dip"
android:layout_marginRight="20dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<fi.harism.curl.CurlView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/larryMoeCurly"
/>
</RelativeLayout>
(Many thanks to harism for his page curl code!)
In my ActionSurface, I have this to detect touches:
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
newX = (int) event.getX();
newY = (int) event.getY();
diffX = newX - x;
diffY = newY - y;
Log.d(TAG, "touched at x:" + newX + " y:" + newY);
}
return true;
}
When I touch my ActionSurface, it responds correctly, but the touch goes through to the CurlView as well. How do I stop the touch from "passing through" to the other surface?
you could add an OnTouchListener to your surface view, and catch the event there (return true):
this.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
return true;
}
});

SimpleOnGestureListener code not working in Android 2.2

I have some code that I wrote to implement a vertical swipe on a
Gallery widget. It works great in Android 1.5 and 1.6 but does not
work in Android 2.2 (I have yet to try it with 2.1).
public class SwipeUpDetector extends SimpleOnGestureListener
implements OnTouchListener
{
private GestureDetector m_detector;
public SwipeUpDetector()
{
m_detector = new GestureDetector(m_context, this);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
{
if (Math.abs(e1.getX() - e2.getX()) < s_swipeMaxOffPath &&
e1.getY() - e2.getY() >= s_swipeMinDistance &&
Math.abs(velocityY) >= s_swipeMinVelocity)
{
int pos = m_gallery.pointToPosition((int)e1.getX(), (int)e2.getY());
startAnimation(pos);
return true;
}
return false;
}
#Override
public boolean onTouch(View v, MotionEvent event)
{
return m_detector == null ? false : m_detector.onTouchEvent(event);
}
}
And to be able to get my gallery to detect the onFling I have the
following:
m_gallery.setOnTouchListener(new SwipeUpDetector());
In Android 1.5 and 1.6 this works great. In Android 2.2 onFling() is
never called. In looking around on Google and StackOverflow I found
one possible solution was to implement onDown() and return true.
However, I am also listening to single clicks and have a context menu
listener set up on this gallery. When I implement onDown() and return
true I do indeed get the swipe to work. But when I do this the
context menu doesn't display on a long click and the single clicks
don't work either... Clicking on items in the gallery cause the
gallery to jump around and I don't get any feedback when I click on an
item in the gallery. It just immediately makes that item the selected
item and moves it to the center.
I looked at the API differences report between 1.6, 2.1, and 2.2 and
didn't see anthing of significance that could have caused this to
break...
What am I doing wrong?
EDIT:
It might also be helpful to know that the gallery is nested inside a couple layouts as follows (this isn't a complete layout... it is just intended to show the hierarchy of where this Gallery lives):
<ScrollView>
<LinearLayout>
<RelativeLayout> <!-- This relative layout is a custom one that I subclassed -->
<Gallery />
</RelativeLayout>
</LinearLayout>
</ScrollView>
EDIT #2:
Here are the requested layouts... There are two of them, for reusability purposes. Here is the first one, which is the main activity's layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:myns="http://com.magouyaware/appswipe"
android:id="#+id/main_layout_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_horizontal"
android:scrollbarAlwaysDrawVerticalTrack="false"
>
<LinearLayout
android:id="#+id/appdocks_layout_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginTop="10dp"
android:layout_gravity="center"
android:orientation="vertical"
android:gravity="center"
android:background="#null"
>
<com.magouyaware.appswipe.TitledGallery
android:id="#+id/running_gallery_layout_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
myns:gallery_title="#string/running_title"
/>
<com.magouyaware.appswipe.TitledGallery
android:id="#+id/recent_gallery_layout_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
myns:gallery_title="#string/recent_title"
/>
<com.magouyaware.appswipe.TitledGallery
android:id="#+id/favs_gallery_layout_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
myns:gallery_title="#string/favs_title"
/>
<com.magouyaware.appswipe.TitledGallery
android:id="#+id/service_gallery_layout_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
myns:gallery_title="#string/service_title"
/>
<com.magouyaware.appswipe.TitledGallery
android:id="#+id/process_gallery_layout_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="gone"
myns:gallery_title="#string/process_title"
/>
<include
android:id="#+id/indeterminate_progress_layout_id"
layout="#layout/indeterminate_progress_layout"
/>
</LinearLayout>
</ScrollView>
And here is the layout file for com.magouyaware.appswipe.TitledGallery... This is nothing more than a RelativeLayout subclass for the purpose of controlling several views as a single item in the code and for reusability:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/titled_gallery_main_layout_id"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:background="#null"
>
<LinearLayout
android:id="#+id/titled_gallery_expansion_layout_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:focusable="true"
android:clickable="true"
android:gravity="center_vertical"
>
<ImageView
android:id="#+id/titled_gallery_expansion_image_id"
android:layout_width="20dp"
android:layout_height="20dp"
android:duplicateParentState="true"
android:clickable="false"
/>
<TextView
style="#style/TitleText"
android:id="#+id/titled_gallery_title_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"
android:paddingLeft="1sp"
android:paddingRight="10sp"
android:textColor="#drawable/titled_gallery_text_color_selector"
android:duplicateParentState="true"
android:clickable="false"
/>
</LinearLayout>
<Gallery
android:id="#+id/titled_gallery_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/titled_gallery_expansion_layout_id"
android:layout_alignWithParentIfMissing="true"
android:spacing="5sp"
android:clipChildren="false"
android:clipToPadding="false"
android:unselectedAlpha=".5"
android:focusable="false"
/>
<TextView
style="#style/SubTitleText"
android:id="#+id/titled_gallery_current_text_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/titled_gallery_id"
android:layout_alignWithParentIfMissing="true"
android:gravity="center_horizontal"
/>
</RelativeLayout>
I was able to receive single/double clicks and long click as well by implementing onSingleTapConfirmed, onDoubleTap and onLongPress in my implementation of SimpleOnGestureListener (while returning true from onDown).
Concerning why we should override onDown method. I think it is related to the issue #8233. It was reported one year ago against 2.1 version. Since only 10 people starred it so far I guess it would not be fixed in the near future.
UPDATE
It turned out that the issue was caused by the combination of ScrollView and Gallery and usage of OnTouchListener. Gallery itself implements OnGestureListener and encapsulates GestureDetector which is disabled when we set our OnTouchListener, resulting in a strange gallery behavior sometimes. On the other hand if we just subclass Gallery component and perform long-click/swipe detection in its onLongPress/onFling methods the parent ScrollView will intercept vertical move events preventing onFling call for such events. The solution is to override Gallery.dispatchTouchEvent and call requestDisallowInterceptTouchEvent(true) for gallery parent.
To summarize: if you want to detect swipes (long-, double-clicks etc.) for the Gallery (and possibly place it inside the ScrollView) use the custom component provided below instead of GestureDetector/OnTouchListener.
public class FlingGallery extends android.widget.Gallery implements OnDoubleTapListener {
private static final int SWIPE_MIN_VELOCITY = 30; // 30dp, set to the desired value
private static final int SWIPE_MIN_DISTANCE = 50; // 50dp, set to the desired value
private static final int SWIPE_MAX_OFF_PATH = 40; // 40dp, set to the desired value
private final float mSwipeMinDistance;
private final float mSwipeMaxOffPath;
private final float mSwipeMinVelocity;
public FlingGallery(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
float density = context.getResources().getDisplayMetrics().density;
this.mSwipeMinDistance = density * SWIPE_MIN_DISTANCE;
this.mSwipeMaxOffPath = density * SWIPE_MAX_OFF_PATH;
this.mSwipeMinVelocity = density * SWIPE_MIN_VELOCITY;
}
public FlingGallery(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.galleryStyle);
}
public FlingGallery(Context context) {
this(context, null);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final ViewParent parent;
if (ev.getAction() == MotionEvent.ACTION_MOVE && (parent = getParent()) != null) {
parent.requestDisallowInterceptTouchEvent(true); // this will be passed up to the root view, i.e. ScrollView in our case
}
return super.dispatchTouchEvent(ev);
}
#Override
public boolean onDoubleTap(MotionEvent e) {
// Your double-tap handler...
return true;
}
#Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// Your single-tap handler...
return true;
}
#Override
public void onLongPress(MotionEvent event) {
// Your long-press handler...
super.onLongPress(event);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (e1 == null) {
return super.onFling(e1, e2, velocityX, velocityY);
}
float dx = e2.getX() - e1.getX();
float dy = e2.getY() - e1.getY();
if (abs(dx) < mSwipeMaxOffPath && abs(velocityY) > mSwipeMinVelocity && abs(dy) > mSwipeMinDistance) {
if (dy > 0) {
// Your from-top-to-bottom handler...
} else {
// Your from-bottom-to-top handler...
}
} else if (abs(dy) < mSwipeMaxOffPath && abs(velocityX) > mSwipeMinVelocity && abs(dx) > mSwipeMinDistance) {
if (dx > 0) {
// Your from-left-to-right handler...
} else {
// Your from-right-to-left handler...
}
}
return super.onFling(e1, e2, velocityX, velocityY);
}
}
If you do not handle the down you will not get any event (scroll, fling, up) linked to this down event. So you must return true.
I tried to understand why but I failed yet. Maybe since SimpleOnGestureListener returns false by default, and some new 2.2 optimizations in the outer Layout feels you do not want the event. You are not a valid target anymore for the chain of events.
To get your longPress working, can't you implement the onLongPress event in your detector and call the code that makes your menu appear?

Categories

Resources