J'ai utilisé ce guide de Google et ce tutoriel pour produire ma propre barre d'action contextuelle.
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;
}
};
Ce menu est conçu pour apparaître lorsque l'utilisateur sélectionne du texte, il remplace donc le menu natif copier/coller. J'arrive maintenant à mon problème.
Étant donné que je remplace les fonctions de sélection de texte, j'ai également ajouté un LongClickListener
à un WebView
et mis en œuvre la méthode onLongClick(View v)
afin de pouvoir détecter quand les utilisateurs font la sélection.
myWebView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mActionMode != null) {
return false;
}
mActionMode = startActionMode(mActionModeCallback);
v.setSelected(true);
return true;
}
});
Lorsque je clique longuement, je vois mon menu personnalisé apparaître, mais aucun texte n'est mis en surbrillance.
J'ai besoin d'avoir la fonctionnalité de sélection de texte; sans elle, mon menu est inutile.
Comment remplacer onLongClick(View v)
, mais conserver la sélection de texte fournie par Android?
Si cela n'est pas possible, puis-je appeler startActionMode(mActionModeCallback)
ailleurs pour que le texte soit sélectionné normalement, mais mon menu personnalisé apparaîtra également?
Si aucun de ces éléments n'est possible ... aide.
LÀ IS UNE MANIÈRE PLUS FACILE! Voir la mise à jour ci-dessous: D
J'ai suivi la suggestion selon cette réponse , avec un peu plus de réglages pour mieux correspondre au code remplacé:
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);
}
}
Essentiellement, cela force votre CAB personnalisé à apparaître au lieu du Android CAB. Maintenant, vous devez modifier votre rappel pour que la surbrillance du texte disparaisse avec le 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;
}
}
}
C'est tout ce qu'on peut en dire. N'oubliez pas que tant que vous utilisez MyWebView
avec le startActionMode
remplacé, il n'y a AUCUNE FAÇON d'obtenir le CAB natif (le menu copier/coller, dans le cas d'une WebView). Il peut être possible d'implémenter ce type de comportement, mais ce n'est pas ainsi que ce code fonctionne.
Cette solution fournit moins de contrôle sur le ActionMode
, mais elle nécessite beaucoup moins de code que la solution ci-dessus.
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);
}
}
Voici un exemple de menu pour vous aider à démarrer:
<!-- 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>
C'est tout! Vous avez terminé! Maintenant, votre menu personnalisé apparaîtra, vous n'avez pas à vous soucier de la sélection, et vous avez à peine à vous soucier du cycle de vie de ActionMode
.
Cela fonctionne presque parfaitement avec un WebView
qui occupe tout son parent Activity
. Je ne sais pas dans quelle mesure cela fonctionnera s'il y a plusieurs View
dans votre Activity
en même temps. Cela nécessitera probablement quelques ajustements dans ce cas.
La façon dont j'ai fait quelque chose de similaire était de remplacer uniquement onTouchListener et d'appeler un GestureDetector pour détecter quand la WebView a été pressée longuement et faire ce que je voulais à partir de là. Voici un exemple de code qui vous permet d'intercepter des événements de longue pression sans sacrifier la sélection de texte dans WebView. J'espère que cela aide.
@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
}
}