1

I am trying to make an app where a user can swipe and change which fragment they are seeing on the screen. I can not use view pager because I want the user to be able to swipe to different fragments forever. Here is the detector in my fragment:

 class MyGestureDetector extends SimpleOnGestureListener {
            @Override
            public boolean onDown(MotionEvent e) {
            return true;
        }
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE & Math.abs(velocityX) > 10) {
                    left();
                }  else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE & Math.abs(velocityX) > 10) {
                    right();
                }

            return false;
        }
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){
                  if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE & distanceX > distanceY) {
                    left();
                  }  else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE & distanceX > distanceY) {
                      right();
                  }
                return false;
            }
    }
    public void right(){
        mCallback.dateNumber(true);
        sportView.setText("Loading");
    }public void left(){
        mCallback.dateNumber(false);
        sportView.setText("Loading");
    }

In my activity, here is the listener that I added to change fragments:

                    @Override
            public void dateNumber(Boolean left_right) {
                //true == right
                //false == left
                if(left_right == false){
                    day = day + 1;
                    Fragment1 rightFragment = new Fragment1();
                    Bundle args = new Bundle();
                    args.putInt("day", day);
                    rightFragment.setArguments(args);

                    android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                    transaction.replace(R.id.fragment_container, rightFragment);
                    transaction.addToBackStack(null);
                    transaction.commit();
                }else if(left_right == true){
                    day = day - 1;
                    Fragment1 leftFragment = new Fragment1();
                    Bundle args = new Bundle();
                    args.putInt("day", day);
                    leftFragment.setArguments(args);

                    android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                    transaction.replace(R.id.fragment_container, leftFragment);
                    transaction.addToBackStack(null);
                    transaction.commit();
                }
                left_right = null;
            }

I know that the swipe gesture is always being recognized but sometimes the new fragment won't open up. Does anyone know why?

Phil
  • 35,852
  • 23
  • 123
  • 164
superuser
  • 731
  • 10
  • 28

2 Answers2

3

First of all, you can really simplify your swipe code using my droidQuery library:

//global variables
private boolean isSwiping = false;
private SwipeDetector.Direction swipeDirection = null;
private View v;//set to the parent layout of the fragments.

//swipe-handling code
$.with(v).swipe(new Function() {
    @Override
    public void invoke($ droidQuery, Object... params) {
        if (params[0] == SwipeDetector.Direction.START)
            isSwiping = true;
        else if (params[0] == SwipeDetector.Direction.STOP) {
            if (isSwiping) {
                isSwiping = false;
                if (swipeDirection != null) {
                    switch(swipeDirection) {
                        case DOWN :
                            //TODO: Down swipe complete, so do something
                            break; 
                        case UP :
                            //TODO: Up swipe complete, so do something
                            break; 
                        case LEFT :
                            //TODO: Left swipe complete, so do something
                            break; 
                        case RIGHT :
                            //TODO: Right swipe complete, so do something (such as):
                            day++;
                            Fragment1 rightFragment = new Fragment1();
                            Bundle args = new Bundle();
                            args.putInt("day", day);
                            rightFragment.setArguments(args);

                            android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
                            transaction.replace(R.id.fragment_container, rightFragment);
                            transaction.addToBackStack(null);
                            transaction.commit();
                            break; 
                        default :
                            break; 
                    }
                }
            }
        }
        else {
            swipeDirection = (SwipeDetector.Direction) params[0];
        }
    }
});

You can find more on Fragment transactions here.

Also, consider keeping an int offset variable that keeps track of the +/- offset from zero. So for instance, you could get the already-instantiated Fragments from an ArrayList, then just swap out the one at mArrayList.get(offset), and when flinging right, do offset++, and 'offset--` for left swipes.

Edit

As requested in the comments, this code can be used to handle swipes and a child image click:

Include the SwipeInterceptorView in your main Layout (res/layout/main.xml):

<?xml version="1.0" encoding="utf-8"?>
<self.philbrown.SwipeInterceptorView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipe_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</self.philbrown.SwipeInterceptorView>

You will need to have class variables:

SwipeInterceptorView view;//instantiated in onCreate
ImageView fragImage;//must be instantiated when the new Fragment is transitioned in

