6

I have a TextView that I'm looking to allow the user to search in for a specific string. If the string is found, it should highlight. Using a background span is too slow and awkward, so I am trying to figure out if I can just have it select the string. I know with EditText this would be possible using setSelection(), but I don't want the user to be able to edit the text, while still being able to manually highlight text, which I can't seem to do manage with an EditText.

I guess, then it's an either or; is it either possible to programmatically select text in a TextView or possible to allow text selection without allowing editing in an EditText?

Note: I'm actually using a custom view extending TextView, so I'm assuming it's either that or extend EditText; I'm just not sure which (if either) will work.

yuttadhammo
  • 5,069
  • 6
  • 34
  • 45
  • It would be possible change the color of part of the TextView string. Just use some HTML around the part to mark and use HTML.fromHTML in your setText. Or you could use a Spannable object. – Phantômaxx May 01 '14 at 17:55
  • Yes, that's what I originally did, but it's slow because the text is large. – yuttadhammo May 01 '14 at 17:59
  • 1
    "Using a background span is too slow and awkward" -- ummmm... really? I haven't seen a problem with this. With [this sample project](https://github.com/commonsguy/cw-omnibus/tree/master/RichText/Search), I can search for occurrences of a substring in a longer string, removing all existing `BackgroundColorSpans` and applying new `BackgroundColorSpans`, in 20-30ms, in ~20 lines of code. "the text is large" -- how large? – CommonsWare May 01 '14 at 18:00
  • 1
    Also, text selection itself is going to use the same `BackgroundColorSpan` solution. – CommonsWare May 01 '14 at 18:01
  • it's ~20,000 characters per page. – yuttadhammo May 01 '14 at 18:02
  • You could also "paginate" your text into smaller portions... who said "paragraphs"? – Phantômaxx May 01 '14 at 18:02
  • It is paginated already... big text :) I can't believe that setSelection() would be as slow as BackgroundColorSpan, but I can test it using an EditText – yuttadhammo May 01 '14 at 18:03
  • setSelection() doesn't work anyway, doesn't actually show the highlight... but highlighting manually is instantaneous... – yuttadhammo May 01 '14 at 18:06
  • ... paginate more! Use pages instead of paragraphs (paragraphs instead of chapters) - **divide et impera!** – Phantômaxx May 01 '14 at 18:06
  • "it's ~20,000 characters per page" -- use Traceview to figure out where you are spending your time. – CommonsWare May 01 '14 at 18:08
  • sorry, can't do that... it's a huge body of text (45 printed volumes) and divided into set sections in a database. I need it as is. – yuttadhammo May 01 '14 at 18:08
  • The time is just spent in setText(); there's nothing else happening. Okay, it's not a huge amount of time, under 1s, but still; not instantaneous. Also, then I have to reset the text when closing the search box, and I just thought it would be a lot easier if I could just select some text, you know, like I can with my finger. – yuttadhammo May 01 '14 at 18:16
  • You need to call setText() regardless. The text will not be in the TextView otherwise. – CommonsWare May 01 '14 at 19:27
  • Of course, I call it once when creating the activity. I'm trying to avoid calling it every time I perform a search. – yuttadhammo May 18 '14 at 22:35
  • Did you ever figure this out? I have a very similar situation. – Nathaniel D. Waggoner Jul 02 '14 at 20:41
  • @NathanielWaggoner No, I just gave in and used the backgroundcolorspan... for now :) – yuttadhammo Jul 02 '14 at 22:27

1 Answers1

1

Not sure whether the question is still actual, I will provide my solution. Maybe will be useful for people coming from search engines.

So the purpose, as I understood, is to select all text in TextView without being able to modify its content. I didn't check how effective it is against very large text, but hope that not so bad.

Please note, API version should be >=11

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.text.Selection;
import android.text.Spannable;
import android.util.AttributeSet;

public class SelectableTextView extends TextView
{
    public SelectableTextView(Context context)
    {
        super(context);
        init();
    }

    public SelectableTextView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init();
    }

    public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
    {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init()
    {
        if (Build.VERSION.SDK_INT > 10)
            setTextIsSelectable(true);
    }

    @Override
    public boolean onTextContextMenuItem(int id)
    {
        switch (id)
        {
            case android.R.id.cut:
                return true;

            case android.R.id.paste:
                return true;

            case android.R.id.shareText:
            {
                String selectedText = getText().toString().substring(getSelectionStart(), getSelectionEnd());

                if (selectedText != null && !selectedText.isEmpty())
                {
                    Intent sendIntent = new Intent();
                    sendIntent.setAction(Intent.ACTION_SEND);
                    sendIntent.putExtra(Intent.EXTRA_TEXT, selectedText);
                    sendIntent.setType("text/plain");
                    sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    getContext().startActivity(sendIntent);
                }

                return true;
            }

            case android.R.id.selectAll:
            {
                selectAllText();
                return true;
            }
        }

        return super.onTextContextMenuItem(id);
    }

    public void selectAllText()
    {
        if (Build.VERSION.SDK_INT > 10)
            Selection.setSelection((Spannable) getText(), 0, length());
    }

}

Ayaz Alifov
  • 8,334
  • 4
  • 61
  • 56