问题
I am creating custom contextual action bars (CAB) within a custom WebView.
My requirements are such that selecting a particular option from the first CAB will open a second CAB. This is the end goal:
I have made this happen, but the selection is not working correctly. I know it looks correct, but if the user taps any of the other three icons, this is the result:
If the user touches the selection after pressing any of the four initial icons, the app crashes due to a null pointer exception.
When the user touches the lingering selection after selecting an action, the app tries to access the ActionMode (let's call it firstActionMode) that created the CAB with the icons in order to invalidate/refresh it. However, firstActionMode is destroyed when the CAB with the colors is created, (call that secondActionMode) causing a null pointer exception.
In order to clear away the selection, I have found that calling clearFocus() within onDestroyActionMode(firstActionMode) method does the job just fine.
private class CustomActionModeCallback extends ActionMode.Callback {
@Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus();
}
}
However, when this is implemented, the selection does not persist when secondActionMode creates its CAB:
Choosing a color from this point actually produces the desired functionality. However, while this "works," it (most unfortunately) does not meet my requirements. I need the selection to remain visible and functional when the CAB created by secondActionMode is shown.
Here is the code for my custom class (
WebView) and its nested ActionModes
public class CustomWebView extends WebView {
private ActionMode.Callback mActionModeCallback;
@Override
public ActionMode startActionMode(Callback callback) {
ViewParent parent = getParent();
if (parent == null) {
return null;
}
if (callback instanceof HighlightActionModeCallback) {
mActionModeCallback = callback;
} else {
mActionModeCallback = new CustomActionModeCallback();
}
return parent.startActionModeForChild(this, mActionModeCallback);
}
private class CustomActionModeCallback implements 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.first_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) {
// This method is called when the handlebars are moved.
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.copy:
// Do stuff
break;
case R.id.bookmark:
// Do stuff
break;
case R.id.highlight:
startActionMode(new HighlightActionModeCallback());
break;
case R.id.note:
// Do stuff
break;
default:
return false;
}
mode.finish(); // Action picked, so close the CAB
return true;
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus(); // This is commented in the first four screens.
}
}
private class HighlightActionModeCallback implements 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.highlight_colors, 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) {
// This method is called when the handlebars are moved.
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) {
mode.finish(); // Action picked, so close the CAB
return true;
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
// Remove the selection highlight and handles.
clearFocus();
}
}
}
How can this be fixed? Any and all help is appreciated.
回答1:
I have now solved this problem; I was over-thinking it. The solution lies not in an ActionMode, but within the menu.
Instead of starting an entirely new ActionMode for the color menu, change the icon menu if the highlighter is selected. Save a reference to the Menu parameter in onCreateActionMode and set a flag of some sort; I'm using a boolean.
private class CustomActionModeCallback implements ActionMode.Callback {
private boolean highlighterClicked = false;
private Menu mMenu;
// Called when the action mode is created; startActionMode() was called
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mMenu = menu;
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.first_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) {
// This method is called when the handlebars are moved.
MenuInflater inflater = mode.getMenuInflater();
if (highlighterClicked) {
menu.clear(); // Remove the four icons
inflater.inflate(R.menu.highlight_colors, menu); // Show the colors
return true; // This time we did stuff, so return true
}
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) {
highlighterClicked = false;
switch (item.getItemId()) {
case R.id.copy:
// Do stuff
break;
case R.id.bookmark:
// Do stuff
break;
case R.id.highlight:
highlighterClicked = true;
onPrepareActionMode(mode, mMenu);
return true;
case R.id.note:
// Do stuff
break;
default:
// Any of the colors were picked
return true;
}
mode.finish(); // Action picked, so close the CAB
return true;
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus();
}
}
The selection highlight and handles stay active and functional, and the color options work as intended.
It is safe to delete the second ActionModeCallback class; it is worthless. Again, way over-thought. :P
来源:https://stackoverflow.com/questions/23096993/nested-contextual-action-bars