1

I have set html data to my textview. When I select any word/characters from textview I want to bold that word and replace original html data with new one.

String html = "<p>hello this is android testing</p>";

Like this my html maybe have many html tags in it. If I want to make "android" word bold, how can I replace original html string with "android".

I want <p>hello this is <strong>android</strong> testing</p> as result.

Kuldeep Sakhiya
  • 3,172
  • 1
  • 18
  • 17

3 Answers3

1

You can first set you string content into the TextView and then use setCustomSelectionActionModeCallback(...) to intercept the selection within the TextView.

In the example bellow a selected word will be surrounded by <strong>...</strong>.

For example selecting "testing" will make the following string visible into the TextView.

hello this is android testing android bla bla android bla bla android bla

Then selecting the last instance on "android" in the already transformed TextView content will make the following string visible into the TextVIew.

hello this is android testing android bla bla android bla bla android bla

Code :

public class MainActivity extends AppCompatActivity {

    String yourString = "<p>hello this is android testing android bla bla android bla bla android bla</p>";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = (TextView)findViewById(R.id.opening_today);
        tv.setText(Html.fromHtml(yourString));
        tv.setCustomSelectionActionModeCallback(new CallbackListener(tv));
    }

    class CallbackListener implements ActionMode.Callback{

        private TextView tv;
        private String strongS = "<strong>";
        private String strongE = "</strong>";

        public CallbackListener(TextView tv) {
            this.tv = tv;
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            int start = tv.getSelectionStart();
            int end = tv.getSelectionEnd();

            if( start == 0 && end == 0)
                return false;

            String content = tv.getText().toString();
            String token = content.substring(start, end);
            String before = content.substring(0, start);
            String after = content.substring(end, content.length());
            content = makeItStrong(before, after, token);
            tv.setText(Html.fromHtml(content));
            return false;
        }

        private String makeItStrong(String before, String after, String token){
            return cleanTags(before, strongS, strongE) + strongS + token + strongE + cleanTags(after, strongS, strongE);
        }

        private String cleanTags(String source, String... exp){
            for(String s: exp){
                source = source.replace(s, "");
            }
            return source;
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return true; }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }

        @Override
        public void onDestroyActionMode(ActionMode mode) {}
    }
}
Guillaume Barré
  • 4,168
  • 2
  • 27
  • 50
  • But I want that change apply in html first then reload the html to textview to see bold change. I want "

    hello this is android testing

    " result. There may be many possible android word in my paragraph, so its being difficult to find the exact.
    – Kuldeep Sakhiya Apr 06 '16 at 07:35
  • Will not work, we are setting txtview.setText(Html.fromHtml(ourhtmldata)); and we are getting selected text position from clean html string, it returns wrong position. – Kuldeep Sakhiya Apr 06 '16 at 09:29
  • @Kuldeep Sakhiya please have a look to my "last" intent... if it's not the good one maybe I'm just not able to understand what you really need. Btw using txtview.setText(Html.fromHtml(ourhtmldata)); you will always have position from the clean html string because this method return an instance of "Spanned" where tags have been "removed". – Guillaume Barré Apr 06 '16 at 11:22
0

Use SelectableTextView to get the selected text. You can see this implementation on github here.

Check the answer here. This might help you

And you can make bold using tag,

 String string= "<b>" + android + "</b> " + remainingString;   

 textView.setText(Html.fromHtml(string));
Community
  • 1
  • 1
Raghu Nagaraju
  • 3,278
  • 1
  • 18
  • 25
0

With the following code, you can highlight formatted text more than once and each time you can get the cumulative result of all highlights.

int getOccuranceNumber(String text ,String selection, int selectionStartIndex){
    int index = text.indexOf(selection);
    int occuranceNumber = 0;
    while (index >= 0) {
        if(index == selectionStartIndex){
            return occuranceNumber;
        }else {
            index = text.indexOf(selection, index + 1);
            occuranceNumber++;
        }
    }
    return occuranceNumber;
}
int getOccuranceIndex(String text ,String selection, int occuranceNumber){
    int index = text.indexOf(selection);
    int i = 0;
    while (i!=occuranceNumber) {

            index = text.indexOf(selection, index + 1);
            i++;

    }
    return index;
}
public String toHex(String arg) {
    TextView textV = new TextView(context);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        textV.setText(Html.fromHtml(arg,Html.FROM_HTML_MODE_LEGACY));
    }else{
        textV.setText(Html.fromHtml(arg));
    }
    String textFormated ="";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        textFormated =  Html.toHtml((Spanned) textV.getText(),TO_HTML_PARAGRAPH_LINES_CONSECUTIVE).toString();
    }else{
        textFormated =  Html.toHtml((Spanned) textV.getText()).toString();
    }
    return textFormated.replace("<p dir=\"rtl\">","").replace("</p>","").replace("\n","");
}
public void highLightText(TextView textView){
    String textFormated ="";
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        textFormated =  Html.toHtml((Spanned) textView.getText(),TO_HTML_PARAGRAPH_LINES_CONSECUTIVE).toString();
    }else{
        textFormated =  Html.toHtml((Spanned) textView.getText()).toString();
    }

    String text = textView.getText().toString();
    String selection = text.substring(textView.getSelectionStart(),textView.getSelectionEnd());
    int occuranceNum = getOccuranceNumber(text,selection,textView.getSelectionStart());
    String selectionHex = toHex(selection);

    int formatedTextSelectionStart = getOccuranceIndex(textFormated,selectionHex,occuranceNum);
    if(formatedTextSelectionStart<0)return;
    int formatedTextSelectionEnd = formatedTextSelectionStart + selectionHex.length();
    String formatedTextPart1 = textFormated.substring(0,formatedTextSelectionStart);
    String formatedTextPart2 = textFormated.substring(formatedTextSelectionStart,formatedTextSelectionEnd);
    String formatedTextPart3 = textFormated.substring(formatedTextSelectionEnd,textFormated.length());
    String colorOpenTag="<font color=\"#f7c627\">";
    String colorCloseTag="</font>";
    String finalText = formatedTextPart1+colorOpenTag+formatedTextPart2+colorCloseTag+formatedTextPart3;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        textView.setText(Html.fromHtml(finalText,Html.FROM_HTML_MODE_LEGACY));
    }else{
        textView.setText(Html.fromHtml(finalText));
    }
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Daniel Raouf
  • 2,307
  • 1
  • 22
  • 28