10

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?

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
  • 2
    Where are you putting that Java code? Try putting it under `onResume()`. Also, why not try using the `android:textIsSelectable` XML attribute? – TheWanderer Oct 11 '18 at 21:42
  • @TheWanderer My code is in ´onCreate()´ right now and it is there only because if I add it to xml it simply doesn’t work (please check the links I added, it is wired, but it is reality). I’ll try putting it to ‘onResume()‘ a bit later (not near my laptop right now) – Sasha Shpota Oct 11 '18 at 21:47
  • @TheWanderer doing it in `onResume()` unfortunately didn't help. – Sasha Shpota Oct 12 '18 at 10:55
  • Can you add [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve), please? I can not reproduce such an issue: selection is working just fine with `android:textIsSelectable="true"` attribute. – ozbek Oct 15 '18 at 10:29
  • @ozbek please find the edit with the detailed test case. – Sasha Shpota Oct 16 '18 at 15:50
  • Putting the same block of code inside onCreate works – shb Oct 21 '18 at 18:48

4 Answers4

4

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.

Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147
  • Thank you. Unfortunately this didn't work, I tried it already. As I mentioned **I only set the parameters in java code because this was the only working way** (at least partly working). With the code you added I get `W/TextView: TextView does not support text selection. Selection cancelled.` in logs. I'll try to narrower the problem add add more details. – Sasha Shpota Oct 16 '18 at 09:39
  • Please find the edit with some new findings and the detailed test case on how to reproduce the problem. – Sasha Shpota Oct 16 '18 at 15:49
  • @OleksandrShpota: Please have a look at updated answer – Jitesh Mohite Oct 17 '18 at 08:01
  • I'm not sure what do you want me to try. As I mentioned xml way didn't work and eventually I identified that the problem was in setting `ScrollingMovementMethod`. Did you read the edits? – Sasha Shpota Oct 19 '18 at 07:59
3

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"
snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • Thank you for such a detailed explanation. I don't have a `ScrollView`, in my case there is only a `RelativeLayout`. But now I'm wandering if it works even without `textView.setMovementMethod(new ScrollingMovementMethod())` call why it is recommended for instance in this highly rated answer https://stackoverflow.com/a/3256305/2065796 ? Basically `android:scrollbars="vertical"` does the job without any java code. – Sasha Shpota Oct 22 '18 at 09:04
  • `android:scrollbars="vertical"` just shows `vertical` scrollbars when content (text) is larger than `View`. it doesn't/should't make `TextView` scrollable "by own" e.g. on touch/swipe. create some dummy `TextView`, set fixed width (e.g. 100dp), height (40dp) and really long text. without `maxLines`, `singleLine`, `ellipsize` and other "shortening" methods text won't fit and will be cutted on bottom. with `android:scrollbars="vertical"` `TextView` will show vertical scrollbar indicating that it have more content, but without `ScrollingMovementMethod` it won't be scrollable by touch – snachmsm Oct 23 '18 at 08:56
  • I my case it is somehow scrollable even without this call. – Sasha Shpota Oct 23 '18 at 09:09
  • check if it is scrollable without `setTextIsSelectable(true);` (or even with forced `setMovementMethod(null)` on the end) - `ArrowKeyMovementMethod` has also calling `Touch.onTouchEvent(...)` like `ScrollableMovementMethod` does in `onTouch`, so there is a chance that it can also handle scroll gesture – snachmsm Oct 23 '18 at 09:41
  • 1
    You are correct, scrolling stops working of I remove `android:textIsSelectable="true"`. – Sasha Shpota Oct 23 '18 at 10:56
3

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"

afakan
  • 31
  • 4
  • odd that it says scrollHorizontally but with `android:scrollbars="vertical"` it still scrolls vertical lol. thanks – A1m Aug 15 '20 at 00:44
0

You can make TextView selectable in two ways

through XML:

 android:textIsSelectable="true"

through Java code:

 textView.setTextIsSelectable(true);
Uma Achanta
  • 3,669
  • 4
  • 22
  • 49