Adding vertical swipe on ViewPager items to "dismiss" them - android

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;

Related

How to implement slide to reveal animation in android?

I have created 5 activities, each has a ListView containing only images. I have used a simple swipe animation to change activities.
Here is the relevant code:
#Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector.onTouchEvent(event)) {
return true;
}
return super.onTouchEvent(event);
}
private void onLeftSwipe() {
Intent intent = new Intent(HimachalActivity.this, IndianWildlifeActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_left_in, R.anim.slide_left_out);
}
private void onRightSwipe() {
Intent intent = new Intent(HimachalActivity.this, BaseActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_right_in, R.anim.slide_right_out);
}
private class SwipeGestureDetector extends GestureDetector.SimpleOnGestureListener {
// Swipe properties, you can change it to make the swipe
// longer or shorter and speed
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_MAX_OFF_PATH = 200;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
try {
float diffAbs = Math.abs(e1.getY() - e2.getY());
float diff = e1.getX() - e2.getX();
if (diffAbs > SWIPE_MAX_OFF_PATH)
return false;
// Left swipe
if (diff > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
HimachalActivity.this.onLeftSwipe();
// Right swipe
} else if (-diff > SWIPE_MIN_DISTANCE
&& Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
HimachalActivity.this.onRightSwipe();
}
} catch (Exception e) {
Log.e("BaseActivity", "Error on gestures");
}
return false;
}
}
The problem is that the next activity or previous activity opens only after full swipe and the animation does not feel that smooth.
I want an animation in which the next activity or previous activity starts revealing itself as soon as I start swiping. Is there any built-in animation that can be applied. If not, please point me to any relevant resources for creating one myself, possibly, one which does not require me to modify the existing code much. Thanks.
As far as I know this feature is easily available in Lollipop material design module.
I reckon, it would be easier in case you follow #Whitney 's advice. Put your fragments in view pager as described here: http://developer.android.com/training/animation/screen-slide.html
The reason it is not working with your current implementation is that the device makes sense of fling gesture only after you have flinged. Thereafter, the animation takes place. Fling is not detected in real time. In case you want to implement real time fling, you might have to override touch events as described here: http://developer.android.com/training/custom-views/making-interactive.html

SwipeRefreshLayout in both directions?

I love the new SwipeRefreshLayout! It looks great and its quite easy to use.
But i want to use it in both directions. I have a message screen and i want to
load older messages by swiping from top to bottom and i want to load newer messages by swiping from bottom to top.
This example: http://www.bignerdranch.com/blog/implementing-swipe-to-refresh/
explained quite easily how to get the top to bottom swiping but how can i implement that for both directions?
Based on the source code https://github.com/futuresimple/android-support-v4/blob/master/src/java/android/support/v4/widget/SwipeRefreshLayout.java it's only listening to vertical changes (See public boolean onTouchEvent(MotionEvent event)).
You should be able to override the class and implement this override yourself to enable it to listen to horizontal and vertical swipes but I don't think there's anything out of the box that allows you to do that.
The bit that should be of most interest is this:
case MotionEvent.ACTION_MOVE:
if (mDownEvent != null && !mReturningToStart) {
final float eventY = event.getY();
float yDiff = eventY - mDownEvent.getY();
if (yDiff > mTouchSlop) {
// User velocity passed min velocity; trigger a refresh
if (yDiff > mDistanceToTriggerSync) {
// User movement passed distance; trigger a refresh
startRefresh();
handled = true;
break;
} else {
// Just track the user's movement
setTriggerPercentage(
mAccelerateInterpolator.getInterpolation(
yDiff / mDistanceToTriggerSync));
float offsetTop = yDiff;
if (mPrevY > eventY) {
offsetTop = yDiff - mTouchSlop;
}
updateContentOffsetTop((int) (offsetTop));
if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
// If the user puts the view back at the top, we
// don't need to. This shouldn't be considered
// cancelling the gesture as the user can restart from the top.
removeCallbacks(mCancel);
} else {
updatePositionTimeout();
}
mPrevY = event.getY();
handled = true;
}
}
}
break;
Please note that I do not recommend this approach. It's not standard interface and no one will expect it to work that way which can be confusing. Overriding this method in a custom layout also has the added risk that you may have to redo everything in the future when a new version comes out and you want to upgrade.

Swipe Horizontal

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.

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.

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