Sasha Shpota
Sasha Shpota

Reputation: 10310

Scrollable TextView doesn't allow to select text after the application is paused

I have a scrollable TextView where a user can select text. I add scroll bar by setting movement method to ScrollingMovementMethod.

Problem: Selection works well unless the application is paused (for instance, after switching apps). Once the app is active again selection stops working and I get the following message in log:

W/TextView: TextView does not support text selection. Selection cancelled.

My setup:

I have an Activity with CoordinatorLayout and a Fragment with a TextView wrapped into RelativeLayout which looks like this:

<TextView
    android:id="@+id/text_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true"
    android:scrollbars="vertical" />

And in Java code I have to do:

textView.setMovementMethod(new ScrollingMovementMethod());
textView.setTextIsSelectable(true);
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

because this was the only working way according to this, this and this issues.

EDIT:

The problem is in the following call

textView.setMovementMethod(new ScrollingMovementMethod());

If I remove it it works, but I can't get why.

Minimal steps to reproduce the issue:

1) Create an empty Activity with a TextView using the following layout.

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_view"
        android:text="Some very very very long text..."
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:scrollbars="vertical" />

</android.support.design.widget.CoordinatorLayout>

2) Set up visibility parameters of the TextView in onStart() method.

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        TextView textView = findViewById(R.id.text_view);
        textView.setMovementMethod(new ScrollingMovementMethod());
        textView.setTextIsSelectable(true);
        textView.setFocusable(true);
        textView.setFocusableInTouchMode(true);
    }
}

3) Try to use context menu on the TextView before and after pausing the application.

EDIT 2:

Removing setMovementMethod(new ScrollingMovementMethod()) solves my problem and the functionality works well after that. But I'm not quite sure why it was added and I'm afraid it will brake something if I remove it. Any idea why one might use ScrollingMovementMethod in combination with android:scrollbars="vertical". May be xml doesn't work in some cases? Ideas? And I'm still interested why using ScrollingMovementMethod brakes selection functionality?

Upvotes: 10

Views: 3370

Answers (4)

afakan
afakan

Reputation: 31

I had very similar problem; texview could scroll but not selectable.

Remove this from your code:

texview.setMovementMethod(new ScrollingMovementMethod())

I add these to your textview in xml:

android:scrollHorizontally="true" android:textIsSelectable="true"

Upvotes: 3

snachmsm
snachmsm

Reputation: 19273

setting ScrollingMovementMethod gives TextView ability to scroll "by own", e.g. when you set really long text and it is cutted on bottom or edge. with ScrollingMovementMethod you can scroll TextView, there is no need to place it in scrollable container, e.g. in ScrollView or HorizontalScrollView

android:scrollbars="vertical" line says that IF this View get "scrollablility" (e.g. by above movement method) then UI should show only vertical scrollbar. from docs:

Defines which scrollbars should be displayed on scrolling or not.

and it is View docs, not TextView especially, because few extending "kinds" of Views can gain "scolllability", including all ViewGroups like ScrollView, ListView, RecyclerView etc.

and finally what this line is doing in your code? inside setTextIsSelectable you have this line:

setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);

so in fact its overwritting movement method you set few lines above in your own code. I bet that some time ago your TextView was scollable "by itself" and some day some smart guy rewritten this and put TextView in e.g. ScrollView in XML, and movement method stayed in code.

textIsSelectable is working until Activity pause, because after resuming you are (again) setting ScrollableMovementMethod, but inside setTextIsSelectable you have

 if (mEditor.mTextIsSelectable == selectable) return;

you set that mTextIsSelectable flag in first run before Activitys pause, and this flag is restored, so code below isn't fired (so movement method isn't re-set with ArrowKeyMovementMethod and your ScrollableMovementMethod stays). So answer for question what this line is doing in your code: it's breaking "selectionability" after pausing and resuming Activity, nothing besides that

note from sources of ScrollingMovementMethod and ArrowKeyMovementMethod: only in ArrowKeyMovementMethod (set as movement method inside setTextIsSelectable like above) you have overriden onTouchEvent method and inside it some lines handling selection

edit: note also that inside setTextIsSelectable you have setting "focusability", so these lines are unnecessary:

textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

so you can shorten your code to just one line:

textView.setTextIsSelectable(true);

or remove all quoted Java code and add one XML line:

android:textIsSelectable="true"

Upvotes: 3

Jitesh Mohite
Jitesh Mohite

Reputation: 34270

Please replace below code from your XML.

<TextView
    android:id="@+id/text_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true"
    android:enabled="true"
    android:textIsSelectable="true"
    android:focusable="true"
    android:longClickable="true" 
    android:scrollbars="vertical" />

Remove below code from program:

textView.setTextIsSelectable(true);
textView.setFocusable(true);
textView.setFocusableInTouchMode(true);

The internal code of setTextIsSelectable() method:

public void setTextIsSelectable(boolean selectable) {
        if (!selectable && mEditor == null) return; // false is default value with no edit data

        createEditorIfNeeded();
        if (mEditor.mTextIsSelectable == selectable) return;

        mEditor.mTextIsSelectable = selectable;
        setFocusableInTouchMode(selectable);
        setFocusable(FOCUSABLE_AUTO);
        setClickable(selectable);
        setLongClickable(selectable);

        // mInputType should already be EditorInfo.TYPE_NULL and mInput should be null

        setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);
        setText(mText, selectable ? BufferType.SPANNABLE : BufferType.NORMAL);

        // Called by setText above, but safer in case of future code changes
        mEditor.prepareCursorControllers();
    }

Programmatically they are also doing the same thing which I mentioned in XML. So depending on your requirements we can use it.

Upvotes: 4

Uma Achanta
Uma Achanta

Reputation: 3739

You can make TextView selectable in two ways

through XML:

 android:textIsSelectable="true"

through Java code:

 textView.setTextIsSelectable(true);

Upvotes: 0

Related Questions