AlignmentSpan не работает для текста RadioButton

Я пытаюсь выровнять правую часть текста RadioButton, используя класс AlignmentSpan. Однако он не работает, потому что текст не выравнивается, как ожидалось.

SpannableStringBuilder builder = new SpannableStringBuilder();

builder.append(option.getLabel());

int start = builder.length() + 1;

builder.append(" ");
builder.append(price);
builder.append("€");

int end = builder.length();

builder.setSpan(new AlignmentSpan.Standard(Alignment.ALIGN_OPPOSITE), start, end,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD);

builder.setSpan(bss, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Как вы можете видеть из кода, я также применяю StyleSpan, который работает правильно.

N.B RadioButton имеет android:layout_width="match_parent"

Ответ 1

Случайный совет, может работать или нет, но... Вы пытались вводить символы управления Unicode в строку? (неправильно) Используя такие символы, как U + 200E, U + 200F, U + 202A... U + 202E, вы можете убедить текстовый рендерер, что они являются частью RTL, смешанной с LTR. Не уверен, что это помогает или нет, возможно, вам придется иметь материал в разделенных абзацах, но это единственное, о чем я мог подумать прямо сейчас.

Ответ 2

Попробуйте один раз android:layout_width="_wrap_content".

и попробуйте это и скажите мне результат....

final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD);

builder.setSpan(bss, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    int end = builder.length();

builder.setSpan(new AlignmentSpan.Standard(Alignment.ALIGN_OPPOSITE), start, end,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Если я не ошибаюсь, вы переопределяете диапазон, первоначально заданный вами. Поэтому попробуйте установить диапазон вместе.

Ответ 3

У меня была аналогичная проблема: как объединить в том же ряду выравнивание влево и вправо текста в TextView (RadioButton тоже TextView). Я нашел способ использования ReplacementSpan.

Идея заключается в добавлении еще одного символа (который не будет нарисован на самом деле) до конца текста и прикрепления к этому символу вашего места замены, где вы сможете делать все, что хотите - например, рисование дополнительного фрагмента текста в надлежащее место (выровненное справа). ResplacementSpan позволяет определить ширину дополнительного символа, и это пространство будет нерушимым.

Итак, мой вариант реализации этой идеи можно использовать следующим образом:

RightAlignLastLetterSpan.attach(
                        textView,
                        "right_aligned_piece_of_text",
                        R.style.TextAppereance_of_your_right_aligned_text);

Он добавляет текст, указанный в качестве второго аргумента в textView, стиль которого задан как третий аргумент. Добавленный текст будет выровнен по правому краю.

Вот полные источники RightAlignLastLetterSpan:

class RightAlignLastLetterSpan extends ReplacementSpan {

    @SuppressWarnings("FieldCanBeLocal") private static boolean DEBUG = false;

    private float textWidth = -1;
    @Nullable private TextAppearanceSpan spanStyle;
    @Nullable private String text;
    @NonNull private TextView tv;

    protected RightAlignLastLetterSpan(@NonNull TextView tv) {this.tv = tv;}

    public static boolean attach(@Nullable TextView tv, @Nullable String text, @StyleRes int resourceTextAppearance) {

        if (tv == null || isEmpty(text)) {
            logWrongArg();
            return false;
        }

        RightAlignLastLetterSpan span = new RightAlignLastLetterSpan(tv);
        span.setSpanStyle(new TextAppearanceSpan(tv.getContext(), resourceTextAppearance));
        span.setText(text);

        SpannableString ss = new SpannableString(new StringBuffer(tv.getText()).append(" _"));
        ss.setSpan(span, ss.length() - 1, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        tv.setText(ss);

        return true;
    }

    public void setSpanStyle(@Nullable TextAppearanceSpan spanStyle) {

        textWidth = -1;
        this.spanStyle = spanStyle;
    }

    public void setText(@Nullable String text) {

        textWidth = -1;
        this.text = text;
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {

        if (textWidth < 0) {
            applyStyle(paint);
            textWidth = isEmpty(this.text) ? 0 : paint.measureText(this.text);
        }

        return Math.round(textWidth);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {

        if (textWidth == 0 || this.text == null) {
            return;
        }

        int lineCount = tv.getLineCount();
        if (lineCount < 1) {return;}

        Rect lineBounds = new Rect();
        int baseline = tv.getLineBounds(lineCount - 1, lineBounds);
        lineBounds.offset(-tv.getPaddingLeft(), -tv.getPaddingTop());
        baseline -= tv.getPaddingTop();

        if (DEBUG) {
            paint.setColor(Color.argb(100, 100, 255, 100));
            canvas.drawRect(lineBounds, paint);

            paint.setColor(Color.argb(100, 255, 100, 100));
            canvas.drawRect(x, top, x + textWidth, bottom, paint);
        }

        applyStyle(paint);

        canvas.drawText(this.text, lineBounds.right - textWidth, baseline, paint);
    }

    public void applyStyle(Paint paint) {

        if (paint instanceof TextPaint && spanStyle != null) {
            TextPaint tp = (TextPaint) paint;
            spanStyle.updateDrawState(tp);
        }
    }
}