使用可选文本处理ClickableSpan的点击需要双击

时间:2016-11-20 21:46:07

标签: android clickable focusable clickablespan

我有TextView,其中每个字都是ClickableSpan。单击时,单词变为粗体,单词的字典定义将显示在另一个 TextView 中。该应用程序正常工作,直到我选择 TextView 中的文本。当文本可选时,单击会显示定义,但双击时单词只有粗体。双击或长按选择文本(但长按不会使单词变为粗体)。

我的猜测是,问题与在动作处理过程中更新绘制状态有关,但我无法找到修复。我尝试设置TextView focusable="false",但没有任何改变。相关代码如下。

curSpan = new WordSpan(index) {
    @Override
    public void onClick(View view) {
        handleWordClick(index,this); // handles code to display definition
        setMarking(true);
        view.invalidate();
        tvText.invalidate();
    }
};

spannableStringBuilder.setSpan(curSpan, totalLength, totalLength + strWord, Spanned.SPAN_COMPOSING);

WordSpan的定义:

class WordSpan extends ClickableSpan
    {
        int id;
        private boolean marking = false;

        public WordSpan(int id) {
            this.id = id;
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            ds.setColor(Color.BLACK);
            ds.setUnderlineText(false);
            if (marking) {
                ds.setTypeface(Typeface.create(myFont,Typeface.BOLD));
            }
        }

        @Override
        public void onClick(View v) {}

        public void setMarking(boolean m) {
            marking = m;
        }
    }

设置TextView的移动方法:

private MovementMethod createMovementMethod ( Context context ) {
        final GestureDetector detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp ( MotionEvent e ) {
                    return true;
                }

                @Override
                public boolean onSingleTapConfirmed ( MotionEvent e ) {
                    return true;
                }
            });

        return new ScrollingMovementMethod() {

            @Override
            public boolean canSelectArbitrarily () {
                return true;
            }

            @Override
            public void initialize(TextView widget, Spannable text) {
                Selection.setSelection(text, text.length());
            }

            @Override
            public void onTakeFocus(TextView view, Spannable text, int dir) {
                if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
                    if (view.getLayout() == null) {
                        // This shouldn't be null, but do something sensible if it is.
                        Selection.setSelection(text, text.length());
                    }
                } else {
                    Selection.setSelection(text, text.length());
                }
            }

            @Override
            public boolean onTouchEvent ( TextView widget, Spannable buffer, MotionEvent event ) {
                // check if event is a single tab
                boolean isClickEvent = detector.onTouchEvent(event);

                // detect span that was clicked
                if (isClickEvent) {
                    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);

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

                    if (link.length != 0) {
                        // execute click only for first clickable span
                        // can be a for each loop to execute every one
                        if (event.getAction() == MotionEvent.ACTION_UP) {
                            link[0].onClick(widget);
                        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                            Selection.setSelection(buffer,
                                                   buffer.getSpanStart(link[0]),
                                                   buffer.getSpanEnd(link[0]));
                        }
                        return true;
                    }
                }

                // let scroll movement handle the touch
                return super.onTouchEvent(widget, buffer, event);
            }
        };
    }

编辑: 我刚刚发现了一个可能有助于解决问题的新怪癖。如果我双击但在点击之间更改单词(点击一个单词&然后快速单击另一个单词),则在第一次点击时显示该单词的定义,在第二次点击时,FIRST单词为粗体但选择了第二个单词(突出显示)并且仍显示第一个字的定义。

  

因此,例如,如果我双击"首先"然后"秒",当我点击   "第一" "第一个"的定义将会显示,当我触摸时   "第二"这个词"第一个"是大胆的,"第二"突出显示   但定义没有改变(仍然显示定义   "第一"。)

1 个答案:

答案 0 :(得分:0)

createMovementMethod替换为以下内容。如果你错误面对错误plz修复它并编辑这个答案。

private MovementMethod createMovementMethod (final Context context ) {

        return new ScrollingMovementMethod() {
            public MotionEvent event;
            public Spannable buffer;
            public TextView widget;
            final GestureDetector detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp ( MotionEvent e ) {

                    return true;
                }

                @Override
                public boolean onSingleTapConfirmed ( MotionEvent e ) {
                    triggerClick();
                    return true;
                }

                @Override
                public boolean onDoubleTap(MotionEvent e) {
                    triggerClick();
                    return true;
                }

                private boolean triggerClick() {
                    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);

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

                    if (link.length != 0) {
                        // execute click only for first clickable span
                        // can be a for each loop to execute every one
                        if (event.getAction() == MotionEvent.ACTION_UP) {
                            link[0].onClick(widget);
                        } else if (event.getAction() == MotionEvent.ACTION_DOWN) {
                            Selection.setSelection(buffer,
                                    buffer.getSpanStart(link[0]),
                                    buffer.getSpanEnd(link[0]));
                        }
                        return true;
                    }

                    return true;
                }
            });
            @Override
            public boolean canSelectArbitrarily () {
                return true;
            }

            @Override
            public void initialize(TextView widget, Spannable text) {
                Selection.setSelection(text, text.length());
            }

            @Override
            public void onTakeFocus(TextView view, Spannable text, int dir) {
                if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
                    if (view.getLayout() == null) {
                        // This shouldn't be null, but do something sensible if it is.
                        Selection.setSelection(text, text.length());
                    }
                } else {
                    Selection.setSelection(text, text.length());
                }
            }

            @Override
            public boolean onTouchEvent (TextView widget, Spannable buffer, MotionEvent event ) {
                // check if event is a single tab
                boolean isClickEvent = detector.onTouchEvent(event);

                //record this for GestureDetector
                this.widget = widget;
                this.buffer = buffer;
                this.event = event;

                // detect span that was clicked
                if (isClickEvent) {
                    //ignore click here
                    return true;
                }

                // let scroll movement handle the touch
                return super.onTouchEvent(widget, buffer, event);
            }
        };
    }