web-dev-qa-db-ja.com

WebViewテキスト選択にカスタムコンテキストアクションバーを使用する

このGoogleのガイドこのチュートリアル を使用して、独自のコンテキストアクションバーを作成しました。

_private ActionMode.Callback mActionModeCallback = new 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.annotation_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) {
        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.custom_button:
                // do some stuff
                break;
            case R.id.custom_button2:
                // do some other stuff
                break;
            default:
                // This essentially acts as a catch statement
                // If none of the other cases are true, return false
                // because the action was not handled
                return false;
        }
        finish(); // An action was handled, so close the CAB
        return true;
    }

    // Called when the user exits the action mode
    @Override
    public void onDestroyActionMode(ActionMode mode) {
        mActionMode = null;
    }
};
_

このメニューは、ユーザーがテキストを選択したときに表示されるように設計されているため、ネイティブのコピー/貼り付けメニューをオーバーライドします。今私は私の問題に行きます。

テキスト選択用の関数をオーバーライドしているので、LongClickListenerWebViewに追加し、onLongClick(View v)メソッドを実装して、ユーザーがいつ選択したかを検出できるようにしました。

_    myWebView.setOnLongClickListener(new View.OnLongClickListener() {

        @Override
        public boolean onLongClick(View v) {
            if (mActionMode != null) {
                return false;
            }

            mActionMode = startActionMode(mActionModeCallback);
            v.setSelected(true);
            return true;
        }
    });
_

長い間クリックすると、カスタムメニューが表示されますが、テキストが強調表示されていません。
テキスト選択機能が必要です。それがなければ、私のメニューは無意味です。

onLongClick(View v)をオーバーライドしながら、Androidが提供するテキスト選択を維持するにはどうすればよいですか?
それが不可能な場合は、どこかでstartActionMode(mActionModeCallback)を呼び出して、テキストが通常どおり選択されるようにできますが、カスタムメニューも表示されますか?
これらのどちらも可能でない場合...助けてください。

22
Sean Beach

そこにIS簡単な方法!以下の更新を参照してください:D


私は この答え に従って提案に従いましたが、オーバーライドされたコードにさらに一致するように少し調整しました。

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

基本的に、これにより、カスタマイズされたCABがAndroid CABの代わりに表示されます。ここで、コールバックを変更して、テキストの強調表示が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;
        }
    }
}

これですべてです。オーバーライドされたMyWebViewstartActionModeを使用している限り、ネイティブCAB(WebViewの場合はコピー/貼り付けメニュー)を取得する方法がないことに注意してください。そのような動作を実装することは可能かもしれませんが、それはこのコードが機能する方法ではありません。


更新:

このソリューションではActionModeに対する制御が少なくなりますが、上記のソリューションよりはるかに少ないコードで済みます。

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

ここにあなたが始めるためのメニューの例があります:

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

それでおしまい!完了です!これでカスタムメニューが表示され、選択を気にする必要がなくなり、ActionModeライフサイクルを気にする必要がほとんどなくなります。

これは、親全体WebViewを占有するActivityとほぼ完璧に機能します。 View内に一度に複数のActivityが存在する場合、どれだけうまく機能するかわかりません。その場合、おそらく多少の調整が必要になります。

35
Sean Beach

私が同様のことをした方法は、onTouchListenerをオーバーライドするだけで、GestureDetectorを呼び出してWebViewが長押しされたことを検出し、そこから私がやりたいことを実行することでした。以下は、WebViewでテキスト選択を犠牲にすることなく長押しイベントをキャッチできるサンプルコードです。うまくいけば、これが役立ちます。

@Override
protected void onCreate(Bundle savedInstanceState) {
    WebView mWebView = (WebView) findViewById(R.id.myWebView);
    GestureDetector mGestureDetector = new GestureDetector(this, new CustomGestureListener());
    mWebView.setOnTouchListener(new OnTouchListener(){
        @Override
        public boolean onTouch(View view, MotionEvent arg1) {

            //Suggestion #1 - this just lets the touch to be handled by the system but allows you to detect long presses
            mGestureDetector.onTouchEvent(arg1);
            return false;

            //Suggestion #2 - this code will only let the touch be handled by the system if you don't detect a long press
            return mGestureDetector.onTouchEvent(arg1);
        }
    });
}

private class CustomGestureListener extends SimpleOnGestureListener {

    @Override
    public void onLongPress(MotionEvent e) {
        //do stuff
    }

}
1
anthonycr