可点击的 span 长按不触发直到松开点击

8
我需要一个可点击的span在我的应用程序中同时具有普通点击和长按的功能,我在这里(In Android - How can I register only long clicks using a ClickableSpan)发现可以扩展LinkMovementMethod类和ClickableSpan类来实现这一点,但目前长按和短按都有效果,当项目被长时间按下时,长按操作不会立即触发,而是要等到您释放项目后才会触发。以下是我的扩展类代码:
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);
}

实际实现

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);
3个回答

9

我知道这个问题已经有一段时间了,也许原作者已经解决了这个问题,但是我对原始的LongClickLinkMovementMethod进行了一些调整,使其在时间过去后触发而不是在我们释放点击时触发:

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

基本上,这个程序使用 Handler 在1000毫秒后触发长按事件。

我已经获取了长按和单击的事件。 - famfamfam

6
简单的方法是使用View.setTag。
代码示例:
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);
    }
}

0

我用稍微更新的Kotlin语法编辑了Pedro Oliveira解决方案

class LongClickLinkMovementMethod : LinkMovementMethod() {
    private var longClickHandler: Handler? = null
    private var isLongPressed = false
    override fun onTouchEvent(
        widget: TextView, buffer: Spannable,
        event: MotionEvent
    ): Boolean {
        val action = event.action
        if (action == MotionEvent.ACTION_CANCEL) {
            longClickHandler?.removeCallbacksAndMessages(null)
        }
        if (action == MotionEvent.ACTION_UP ||
            action == MotionEvent.ACTION_DOWN
        ) {
            var x = event.x.toInt()
            var y = event.y.toInt()
            x -= widget.totalPaddingLeft
            y -= widget.totalPaddingTop
            x += widget.scrollX
            y += widget.scrollY
            val layout = widget.layout
            val line = layout.getLineForVertical(y)
            val off = layout.getOffsetForHorizontal(line, x.toFloat())
            val link = buffer.getSpans(
                off, off,
                LongClickableSpan::class.java
            )
            if (link.isNotEmpty()) {
                if (action == MotionEvent.ACTION_UP) {
                    longClickHandler?.removeCallbacksAndMessages(null)
                    if (!isLongPressed) {
                        link[0].onClick(widget)
                    }
                    isLongPressed = false
                } else {
                    Selection.setSelection(
                        buffer,
                        buffer.getSpanStart(link[0]),
                        buffer.getSpanEnd(link[0])
                    )
                    longClickHandler?.postDelayed({
                        link[0].onLongClick(widget)
                        isLongPressed = true
                    }, LONG_CLICK_TIME)
                }
                return true
            }
        }
        return super.onTouchEvent(widget, buffer, event)
    }

    companion object {
        private const val LONG_CLICK_TIME = 500L
        val instance: MovementMethod?
            get() {
                if (sInstance == null) {
                    sInstance = LongClickLinkMovementMethod()
                    // Handler deprecated https://dev59.com/RlIH5IYBdhLWcg3wI5kj#62477706
                    sInstance!!.longClickHandler = Handler(Looper.getMainLooper())
                }
                return sInstance
            }

        private var sInstance: LongClickLinkMovementMethod? = null
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接