1

I read a .txt file from a folder in a textview with this code:

    String txt = "";
        StringBuffer sbuffer1 = new StringBuffer();
        InputStream is = this.getResources().openRawResource(R.raw.file);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        try {

            while ((txt = reader.readLine()) !=null){
                sbuffer1.append(txt + "\n");

            }
            textview.setText(sbuffer1);
            is.close();

        }catch(Exception e) {
            e.printStackTrace();
     }

The .txt file contains a very long text and I want specific words of the text to be clickable, and when a specific word is clicked I want to display a toast message. How can I do that?

This is an example code of spannablestring but I don't know how to apply it in my code case:

    String text = " what do I put here??";
    SpannableString ss = new SpannableString(text);
    ClickableSpan clickableSpan1 = new ClickableSpan() {
           @Override
           public void onClick(View widget) {
                Toast.makeText(MainActivity.this, "One", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void updateDrawState(TextPaint ds) {

                super.updateDrawState(ds);
                ds.setColor(Color.BLUE);
                ds.setUnderlineText(false);
            }
        };

        ClickableSpan clickableSpan2 = new ClickableSpan() {

            @Override
            public void onClick(View widget) {
                Toast.makeText(MainActivity.this, "Two", Toast.LENGTH_SHORT).show();
            }
         };

        ss.setSpan(clickableSpan1, 7, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            ss.setSpan(clickableSpan2, 16, 20, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        textView.setText(ss);
        textView.setMovementMethod(LinkMovementMethod.getInstance());
        }
    }
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62

2 Answers2

0

Try this:

    String txt = "";

    StringBuilder sbuffer1 = new StringBuilder();
    InputStream is = this.getResources().openRawResource(R.raw.file);
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));

    try {

        while ((txt = reader.readLine()) !=null){
            sbuffer1.append(txt).append("\n");
        }

        is.close();
    }catch(Exception e) {
        e.printStackTrace();
    }

    String text = sbuffer1.toString();
    SpannableStringBuilder builder = new SpannableStringBuilder(text);

    ClickableSpan clickable0 = new ClickableSpan() {
        @Override
        public void onClick(View textView) {
            Toast.makeText(MainActivity.this, "word0", Toast.LENGTH_LONG).show();
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(Color.BLUE);
            ds.setUnderlineText(true);
        }
    };

    ClickableSpan clickable1 = new ClickableSpan() {
        @Override
        public void onClick(View textView) {
            Toast.makeText(MainActivity.this, "word1", Toast.LENGTH_LONG).show();
        }

        @Override
        public void updateDrawState(TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(Color.BLUE);
            ds.setUnderlineText(true);
        }
    };

    String word0 = "word0";
    String word1 = "word1";

    builder.setSpan(clickable0, text.indexOf(word0), text.indexOf(word0) + word0.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    builder.setSpan(clickable1, text.indexOf(word1), text.indexOf(word1) + word1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textview.setText(builder);
    textview.setMovementMethod(LinkMovementMethod.getInstance());

The above code is for 2 words, change them as you like. I hope I have no typos

  • What stands for the // *your code* parts? Sorry for the delay btw – user8467834 Jul 21 '18 at 16:41
  • @user8467834 the code you will write to be executed when the word is clicked –  Jul 21 '18 at 16:43
  • It doesn't work but if you see I called my TextView "textview" not textView and in the Onclick part it doesn't recognize it if I write "textview" and not "textView" why? In the other parts I changed all textView to textview and it went fine, but not in the @Override public void onClick(View textView) { // your code }..... shall I change something in the xml? – user8467834 Jul 26 '18 at 07:50
  • I made minor changes to use textview. In onClick the name textView is irrelevant you could use any name. It works. I'm using it right now. Remember that you provide the words to be clickable. I only set 2 words and a Toast when clicked. –  Jul 26 '18 at 09:06
0

Limitations: only one word per sentence is clickable and should be unique

To know what word should be clickable we need to create sentences structure, e.g. in json file (R.raw.sentences - sentences.json).

{
  "sentences": [
    {
      "phrase": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
      "target": "consectetur",
      "action": "consectetur!"
    },
    {
      "phrase": "Maecenas dictum massa laoreet pellentesque auctor.",
      "target": "auctor",
      "action": "auctor!"
    },
    {
      "phrase": "Maecenas consequat, eros at finibus semper, neque sem euismod nisi, ac vulputate justo dui vitae urna.",
      "target": "vitae",
      "action": "vitae!"
    }
  ]
}

Next step is to convert json to string and parse it.

private CharSequence rawToClickableSpanString(@RawRes int rawRes) {
    String str = streamToString(getResources().openRawResource(rawRes));
    return stringToSpannableString(str);
}

private CharSequence stringToSpannableString(String jsonString) {
    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();

    Moshi moshi = new Moshi.Builder().build();
    JsonAdapter<Sentences> jsonAdapter = moshi.adapter(Sentences.class);
    Sentences sentences = null;

    try {
        sentences = jsonAdapter.fromJson(jsonString);
    } catch (IOException e) {
        e.printStackTrace();
    }

    if (sentences != null) {
        for (SentencesItem sentencesItem : sentences.getSentences()) {
            final String target = sentencesItem.getTarget();
            final int start = sentencesItem.getPhrase().indexOf(target);
            final int end = start + target.length();

            SpannableString ss = new SpannableString(sentencesItem.getPhrase());
            ss.setSpan(new CustomClickableSpan(sentencesItem.getAction()) {

                @Override
                void onClick(View view, String action) {
                    Toast.makeText(view.getContext(), action, Toast.LENGTH_SHORT).show();
                }

            }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            spannableStringBuilder.append(ss);
            spannableStringBuilder.append("\n");
        }
    }

    CharSequence charSequence = null;

    if (spannableStringBuilder.length() > "\n".length()) {
        charSequence = spannableStringBuilder.subSequence(0, spannableStringBuilder.length() - "\n".length());
    }

    return charSequence;
}

public String streamToString(InputStream is) {
    StringBuilder sb = new StringBuilder();
    try {
        BufferedReader input = new BufferedReader(new InputStreamReader(is));
        String line;

        while ((line = input.readLine()) != null) {
            sb.append(line);
            sb.append("\n");
        }

        if (sb.length() > "\n".length()) {
            sb.setLength(sb.length() - "\n".length());
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
    return sb.toString();
}

ClickableSpan should store text to be shown, so we create custom ClickableSpan.

private abstract class CustomClickableSpan extends ClickableSpan {
    private String action;

    public CustomClickableSpan(String action) {
        this.action = action;
    }

    @Override
    public void onClick(View widget) {
        onClick(widget, action);
    }

    abstract void onClick(View widget, String action);
}

Finally we pass the result to textView and we set it as clickable.

TextView textView = findViewById(R.id.textView1);
textView.setText(rawToClickableSpanString(R.raw.sentences));
textView.setMovementMethod(LinkMovementMethod.getInstance());

enter image description here


I have used Moshi library to parse pojo json:

implementation 'com.squareup.retrofit2:converter-moshi:2.0.0'

and library for annotations:

implementation 'org.glassfish:javax.annotation:10.0-b28'

Classes for pojo from json file:

@Generated("com.robohorse.robopojogenerator")
public class Sentences{

    @Json(name = "sentences")
    private List<SentencesItem> sentences;

    public void setSentences(List<SentencesItem> sentences){
        this.sentences = sentences;
    }

    public List<SentencesItem> getSentences(){
        return sentences;
    }

    @Override
    public String toString(){
        return 
            "Sentences{" + 
            "sentences = '" + sentences + '\'' + 
            "}";
        }
}

@Generated("com.robohorse.robopojogenerator")
public class SentencesItem{

    @Json(name = "phrase")
    private String phrase;

    @Json(name = "action")
    private String action;

    @Json(name = "target")
    private String target;

    public void setPhrase(String phrase){
        this.phrase = phrase;
    }

    public String getPhrase(){
        return phrase;
    }

    public void setAction(String action){
        this.action = action;
    }

    public String getAction(){
        return action;
    }

    public void setTarget(String target){
        this.target = target;
    }

    public String getTarget(){
        return target;
    }

    @Override
    public String toString(){
        return 
            "SentencesItem{" + 
            "phrase = '" + phrase + '\'' + 
            ",action = '" + action + '\'' + 
            ",target = '" + target + '\'' + 
            "}";
        }
}
deadfish
  • 11,996
  • 12
  • 87
  • 136