Search code examples
androidontouchlistenertouch-event

Slide Up an image using EventMotion in Android


I am trying to slide up a layout in android using touch listener. I am expecting the MOTION_UP should be intercepted when the user touches the screen and drags the finger up the screen. And MOTION_DOWN the other way. But when i debug, both Up and Down are called.

I am trying to slide the layout up when I drag up using animation. I don't understand what i am doing wrong. Did anyone had this situation before? Any hints are helpful, Thank you.

This is my code:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class MainActivity extends Activity implements AnimationListener,
        OnDragListener {

    // Animation
    private Animation animSlideUp;
    private ImageView img;
    private Rect rect;

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        img = (ImageView) findViewById(R.id.interstitialImg);
        img.setOnDragListener(this);
        img.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                    System.out.println("up");

                    rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v
                            .getBottom());
                    v.getHitRect(rect);
                    if (rect.contains(Math.round(v.getX() + event.getX()),
                            Math.round(v.getY() + event.getY()))) {
                        System.out.println("inside");
                        // Animation slide_up = AnimationUtils.loadAnimation(
                        // getApplicationContext(), R.anim.slide_up);
                        // img.startAnimation(slide_up);
                    } else {
                        // outside
                        System.out.println("outside");
                    }
                }
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    System.out.println("down");
                    // Construct a rect of the view's bounds
                    rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v
                            .getBottom());
                    return true;
                }
                if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    // if (!rect.contains(v.getLeft() + (int) event.getX(),
                    // v.getTop() + (int) event.getY())) {
                    // // User moved outside bounds
                    // System.out.println("moved outside bounds " + rect);
                    // }
                }
                return true;
            }
        });
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // check for zoom in animation
        if (animation == animSlideUp) {
            System.out.println("animation: " + animation);
            overridePendingTransition(R.anim.slide_up, R.anim.slide_up);
            finish();
        }

    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onAnimationStart(Animation animation) {
        // TODO Auto-generated method stub
    }

    @SuppressLint("NewApi")
    @Override
    public boolean onDrag(View v, DragEvent e) {
        if (e.getAction() == DragEvent.ACTION_DROP) {
            System.out.println("action drop");
            View view = (View) e.getLocalState();
            ViewGroup from = (ViewGroup) view.getParent();
            from.removeView(view);
            RelativeLayout to = (RelativeLayout) v;
            to.addView(view);
            view.setVisibility(View.VISIBLE);
        }
        return true;
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        overridePendingTransition(R.anim.slide_up, R.anim.slide_up);
    }
}

Solution

  • I am expecting the MOTION_UP should be intercepted when the user touches the screen and drags the finger up the screen.

    Uh, no.

    Docs to the rescue:

    public static final int ACTION_DOWN A pressed gesture has started, the motion contains the initial starting location.

    Think of ACTION_DOWN as the very start of user interaction. DOWN DOES NOT INDICATE DIRECTION. In a crude sense, it tells you that the user has put his/her finger(s) down on the screen.

    Similarly:

    public static final int ACTION_UP A pressed gesture has finished, the motion contains the final release location as well as any intermediate points since the last down or move event.

    Again, UP is not what you're assuming it to be. It means that the user has lifted his/her finger(s) off the screen.

    Let's look at your code now:

    rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v
                            .getBottom());
    v.getHitRect(rect);
    if (rect.contains(Math.round(v.getX() + event.getX()),
                            Math.round(v.getY() + event.getY()))) {
        System.out.println("inside");
        // Animation slide_up = AnimationUtils.loadAnimation(
        // getApplicationContext(), R.anim.slide_up);
        // img.startAnimation(slide_up);
    } else {
        // outside
        System.out.println("outside");
    }
    

    First, getHitRect(Rect) fills the rect you supply as an argument. So, initializing it with v.getLeft(), v.getTop(), v.getRight(), v.getBottom() is not required.

    Second, since you are setting an OnTouchListener on img, why do you need to check if event.getX() and event.getY() are inside the view's bounds? In fact, you don't. Because, onTouch(...) will not be triggered if the user touches outside.

    Third, you may not need to implement an OnTouchListener to do what you want.. or at least, not in the way you currently are. For example, if all you are looking for is if the user has flinged the view UP or DOWN, you can rely on a SimpleOnGestureListener. The accepted answer on this question will show you how to use it: Android: How to handle right to left swipe gestures.

    So what exactly are these ACTION_DOWN and ACTION_UP events used for? Well, you work with them when you need finer control: like reacting to user input immediately. For example, you cannot even afford the basic functionality of moving a view around the screen using a SimpleOnGestureListener.