Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long Click on Clickable span not firing until click is released

I need a clickable span to have both a normal click and a long click methods in my app and i found out here (In Android - How can I register only long clicks using a ClickableSpan) that i could extend the LinkMovementMethod class and the ClickableSpan class to allow to do that but currently the long and short clicks both work but for a long click instead of firing the long click action when the item has been pressed long enough it will wait until you release the item to fire. Here is my code for the extended classes:

LinkMovementClass

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;
}

LongClickableSpanClass

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

public abstract class LongClickableSpan extends ClickableSpan {

    abstract public void onLongClick(View view);
}

Actual implementation

LongClickableSpan eruptionText = new LongClickableSpan() {

                                @Override
                                public void onClick(View tvEruptions) {
                                    LinkFunctions.link_eruption_detail(getApplicationContext(),PostErupionID);
                                }

                                @Override
                                public void onLongClick(View tvEruptions) {
                                    if(SignedInUserID != 0) {
                                        DialogFragment newFragment = new Dialogs.QuickActionsDialogFragment();
                                        // Supply num input as an argument.
                                        Bundle args = new Bundle();
                                        args.putLong("eruptionID", PostErupionID);
                                        newFragment.setArguments(args);
                                        newFragment.show(getFragmentManager(), "QuickActions");
                                    }
                                }

                            };
                            ss.setSpan(eruptionText, startpos[(int) j], endpos[(int) j], Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
like image 546
wBoekel Avatar asked Nov 08 '14 02:11

wBoekel


2 Answers

I know this question is old and probably the OP has already solved the problem but I made some tweaks on the original LongClickLinkMovementMethod to fire after the time has passed instead of when we release the tap:

import android.os.Handler;
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 Handler mLongClickHandler;
    private static int LONG_CLICK_TIME = 1000;
    private boolean mIsLongPressed = false;

    @Override
    public boolean onTouchEvent(final TextView widget, Spannable buffer,
                                MotionEvent event) {
        int action = event.getAction();

        if(action == MotionEvent.ACTION_CANCEL){
            if(mLongClickHandler!=null){
                mLongClickHandler.removeCallbacksAndMessages(null);
            }
        }

        if (action == MotionEvent.ACTION_UP ||
                action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            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);

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

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    if(mLongClickHandler!=null){
                        mLongClickHandler.removeCallbacksAndMessages(null);
                    }
                    if(!mIsLongPressed) {
                        link[0].onClick(widget);
                    }
                    mIsLongPressed = false;
                } else {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                    mLongClickHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            link[0].onLongClick(widget);
                            mIsLongPressed = true;
                        }
                    },LONG_CLICK_TIME);
                }
                return true;
            }
        }

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


    public static MovementMethod getInstance() {
        if (sInstance == null) {
            sInstance = new LongClickLinkMovementMethod();
            sInstance.mLongClickHandler = new Handler();
        }

        return sInstance;
    }
    private static LongClickLinkMovementMethod sInstance;
}

So basicaly this uses an Handler to fire the long click event after 1000ms

like image 168
Pedro Oliveira Avatar answered Nov 06 '22 01:11

Pedro Oliveira


The simple way is to use View.setTag.

Code example:

tv.setOnLongClickListener(new OnLongClickListener() {

    @Override
    public boolean onLongClick(View v) {
        Toast.makeText(getApplicationContext(), "onLongClick", Toast.LENGTH_SHORT).show();
        v.setTag("Test");
        return true;
    }
});

private static class NonLongClickableUrlSpan extends URLSpan {

    public NonLongClickableUrlSpan(String url) {
        super(url);
    }

    @Override
    public void onClick(View widget) {
        if (widget.getTag() != null) {
            widget.setTag(null);
            return;
        }
        super.onClick(widget);
    }
}
like image 39
kylin17 Avatar answered Nov 06 '22 01:11

kylin17