ForegroundColorSpan не применяется к ReplacementSpan

Я пытаюсь использовать replaceSpans для форматирования ввода в поле EditText (без изменения содержимого):

public class SpacerSpan extends ReplacementSpan {
    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        return (int) paint.measureText(text.subSequence(start,end)+" ");
    }
    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        canvas.drawText(text.subSequence(start,end)+" ", 0, 2, x, y, paint);
    }
}

Это работает, как и ожидалось, и добавляет интервал после составного раздела. Однако, если я также применяю ForegroundColorSpan, цвет не устанавливается для составного раздела:

EditText edit = (EditText) findViewById(R.id.edit_text);

SpannableString content = new SpannableString("1234567890");

ForegroundColorSpan fontColor = new ForegroundColorSpan(Color.GREEN);
SpacerSpan spacer = new SpacerSpan();
content.setSpan(fontColor, 0, content.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
content.setSpan(spacer, 4, 5, Spanned.SPAN_MARK_MARK);

edit.setText(content);

Результат выглядит следующим образом: http://i.cubeupload.com/4Us5Zj.png

Если я применяю AbsoluteSizeSpan, указанный размер шрифта также применяется к разделу Replace Span. Это предполагаемое поведение, я что-то упустил или ошибка в Android?


person Andreas Wenger    schedule 04.02.2015    source источник
comment
Поскольку единственные конкретные реализации ReplacementSpan в SDK предназначены для замены текста изображениями, возможно, этот материал не был протестирован так хорошо для замены текста большим количеством текста. Одно из различий между ForegroundColorSpan и AbsoluteSizeSpan заключается в том, что последний переопределяет как updateMeasureState(), так и updateDrawState(), а ForegroundColorSpan переопределяет только updateDrawState(). Это имеет смысл, поскольку цвет переднего плана не влияет на измерения. Однако возможно, что Paint, переданное в draw(), является неправильным.   -  person CommonsWare    schedule 04.02.2015
comment
Да, кажется, что Paint, переданный draw(), отличается от другого (выводится, если я регистрирую установленную краску): ForegroundColor. r: 0, g: 255, b: 0 Color during draw. r: 0, g: 0, b: 0   -  person Andreas Wenger    schedule 04.02.2015
comment
В качестве теста вы можете попробовать создать подкласс ForegroundColorSpan, который переопределяет updateMeasureState(), чтобы делать то, что делает updateDrawState(), а затем попробовать применить свой подкласс вместо ForegroundColorSpan. Если ваш подкласс работает, хорошая новость заключается в том, что вы точно будете знать источник проблемы. Плохая новость заключается в том, что вы можете потерять свой подкласс, если Spanned пройдет через Bundle, поскольку для этого AFAIK поддерживаются только встроенные ParcelableSpans.   -  person CommonsWare    schedule 04.02.2015


Ответы (1)


CommonWare указал мне правильное направление. Кажется, что ReplacementSpans рендерится до того, как будут проведены консультации с любыми CharacterStyleSpan [1]

Возможное (но некрасивое) исправление состоит в том, чтобы реализовать пользовательский ForegroundColorSpan, который расширяет MetricAffectingSpan (MetricAffectingSpans консультируются перед отрисовкой ReplacementSpans [1]).

public class FontColorSpan extends MetricAffectingSpan {

    private int mColor;

    public FontColorSpan(int color) {
        mColor = color;
    }

    @Override
    public void updateMeasureState(TextPaint textPaint) {
        textPaint.setColor(mColor);
    }
    @Override
    public void updateDrawState(TextPaint textPaint) {
        textPaint.setColor(mColor);
    }
} 

Я думаю, это ошибка, о которой следует сообщить?

[1]http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.2_r1/android/text/TextLine.java#936

person Andreas Wenger    schedule 04.02.2015
comment
Это также объясняет, почему был применен AbsoluteSizeSpan (он расширяет MetricAffectingSpan) - person Andreas Wenger; 04.02.2015