How to override web view text selection menu in android

后端 未结 4 1881
有刺的猬
有刺的猬 2020-12-15 06:01

The basic android\'s web text selection menu is as shown in image attached below. It has options like copy, share, select all, web search.

I want to over ri

相关标签:
4条回答
  • 2020-12-15 06:18

    You need to overwrite action menus of activity

    more info you can read :https://developer.android.com/guide/topics/ui/menus.html

    HOW TO OVERWRITE:

    @Override
    public void onActionModeStarted(android.view.ActionMode mode) {
        mode.getMenu().clear();
        Menu menus = mode.getMenu();
        mode.getMenuInflater().inflate(R.menu.highlight,menus);
        super.onActionModeStarted(mode);
    }
    

    highlight

        <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/impclick"
            android:title="Mark As Important"
          />
        <item android:id="@+id/colorclick"
            android:title="Mark with color" />
    </menu>
    
    0 讨论(0)
  • 2020-12-15 06:18

    what you need is action mode, in activity:

        @Override
        public void onActionModeStarted(ActionMode mode) {
            Menu menu = mode.getMenu();
    
            // you can remove original menu: copy, cut, select all, share ... or not
            menu.clear();
    
            // here i will get text selection by user
            menu.add(R.string.action_menu_preview_card)
                    .setEnabled(true)
                    .setVisible(true)
                    .setOnMenuItemClickListener(item -> {
                        if (mWebview != null) {
                            mWebview.evaluateJavascript("window.getSelection().toString()", value -> {
                                value = StringUtil.trimToNull(value);
                                if (value != null) {
                                    // do something about user select
                                }
                            });
                        }
                        // Post a delayed runnable to avoid a race condition
                        // between evaluateScript() result and mode.finish()
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                mode.finish();
                            }
                        }, 200);
                        return true;
                    });
            super.onActionModeStarted(mode);
        }
    

    i has test it above android 21, this can handle action mode menu click, and mode.getMenuInflater().inflate(...) can't do it.

    0 讨论(0)
  • 2020-12-15 06:30

    This solution is not depend on Action mode of Activity and work for all android platform

    I tried to give answer but it exceeds the character limits so I am putting some code part

    Reference Link 1 For Selection On Web View

    https://github.com/btate/BTAndroidWebViewSelection

    Reference Link 2 For Making Web View Marker

    https://github.com/liufsd/WebViewMarker

    Both above links really plays important role and developed by some awesome developers. First Of All Need Some Research On TextSelectionSupport Class From Reference Link 1. I customised two lines of code in TextSelectionSupport class to get rectangle of selection in Selection Listener here.

    Clone the Sample Project from here https://github.com/ab-cse-2014/WebViewSelection.git

    See The implementation of CustomWebView and Use Of TextSelectionSupport class.

    This is my web view class in project

     import android.content.Context;
     import android.graphics.Rect;
     import android.os.Build;
     import android.support.annotation.RequiresApi;
     import android.support.v7.app.AppCompatActivity;
     import android.util.AttributeSet;
     import android.util.Log;
     import android.view.Gravity;
     import android.view.LayoutInflater;
     import android.view.View;
     import android.webkit.WebView;
     import android.widget.PopupWindow;
     import android.widget.Toast;
    
     import com.cse.webviewtextselection.R;
     import com.cse.webviewtextselection.webviewmaker.TextSelectionSupport;
    
     public class CustomWebView extends WebView {
    
    private final String TAG = this.getClass().getSimpleName();
    
    private Context mContext;
    
    private TextSelectionSupport mTextSelectionSupport;
    
    private PopupWindow mPopupWindow;
    
    private int currentTop;
    
    public CustomWebView(Context context) {
        super(context);
        mContext = context;
        initSetUp();
        preparePopupWindow();
    }
    
    public CustomWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initSetUp();
        preparePopupWindow();
    }
    
    public CustomWebView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initSetUp();
        preparePopupWindow();
    }
    
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public CustomWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mContext = context;
        initSetUp();
        preparePopupWindow();
    }
    
    private void initSetUp() {
    
        mTextSelectionSupport = TextSelectionSupport.support((AppCompatActivity) mContext, this);
        mTextSelectionSupport.setSelectionListener(new TextSelectionSupport.SelectionListener() {
            @Override
            public void startSelection() {
    
            }
    
            @Override
            public void selectionChanged(String text, Rect rect) {
                Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
                showPopAtLocation(mPopupWindow, rect.left, rect.top);
            }
    
            @Override
            public void endSelection() {
                if (mPopupWindow != null) {
                    mPopupWindow.dismiss();
                }
            }
        });
    }
    
    private void preparePopupWindow() {
    
        LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View customPopupView =  layoutInflater.inflate(R.layout.custom_popup_layout, null);
        mPopupWindow = new PopupWindow(customPopupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
        mPopupWindow.setAnimationStyle(android.R.style.Animation_Dialog);
    
    }
    
    private void showPopAtLocation(PopupWindow mPopupWindow, int x, int y) {
    
        if (mPopupWindow != null) {
    
            if (currentTop != 0 || currentTop > ((AppCompatActivity)mContext).getWindow().getDecorView().getHeight()) {
    
                    if (y > currentTop) {
    
                        y -= currentTop;
    
                    }
    
            }
    
            Log.d("Current Top : ", String.valueOf(currentTop));
            Log.d("Y : ", String.valueOf(y));
    
            //mPopupWindow.showAtLocation(((AppCompatActivity)mContext).findViewById(R.id.parentRelativeLayout), Gravity.NO_GRAVITY, x, y);
            mPopupWindow.showAtLocation(((AppCompatActivity)mContext).getWindow().getDecorView(), Gravity.NO_GRAVITY, x, y);
    
    
        }
    
    }
    
    @Override
    protected void onScrollChanged(int newLeft, int newTop, int oldLeft, int oldTop) {
    
        currentTop = newTop;
    
    
        super.onScrollChanged(newLeft, newTop, oldLeft, oldTop);
    }
     }
    

    Custom Popup Menu XML like androids smart text selection(custom_popup_layout.xml)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/myCustomMenuLinearLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:background="@android:color/transparent">
    
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:background="@android:color/white"
        android:elevation="5dp"
        android:layout_margin="12dp">
    
        <TextView
            android:id="@+id/menuOne"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Mark With Color"
            android:textColor="@android:color/black"
            android:padding="10dp"
            android:maxLines="1"/>
    
        <TextView
            android:id="@+id/menuTwo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Mark As Important"
            android:textColor="@android:color/black"
            android:padding="10dp"
            android:maxLines="1"/>
    
        <TextView
            android:id="@+id/menuThree"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Show More"
            android:textColor="@android:color/black"
            android:padding="10dp"
            android:maxLines="1"/>
    
    </LinearLayout>
    
    
    
    </LinearLayout>
    

    Output Screen Shots

    0 讨论(0)
  • 2020-12-15 06:31

    i think here can help you .

    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:

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

    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.

    0 讨论(0)
提交回复
热议问题