Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve BackgroundColorSpan for selected text in android

I am trying to set BackgroundColorSpan to selected text in my Edit-text. So when i select any text and click on button it will set Background color to that particular text and then i am saving that note to my SDCard with .html format and then again i am retrieving that note to edit again in the same format.

The problem i am facing right now is when i apply BackgroundColorSpan to selected text it show's that string with background color which i applied but once i save that note to my SDCard and re-open, it does not show Background color to that particular string instead of that it show me normal string without background color.

Below is my code Which i used to set Background Color to Edit-text selected area

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

and below is code to save my notes to SDcard.

            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();

With this above code i am successfully able to save my file in HTML format, but i am not getting the string with Background color.

I tried to print that string in Log and then pasted that string in w3School then i get exact result what i expect it to be but in android i don't know why it is not working.

String which i get in Logcat is below

<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>

You can try this string here which gives perfect result with background color to string but while setting to android Edit-ext i don't know what is happening and it's not setting as i expect it to be.

Edit

Below is code which i used to retrieve text from my SDcard file

            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);
like image 972
InnocentKiller Avatar asked Sep 08 '14 12:09

InnocentKiller


4 Answers

The same Problem I faced while creating a project of saving the HTML notes.

As you said that you have made your customized HTML.java class I also customized the same class for my purpose.

The default Html.java class does not contain the functionality for backgroundcolor, font size, bullet etc.

So Here i am sharing the content of that class so you can get the idea from it to set the Backround Color to you HTML Note.

Let's assume your customized Html.java = AppHtml.java so other can understand it better.

1) First add the background color tag in your AppHtml.java class. Add this below code to your withinParagraph method.

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("\">");
            }

then complete the font style that you have started

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

2) Here is my Font class

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) Here is my startFont method.

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) Here is my endFont method.

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);

            }
        }

    }
}
like image 102
Smit Patel Avatar answered Nov 12 '22 05:11

Smit Patel


I do not know what is AppHtml in your source code but for Html<->Spannable parsing in Android we use android.text.Html class. This class though doesn't support BackgroundColorSpan for some reason.

I would suggest experimenting with Html.fromHtml(String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler) method and pass a TagHandler to support what you need. Docs are here.

like image 4
Maciej Pigulski Avatar answered Nov 12 '22 05:11

Maciej Pigulski


  • Create your own custom format and save it to a file

I don't know whether this is exactly what you are looking for, but I don't see the point to use HTML here. As other answers pointed out, fromHtml() is quite limited and using you custom file format will solve your saving / restoring problem in no time.

You need to store on a file the following pieces information:

  1. How many spans you have

  2. start, end, color for each span

  3. your text

The following code snippet shows how to implement the store and load methods. It uses the Spanned text in a TextView. The complete Eclipse project took me about half an hour and can be found here.

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);
    }
}
like image 3
Gil Vegliach Avatar answered Nov 12 '22 06:11

Gil Vegliach


Current implementation of Html.fromHtml only supports Font's color and typeface attributes. You can check the same here.

As you said you are using modified version of Html, it is much more easier to support additional attributes by yourself.

While parsing the XML, when font open tag is encountered, all the font attribute values are stored in Font private class. When font close tag is encountered, for each of those attributes, apply corresponding span styles.

Step 1 : In Font private class add a data member for holding the background color and modify the constructor to accept additional paramter.

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

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

Step 2 : In startFont method, handle the background-color attribute.

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);

}

Step 3: In endFont method, add the BackgroundColorSpan to the text.

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. : Since Html constructor is private, specializing the Html is not possible.

like image 2
Manish Mulimani Avatar answered Nov 12 '22 04:11

Manish Mulimani