Next, include the following components in onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //set main view to the main layout
    setContentView(R.layout.main);
    //get a reference to the content view
    view = (SwipeInterceptorView) findViewById(R.id.swipe_view);
    //add Swiper
    view.setSwipeListener(new SwipeListener() {
        public void onUpSwipe(View v) {
            //TODO handle up swipe
        }
        public void onRightSwipe(View v) {
            //TODO handle right swipe
        }
        public void onLeftSwipe(View v) {
            //TODO handle left swipe
        }
        public void onDownSwipe(View v) {
            //TODO handle down swipe
        }
    });
    view.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return super.onTouch(v, event);
        }
    });
}

When the new Fragment containing the ImageView is transitioned in, you need to reference it and update the swipe interceptor's onTouch method:

fragImage = (ImageView) fragment/* references the now non-null, on-display fragment */.getView().findViewById(R.id.yourImageId);
int[] origin = new int[2];
fragImage.getLocationOnScreen(origin);
final Rect bounds = new Rect(origin[0], origin[1], fragImage.getRight(), fragImage.getBottom());
view.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public void onTouch(View v, MotionEvent event) {
        if (bounds.contains(event.getRawX(), event.getRawY())) {
            return false;//now clicks will be handled by the Image.
        }
        return v.onTouchEvent(event);
    }
});
sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Phil
  • 35,852
  • 23
  • 123
  • 164
  • 1
    You can put it in `onCreate`, or in a method that `onCreate` calls. – Phil Aug 08 '13 at 14:05
  • if (params[0] == SwipeDetector.Direction.START) – superuser Aug 08 '13 at 14:11
  • @user2583347 add the import `import self.philbrown.droidQuery.SwipeDetector;` – Phil Aug 08 '13 at 15:05
  • How are you importing `droidQuery`? You should be able to simply copy `versions/current/droidquery.jar` into your `libs` directory (create it if needed). Then go to *Project->Clean...* to clean your project. Also check imports. Auto-imports may be incorrect because of the name of `$.class`. – Phil Aug 08 '13 at 15:33
  • @user2583347 if you are still having issues, consider one of the other swipe detection algorithms discussed at [Android - basic gesture detection](http://stackoverflow.com/questions/937313/android-basic-gesture-detection). – Phil Aug 08 '13 at 15:35
  • What I had done was I had messed up what I put in the libs directory – superuser Aug 08 '13 at 15:50
  • If I wanted to do image.setOnTouchListener(); with this gesture recognizer, how could I do it? – superuser Aug 08 '13 at 17:28
  • @user2583347 you can use the code from my [original post](http://stackoverflow.com/questions/937313/android-basic-gesture-detection) (scroll to bottom) to see how you can use `SwipeDetector` with an `OnClickListener`. – Phil Aug 08 '13 at 17:31
  • so what would the swiper variable be in my case? – superuser Aug 08 '13 at 20:57
  • @user2583347 your `SwipeInterceptorView` would be your root layout that you set in `setContentView(View)` in `onCreate`. You would then create an instance of `SwipeDetector` (`public class MySD implements SwipeDetector`) that you would pass to `setSwipeListener`. For the `onTouch` part, check if the touch is in the bounds where the Image exists. If it is, return `false` for the Image to take over. – Phil Aug 08 '13 at 21:09
  • @user2583347, I have added a large addition to my answer to provide the code you need, or to get you in the right direction. Good luck! – Phil Aug 09 '13 at 13:36
  • Thank you again. This was a big help! – superuser Aug 09 '13 at 14:07
  • I am getting this error when I add the swipeinterceptorview to my main layout inside the relative layout I create: – superuser Aug 19 '13 at 21:30
  • 08-19 17:26:12.675: E/AndroidRuntime(3941): Caused by: java.lang.ClassNotFoundException: self.philbrown.SwipeInterceptorView in loader dalvik.system.PathClassLoader[/data/app/com.example.sportsschedule-2.apk] – superuser Aug 19 '13 at 23:07
  • @user2583347, to get the most help, I would suggest asking a new question about this. Include the code that does work, and the layout file that doesn't work. You may also want to link this question in your new question. You can use the [tag:droidQuery] tag in your question to help me find it in the morning. – Phil Aug 20 '13 at 02:57
  • I just posted the question – superuser Aug 20 '13 at 12:39
0

Use ViewPager and FragmentPagerAdapter from Android Support Library v4.

Kirill Ryabin
  • 193
  • 1
  • 2
  • 15