Swipe Horizontal - android

I need to swipe horizontally between list of questions and their respective choices in a radio group. I have read that I have different choices such as:
1- Gesture Detector
2- ViewPage
3- ViewPage with fragments
I have tried ViewPage but I am facing different problems on how to swipe backward and indicating which choice has been checked by the user.
I need to use the easiest way to swipe forward and backward (indicating which choice has been checked)
I want to depend on Andriod methods as much as possible without storing the checked choices for example by myself since I believe that it is more optimized such methods from both run-time or code size.
Please if there are other classes that I can use, guide me on that or which of them is really the best to perform my target

I would use ViewPager with a FragmentPagerAdapter. The items of the adapter would be different instances of the same fragment. Each fragment would contain a static question text and a radio group of possible answers to the question.
ViewPager then will take care of swiping substituting one such fragment with another.

I highly recommend this Library:
ViewPagerIndicator by Jake Warton
Is very customizable.

You can easilly use the gesture detector creating a class that extend SimpleOnGestureListener that possess the onFling methods like that :
protected class ExampleGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) {// User swipe vertically
}
if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_MIN_VELOCITY) {// Right swipe
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_MIN_VELOCITY) {// Left swipe
}
return false;
}
}
You just have to define the variable as you want, create your gestureDetector like
gestureDetector = new GestureDetector(new ExampleGestureDetector());
And redefine the touch listener for the view you want
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View wv, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
EDIT : After testing it, it appear that it only work with scrollable item.

Related

Adding vertical swipe on ViewPager items to "dismiss" them

I know that people who post no code and do not explain what they have done are not welcome here, but I think that the following question might really interest a lot of developers, and might worth trying to ask it.
I am using a classic ViewPager to display some informations in my application, and I have recently seen a great example in the planning poker application.
This is great and awesome, but what I really would like to achieve is a vertical swipe to remove a card in the ViewPager.
To be honnest, I have no place to start and even by reviewing the ViewPager source code, I feel a little bit lost.
Do you have any idea if someone already tried to acheve such a behaviour?
In case the answer is no, I will go on trying and post updates here, but as I have already wasted some hours, I fear that that will be hard.
Yes it is possible and very easy as well. You just have to implement a gestureListner in you activity and then in the onFling() method track for the vertical swipe. Rest is all up to you implementation.
I am adding a little piece of code just to give you a starting point.
private int swipe_Min_Distance = 200;
private int swipe_Max_Distance = 600;
private int swipe_Min_Velocity = 100;
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if(xDistance > this.swipe_Max_Distance || yDistance > this.swipe_Max_Distance)
return false;
velocityX = Math.abs(velocityX);
velocityY = Math.abs(velocityY);
boolean result = false;
if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance){
if(e1.getY() > e2.getY()) // bottom to up swipe
{
//here you can do what ever you want after detection of your swipe
}
else{ //top to bottom swipe
//here you can do what ever you want after detection of your swipe
}
result = true;
}
return result;

Android onGestureListener and onTouchListner

I am working in android. I have some functionality which is to be done on these following methods:-
1. MotionEvent.ACTION_DOWN
2. MotionEvent.ACTION_UP
3. MotionEvent.ACTION_MOVE
and i also want to do fling() on that image.
For above requirement i implemented onGestureListener and onTouchListener in my application, but these are not working properly. onTouchListener is working but onGestureListener is not working. When i remove onTouchListner code then onGestureListener is working correctly.
So please suggest me what should i do for this. I want to implement these four methods in my application.
1. MotionEvent.ACTION_DOWN
2. MotionEvent.ACTION_UP
3. MotionEvent.ACTION_MOVE
4. onFling
You can acheive this by implementing the OnGestureListener from your activity overriding the below methods
// For touch
public boolean onSingleTapUp(MotionEvent event) { return false; }
// For Fling
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
Hope this helps.
EDIT 1: Detailed explanation:
1> implement the OnGestureListener from your activity
public class MyActivity implements OnGestureListener
2> create an instance of GestureDetector:
private GestureDetector gestureScanner;
And at onCreate:
// Avoid a deprecated constructor
// (http://developer.android.com/reference/android/view/GestureDetector.html)
gestureScanner = new GestureDetector(this, this);
3> Override the below methods:
#Override
public boolean onTouchEvent(MotionEvent event) {
return gestureScanner.onTouchEvent(event);
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
/* on scroll to the next page in story */
if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
// ...
}
/* on scroll to the previous page in story */
else if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
// ...
}
return false;
}
#Override
public boolean onSingleTapUp(MotionEvent event) {
return false;
}
EDIT 2: For handling Move
Override the onScroll method have a look at the details here
Just as a word of advice coming from a fellow Android developer who has chased down the primrose path of the swipe or fling event... When writing a listener or trying to implement the OnGestureListener class be sure to have import android.view.GestureDetector.OnGestureListener in your imports.
Otherwise the OnGesturListener will be using a GestureOverlayView's imports and that's not what it seems you are looking for. Those are helpful especially when trying to implement gesture captures using the Gestures application and importing the saved gestures and creating a Gestures Library for your application during run time to then "predict" what the user is doing by running some fancy algorithm in the background and seeing who deviates the least from the user's input. Which turns out to be an array of locations that came in from the OS as the dragged their little stubby fingers across the flat screen... It's a different approach than just trying to capture a fling, or long press. Which seems as what the users are looking for above and not some fancy multi-press gesture capture, which this approach makes very simple. This technique is also very helpful in video games and giving possibly a "backdoor" to any people out there who like to develop games. Or in the 9-5 world, to put a Support or Admin backdoor into your mobile application to hide some settings or logs from everyday users. In which one can also use the above intended techniques but then you have to get an array of points, or locations across the screen and it gets tedious for any non-math people or self taught developers. Hope I contributed or helped anyone. No one mentioned this to me when I was learning this and I never found it in any of the several books I have read about Android also.

