LoginSignup
1
0

More than 5 years have passed since last update.

Android7の場合ActionModeメニューのカスタマイズ

Posted at

WebViewのActionModeメニューのカスタマイズ

WebViewの文字を選択する時に表示されるActionModeメニューをカスタマイズするプログラムはAndroid7でクラッシュになったため、発生原因と対応策をメモします。

Action Modeメニュー表示場所

Android6以前は、アプリ画面の一上のActionBarにActionModeのメニューが表示されますが、Android6からiOSのようにFloating Action Modeになり、浮かんでいるコンテキストメニューが表示されるようになりました。

独自のActionModeメニューの定義

ActionModeが表示された時に、OSにより表示されるメニューをクリアして、独自なメニューに入れ替える。

  • メニュー定義
mymenu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_menu_item_search"
        android:icon="@android:drawable/ic_menu_search"
        android:onClick="onActionItemClicked"
        android:orderInCategory="1"
        app:showAsAction="always"
        android:title="@string/action_menu_item_search">
    </item>
    <item
        android:id="@+id/action_menu_item_copy"
        android:icon="@drawable/ic_menu_copy"
        android:onClick="onActionItemClicked"
        android:orderInCategory="2"
        app:showAsAction="always"
        android:title="@string/action_menu_item_copy">
    </item>


</menu>
  • ActionMode起動メソッド
MyActivity
    @Override
    public void onActionModeStarted(ActionMode mode) {
        if (actionMode == null) {
            actionMode = mode;
            Menu menu = mode.getMenu();
            menu.clear();

            // Inflate your own menu items
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.mymenu, menu);
        }
        super.onActionModeStarted(mode);
    }

Android7 以降の場合InflateExceptionが発生

Android6までは問題なく動きましたが、Android7.0とAndroid7.1.1でInflateExceptionが発生しました。

エラースタック
W/System.err: android.view.InflateException: Couldn't resolve menu item onClick handler onActionItemClicked in class android.app.ContextImpl
W/System.err:     at android.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:242)
W/System.err:     at android.view.MenuInflater$MenuState.setItem(MenuInflater.java:460)
W/System.err:     at android.view.MenuInflater$MenuState.addItem(MenuInflater.java:494)
W/System.err:     at android.view.MenuInflater.parseMenu(MenuInflater.java:190)
W/System.err:     at android.view.MenuInflater.inflate(MenuInflater.java:111)
W/System.err:     at com.myapp.joke.MyActivity.onActionModeStarted(MyActivity.java:349)
W/System.err:     at android.support.v7.view.WindowCallbackWrapper.onActionModeStarted(WindowCallbackWrapper.java:158)
W/System.err:     at com.android.internal.policy.DecorView.startActionMode(DecorView.java:868)
W/System.err:     at com.android.internal.policy.DecorView.startActionModeForChild(DecorView.java:816)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.ViewGroup.startActionModeForChild(ViewGroup.java:828)
W/System.err:     at android.view.View.startActionMode(View.java:5828)
W/System.err:     at org.chromium.content.browser.ContentViewCore.startFloatingActionMode(ContentViewCore.java:2253)
W/System.err:     at org.chromium.content.browser.ContentViewCore.startActionMode(ContentViewCore.java:2238)
W/System.err:     at org.chromium.content.browser.ContentViewCore.showSelectActionMode(ContentViewCore.java:2213)
W/System.err:     at org.chromium.content.browser.ContentViewCore.onSelectionEvent(ContentViewCore.java:2319)
W/System.err:     at org.chromium.android_webview.AwContents.nativeOnDraw(Native Method)
W/System.err:     at org.chromium.android_webview.AwContents.access$4700(AwContents.java:98)
W/System.err:     at org.chromium.android_webview.AwContents$AwViewMethodsImpl.onDraw(AwContents.java:2980)
W/System.err:     at org.chromium.android_webview.AwContents.onDraw(AwContents.java:1307)
W/System.err:     at com.android.webview.chromium.WebViewChromium.onDraw(WebViewChromium.java:1697)
W/System.err:     at android.webkit.WebView.onDraw(WebView.java:2534)
W/System.err:     at android.view.View.draw(View.java:17185)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16167)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3711)
W/System.err:     at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3691)
W/System.err:     at android.view.View.updateDisplayListIfDirty(View.java:16130)
W/System.err:     at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:648)
W/System.err:     at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:654)
W/System.err:     at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:762)
W/System.err:     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2800)
W/System.err:     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2608)
W/System.err:     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2215)
W/System.err:     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1254)
W/System.err:     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6337)
W/System.err:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:874)
W/System.err:     at android.view.Choreographer.doCallbacks(Choreographer.java:686)
W/System.err:     at android.view.Choreographer.doFrame(Choreographer.java:621)
W/System.err:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:860)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:751)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err:     at android.os.Looper.loop(Looper.java:154)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6119)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
W/System.err: Caused by: java.lang.NoSuchMethodException: onActionItemClicked [interface android.view.MenuItem]
W/System.err:     at java.lang.Class.getMethod(Class.java:1981)
W/System.err:     at java.lang.Class.getMethod(Class.java:1637)
W/System.err:     at android.view.MenuInflater$InflatedOnMenuItemClickListener.<init>(MenuInflater.java:240)
W/System.err:   ... 71 more

例外の原因

WebViewから生成されるFloatingActionModeのコンテキストがMyActivityではなく、DecorViewになっています。DecorViewのベースコンテキストがApplicationであります。Menu定義ファイルに設定されたonclickメソッド「onActionItemClicked」がMyActivityに定義されていますが、DecorViewからベースコンテキストを探しても見つからないため、InflateExceptionがスローされます。

臨時対応策

他の良い方法があるかもしれませんが、臨時対策として、リフレクションでMenuInflaterのコンテキストをMyActivityに入れ替えます。

MyActivity修正後
    public void onActionModeStarted(ActionMode mode) {
        if (actionMode == null) {
            actionMode = mode;
            Menu menu = mode.getMenu();
            menu.clear();

            // Inflate your own menu items
            MenuInflater inflater = mode.getMenuInflater();
            if(Build.VERSION.SDK_INT >= 24) {
                try {
                    Field field = FieldUtils.getField(inflater.getClass(), "mContext", true);
                    field.set(inflater, this);
                } catch (IllegalAccessException e) {
                }
            }
            inflater.inflate(R.menu.mymenu, menu);
        }
        super.onActionModeStarted(mode);
    }
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0