Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to override "Copy", "Share" and "Select All" options while selecting a text in a TextView

I have a TextView where a user is able to select a text. By default the following options appear: "Copy", "Share" and "Select All". I need to override them with custom options. But I can't find how to do that. I went through the documentation and this nice article but no lack. The article explains how to extend the menu when a user presses three-dots-button which is not what I need.

Question: How can I override default "Copy", "Share" and "Select All" options in text section menu?

Here is how my view looks like:

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

And in java code I have:

transcript.setTextIsSelectable(true);
transcript.setFocusable(true);
transcript.setFocusableInTouchMode(true);
like image 312
Sasha Shpota Avatar asked Mar 05 '23 06:03

Sasha Shpota


1 Answers

You can use TextView.setCustomSelectionActionModeCallback() to do this.

Documentation: https://developer.android.com/reference/android/widget/TextView.html#setCustomSelectionActionModeCallback(android.view.ActionMode.Callback)

I put together a very simple app to demonstrate how to use this feature.

MainActivity.java

public class MainActivity extends AppCompatActivity {

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

        TextView text = (TextView) findViewById(R.id.text);

        CustomActionModeCallback callback = new CustomActionModeCallback(this);
        text.setCustomSelectionActionModeCallback(callback);
    }
}

activity_main.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="16dp"
        android:text="@string/lorem_ipsum"
        android:textIsSelectable="true"/>

</FrameLayout>

CustomActionModeCallback.java

public class CustomActionModeCallback implements ActionMode.Callback {

    private final Context context;

    public CustomActionModeCallback(Context context) {
        this.context = context;
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        menu.clear();
        mode.getMenuInflater().inflate(R.menu.menu_custom, menu);
        return true;
    }

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

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        if (item.getItemId() == R.id.custom_one) {
            Toast.makeText(context, "One!", Toast.LENGTH_SHORT).show();
            mode.finish();
            return true;
        }
        else if (item.getItemId() == R.id.custom_two) {
            Toast.makeText(context, "Two!", Toast.LENGTH_SHORT).show();
            mode.finish();
            return true;
        }
        else if (item.getItemId() == R.id.custom_three) {
            Toast.makeText(context, "Three!", Toast.LENGTH_SHORT).show();
            mode.finish();
            return true;
        }

        return false;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {

    }
}

menu_custom.xml

<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/custom_one"
        android:title="One"
        app:showAsAction="never"/>

    <item
        android:id="@+id/custom_two"
        android:title="Two"
        app:showAsAction="never"/>

    <item
        android:id="@+id/custom_three"
        android:title="Three"
        app:showAsAction="never"/>

</menu>

Nothing much to comment on in MainActivity or either xml file. All the magic happens in CustomActionModeCallback.

Both onCreateActionMode() and onPrepareActionMode() can be used to add your custom menu items to the menu. If you use onCreateActionMode(), the system will add some extra options into an overflow menu, like this:

enter image description here enter image description here

If you use onPrepareActionMode(), the extra items won't be added.

enter image description here

Note that you must return true from onCreateActionMode() no matter what (returning false causes the menu to not be displayed), but you only have to return true from onPrepareActionMode() if you've actually modified the menu.

You can handle the user's clicks on your custom items inside onActionItemClicked(). In my example, I simply show a Toast and then close the contextual menu (using ActionMode.finish()). In this method, you should return true only on menu items that you handle yourself; returning false allows the system default action to happen (such as if you want to give the user the option to select all text).

Finally, onDestroyActionMode() is called when the menu is closed. Perhaps you have some use for this; I did not.

like image 150
Ben P. Avatar answered Mar 10 '23 09:03

Ben P.