Как получить BackgroundColorSpan для выделенного текста в Android

Я пытаюсь установить BackgroundColorSpan для выделенного текста в моем Edit-text. Поэтому, когда я выбираю любой текст и нажимаю кнопку, он устанавливает цвет фона для этого конкретного текста, а затем я сохраняю эту заметку на свою SDCard в формате .html, а затем снова извлекаю эту заметку для редактирования. снова в том же формате.

Проблема, с которой я сталкиваюсь сейчас, заключается в том, что когда я применяю BackgroundColorSpan к выделенному тексту, он показывает ту строку с цветом фона, который я применил, но как только я сохраняю эту заметку на свою SDCard и снова открываю, она не отображается Цвет фона для этой конкретной строки вместо этого показывает мне обычную строку без цвета фона.

Ниже приведен мой код, который я использовал для установки цвета фона в выбранную область редактирования текста.

mSpannable.setSpan(new BackgroundColorSpan(color),startSelection, endSelection, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

а ниже код для сохранения моих заметок на SD-карту.

            Spanned spannedText = edtNoteDescription.getText();
            StringBuilder output = new StringBuilder();
            AppHtml.withinHtml(output, spannedText);
            File mFile = new File(Environment.getExternalStorageDirectory()
                    + File.separator + "MyNote/");
            }
            File myFile = new File(mFile, "/" + strNoteTitle + ".html/");
            myFile.createNewFile();
            FileOutputStream fOut = new FileOutputStream(myFile);
            OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
            myOutWriter.append(output);
            myOutWriter.close();
            fOut.close();

С помощью этого приведенного выше кода я успешно могу сохранить свой файл в формате HTML, но я не получаю строку с цветом фона.

Я попытался напечатать эту строку в журнале, а затем вставил эту строку в w3School, после чего я получил точный результат, который я ожидал, но в Android я не знаю, почему он не работает.

Строка, которую я получаю в Logcat, ниже

<p><font color ="#7dff00">This</font> <font color ="#ff5100">Is</font>&#160; a&#160; <font color ="#04ff00"><b><font style = "background-color:#2929dd">String</font></b></font>... </p>

Вы можете попробовать эту строку здесь, которая дает идеальный результат с цветом фона для строки но при настройке Android Edit-ext я не знаю, что происходит, и это не так, как я ожидаю.

Изменить

Ниже приведен код, который я использовал для извлечения текста из моего файла SDcard.

            Bundle bundle = getIntent().getExtras();
            strGetPath = bundle.getString(GlobalClass.notesPath);
            filePath = new File(strGetPath);
            fileInputStream = new FileInputStream(filePath);
            int size = fileInputStream.available();
            bytbuffer = new byte[size];
            fileInputStream.read(bytbuffer);
            fileInputStream.close();
            String strGetData = new String(bytbuffer);
            Spanned spanHTMLData = AppHtml.fromHtml(strGetData);
            LogM.e("===== Getting Data =====" + strGetData);
            edtNoteDescription.setText(spanHTMLData);

person InnocentKiller    schedule 08.09.2014    source источник
comment
Можете ли вы показать код, который читает текст из файла и применяет его к EditText?   -  person Manish Mulimani    schedule 11.09.2014
comment
@ManishMulimani, проверьте мои изменения.   -  person InnocentKiller    schedule 11.09.2014


Ответы (5)


Та же проблема, с которой я столкнулся при создании проекта сохранения заметок HTML.

Как вы сказали, вы создали свой собственный класс HTML.java, я также настроил тот же класс для своей цели.

Класс Html.java по умолчанию не содержит функций для цвета фона, размера шрифта, маркера и т. д.

Итак, здесь я делюсь содержимым этого класса, чтобы вы могли понять, как установить Backround Color для вашего HTML-примечания.

Предположим, что ваш настроенный Html.java = AppHtml.java, чтобы другие могли лучше его понять.

1) Сначала добавьте тег цвета фона в свой класс AppHtml.java. Добавьте приведенный ниже код в свой метод inParagraph.

