Use a Custom Contextual Action Bar for Webview Text Selection

Use a custom contextual action bar for WebView text selection

THERE IS AN EASIER WAY! See update below :D


For the sake of completeness, here is how I fixed the problem:

I followed the suggestion according to this answer, with a little more tweaking to more closely match the overridden code:

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

Essentially, this forces your customized CAB to appear instead of the Android CAB. Now you have to modify your callback so that the text highlight will go away along with the 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;
}
}
}

That's all there is to it. Be aware that as long as you are using MyWebView with the overridden startActionMode there is NO WAY to get the native CAB (the copy/paste menu, in the case of a WebView). It may be possible to implement that sort of behavior, but that is not the way this code works.


UPDATE: There is a much easier way to do this! The above solution works well, but here is an alternative, easier way.

This solution provides less control over the ActionMode, but it requires far less code than the above solution.

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

Here is an example Menu to get you started:

<!-- 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>

That's it! You're done! Now your custom menu will show up, you don't have to worry about the selection, and you barely have to concern yourself with the ActionMode lifecycle.

This works nearly flawlessly with a WebView that occupies its entire parent Activity. I am not sure how well it will work if there are multiple Views within your Activity at one time. It will likely require some tweaking in that case.

Getting selected text in a WebView via a contextual action bar

You can't do that yet with the current API.

I filed a feature request for this - Issue 24841: WebView should allow applications to supply a custom Contextual Action Bar http://code.google.com/p/android/issues/detail?id=24841

Basically, WebView in 4.0 has hardcoded its own Contextual Action Bar (CAB). That CAB has a reference back to the WebView and with that reference, it can get the selected text. I'm not sure how you were able to detect the ActionMode starting and modify the menu, but if you were able to do all of that, then you are stuck because getSelection() is package-private currently. I filed that as a separate issue and linked it to the previous issue above.

How to override Contextual Action Bar when selecting text in a WebView in Android?

THERE IS A BETTER WAY! See the following answer: https://stackoverflow.com/a/22391169/2608235


In short, you can't use an OnLongClickListener and keep the selection within a WebView. Android does some weird stuff behind the scenes for selecting text within WebViews. If you override OnLongClickListener in order to call startActionMode, you will lose the selection, as you have found out.

What you should do instead is override startActionMode in your fragment, rather than its parent View (in your case, CustomWebView).

I don't have the mod permission to mark this question as a duplicate, but it is.

See my question for more info: Use a custom contextual action bar for WebView text selection

Overriding the default Contextual Action Bar for text selection(in WebView) in Android

I have been able to resolve this. I was also facing this issue and could not find any solution on the web.

So, if you set up a LongClick listener, the Webview would stop showing selection at all. After delving deep into the Webview code, I found that it was calling WebView's method startRunMode and passing an instance of SelectActionCallbackMode class.

I simply extended the Webview class and overrided the startRunMode method like this:

public ActionMode startActionMode(ActionMode.Callback callback) 
{
actionModeCallback = new CustomizedSelectActionModeCallback();
return super.startActionMode(actionModeCallback);
}

This forced the Webview to display my Callback instead of displaying Webview's default one. This ensured that selection worked as smoothly as before and my CAB was displayed each time selection was made. Only caveat was that I had to write code to dismiss the CAB myself.

Tested on 4.1, 4.2 and 4.3 devices.

Hope this helps.

Remove CAB but preserve webview text selection

This answer solves the problem:
android webview: prevent text selection actionMode actionBar

not the most elegant solution ever, but I just tested it in an app I'm building and it works like a charm.

Contextual Action Bar text selection

I have made my own version of this editing the code a bit to meet my needs, I do not believe that there is any actual way of doing this.

webview contexual menu on select text

After much trial and error, I have come to a reasonable solution.
Just posting it here for completeness, just in case anyone faces similar issue.

The solution was creating an alternative menu that I implemented using PopupWindow.
And I, called this on long press event using a GestureDetector.

myWebView.setWebChromeClient(new MyWebChromeClient());
mGestureDetector = new GestureDetector(getActivity(), new CustomGestureListener());
mGestureDetector = new GestureListener(getActivity(),myWebView);
final GestureDetector gd = new GestureDetector(getActivity(), mGestureDetector);

//====== web-view popup menu
myWebView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
gd.onTouchEvent(motionEvent);

return false;
}
});

The Gesture Listener is quite basic, code can be found here

Rest of the interaction with the web-view data is done using JavaScript, using below code, more about it can be found in google dev guide

WebView xview = (WebView)view;
xview.loadUrl("javascript:alert(showAndroidToast('underline'))");

Well this has done the job for me, result can be seen in below image, hope it helps someone.

Or unless someone has a better solution/code.

Sample Image

Remove CAB but preserve webview text selection

This answer solves the problem:
android webview: prevent text selection actionMode actionBar

not the most elegant solution ever, but I just tested it in an app I'm building and it works like a charm.



Related Topics



Leave a reply



Submit