View with gesture listener and onLongClickListener

I'm trying to implement a view that has both a longClickListener and a gesture dectector. Basically, I need a button to show a view when the user long clicks on the first view, and then I want to dectect a fling motion up. I would like to make it so that the user does not have to lift their finger at all, and hit both the longclick, and the fling motion.
Here is my code for the longClickListener:
flipCard.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View view) {
answerRight.setVisibility(View.VISIBLE);
answerRight.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.grow_from_middle));
answerWrong.setVisibility(View.VISIBLE);
answerWrong.startAnimation(AnimationUtils.loadAnimation(view.getContext(), R.anim.grow_from_middle));
return false;
}
});
Here is the code for my gesture dector:
gestureDetectorScore = new GestureDetector(new ScoreGestureDetector());
gestureListenerScore = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetectorScore.onTouchEvent(event)) {
return true;
}
return false;
}
};
private class ScoreGestureDetector extends SimpleOnGestureListener {
private static final int SWIPE_MIN_DISTANCE = 5;
private static final int SWIPE_THRESHOLD_VELOCITY = 2;
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
//if (Math.abs(e1.getX() - e2.getX()) > SWIPE_MAX_OFF_PATH)
//return false;
// right to left swipe
//if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE){
Toast.makeText(AndroidOrientationSensor.this, "Up Swipe", Toast.LENGTH_SHORT).show();
}
//else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE){
Toast.makeText(AndroidOrientationSensor.this, "Down Swipe", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
// nothing
}
return false;
}
#Override
public boolean onDown(MotionEvent e1){
Toast.makeText(AndroidOrientationSensor.this, "Up Swipe", Toast.LENGTH_SHORT).show();
return true;
}
}
Finally, I am setting the gesture dectector to the the "flipCard" view like this:
flipCard.setOnTouchListener(gestureListenerScore);
Any help would be greatly appreciated.
I think the problem you're going to have is if the touch event is consumed by the button, then the second view won't receive it.
Even if it did, the fling I imagine will only get called when the user performs a fling from start to finish.
The only thing I can think of doing (despite it being very horrible / hacky) is to try and inset a fake up touch event. This might then allow the user to perform the fling, but this isn't particularly stable either.
Better way to handle it would be to have a view group consume the touch events, pass the touch events to a gesture detector to detect a long click, then set a flag within the view groups touch listener to indicate a fling is expected, then manually detect when prev y and current y go beyond a threshold. This will give you some idea of velocity but probably no where near as meaningful as the velocity provided in the onFling callback method.
What I ended up doing to solve this problem is placing a gestureDectector on the view and overriding the onDown method. This allowed me to simulate a click event. I was unable to get both a long click and a swipe event, but for my purposes the click event triggers during the swipe which seems to work well enough. Thanks to GauntFace for the inspiration.
The GestureListener also has a onLongPress event.

Swipe/Fling tab-changing in conjunction with ScrollView?

