1

我想为 EditText 中的每个字符设置 ClickableSpans,但它们的位置与每行末尾的字符不匹配。

每个字母都使用 BreakIterator 设置了 ClickableSpan。每个 Clickable 都会打开一个 AlertDialog,显示单击的字母。如果字母不在行尾,它工作正常。一个红叉表示点击位置:

在行中间单击“S”可以正常工作:

但是,单击一行末尾的字母会激活前一个字母的 ClickableSpan:

'Y' 被点击,但 'X' ClickableSpan 被激活:

单击下一行第一个字母之前的区域将激活上一行的最后一个字母:

单击“Z”之前的区域,并激活上一行的“X”:

如何让 ClickableSpan 正确定位在行尾?

Java代码:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);
    EditText et = findViewById(R.id.editText);
    et.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abc");

    String text = et.getText().toString();
    et.setMovementMethod(LinkMovementMethod.getInstance());
    Spannable spannable = et.getText();

    BreakIterator iterator = BreakIterator.getCharacterInstance();
    iterator.setText(text);
    int iteratorStart = iterator.first();
    for (int iteratorEnd = iterator.next(); iteratorEnd != BreakIterator.DONE;
         iteratorStart = iteratorEnd, iteratorEnd = iterator.next()) {
        String letter = text.substring(iteratorStart, iteratorEnd);
        ClickableSpan cs = generateClickableSpan(letter);
        spannable.setSpan(cs, iteratorStart, iteratorEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}


private ClickableSpan generateClickableSpan(final String letter) {
    return new ClickableSpan() {
        final String currentLetter;

        {
            currentLetter = letter;
        }

        @Override
        public void onClick(View widget) {
            AlertDialog.Builder adBuilder = new AlertDialog.Builder(MainActivity.this);
            AlertDialog ad = adBuilder.create();
            ad.setMessage(letter);
            ad.show();
            TextView tv = ad.findViewById(android.R.id.message);
            tv.setTextSize(24);
            ad.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        }

        public void updateDrawState(TextPaint ds) {
            ds.setUnderlineText(false);
        }
    };
}

编辑文本 xml:

<EditText
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:background="#00000000"
            android:inputType="none"
            android:textIsSelectable="true"
            android:layout_gravity="top"
            android:textSize="42sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

我已经尝试了以下答案中的解决方案和建议,包括制作自定义 LinkMovementMethod 类,但没有任何效果:

EditText 中的 ClickableSpan 到文本调用 click() 到行尾

ClickableSpan 奇怪的行为:点击空白时调用 onClick()

谢谢!!

4

1 回答 1

0

我通过使用第二个链接来解决它,因为缓冲区可能会获得两个具有相同偏移量的链接。

public static class MySpanMovementMethod extends LinkMovementMethod {

        @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);
                int end = layout.getLineEnd(line);

                Log.i(TAG, "tts span " + line + "=" + off + "=" + end);

                if (off >= widget.getText().length()) {
                    // Return true so click won't be triggered in the leftover empty space
                    return true;
                }
                TtsClickSpan[] links = buffer.getSpans(off, off, TtsClickSpan.class);

                if (links != null && links.length > 1) {

                    if (action == MotionEvent.ACTION_UP) {
                        links[1].onClick(widget);
                    }
                    return true;
                }

            }

            return super.onTouchEvent(widget, buffer, event);
        }
    }
于 2020-02-26T03:29:40.233 回答