I'm pretty new to android, so I might was a bit too hasty to write my own component for such a task.
Anyway, it's a little component that allows you to switch between views with finger flicks, based on a ViewFlipper. Views are switched with simple Translate animation (300ms duration).
Maybe someone will find it useful.
- Code: Select all
package eu.es.flicker;
import java.util.ArrayList;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.TranslateAnimation;
import android.widget.ViewFlipper;
import eu.es.flicker.R;
public class FlingFlipper extends ViewFlipper {
public FlingFlipper(final Context context) {
super(context);
this.init();
}
public FlingFlipper(final Context context, final AttributeSet attrs) {
super(context, attrs);
this.doAttributes(attrs);
this.init();
}
/* ***** ATTRIBUTES ***** */
public final static int BOTH = 0;
public final static int HORIZONTAL = 1;
public final static int VERTICAL = 2;
private int directions;
private void doAttributes(final AttributeSet attrs) {
final TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.FlingFlipper);
directions = ta.getInt(R.styleable.FlingFlipper_directions, BOTH);
}
public int getDirections() {
return directions;
}
public void setDirections(int directions) {
this.directions = directions;
}
private FlingFlipper flipper;
/* ***** INIT FingerFlipper ***** */
private void init() {
gestures();
flipper = this;
}
private final GestureDetector gdt = new GestureDetector(new GestureListener());
private void gestures() {
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(final View view, final MotionEvent event) {
gdt.onTouchEvent(event);
return true;
}
});
}
public void setOnDoubleTapListener(final OnDoubleTapListener onDoubleTapListener) {
gdt.setOnDoubleTapListener(onDoubleTapListener);
}
/* ***** GestureListener that listens when user flings in one direction on out FlingFlipper ***** */
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;
/* FLING DIRECTIONS */
private static final int R_TO_L = 0;
private static final int L_TO_R = 1;
private static final int B_TO_T = 2;
private static final int T_TO_B = 3;
private class GestureListener extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (directions==BOTH || directions==HORIZONTAL) {
if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
return flipper.onFling(R_TO_L);
} else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
return flipper.onFling(L_TO_R);
}
}
if (directions==BOTH || directions==VERTICAL) {
if(e1.getY() - e2.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
return flipper.onFling(B_TO_T);
} else if (e2.getY() - e1.getY() > SWIPE_MIN_DISTANCE && Math.abs(velocityY) > SWIPE_THRESHOLD_VELOCITY) {
return flipper.onFling(T_TO_B);
}
}
return false;
}
}
private boolean onFling(final int direction) {
final ArrayList<TranslateAnimation> deltasHolders = getDeltas().get(direction);
setOutAnimation(prepareAnimation(deltasHolders.get(0)));
setInAnimation(prepareAnimation(deltasHolders.get(1)));
if (direction==R_TO_L || direction==B_TO_T) {
showNext();
} else {
showPrevious();
}
if (onFlingPerformedListener != null) {
onFlingPerformedListener.onFlingPerformed(direction);
}
return false;
}
private TranslateAnimation prepareAnimation(final TranslateAnimation animation) {
animation.reset();
animation.setDuration(300);
return animation;
}
private ArrayList<ArrayList<TranslateAnimation>> deltas;
@SuppressWarnings("serial")
private ArrayList<ArrayList<TranslateAnimation>> getDeltas() {
if (deltas == null) {
deltas = new ArrayList<ArrayList<TranslateAnimation>>(4) {
{
add(new ArrayList<TranslateAnimation>(2){{
add(new TranslateAnimation(0, -flipper.getWidth(), 0, 0));
add(new TranslateAnimation(flipper.getWidth(), 0, 0, 0));
}});
add(new ArrayList<TranslateAnimation>(2){{
add(new TranslateAnimation(0, flipper.getWidth(), 0, 0));
add(new TranslateAnimation(-flipper.getWidth(), 0, 0, 0));
}});
add(new ArrayList<TranslateAnimation>(2){{
add(new TranslateAnimation(0, 0, 0, -flipper.getHeight()));
add(new TranslateAnimation(0, 0, flipper.getHeight(), 0));
}});
add(new ArrayList<TranslateAnimation>(2){{
add(new TranslateAnimation(0, 0, 0, flipper.getHeight()));
add(new TranslateAnimation(0, 0, -flipper.getHeight(), 0));
}});
}
};
}
return deltas;
}
/* ***** FlingFlipper Listeners ***** */
private OnFlingPerformedListener onFlingPerformedListener;
public interface OnFlingPerformedListener {
public void onFlingPerformed(final int direction);
}
public void setOnFlingPerformedListener(final OnFlingPerformedListener onFlingPerformedListener) {
this.onFlingPerformedListener = onFlingPerformedListener;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
deltas = null;
}
}
And in xml:
- Code: Select all
<eu.es.flicker.FlingFlipper
android:id="@+id/quote_ticker_chart_flipper"
android:layout_width="300px"
android:layout_height="193px"
>
... Some views you want to be switched on a fling ...
</eu.es.flicker.FlingFlipper>
Some values (and animations) are hardcoded (since I don't need others in my app), though you can easily change them or make customizable.
There is also a possibility to include attributes for enabling only horizontal or vertical flings.
Comment please.