The best I could find on this particular issue (although I do not use a Gallery): ScrollView and Gallery interfering - doesn't really give a specific answer though. And my implementation does not use a Gallery, obviously.
Jump down to the next bold part for the interesting part
So I got Fling/Swipe/Flick/whatever you want to call it to work a while ago on my application. Inspiration was gathered from a couple of different places, some of them being "basic gesture detection" here on Stack Overflow ( Fling gesture detection on grid layout ), Code Shogun ( http://www.codeshogun.com/blog/2009/04/16/how-to-implement-swipe-action-in-android/ ) and Developing Android ( http://developingandroid.blogspot.com/2009/09/implementing-swipe-gesture.html ), but I do not use a ViewFlipper in my application. When a fling occurs I simply change the tab (wrapping around at the ends).
Now, some of my tabs contain ScrollViews. These ScrollViews obviously respond to up/down scrolls in order to let you view all data inside it, no surprise there.
The issue is that it would appear the 'scroll' function of these ScrollViews overwrite my fling gesture. I cannot fling inside a ScrollView (scroll just fine), but it works flawlessly outside them (on the same tab, on other views such as TableRow or whatever).
I had a quick look at http://blog.velir.com/index.php/2010/11/17/android-snapping-horizontal-scroll/ too, which provides a way to implement HorizontalScrollView. But it still handles gestures through a class that extends SimpleOnGestureListener (and overwrites onFling), which is the same implementation as I have (which leads me to believe it won't really help).
Source code for ScrollView from Google: http://google.com/codesearch/p?hl=en#uX1GffpyOZk/core/java/android/widget/ScrollView.java&d=3
Is there any way to have my implementation of Swipe and ScrollView working together effortlessly?
This is where the problem lies, I guess. ScrollView.java also uses a method called onTouchEvent and the documentation for the onTouchEvent for Activity states:
"Called when a touch screen event was
not handled by any of the views under
it. This is most useful to process
touch events that happen outside of
your window bounds, where there is no
view to receive it."
So the ScrollView does "override" it - what do I do? Is there no way to ensure both are checked? My onTouchEvent which is not hit when the onTouchEvent is handled by the ScrollView:
#Override
/** Used for swipe gestures */
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event))
return true;
else
return false;
}
More general source code below, it's probably not very vital. The gestureDetector inside my Tabs class with its associated listener:
// Gestures
gestureDetector = new GestureDetector(new MyGestureDetector());
gestureListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return false;
}
};
My gesture class which is a nested class of my Tabs class (which extends TabActivity) - it's the same as any other code you will find on this subject:
/** GestureDetector used to swipe between classes */
class MyGestureDetector extends SimpleOnGestureListener {
TabHost tabHost = getTabHost();
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
try {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) return false;
if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
// my tab code
return true;
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
// my tab code
return true;
}
} catch (Exception e) {
Log.e("MyGestureDetector onFling", e.toString());
}
return false;
}
}
I would suggest you have a look at the Google I/O 2010 app source code, as their FlingableTabHost implementation would appear to have solved this problem:
http://iosched.googlecode.com/svn/trunk/src/com/google/android/apps/iosched/ui/ScheduleActivity.java
I think the key is in extending TabHost and overriding its onInterceptTouchEvent method.
In looking to solve a similar issue I am having, I came across this tid-bit:
eventsInterceptionEnabled: when set to true, this property tells the overlay to steal the events from its children as soon as it knows the user is really drawing a gesture. This is useful when there's a scrollable view under the overlay, to avoid scrolling the underlying child as the user draws his gesture
From the Android Dev site, it talks about using this attribute in the <android.gesture.GestureOverlayView> Root in your layout file.
For what it's worth, I found the onFling method very unreliable. I override the onScroll method in the SimpleGestureDetector, and define my onInterceptTouchEvent as:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//Call super first because it does some hidden motion event handling
boolean result = super.onInterceptTouchEvent(ev);
if (this.mGestureScanner.onTouchEvent(ev)) return true;
return result;
}

Android: GestureDetector not working (gestureDetector.onTouchEvent(event) always false) with Tabs (TabActivity, Tabwidget)

I have implemented my TabActivity with different child activities:
intent = new Intent().setClass(this, MyChildTabActiviy.class);
// Initialize a TabSpec for each tab and add it to the TabHost
spec = getTabHost.newTabSpec("tag").setIndicator("indicator", getResources().getDrawable(R.drawable.icon)).setContent(intent);
getTabHost.addTab(spec);
...
So far no problems, everything works perfectly fine. I'm switching programmatically between tabs, replacing activities within tabs with ActivityGroups, etc. just as it's shown in many tutorials.
But my problem is, that when I want to check for a fling gesture my gestureDetector.onTouchEvent(event) is always returning false, thus no gesture is registrated.
This is my implementation of gestureDetector:
public class MyChildTabActiviy extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
// ... building views, controls, etc.
GestureDetector gestureDetector = new GestureDetector(this, new MyGestureDetector());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
class MyGestureDetector extends SimpleOnGestureListener {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
return false;
// left to right swipe and right to left swipe
if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
//... fling logic ...
return true;
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
//... fling logic ...
return true;
}
return false;
}
}
The thing is, that this code (and also the fling detection) works perfectly fine, when I'm starting these activities (there are four basic activities, that I sometimes switch to other activities) outside of a TabActivity, e.g. as a Launcher Activity. But I can't get it to work within a TabActivity. I already tried to append the GestureDetector to the TabActivity, but it doesn't work. I tried to append the GestureDetector to specific views like some layout views or buttons, ViewFlippers, etc. but it just doesn't work.
When I'm debugging, I can see that the touch event is triggered and a motion is registered, but it just isn't evaluated as a fling or any other gesture.
So my question is, are there are any limitations regarding the usage of GestureDetectors with Tabs in Android? As I said, the gestures are registrated perfectly outside a TabActivity.
I would greatly appreciate the help of someone who knows the answer.
If there is a limitation, how could someone get a workaround for that problem?
Thanks in advance for answers.
Have a look at the answer mentioned here. He's pretty much done the same thing as you, but if you look at the first comment on the highest rated answer, Cdsboy got it working by implementing OnDown and returning true. I'm not sure why that is needed, but it worked for me.
As a complement to #Abhinav 's answer (that btw helped me too), I'd like to say that I think overriding onDown() is needed because its default implementation in SimpleOnGestureListener is to return false. Being ACTION_DOWN the first one to reach the listener, it would make it discard the event, whatever it is.

Categories

Resources