Barra de acción de corte / copia personalizada para EditText que muestra los controles de selección de texto

Tengo una aplicación en la que deseo poder mostrar un TextView (o EditText) que permite al usuario seleccionar texto, luego presionar un botón para hacer algo con ese texto. Implementar esto en versiones de Android anteriores a Honeycomb no es un problema, pero en Honeycomb y por encima de la acción predeterminada de pulsación larga es mostrar una barra de acción con las opciones Copiar / Cortar / Pegar. Puedo interceptar la pulsación larga para mostrar mi propia barra de acción, pero luego no aparece el manejador de selección de texto.

Una vez que he comenzado mi propio Modo de Acción, ¿cómo se muestran los controles de selección de texto?

Aquí está el código que estoy usando para iniciar el Modo de Acción, que funciona, excepto que no se muestran los controles de selección de texto que se muestran:

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; } } 

Descubrí la respuesta a mi propia pregunta; TextView (y, por lo tanto, EditText) tiene un método setCustomSelectionActionModeCallback() que se debe usar en lugar de startActionMode() . El uso de esto permite la personalización del menú utilizado por TextView para la selección de texto. Código de muestra:

 bodyView.setCustomSelectionActionModeCallback(new StyleCallback()); 

donde StyleCallback personaliza el menú de selección de texto al eliminar Seleccionar todo y agregar algunas acciones de estilo:

 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) { } } 

El XML para las adiciones de menú es:

       

La solución anterior es buena si desea personalizar las opciones en la barra de acciones. Pero si desea anular la barra de acciones, copiar, pegar, etc., a continuación se muestra el código …

 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; } }); } } 

La forma más sencilla de hacerlo es agregar una línea en el estilo de tema principal que haya definido en la etiqueta de application de AndroidManifest . Abra su estilo de tema y agregue lo siguiente:

 @color/your_color 

O

 @color/your_color 

Por ejemplo: el estilo de mi tema que he definido: