问题
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 ActionMode
s
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