Как переопределить меню выбора текста веб-представления в android

Основное меню выбора текста в веб-браузере показано на рисунке ниже. Он имеет такие опции, как копирование, общий доступ, выбор всех, веб-поиск.

введите описание изображения здесь

Я хочу переместить это меню и хочу, чтобы они были в моем списке меню, например "mark color", "mark as imp" и т.д. Я просматриваю большинство вопросов, доступных о контекстном меню при переполнении стека. Большая часть вопроса связана с контекстным меню, но не дает результата, как ожидалось. Я хочу меню, как показано ниже.

введите описание изображения здесь

Когда я выполняю выбор монитора Android, отображается форма представления вида viewRoot, например

D/ViewRootImpl: #1 mView = android.widget.PopupWindow$PopupDecorView{648898f V.E...... ......I. 0,0-0,0}
D/ViewRootImpl: #1 mView = android.widget.PopupWindow$PopupDecorView{a66541c V.E...... ......I. 0,0-0,0}
D/ViewRootImpl: MSG_RESIZED_REPORT: ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl: MSG_RESIZED_REPORT: ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1

Как добиться такой реализации?

Я также прошел https://github.com/naoak/WebViewMarker, но не получал надлежащего результата.

Что я еще сделал?

Я расширяю WebView android, и я хочу поддерживать минимальный SDK 19. Когда я выполняю длинное нажатие, я получил длинное событие, но я не могу получить такие вызовы api для создания меню.

Ответ 1

Это решение не зависит от режима действия Activity и работы для всех платформ Android.

Я попытался дать ответ, но он превышает пределы символов, поэтому я помещаю часть кода

Ссылка Ссылка 1 для выбора в веб-представлении

https://github.com/btate/BTAndroidWebViewSelection

Ссылка 2 для создания маркера веб-представления

https://github.com/liufsd/WebViewMarker

Оба вышеупомянутых канала действительно играют важную роль и разрабатываются некоторыми удивительными разработчиками. Прежде всего, нужно некоторое исследование по классу TextSelectionSupport из ссылки Ссылка 1. Я настроил две строки кода в классе TextSelectionSupport, чтобы получить прямоугольник выбора в Selection Listener здесь.

Клонировать пример проекта отсюда https://github.com/ab-cse-2014/WebViewSelection.git

См. Реализация класса CustomWebView и использования класса TextSelectionSupport.

Это мой класс веб-представления в проекте

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

Пользовательское всплывающее меню XML, например, выбор интеллектуального текста androids (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>

Экранные снимки экрана

Выбор один

Выбор два

Выбор три

Ответ 2

Вам нужно перезаписать меню действий

больше информации вы можете прочитать:https://developer.android.com/guide/topics/ui/menus.html

КАК ПЕРЕЗАПИСАТЬ:

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

выделить

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

Ответ 3

вам нужен режим действия, в действии:

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

Я тестировал его выше Android 21, это может обработать щелчок меню режима действий, а mode.getMenuInflater(). inflate (...) не может этого сделать.

Ответ 4

Я думаю, здесь может вам помочь.

Для полноты, вот как я исправил проблему:

Я следовал предложению в соответствии с этим ответом, с немного большей настройкой, чтобы более точно соответствовать переопределенному коду:

Открытый класс MyWebView расширяет 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);
}

}

По сути, это заставляет вашу настраиваемую CAB появляться вместо Android CAB. Теперь вам нужно изменить свой обратный вызов, чтобы подсветка текста исчезла вместе с CAB:

Открытый класс MyWebView расширяет WebView {   ...   частный класс CustomActionModeCallback реализует ActionMode.Callback {       ...       // Все до этой точки такое же, как в вопросе

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

}

Вот и все. Имейте в виду, что до тех пор, пока вы используете MyWebView с переопределенным startActionMode, нет НИКАКИХ способов получить собственный CAB (меню копирования/вставки в случае WebView). Возможно, возможно реализовать такое поведение, но это не так, как работает этот код. ОБНОВЛЕНИЕ: есть намного более простой способ сделать это! Вышеупомянутое решение работает хорошо, но вот альтернативный, более простой способ.

Это решение обеспечивает меньший контроль над ActionMode, но для этого требуется гораздо меньше кода, чем указанное выше решение.

Открытый класс MyActivity расширяет действие {

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

}

Вот пример меню, чтобы вы начали:

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

Что это! Все готово! Теперь ваше пользовательское меню появится, вам не нужно беспокоиться о выборе, и вам едва ли придется заботиться о жизненном цикле ActionMode.

Это почти безупречно работает с WebView, который занимает всю свою родительскую активность. Я не уверен, насколько хорошо он будет работать, если в вашей деятельности одновременно будет несколько просмотров. Вероятно, это потребует некоторой настройки в этом случае.