解決設置clickablespan后長按沖突的問題


解決設置ClickableSpan后長按沖突的問題

問題描述

3月份修改別人代碼的時候想要屏蔽TextView的長按事件,發現TextView有重寫OnTouchEvent方法,然后在其中加了長按事件的判斷,是長按事件則不做任何處理。結果測試發現並沒有得到想要的效果,所以繼續查看代碼,最終發現,代碼里對TextView設置了setSpan(new ClickableSpan),導致長按事件無法被我們捕捉到。

分析

因為查看代碼是因為添加了setSpan(new ClickableSpan)方法導致長按無法被檢測到,所以主要是分析ClickableSpan來尋找解決方法。

ClickableSpan
If an object of this type is attached to the text of a TextView with a movement method of LinkMovementMethod, the affected spans of text can be selected. If selected and clicked, the {@link #onClick} method will be called.

翻譯一下:如果這個類型被添加到TextView的text上,並且這個TextView有一個LinkMovementMethod的行為方法,則text可以被選擇。如果選擇並且點擊,則onClick方法會被調用。
就是說ClickanleSpan被設置到TextView的某段text上,點擊被設置的text,則會調用ClickanleSpan的onClick方法。

LinkMovementModel
A movement method that traverses links in the text buffer and scrolls if necessary. Supports clicking on links with DPad Center or Enter.

翻譯一下:鏈接文字的行為方法並在需要是滾動。支持DPad中心或輸入的鏈接點擊。
查看LInkMovementModel的源碼發現,它有一個onTouchEvent方法:

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

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

            ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);

            if (links.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    links[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                        buffer.getSpanStart(links[0]),
                        buffer.getSpanEnd(links[0]));
                }
                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

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

查看源碼發現,它只是處理了ACTION_UP(抬起)與ACTION_DOWN(按下)事件,里面沒有處理長按事件,也沒有長按事件的接口。
所以解決方法找到了,就是重寫LInkMovementModel的onTouchEvent()方法。

解決方法

寫一個LinkMovementClickMethod類,繼承LinkMovementMethod類,然后重寫onTouchEvent()方法。
將源碼中的onTouchEvent代碼復制過來,加上一個判斷,如果點擊不是長按,則按照源碼的處理方式處理,代碼如下:

public class LinkMovementClickMethod extends LinkMovementMethod {

    private long lastClickTime;

    private static final long CLICK_DELAY = 500l;

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

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

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

            if (link.length != 0) {
            ============================修改的部分==============================
               if (action == MotionEvent.ACTION_UP) {
                    if (System.currentTimeMillis() - lastClickTime < CLICK_DELAY) {
                        link[0].onClick(widget);
                    }
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                    lastClickTime = System.currentTimeMillis();
                }
            ============================修改的部分==============================
                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }
        return super.onTouchEvent(widget, buffer, event);
    }

    public static LinkMovementClickMethod getInstance() {
        if (null == sInstance) {
            sInstance = new LinkMovementClickMethod();
        }
        return sInstance;
    }

    private static LinkMovementClickMethod sInstance;

}

這樣長按事件就被排除在外,問題得以解決。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM