基本的android的网络文字选择菜单如下图所示。它有复制,共享,全选,网络搜索等选项。
我想过度使用这个菜单并希望它们作为我自己的菜单列表,如“标记颜色”,“标记为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但没有得到正确的结果。
我做了什么?
我扩展了Android的WebView,我想支持最小的SDK 19.当我执行长按时我得到了长按事件,但我无法获得这样的菜单创建api调用。
答案 0 :(得分:6)
您需要覆盖活动的操作菜单
您可以阅读的更多信息:https://developer.android.com/guide/topics/ui/menus.html
如何覆盖:
NSInteger numberOfLines = [self lineCountForText:@"YOUR TEXT"];
- (int)lineCountForText:(NSString *) text
{
UIFont *font = [UIFont systemFontOfSize: 15.0];
int width=Your_LabelWidht;
CGRect rect = [text boundingRectWithSize:CGSizeMake(width, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil];
return ceil(rect.size.height / font.lineHeight);
}
高亮
@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);
}
答案 1 :(得分: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
}
});
}
mode.finish();
return true;
});
super.onActionModeStarted(mode);
}
我已经在android 21上面测试了它,这可以处理动作模式菜单点击,而mode.getMenuInflater()。inflate(...)不能这样做。
答案 2 :(得分:2)
此解决方案不依赖于Activity的Action模式,适用于所有Android平台
我试图给出答案,但它超出了字符数限制,所以我放了一些代码
参考链接1用于在Web视图上进行选择
参考链接2用于制作Web视图标记
上述两个链接都发挥了重要作用,并由一些优秀的开发人员开发。首先需要对参考链接中的TextSelectionSupport类进行一些研究1.我在TextSelectionSupport类中定制了两行代码,以在此处获取Selection Listener中的选择矩形。
从此处克隆示例项目 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);
}
}
自定义弹出菜单XML类似于机器人智能文本选择(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>
输出屏幕截图
答案 3 :(得分:1)
我认为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;
}
}
}
这就是它的全部。请注意,只要您将MyWebView与重写的startActionMode一起使用,就无法获得本机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生命周期。
使用占用整个父Activity的WebView几乎完美无瑕。如果您的活动中一次有多个视图,我不确定它的效果如何。在这种情况下,可能需要进行一些调整。