if (style[j] instanceof BackgroundColorSpan) {
                out.append("<font style = \"background-color:#");
                String color = Integer
                        .toHexString(((BackgroundColorSpan) style[j])
                                .getBackgroundColor() + 0x01000000);
                while (color.length() < 6) {
                    color = "0" + color;
                }
                out.append(color);
                out.append("\">");
            }

затем завершите стиль шрифта, который вы начали

if (style[j] instanceof BackgroundColorSpan) {
                out.append("</font>");
            }

2) Вот мой класс Font

private static class Font {
    public String mColor;
    public String mFace;
    public String mbgColor;
    public String mSize;

    public Font(String color, String face, String bgColor, String size) {
        mColor = color;
        mFace = face;
        mbgColor = bgColor;
        mSize = size;
    }
}

3) Вот мой метод startFont.

private static void startFont(SpannableStringBuilder text,
        Attributes attributes) {
    String color = attributes.getValue("", "color");
    String face = attributes.getValue("", "face");
    String bgColor = attributes.getValue("", "style");
    String size = attributes.getValue("", "size");

    int len = text.length();
    text.setSpan(new Font(color, face, bgColor, size), len, len,
            Spannable.SPAN_MARK_MARK);
}

4) Вот мой метод endFont.

private static void endFont(SpannableStringBuilder text) {
    int len = text.length();
    Object obj = getLast(text, Font.class);
    int where = text.getSpanStart(obj);

    text.removeSpan(obj);

    if (where != len) {
        Font f = (Font) obj;
        if (f.mColor != null) {
            if (!TextUtils.isEmpty(f.mColor)) {
                if (f.mColor.startsWith("@")) {
                    Resources res = Resources.getSystem();
                    String name = f.mColor.substring(1);
                    int colorRes = res.getIdentifier(name, "color",
                            "android");
                    if (colorRes != 0) {
                        ColorStateList colors = res
                                .getColorStateList(colorRes);
                        text.setSpan(new TextAppearanceSpan(null, 0, 0,
                                colors, null), where, len,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                } else {
                    int c = getHtmlColor(f.mColor);
                    if (c != -1) {
                        text.setSpan(
                                new ForegroundColorSpan(c | 0xFF000000),
                                where, len,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                }
            }
        }
        if (f.mFace != null) {
            text.setSpan(new TypefaceSpan(f.mFace), where, len,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        if (f.mbgColor != null) {
            String bg_COLOR = f.mbgColor.substring(
                    f.mbgColor.lastIndexOf("#"), f.mbgColor.length());

            if (!TextUtils.isEmpty(bg_COLOR)) {
                if (bg_COLOR.startsWith("@")) {
                    Resources res = Resources.getSystem();
                    String name = bg_COLOR.substring(1);
                    int colorRes = res.getIdentifier(name, "color",
                            "android");
                    if (colorRes != 0) {
                        ColorStateList colors = res
                                .getColorStateList(colorRes);
                        text.setSpan(new TextAppearanceSpan(null, 0, 0,
                                colors, null), where, len,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                } else {
                    int c = getHtmlColor(bg_COLOR);
                    if (c != -1) {
                        text.setSpan(
                                new BackgroundColorSpan(c | 0xFF000000),
                                where, len,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                }
            }
        }

        if (f.mSize != null) {

            if (!TextUtils.isEmpty(f.mSize)) {

                int size = Integer.parseInt(f.mSize);

                text.setSpan((new AbsoluteSizeSpan(size)), where, len,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

            }
        }

    }
}
person Smit Patel    schedule 13.09.2014
comment
Привет, я пытаюсь реализовать это, но класс HTml является закрытым, я не могу расширить или реализовать, и если я попытаюсь скопировать/вставить, пропало много классов, таких как ArrayUtils и другие, я не знаю почему, но они отсутствуют , вы можете опубликовать рабочий класс, пожалуйста? большое спасибо =) - person user2582318; 29.06.2015
comment
@ user2582318 Вы можете получить класс отсюда: grepcode.com/file/repo1.maven.org/maven2/com.google.android/ - person Smit Patel; 29.06.2015
comment
да, я скачиваю, но мне нужно скачать больше 10, вы сделали то же самое? или ваш AndroidStudio/Eclipse легко нашел файлы? я видел, что у меня есть в пути sdk папка с именем sources, некоторые файлы есть, но почему моя Android Studio не видит эти файлы? ты знаешь? спасибо =) - person user2582318; 29.06.2015
comment
@user2582318 user2582318 Да, я делаю то же самое, вам нужно добавить все файлы, которые требуются, или вы можете проверить в Google рабочий класс для HTML.java - person Smit Patel; 29.06.2015
comment
кхм много работы =~~, но я сделаю, спасибо, я уже пробовал, HTMLSPanner тоже, но ничего не получилось... - person user2582318; 29.06.2015
comment
лол, это невозможно, я до сих пор добавлял классы, для каждого класса мне нужно добавить еще 2, почему? я не могу понять =( - person user2582318; 29.06.2015

Я не знаю, что такое AppHtml в вашем исходном коде, но для парсинга Html‹->Spannable в Android мы используем класс android.text.Html. Этот класс по какой-то причине не поддерживает BackgroundColorSpan.

Я бы предложил поэкспериментировать с методом Html.fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) и передать TagHandler для поддержки того, что вам нужно. Документы: здесь.

person Maciej Pigulski    schedule 11.09.2014
comment
Спасибо, я попробую это и дам вам знать. +1 за отличный ответ. - person InnocentKiller; 11.09.2014
comment
Html.TagHandler будет вызываться, когда синтаксический анализатор знает, как интерпретировать тег. В этом случае парсер знает, как интерпретировать тег font. Проблема связана с обработкой атрибутов тега шрифта. Текущая реализация обрабатывает только атрибуты шрифта color и typeface. - person Manish Mulimani; 13.09.2014

  • Создайте свой собственный формат и сохраните его в файл

Я не знаю, это ли именно то, что вы ищете, но я не вижу смысла использовать здесь HTML. Как указывалось в других ответах, fromHtml() весьма ограничен, и использование вашего пользовательского формата файла решит вашу проблему сохранения/восстановления в кратчайшие сроки.

Вам необходимо хранить в файле следующую информацию о фрагментах:

  1. Сколько у тебя пролетов

  2. начало, конец, цвет для каждого диапазона

  3. твой текст

В следующем фрагменте кода показано, как реализовать методы хранения и загрузки. Он использует текст Spanned в файле TextView. Полный проект Eclipse занял у меня около получаса, и его можно найти здесь.

public void store(View v)
{
    Spanned s = (Spanned) mTextView.getText();
    BackgroundColorSpan[] spans = s.getSpans(0, s.length(), BackgroundColorSpan.class);

    BufferedWriter bw = null;
    try
    {
        int len = spans.length;
        bw = new BufferedWriter(new FileWriter(mFile));
        bw.write(String.valueOf(len));
        bw.newLine();
        for (BackgroundColorSpan span : spans)
        {
            int start = s.getSpanStart(span);
            int end = s.getSpanEnd(span);
            int color = span.getBackgroundColor();
            bw.write("" + start + "," + end + "," + color);
            bw.newLine();
        }
        bw.write(mText);
        clear(v);
    }
    catch (IOException e)
    {
        Log.e(TAG, "IO error", e);
    }
    finally
    {
        closeQuietly(bw);
    }
}

public void load(View v)
{
    BufferedReader br = null;
    try
    {
        br = new BufferedReader(new FileReader(mFile));

        int len = Integer.parseInt(br.readLine());
        BackgroundColorSpan[] spans = new BackgroundColorSpan[len];
        int[] starts = new int[len];
        int[] ends = new int[len];
        for (int i = 0; i < len; i++)
        {
            String[] tokens = br.readLine().split(",");
            starts[i] = Integer.parseInt(tokens[0]);
            ends[i] = Integer.parseInt(tokens[1]);
            int color = Integer.parseInt(tokens[2]);
            spans[i] = new BackgroundColorSpan(color);
        }
        mText = br.readLine();

        SpannableString s = new SpannableString(mText);
        for (int i = 0; i < len; i++)
        {
            s.setSpan(spans[i], starts[i], ends[i], Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        mTextView.setText(s);
    }
    catch (IOException e)
    {
        Log.e(TAG, "IO error", e);
    }
    finally
    {
        closeQuietly(br);
    }
}
person Gil Vegliach    schedule 12.09.2014
comment
Перечитывая это через несколько лет, я понял, что вы можете реализовать ту же логику с Scanner трюками, вероятно, в одной трети кода. - person Gil Vegliach; 09.01.2017
comment
У меня есть аналогичный код, однако для повторного заполнения текста требуется время, когда пользователь охватывает много символов. - person Omar Boshra; 01.08.2017
comment
@OmarAhmed: что занимает много времени? s.setSpan? Или чтение файла? - person Gil Vegliach; 03.08.2017
comment
Цикл, который сбрасывает диапазон для начального и конечного индекса каждого слова. Если слов слишком много, нужно время. - person Omar Boshra; 03.08.2017
comment
@OmarAhmed: Понятно. Меня интересует, какая конкретно операция требует времени. Я не думаю, что цикл является проблемой, так как любая функция рендеринга все равно должна будет пройти через текст. Так что я бы сделал ставку на ввод-вывод или, может быть, на метод setSpan. Профайлер поможет найти узкое место. - person Gil Vegliach; 03.08.2017
comment
Я обнаружил, что respan на самом деле занимает время, поначалу он незначителен, но когда много текста охватывается, это занимает больше времени. - person Omar Boshra; 04.08.2017

Текущая реализация Html.fromHtml поддерживает только атрибуты color и typeface Font. Вы можете проверить тот же здесь.

Как вы сказали, вы используете модифицированную версию Html, гораздо проще самостоятельно поддерживать дополнительные атрибуты.

При синтаксическом анализе XML, когда встречается тег открытия шрифта, все значения атрибутов шрифта сохраняются в закрытом классе Font. Когда встречается тег закрытия шрифта, для каждого из этих атрибутов применяются соответствующие стили диапазона.

Шаг 1. В закрытом классе Font добавьте элемент данных для хранения цвета фона и измените конструктор, чтобы он принимал дополнительные параметры.

private static class Font {
    ...
    public String mBackgroundColor; // Data member to hold background color

    public Font(String color, String face, String backgroundColor) {
        ...
        mBackgroundColor = backgroundColor;
    }
    ...
}

Шаг 2. В методе startFont обработайте атрибут background-color.

private static void startFont(SpannableStringBuilder text,
                                  Attributes attributes) {
    ...
    String backgroundColor = null;
    // In this specific example, background-color attribute is present in style attribute.
    String style = attributes.getValue("", "style");
    if(style != null  && style.contains("background-color")) {
        String[] array = style.split(":");
        if(array.length == 2)
           backgroundColor = array[1]; 
    } else {
        // If background-color is specified as an attribute itself use this
        backgroundColor = attributes.getValue("", "background-color");
    }

    // Pass the background-color to the Font constructor
    text.setSpan(new Font(color, face, backgroundColor), len, len, Spannable.SPAN_MARK_MARK);

}

Шаг 3. В методе endFont добавьте к тексту BackgroundColorSpan.

private static void endFont(SpannableStringBuilder text) {
    ...
    if(f.mBackgroundColor != null) {
        text.setSpan(new BackgroundColorSpan(Color.parseColor(f.mBackgroundColor)), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

P.S. : Поскольку конструктор Html является закрытым, специализация Html невозможна.

person Manish Mulimani    schedule 11.09.2014

К сожалению, fromHtml() довольно ограничен и не может анализировать атрибут стиль шрифта.

Довольно старая запись в блоге Commonware имеет список рабочих атрибутов.

У вас есть несколько вариантов:

  • Установите пролеты фона вручную
  • Создайте свой собственный синтаксический анализатор, который поддерживает это, и поделитесь им с общественностью (ура)
  • Используйте веб-представление

Теперь использование WebView кажется наиболее простым решением, поэтому вот пример:

WebView webView = (WebView) findViewById(R.id.webView);
String strGetData = "<p><font color =\"#7dff00\">This</font> <font color =\"#ff5100\">Is</font>&#160; a&#160; <font color =\"#04ff00\"><b><font style = \"background-color:#2929dd\">String</font></b></font>... </p>\n";

webView.loadData(strGetData, "text/html", "utf-8");
person Simas    schedule 11.09.2014