Search code examples
androidbuttonrepeatonlongclicklistener

How can I repeat/retrigger onLongClick in Android


Hi have a "scrub" or "scan" button on a music player that I'm writing. What I'd like my button to do is jump a number of milliseconds ahead in the tune every couple of second if the user holds the button for a long period of time.

How can I get multiple onLongClick() events to occur if I hold the button? At the moment I only get one and thats it. There must be a way to reset the timer so it happens again surely.. I hope?


Solution

  • Here's the solution that I cobbled together thats working well for me. I would have accepted the previous answers but they didn't take into account moving outside of the button's view. I hope the below code helps somebody else out in the same predicament. 90% of this code is not mine - so many thanks to the other stackoverflow posting that provided this.

    EDIT: Actually, this is not perfect either - it doesn't animate button touches.

    import android.content.Context;
    import android.graphics.Rect;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    import android.widget.Button;
    
    public class AutoRepeatButton extends Button
    {
    
      private long     initialRepeatDelay                 = 500;
      private long     repeatIntervalInMilliseconds       = 1000;
      private boolean  mRepeatStarted = false;
    
      private Runnable repeatClickWhileButtonHeldRunnable = new Runnable()
                                                          {
                                                            @Override
                                                            public void run()
                                                            {
                                                              // Perform the present
                                                              // repetition of the
                                                              // click action
                                                              // provided by the
                                                              // user
                                                              // in
                                                              // setOnClickListener().
                                                              mRepeatStarted = true;
    
                                                              performLongClick();
    
                                                              // Schedule the next
                                                              // repetitions of the
                                                              // click action, using
                                                              // a faster repeat
                                                              // interval than the
                                                              // initial repeat
                                                              // delay interval.
                                                              postDelayed( repeatClickWhileButtonHeldRunnable, repeatIntervalInMilliseconds );
                                                            }
                                                          };
    
      private void commonConstructorCode()
      {
        mRepeatStarted = false;
    
        this.setOnTouchListener( new OnTouchListener()
        {
          @Override
          public boolean onTouch( View v, MotionEvent event )
          {
            int action = event.getAction();
    
            if( action == MotionEvent.ACTION_DOWN )
            {
              // Just to be sure that we removed all callbacks,
              // which should have occurred in the ACTION_UP
              removeCallbacks( repeatClickWhileButtonHeldRunnable );
    
              // Schedule the start of repetitions after a one half second delay.
              postDelayed( repeatClickWhileButtonHeldRunnable, initialRepeatDelay );
            }
            else
            if( action == MotionEvent.ACTION_UP )
            {
              // Cancel any repetition in progress.
              removeCallbacks( repeatClickWhileButtonHeldRunnable );
    
              if( mRepeatStarted == false )
              {
                // PDS: I put this here..
                performClick();
              }
    
              mRepeatStarted = false;      
              return true;
            }
            else    
            if( action == MotionEvent.ACTION_MOVE )
            {
              int[] l = new int[2];
              v.getLocationOnScreen( l );
    
              Rect rect = new Rect( l[0], l[1], l[0] + v.getWidth(), l[1] + v.getHeight() );
    
              if( ! rect.contains( v.getLeft() + (int) event.getX(),
                                   v.getTop()  + (int) event.getY())) 
              {
                // User moved outside bounds
                removeCallbacks( repeatClickWhileButtonHeldRunnable );
    
                mRepeatStarted = false;
              }
            }
            else
            if( action == MotionEvent.ACTION_CANCEL )
            {
              removeCallbacks( repeatClickWhileButtonHeldRunnable );
    
              mRepeatStarted = false;
            }
    
            // Returning true here prevents performClick() from getting called
            // in the usual manner, which would be redundant, given that we are
            // already calling it above.
            return true;
          }
        } );
      }
    
      public AutoRepeatButton( Context context, AttributeSet attrs, int defStyle )
      {
        super( context, attrs, defStyle );
        commonConstructorCode();
      }
    
      public AutoRepeatButton( Context context, AttributeSet attrs )
      {
        super( context, attrs );
        commonConstructorCode();
      }
    
      public AutoRepeatButton( Context context )
      {
        super( context );
        commonConstructorCode();
      }
    }