基本的なAndroidのWebテキスト選択メニューは、以下の画像に示すとおりです。コピー、共有、すべて選択、ウェブ検索などのオプションがあります。
私はこのメニューを乗り越えて、「マークカラー」、「インプとしてマーク」などの独自のメニューリストとしてそれらを望んでいます。スタックオーバーフローのコンテキストメニューについて利用可能な質問のほとんどを見回します。質問のほとんどはコンテキストメニューに関連していますが、期待どおりの結果が得られません。下の画像のようなメニューが欲しい
選択を実行すると、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 も試してみましたが、適切な結果が得られませんでした。
私がまだやったことは?
Android=のWebViewを拡張し、最低限のSDK 19をサポートしたい.
このソリューションは、アクティビティのアクションモードに依存せず、すべての人に機能しますAndroid platform
回答しようとしましたが、文字数制限を超えているため、コード部分を入れています
Webビューでの選択用の参照リンク1
Webビューマーカーを作成するための参照リンク2
上記の両方のリンクは本当に重要な役割を果たし、一部の素晴らしい開発者によって開発されました。まず、参照リンクからのTextSelectionSupportクラスに関する調査が必要です。ここでは、Selection Listenerで選択範囲の四角形を取得するために、TextSelectionSupportクラスの2行のコードをカスタマイズしました。
ここからサンプルプロジェクトのクローンを作成 https://github.com/ab-cse-2014/WebViewSelection.git
CustomWebViewの実装およびTextSelectionSupportクラスの使用を参照してください。
これはプロジェクトの私のWebビュークラスです
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);
}
}
Androids smart text selection(custom_popup_layout.xml)のようなカスタムポップアップメニュー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>
出力スクリーンショット
アクティビティのアクションメニューを上書きする必要があります
あなたが読むことができるより多くの情報: 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>
あなたが必要なのは、活動中のアクションモードです:
@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
}
});
}
mode.finish();
return true;
});
super.onActionModeStarted(mode);
}
Android 21の上でテストしています。これはアクションモードメニューのクリックを処理でき、mode.getMenuInflater()。inflate(...)はそれを実行できません。
私は here があなたを助けることができると思います。
完全を期すために、問題を修正する方法を次に示します。
私はこの回答に従って提案に従いましたが、オーバーライドされたコードにさらに厳密に一致するように少し調整しました。
パブリッククラス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;
}
}
}
これですべてです。オーバーライドされたstartActionModeでMyWebViewを使用している限り、ネイティブCAB(WebViewの場合はコピー/貼り付けメニュー)を取得する方法がないことに注意してください。そのような動作を実装することは可能かもしれませんが、それはこのコードが機能する方法ではありません。更新:これを行うにははるかに簡単な方法があります!上記の解決策はうまく機能しますが、これは別の簡単な方法です。
このソリューションでは、ActionModeに対する制御が少なくなりますが、上記のソリューションよりはるかに少ないコードで済みます。
パブリッククラスMyActivityは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);
}
}
ここにあなたが始めるためのメニューの例があります:
<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でほぼ完璧に機能します。アクティビティ内に一度に複数のビューがある場合、どれだけうまく機能するかわかりません。その場合は、多少の調整が必要になる可能性があります。