android - sobre - Use una barra de acción contextual personalizada para la selección de texto de WebView
manual de programacion android (2)
He utilizado esta guía de Google y este tutorial para producir mi propia barra de acción contextual.
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
// Called when the action mode is created; startActionMode() was called
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.annotation_menu, menu);
return true;
}
// Called each time the action mode is shown.
// Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.custom_button:
// do some stuff
break;
case R.id.custom_button2:
// do some other stuff
break;
default:
// This essentially acts as a catch statement
// If none of the other cases are true, return false
// because the action was not handled
return false;
}
finish(); // An action was handled, so close the CAB
return true;
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
}
};
Este menú está diseñado para aparecer cuando el usuario selecciona texto, por lo que reemplaza el menú copiar / pegar nativo. Ahora entiendo mi problema.
Como estoy reemplazando funciones para la selección de texto, también agregué LongClickListener
a WebView
e implementé el onLongClick(View v)
para poder detectar cuándo los usuarios hacen la selección.
myWebView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mActionMode != null) {
return false;
}
mActionMode = startActionMode(mActionModeCallback);
v.setSelected(true);
return true;
}
});
Cuando hago clic largo, veo aparecer mi menú personalizado, pero no se resalta ningún texto.
Necesito tener la funcionalidad de selección de texto; sin eso, mi menú no tiene sentido.
¿Cómo onLongClick(View v)
, pero mantengo la selección de texto proporcionada por Android?
Si eso no es posible, ¿puedo hacer la llamada a startActionMode(mActionModeCallback)
en otro lugar para que el texto se seleccione como normal, pero también aparecerá mi menú personalizado?
Si ninguno de esos es posible ... ayuda.
La forma en que hice algo similar fue solo anular el onTouchListener e invocar un GestureDetector para detectar cuándo se presionó mucho el WebView y hacer lo que quería desde allí. Aquí hay un código de muestra que le permite capturar eventos de impresión prolongada sin sacrificar la selección de texto en WebView. Espero que esto ayude.
@Override
protected void onCreate(Bundle savedInstanceState) {
WebView mWebView = (WebView) findViewById(R.id.myWebView);
GestureDetector mGestureDetector = new GestureDetector(this, new CustomGestureListener());
mWebView.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View view, MotionEvent arg1) {
//Suggestion #1 - this just lets the touch to be handled by the system but allows you to detect long presses
mGestureDetector.onTouchEvent(arg1);
return false;
//Suggestion #2 - this code will only let the touch be handled by the system if you don''t detect a long press
return mGestureDetector.onTouchEvent(arg1);
}
});
}
private class CustomGestureListener extends SimpleOnGestureListener {
@Override
public void onLongPress(MotionEvent e) {
//do stuff
}
}
¡HAY UNA MANERA MÁS FÁCIL! Ver la actualización a continuación: D
En aras de la integridad, aquí es cómo resolví el problema:Seguí la sugerencia de acuerdo con esta respuesta , con un poco más de ajuste para que coincida más estrechamente con el código anulado:
public class MyWebView extends WebView {
private ActionMode mActionMode;
private mActionMode.Callback mActionModeCallback;
@Override
public ActionMode startActionMode(Callback callback) {
ViewParent parent = getParent();
if (parent == null) {
return null;
}
mActionModeCallback = new CustomActionModeCallback();
return parent.startActionModeForChild(this, mActionModeCallback);
}
}
Esencialmente, esto obliga a su CAB personalizado a aparecer en lugar de CAB de Android. Ahora debe modificar su devolución de llamada para que el resaltado de texto desaparezca junto con el CAB:
public class MyWebView extends WebView {
...
private class CustomActionModeCallback implements ActionMode.Callback {
...
// Everything up to this point is the same as in the question
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus(); // This is the new code to remove the text highlight
mActionMode = null;
}
}
}
Eso es todo al respecto. Tenga en cuenta que, siempre que utilice MyWebView
con el startActionMode
reemplazado, startActionMode
hay forma de obtener el CAB nativo (el menú copiar / pegar, en el caso de una WebView). Es posible implementar ese tipo de comportamiento, pero esa no es la forma en que funciona este código.
Esta solución proporciona menos control sobre ActionMode
, pero requiere mucho menos código que la solución anterior.
public class MyActivity extends Activity {
private ActionMode mActionMode = null;
@Override
public void onActionModeStarted(ActionMode mode) {
if (mActionMode == null) {
mActionMode = mode;
Menu menu = mode.getMenu();
// Remove the default menu items (select all, copy, paste, search)
menu.clear();
// If you want to keep any of the defaults,
// remove the items you don''t want individually:
// menu.removeItem(android.R.id.[id_of_item_to_remove])
// Inflate your own menu items
mode.getMenuInflater().inflate(R.menu.my_custom_menu, menu);
}
super.onActionModeStarted(mode);
}
// This method is what you should set as your item''s onClick
// <item android:onClick="onContextualMenuItemClicked" />
public void onContextualMenuItemClicked(MenuItem item) {
switch (item.getItemId()) {
case R.id.example_item_1:
// do some stuff
break;
case R.id.example_item_2:
// do some different stuff
break;
default:
// ...
break;
}
// This will likely always be true, but check it anyway, just in case
if (mActionMode != null) {
mActionMode.finish();
}
}
@Override
public void onActionModeFinished(ActionMode mode) {
mActionMode = null;
super.onActionModeFinished(mode);
}
}
Aquí hay un menú de ejemplo para comenzar:
<!-- my_custom_menu.xml -->
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/example_item_1"
android:icon="@drawable/ic_menu_example_1"
android:showAsAction="always"
android:onClick="onContextualMenuItemClicked"
android:title="@string/example_1">
</item>
<item
android:id="@+id/example_item_2"
android:icon="@drawable/ic_menu_example_2"
android:showAsAction="ifRoom"
android:onClick="onContextualMenuItemClicked"
android:title="@string/example_2">
</item>
</menu>
¡Eso es! ¡Terminaste! Ahora aparecerá su menú personalizado, no tiene que preocuparse por la selección, y apenas tiene que preocuparse por el ciclo de vida de ActionMode
.
Esto funciona de forma casi perfecta con una WebView
que ocupa toda su Activity
principal. No estoy seguro de qué tan bien funcionará si hay varias View
dentro de su Activity
a la vez. Es probable que requiera algunos ajustes en ese caso.