Custom Cut/Copy Action Bar for Edittext That Shows Text Selection Handles

Custom cut/copy action bar for EditText that shows text selection handles

I figured out the answer to my own question; TextView (and therefore EditText) has a method setCustomSelectionActionModeCallback() which should be used instead of startActionMode(). Using this enables customisation of the menu used by TextView for text selection. Sample code:

bodyView.setCustomSelectionActionModeCallback(new StyleCallback());

where StyleCallback customises the text selection menu by removing Select All and adding some styling actions:

class StyleCallback implements ActionMode.Callback {

public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Log.d(TAG, "onCreateActionMode");
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.style, menu);
menu.removeItem(android.R.id.selectAll);
return true;
}

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

public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Log.d(TAG, String.format("onActionItemClicked item=%s/%d", item.toString(), item.getItemId()));
CharacterStyle cs;
int start = bodyView.getSelectionStart();
int end = bodyView.getSelectionEnd();
SpannableStringBuilder ssb = new SpannableStringBuilder(bodyView.getText());

switch(item.getItemId()) {

case R.id.bold:
cs = new StyleSpan(Typeface.BOLD);
ssb.setSpan(cs, start, end, 1);
bodyView.setText(ssb);
return true;

case R.id.italic:
cs = new StyleSpan(Typeface.ITALIC);
ssb.setSpan(cs, start, end, 1);
bodyView.setText(ssb);
return true;

case R.id.underline:
cs = new UnderlineSpan();
ssb.setSpan(cs, start, end, 1);
bodyView.setText(ssb);
return true;
}
return false;
}

public void onDestroyActionMode(ActionMode mode) {
}
}

The XML for the menu additions is:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/italic"
android:showAsAction="always"
android:icon="@drawable/italic"
android:title="Italic"/>
<item android:id="@+id/bold"
android:showAsAction="always"
android:icon="@drawable/bold"
android:title="Bold"/>
<item android:id="@+id/underline"
android:showAsAction="always"
android:icon="@drawable/underline"
android:title="Underline"/>
</menu>

How to modify OS contextual action bar for text selection in EditText?

See this page regarding how to use the contextual action mode: http://developer.android.com/guide/topics/ui/menus.html#CAB

Android - edittext - no textselection handles are shown and no sharing option

Upd2:

Also, as tjm1706 figured out, it's important to carefully review the Theme property. In this case, the key was to remove <item name="android:popupBackground">@color/window_background</item>

Upd1:

  1. To avoid this "double-toolbar" on editing, add this to your Theme's style:

    <item name="windowActionModeOverlay">true</item>
    <item name="actionModeBackground">@drawable/myapp_action_mode_background</item>

    taken from here

  2. To add Share (or any other button) - you need to set on your EditText setCustomSelectionActionModeCallback:

    Check out this question: Custom cut/copy action bar for EditText that shows text selection handles - it contains detailed example.

Original:

Try to remove: android:textIsSelectable="true". It's not possible to type with it. And if you don't need EditText to be editable - probably, simple TextView suits you better.

Without it, your EditText works fine for me:

enter image description here

How to start contextual action bar for text view programmatically with default opions copy and select all?

Select your text manually and then perform a long click:

textView.post(new Runnable() {
@Override
public void run() {
Selection.selectAll((Spannable) tv.getText());
tv.performLongClick();
}
});

Edit:

The problem is that the built-in text selection CAB is private and uses multiple private classes. You'd need to copy a lot of code or access a bunch of methods with reflection to use it.

You could make your own CAB menu but you'd also need the controller handles which is again a lot of code.

A simple solution would be to live with what you have and use my above suggested method. If you want to avoid calling your long click listener you can remove it while calling the cab:

Selection.selectAll((Spannable) tv.getText());
tv.setOnLongClickListener(null);
tv.performLongClick();
tv.setOnLongClickListener(mLongClickListener);

EditText: Disable Paste/Replace menu pop-up on Text Selection Handler click event

Solution: Override isSuggestionsEnabled and canPaste in EditText.

For the quick solution, copy the class below - this class overrides the EditText class, and blocks all events accordingly.

For the gritty details, keep reading.

The solution lies in preventing PASTE/REPLACE menu from appearing in the show() method of the (non-documented) android.widget.Editor class. Before the menu appears, a check is done to if (!canPaste && !canSuggest) return;. The two methods that are used as the basis to set these variables are both in the EditText class:

  • isSuggestionsEnabled() is public, and may thus be overridden.
  • canPaste() is not, and thus must be hidden by introducing a function of the same name in the derived class.

So incorporating these updates into a class that also has the setCustomSelectionActionModeCallback, and the disabled long-click, here is the full class to prevent all editing (but still display the text selection handler) for controlling the cursor:

package com.cjbs.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

/**
* This is a thin veneer over EditText, with copy/paste/spell-check removed.
*/
public class NoMenuEditText extends EditText
{
private final Context context;

/** This is a replacement method for the base TextView class' method of the same name. This
* method is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
* appears when triggered from the text insertion handle. Returning false forces this window
* to never appear.
* @return false
*/
boolean canPaste()
{
return false;
}

/** This is a replacement method for the base TextView class' method of the same name. This method
* is used in hidden class android.widget.Editor to determine whether the PASTE/REPLACE popup
* appears when triggered from the text insertion handle. Returning false forces this window
* to never appear.
* @return false
*/
@Override
public boolean isSuggestionsEnabled()
{
return false;
}

public NoMenuEditText(Context context)
{
super(context);
this.context = context;
init();
}

public NoMenuEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
init();
}

public NoMenuEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
this.context = context;
init();
}

private void init()
{
this.setCustomSelectionActionModeCallback(new ActionModeCallbackInterceptor());
this.setLongClickable(false);
}

/**
* Prevents the action bar (top horizontal bar with cut, copy, paste, etc.) from appearing
* by intercepting the callback that would cause it to be created, and returning false.
*/
private class ActionModeCallbackInterceptor implements ActionMode.Callback
{
private final String TAG = NoMenuEditText.class.getSimpleName();

public boolean onCreateActionMode(ActionMode mode, Menu menu) { return false; }
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; }
public void onDestroyActionMode(ActionMode mode) {}
}
}

I've tested this in Android v4.4.2 and v4.4.3.

I want to create custom action bar like copy, paste which will work for all apps for selection of word in android

This is not supported and hopefully is not possible outside of rooted devices. You cannot hack into the GUIs of other applications to affect how they handle these sorts of events.



Related Topics



Leave a reply



Submit