Search code examples
androidgesturetapmotioneventgesturedetector

How do I make imageView respond to double tap and recognize when imageView is pressed, unpressed(action down, up)?


I want image to respond to 3 actions:

image pressed(action down) - image becomes large image unpressed(action up) - image becomes normal size again double tap on image - image moves somewhere

I have been trying to do it this way:

public class Working_gestures extends Activity {
ImageView sample ;

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

    sample = (ImageView)findViewById(R.id.sample);

    Gestures x = new Gestures(sample);

}
 //inner class
private class Gestures extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener{

    ImageView view ;
    public Gestures(ImageView v) {
        view = v;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        //must return true for doubleTap to work, but onDown() "blocks" onTouch
        return true ;
    }

    @Override
    public boolean onDoubleTap(MotionEvent e){
         //do something on image
         return true ;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //image increases in size and stays large
                Log.wtf("x", "action down");
                break ;
            case MotionEvent.ACTION_UP:
                //image comes back to normal size
                Log.wtf("x", "action up!");
                break ;
        }
        return true ;
    }
}

}

But onTouch is never triggered! Because onDown (aka MotionEvent.ACTION_DOWN) fires up onDoubleTap and others - singletap, fling etc.

I also tried to override onShowPress() - but it only recognizes action down. single tap up, singleTapconfirmed() did not help either.


Solution

  • after struggling so much with gesture listener and its methods - onFling, onLongPress etc I decided to write my own, my idea is not that different from recording the time of of each press, there is similar solution on SO.

    I understand what my problem was: if you use onTouch - all your touches are intercepted and interpreted through it, but simpleGestureListener processes its own events through onDown, so onTouch is never called; in this situation I had to write pretty basic logic:

    • on ACTION_DOWN, launch count down timer for 300 miliseconds or so - after it ticks around 290, check again if the event is still action down
    • if event is action down - it is essentially long press and my image responds accordingly - becomes larger, if not - just do nothing
    • on ACTION_UP: check if it was long press, if it was - the user unpressed his hold, and image becomes smaller, but otherwise - it was quick tap!

    Code below:

    public class DoubleTapWorking1 extends Activity implements View.OnTouchListener {
    ImageView sample;
    boolean still_down ;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sample = (ImageView) findViewById(R.id.sample);
        sample.setOnTouchListener(this);
        still_down = false ;
    
    }
    
    @Override
    public boolean onTouch(final View v,final MotionEvent e) {
    
         switch (e.getAction()){
             case MotionEvent.ACTION_DOWN:
                 still_down =false;
                 CountDownTimer timer = new CountDownTimer(500,10){
                     @Override
                     public void onTick(long millisUntilFinished) {
                         if((int)millisUntilFinished < 100 && e.getAction() == MotionEvent.ACTION_DOWN){
                             still_down = true ;
                         }
                     }
                     @Override
                     public void onFinish() {
                         if( still_down){                           
                             //code to make imageView larger
                         }
                     }
                 }.start();
                 break ;
             case MotionEvent.ACTION_UP:
                 if(still_down) {
                     //your code to make image smaller
                 }
                 else{
                     action_on_single_tap(v);
                 }
                 break ;
         }
    
       return true;
      }
    
      private void action_on_single_tap(final View v){
          //your code to do with image on single tap - move somewhere else, make invisible etc.
    }
    

    }

    EDIT: another better solution.

    I had some problems with countdown timer - it did not count down to the end for some reason. I applied some old idea - distinguish between long click and click using 2 listeners - LongClickListener, ClickListener. This worked out much better than the solution above.

    Code below:

    private class Gestures_future implements View.OnClickListener, View.OnLongClickListener{
    
         int margin_long_press_x1;
         int margin_long_press_y1;
         int margin_long_press_x2;
         int margin_long_press_y2;
         RelativeLayout.LayoutParams params_generic = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    public Gestures_future(final int margin_long_press_x1,final int margin_long_press_y1,final int margin_long_press_x2,final int margin_long_press_y2
        ){
            params_generic.leftMargin = 30;
            params_generic.topMargin = 40;
    
            this.margin_long_press_x1 = margin_long_press_x1;
            this.margin_long_press_y1 = margin_long_press_y1;
            this.margin_long_press_x2 = margin_long_press_x2;
            this.margin_long_press_y2 = margin_long_press_y2;
        }
        @Override
        public void onClick(View v) {
            Log.wtf("x", "on click!");
            TextView d = (TextView)v;
            int current_id = v.getId();
           String answer_content = d.getText().toString();
            if (answers_player1_ids.contains(current_id)) {
            if (Battlefield.getStateOfAnswer1PlacePlayer1() == false) {
                d.setText("");
                Battlefield.answer1_place_busy_player1 = true;
                answer_place1_player1.setTextSize(20);
                answer_place1_player1.setText(answer_content);
                upper_layout_answer_counter++;
            } else if (Battlefield.getStateOfAnswer2PlacePlayer1() == false) {
                d.setText("");
                Battlefield.answer2_place_busy_player1 = true;
                answer_place2_player1.setTextSize(20);
                answer_place2_player1.setText(answer_content);
                upper_layout_answer_counter++;
                if (upper_layout_answer_counter == 2) {
                    upper_layout_answer_counter = 0;
                    see_answers1.performClick();
                }
            }
        }
            else if (answers_player2_ids.contains(current_id)) {
            if (Battlefield.getStateOfAnswer1Place() == false){
                d.setText("");
                Battlefield.answer1_place_busy = true;
                answer_place1.setTextSize(20);
                answer_place1.setText(answer_content);
                lower_layout_answer_counter++;
            } else if (Battlefield.getStateOfAnswer2Place() == false) {
                d.setText("");
                Battlefield.answer2_place_busy = true;
                answer_place2.setTextSize(20);
                answer_place2.setText(answer_content);
                lower_layout_answer_counter++;
                if (lower_layout_answer_counter == 2) {
                    lower_layout_answer_counter = 0;
                    see_answers2.performClick();
                }
            }
        }
    }
        @Override
        public boolean onLongClick(View v){
            Log.wtf("x", "on long click!");
            final TextView d = (TextView)v;
            Log.wtf("x", d.getText().toString() +  " long clicked!" );
                        d.setTextSize(30);
                        RelativeLayout.LayoutParams x = new RelativeLayout.LayoutParams(350, 500);
                        x.setMargins(margin_long_press_x1, margin_long_press_y1, 0, 0);
                        d.setLayoutParams(x);
                        d.requestLayout();
            new CountDownTimer(3000,500){
                @Override
                public void onTick(long millisUntilFinished) {
                }
                @Override
                public void onFinish() {
                    RelativeLayout.LayoutParams t1 = new RelativeLayout.LayoutParams(130, 170);
                    t1.setMargins(margin_long_press_x2, margin_long_press_y2, 0, 0);
                    d.setTextSize(10);
                    d.setLayoutParams(t1);
                    d.requestLayout();
                }
            }.start();
            return true;
        }
    
    }
    

    The solution above works well for me.