13

I would like to register clicks on a text wrapped in a ClickableSpan only if they are clicked for over say 1 second. Is there any way to do this? If not, capturing a double click would also be fine.

It would be great if the onClick method captured an event that had some meta data about the click - then I could say ignore if the click length was short.

Any advice?

Thanks, Victor

Dirk Jäckel
  • 2,979
  • 3
  • 29
  • 47
Victor Grazi
  • 15,563
  • 14
  • 61
  • 94

2 Answers2

11

In case anyone needs it, I found it on this place

    package leeon.mobile.BBSBrowser;

    import android.text.Layout;
    import android.text.Selection;
    import android.text.Spannable;
    import android.text.method.LinkMovementMethod;
    import android.text.method.MovementMethod;
    import android.view.MotionEvent;
    import android.widget.TextView;

    public class LongClickLinkMovementMethod extends LinkMovementMethod {

      private Long lastClickTime = 0l;
      private int lastX = 0;
      private int lastY = 0;
      @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer,
                                    MotionEvent event) {
            int action = event.getAction();

            if (action == MotionEvent.ACTION_UP ||
                action == MotionEvent.ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();
                lastX = x;
                lastY = y;
                int deltaX = Math.abs(x-lastX);
                int deltaY = Math.abs(y-lastY);

                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();

                x += widget.getScrollX();
                y += widget.getScrollY();

                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);

                LongClickableSpan[] link = buffer.getSpans(off, off, LongClickableSpan.class);

                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                      if (System.currentTimeMillis() - lastClickTime < 1000)
                        link[0].onClick(widget);
                      else if (deltaX < 10 && deltaY < 10)
                        link[0].onLongClick(widget);
                    } else if (action == MotionEvent.ACTION_DOWN) {
                        Selection.setSelection(buffer,
                                               buffer.getSpanStart(link[0]),
                                               buffer.getSpanEnd(link[0]));
                        lastClickTime = System.currentTimeMillis();
                    }
                    return true;
                }
            }

            return super.onTouchEvent(widget, buffer, event);
        }


        public static MovementMethod getInstance() {
            if (sInstance == null)
                sInstance = new LongClickLinkMovementMethod();

            return sInstance;
        }

        private static LongClickLinkMovementMethod sInstance;
    }


      [1]: http://java2s.com/Open-Source/Java/SSH/brick-leeon/leeon/mobile/BBSBrowser/LongClickLinkMovementMethod.java.htm

LongClickableSpan in the same place:

    package leeon.mobile.BBSBrowser;

    import android.text.style.ClickableSpan;
    import android.view.View;

    public abstract class LongClickableSpan extends ClickableSpan {

      abstract public void onLongClick(View view);

    }
rupps
  • 9,712
  • 4
  • 55
  • 95
  • Hi, Thanks for this example. If my class used to extend ClickableSpan, how do I need to change it now to use your class? – bahar_p Jan 02 '14 at 12:58
  • i've added longclickablespan, just set the text movement method of your textView to LongClickMovementMethod.getInstance and substitute your ClickableSpans for LongClickableSpans, than now have a onLongClick method ! Hope it helps ! – rupps Jan 15 '14 at 00:14
  • It works only after you release the touch. If you could modify it in such way that even if there is touchdown for certain time say 1 or 2 sec and it fires the event will make this answer more awesome. – MrDumb Sep 11 '15 at 07:47
  • 1
    i'm not on android at the moment, will add your suggestion, should be easy to do with `widget.postDelayed(executeLongPressRunnable);` after `lastClickTime = System.currentTimeMillis();`, where `executeLongPressRunnable` is a 'Runnable' with the stuff inside `if (action == MotionEvent.ACTION_UP) {...` . On Action UP you should also set a flag so the runnable doesn't reclick in the case the touch has been released. – rupps Sep 15 '15 at 02:04
  • hi buddy, still fire onclick and onlongclick event, can u help – famfamfam Mar 15 '21 at 18:30
2

I want to do the same, the only method that comes to mind is custom MovementMethod class replacing LinkMovementMethod and a replacement for ClickableSpan that adds a long click abstract method. It is actually quite simple:

  1. Download Android source and locate the LinkMovementMethod and ClickableSpan classes
  2. Create MyCustomClickableSpan class from ClickableSpan with added abstract method onLongClick()
  3. Create MyCustomLinkMovementMethod class from LinkMovementMethod where onTouchEvent is customized so that it calls MyCustomClickableSpan's onLongClick() in the right place
  4. Use the new classes in your code instead of LinkMovementMethod and ClickableSpan

EDIT: Works like a charm. I've made some stupid mistakes like downloading LinkMovementMethod from different Android version and not using the new ClickableSpan class in setSpan(), but nothing serious.

comodoro
  • 1,489
  • 1
  • 19
  • 30
  • old question .. but I wonder if you still have this code around and could post your OnTouchEvent here.. – rupps Dec 06 '13 at 22:30