Custom cut/copy action bar for EditText that shows text selection handles
Solution 1
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>
Solution 2
Above solution is good if you want to customize the options in action bar. But if you want to override action bar copy/Paste etc, below is the code...
public class MainActivity extends Activity {
EditText editText;
private ClipboardManager myClipboard;
private ClipData myClip;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myClipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
editText = (EditText) findViewById(R.id.editText3);
myClipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
editText = (EditText) findViewById(R.id.editText3);
editText.setCustomSelectionActionModeCallback(new Callback() {
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case android.R.id.copy:
int min = 0;
int max = editText.getText().length();
if (editText.isFocused()) {
final int selStart = editText.getSelectionStart();
final int selEnd = editText.getSelectionEnd();
min = Math.max(0, Math.min(selStart, selEnd));
max = Math.max(0, Math.max(selStart, selEnd));
}
// Perform your definition lookup with the selected text
final CharSequence selectedText = editText.getText()
.subSequence(min, max);
String text = selectedText.toString();
myClip = ClipData.newPlainText("text", text);
myClipboard.setPrimaryClip(myClip);
Toast.makeText(getApplicationContext(), "Text Copied",
Toast.LENGTH_SHORT).show();
// Finish and close the ActionMode
mode.finish();
return true;
case android.R.id.cut:
// add your custom code to get cut functionality according
// to your requirement
return true;
case android.R.id.paste:
// add your custom code to get paste functionality according
// to your requirement
return true;
default:
break;
}
return false;
}
});
}
}
Clyde
One-time compiler writer and business owner, now indie app and electronics developer.
Updated on June 28, 2020Comments
-
Clyde almost 4 years
I have an app where I want to be able to show a TextView (or EditText) that allows the user to select some text, then press a button to have something done with that text. Implementing this on Android versions prior to Honeycomb is no problem but on Honeycomb and above the default long-press action is to show an action bar with Copy/Cut/Paste options. I can intercept long-press to show my own action bar, but then I do not get the text selection handles displayed.
Once I have started my own ActionMode how do I get the text selection handles displayed?
Here is the code I'm using to start the ActionMode, which works except there are no text selection handles displayed:
public boolean onLongClick(View v) { if(actionMode == null) actionMode = startActionMode(new QuoteCallback()); return true; } class QuoteCallback implements ActionMode.Callback { public boolean onCreateActionMode(ActionMode mode, Menu menu) { MenuInflater inflater = mode.getMenuInflater(); inflater.inflate(R.menu.quote, menu); return true; } public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch(item.getItemId()) { case R.id.quote: Log.d(TAG, "Selected menu"); mode.finish(); // here is where I would grab the selected text return true; } return false; } public void onDestroyActionMode(ActionMode mode) { actionMode = null; } }
-
Maniac almost 11 yearsI'm trying the same, but because I add some more items, they don't fit in the ActionBar on phones. If I let them go to the overflow menu, the ActionMode is always destroyed as soon as I click on the overflow button. Did you encounter the same problem? Has anyone a workaround for it?
-
thisiscrazy4 almost 11 yearsAre we able to override methods for copy, share, web search etc. ?
-
Clyde almost 11 years@thisiscrazy4 Dunno about override, but you can remove any pre-populated menu items then add your own.
-
thisiscrazy4 almost 11 yearssetCustomSelectionActionModeCallback() is only for TextView and EditText. Is there anything for WebView?
-
Mahadevan Sreenivasan about 10 years@Maniac Did you find a solution to this problem?
-
Maniac almost 10 yearsNo. Didn't have more ideas, so didn't try anything more.
-
David almost 10 yearsThis only works with API>10, is there something for lower APIs?
-
Iman Marashi about 9 yearsWhat is bodyView exactly?
-
Clyde about 9 yearsbodyView is the TextView that is showing the text being edited.
-
Rahul almost 9 yearsIs there any way by which we can do this on a button cliick, like.. i have a textview, which is selectable. i want to launch the defaultactionmode associated wit text view (with select all and copy), on click of a button. I cant use performLongClick () of textview as, it is already overridden. Is there any possibility to acheive this? I tried startActionMode(), but it opens with a blank action bar.
-
Admin over 8 years@Maniac I have the same problem right now. Did you solve it? I have about 6 items and not all are showing. and Clyde: Do your copy/paste/cut functions still work after this customization?
-
Admin over 8 yearsI followed your solution and it workd almost. The only catch is that not all of my custom items is showing on the text selection toolbar. Anyway to implement a horizontal list to it? and the other thing is that the default functions like copy/paste, they dont work anymore, but the icons is showing, is it possible to reactive them again?
-
Mina Dahesh about 8 years@Clyde i do so for my app, thanks.if i want to use Toast in this class, how can i define it?
-
Clyde about 8 years@MinaDahesh Your question is completely orthogonal to the topic of this answer. To learn how to use Toast I recommend you read the Toast Developer Guide.
-
Mina Dahesh about 8 years@Clyde. i've one question, as i use this code, i can't use the the default menu btns like copy and paste... why ? and how can i solve it?
-
Mina Dahesh about 8 years@Clyde. this is the link of my question- would you please check it? stackoverflow.com/questions/37052973/…
-
shivamDev31 over 7 years@Clyde - How you are getting reference to bodyView in StyleCallback class?
-
n-y over 2 yearsand dont forget
bodyView.setTextIsSelectable(true);