Android EditText长按菜单中分享功能的隐藏方法

发布时间:2019-08-08 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Android EditText长按菜单中分享功能的隐藏方法脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

常见的EdITText长按菜单如下

Android EditText长按菜单中分享功能的隐藏方法

oppo

Android EditText长按菜单中分享功能的隐藏方法

小米

需求是隐藏掉其中的分享/搜索功能,禁止将内容分享到其他应用。

最终解决方案

这里先说下最终解决方案

华为/oppo等手机,该菜单实际是谷歌系统的即没有改过代码,像小米的菜单则是自定义,该部分的源代码改动过。
两方面修改:

1.谷歌系统自带的 通过 EditText.setCustomSelectionActionModeCallback()方法设置自定义的选中后动作模式接口,只保留需要的菜单项

代码如下

  editText.customSelectionActionModeCallback = object : ActionMode.Callback {  override fun onCreateActionMode(  mode: ActionMode?,  menu: Menu?  ): Boolean {  menu?.let {  val size = menu.size()  for (i in size - 1 downTo 0) {  val item = menu.getItem(i)  val itemId = item.itemId  //只保留需要的菜单项   if (itemId != andROId.R.id.cut  && itemId != android.R.id.copy  && itemId != android.R.id.selectAll  && itemId != android.R.id.paste  ) {  menu.removeitem(itemId)  }  }  }  return true  }   override fun onActionItemClicked(  mode: ActionMode?,  item: MenuItem?  ): Boolean {  return false  }   override fun onPRepareActionMode(  mode: ActionMode?,  menu: Menu?  ): Boolean {  return false  }   override fun onDestroyActionMode(mode: ActionMode?) {  }  }

2.小米等手机自定义菜单无法进行隐藏,可以是分享、搜索等功能失效,即在BaseActivity的startActivityForResult中进行跳转拦截,如果是调用系统的分享/搜索功能,则不允许跳转

  override fun startActivityForResult(  intent: Intent?,  requestCode: Int  ) {  if (!canStart(intent)) return  suPEr.startActivityForResult(intent, requestCode)  }   @SupPressLint("RestrictedApi")  @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)  override fun startActivityForResult(  intent: Intent?,  requestCode: Int,  options: Bundle?  ) {  if (!canStart(intent)) return  super.startActivityForResult(intent, requestCode, options)  }   private fun canStart(intent: Intent?): Boolean {  return intent?.let {  val action = it.action  action != Intent.ACTION_CHOOSER//分享  && action != Intent.ACTION_VIEW//跳转到浏览器  && action != Intent.ACTION_SEArch//搜索  } ?: false  }

如果以上不满足要求,只能通过自定义长按菜单来实现自定义的菜单栏。

解决思路(RTFSC)

分析源码菜单的创建和点击事件

既然是长按松手后弹出的,应该在onTouchEvent中的ACTION_UP事件或者在PerforMLongClick中,从两方面着手
先看perfomLongEvent EditText没有实现 去它的父类TextView中查找

 TextView.java  public boolean performLongClick() {  ・・・省略部分代码  if (mEditor != null) {  handled |= mEditor.performLongClick(handled);  mEditor.mIsBeingLongClicked = false;  }   ・・・省略部分代码  return handled;  }

可看到调用了 mEditor.performLongClick(handled)方法

 Editor.java   public boolean performLongClick(boolean handled) {  if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY)  && mInsertionControllerEnabled) {  final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,  mLastDownPositionY);//获取当前松手时的偏移量  Selection.setSelection((Spannable) mTextView.getText(), offset);//设置选中的内容  getInsertionController().show();//插入控制器展示  mIsInsertionActionModeStartPending = true;  handled = true;  ・・・  }  if (!handled && mTextActionMode != null) {  if (touchPositionIsInSelection()) {  start@R_304_2514@AndDrop();//开始拖动  ・・・  } else {  stopTextActionMode();  selectcurrentWordAndStartDrag();//选中当前单词并且开始拖动  ・・・  }  handled = true;  }  if (!handled) {  handled = selectCurrentWordAndStartDrag();//选中当前单词并且开始拖动  ・・・  }  }   return handled;  }

从上面代码分析

1.长按时会先选中内容 Selection.setSelection((Spannable) mTextView.getText(), offset)

2.显示插入控制器  getInsertionController().show()

3.开始拖动/选中单词后拖动 startDragAndDrop()/ selectCurrentWordAndStartDrag()

看着很像了

看下第二步中展示的内容

 Editor.java -> InsertionPointCursorController   public void show() {  getHandle().show();  if (mSelectionModifierCursorController != null) {  mSelectionModifierCursorController.hide();  }  }   ・・・  private InsertionHandleView getHandle() {  if (mSelectHandlecenter == null) {  mSelectHandleCenter = mTextView.getContext().getDrawable(  mTextView.mTextSelectHandleRes);  }  if (mHandle == null) {  mHandle = new InsertionHandleView(mSelectHandleCenter);  }  return mHandle;  }

实际是InsertionHandleView 执行了show方法。  查看其父类HandlerView的构造方法

  private HandleView(Drawable drawableLtr, Drawable drawableRtl, final int id) {  super(mTextView.getContext());  ・・・  mContainer = new PopupWindow(mTextView.getContext(), null,  com.android.internal.R.attr.textSelectHandleWindowStyle);  ・・・  mContainer.setContentView(this);  ・・・  }

由源码可看出 HandlerView实际上是PopWindow的View。 即选中的图标实际上是popwidow
看源码可看出HandleView有两个实现类 InsertionHandleView  和SelectionHandleView 由名字可看出一个是插入的,一个选择的 看下HandleView的show方法

 Editor.java ->HandleView   public void show() {  if (isShowing()) return;  getPositionListener().addSubscriber(this, true );  // Make sure the offset is always considered new, even when focusing at same position  mPreviousOffset = -1;  positionAtCursorOffset(getCurrentCursorOffset(), false, false);  }

看下positionAtCursorOffset方法

 Editor.java ->HandleView    protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition,  boolean FromTouchScreen) {  ・・・  if (offsetChanged || forceUpdatePosition) {  if (offsetChanged) {  updateSelection(offset);  ・・・  }  ・・・  }  }

里面有一个updateSelection更新选中的位置,该方法会导致EditText重绘,再看show方法的getPositionListener().addSubscriber(this, true )

getPositionListener()返回的实际上是ViewTreeObserver.OnPreDrawListener的实现类PositionListener
重绘会调用OnPreDraw的方法

 Editor.java-> PositionListener    @Override  public boolean onPreDraw() {  ・・・  for (int i = 0; i < MAXIMUM_NUMBER_OF_LISTENERS; i++) {  ・・・  positionListener.updatePosition(mPositionX, mPositionY,  mPositionHasChanged, mScrollHasChanged);  ・・・  }  ・・・  return true;  }

调用了positionListener.updatePosition方法, positionListener这个实现类对应的是HandlerView

重点在HandleView的updatePosition方法,该方法进行popWindow的显示和更新位置

看一下该方法的实现

 Editor.java ->HandleView   @Override  public void updatePosition(int parentPositionX, int parentPositionY,  boolean parentPositionChanged, boolean parentScrolled) {  ・・・  if (isShowing()) {  mContainer.update(pts[0], pts[1], -1, -1);  } else {  mContainer.showAtLocation(mTextView, Gravity.NO_GRAVITY, pts[0], pts[1]);  }  }   ・・・  }  }

到此我们知道选中的图标即下面红框内的实际上popWindow展示

Android EditText长按菜单中分享功能的隐藏方法

点击选中的图标可以展示菜单,看下HandleView的onTouchEvent方法

 Editor.java ->HandleView  @Override  public boolean onTouchEvent(MotionEvent ev) {  updateFloatingToolbarVisibility(ev);  ・・・  }

updateFloatingToolbarVisibility(ev)真相在这里,该方法进行悬浮菜单栏的展示
经过进一步查找,可以看到会调用下面SelectionActionModeHelper的这个方法

 SelectionActionModeHelper.java   public void invalidateActionModeAsync() {  cancelAsyncTask();  if (skipTextClassification()) {  invalidateActionMode(null);  } else {  resetTextClassificationHelper();  mTextClassificationAsyncTask = new TextClassificationAsyncTask(   mTextView,   mTextClassificationHelper.getTimeoutDuration(),   mTextClassificationHelper::classifyText,   this::invalidateActionMode)   .execute();  }  }

会启动一个叫TextClassificationAsyncTask的异步任务,该异步任务最后会执行mEditor.getTextActionMode().invalidate()

  private void invalidateActionMode(@Nullable SelectionResult result) {  ・・・  final ActionMode actionMode = mEditor.getTextActionMode();  if (actionMode != null) {  actionMode.invalidate();  }  ・・・  }

最后看下mTextActionMode 如何在Editor中赋值

 Editor.java   void startInsertionActionMode() {  ・・・  ActionMode.Callback actionModeCallback =  new TextActionModeCallback(false /* hasSelection */);  mTextActionMode = mTextView.startActionMode(  actionModeCallback, ActionMode.TYPE_FLOATING);  ・・・  }

看下mTextView.startActionMode的注释,在View类中,Start an action mode with the given type. 根据给的类型,开启一个动作模式,该模式是一个TYPE_FLOATING模式,菜单的生成就在TextActionModeCallback类中
在TextActionModeCallback的onCreateActionMode方法中

 Editor.java ->TextActionModeCallback   @Override  public boolean onCreateActionMode(ActionMode mode, Menu menu) {  mode.setTitle(null);  mode.setSuBTitle(null);  mode.setTitleOptionalHint(true);  //生成菜单  populateMenuWithItems(menu);   Callback customCallback = getCustomCallback();  if (customCallback != null) {  if (!customCallback.onCreateActionMode(mode, menu)) {   // The custom mode can choose to cancel the action mode, dismiss selection.   Selection.setSelection((Spannable) mTextView.getText(),   mTextView.getSelectionEnd());   return false;  }  }  ・・・  }

生成的菜单的方法populateMenuWithItems(menu)中,生成完菜单会执行自定义的回调getCustomCallback() , 看下该回调如何赋值。

在TextView中

 TextView.java  public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {  createEditorIfNeeded();  mEditor.mCustomSelectionActionModeCallback = actionModeCallback;  }

因此我们可以在自定义回调的onCreateActionMode方法中,删除不需要的菜单项。

但该方法对小米手机无效,小米手机的菜单展示,不是通过startActionMode来展示的。不过可以对菜单中的分享等功能进行禁止跳转,解决方法看最上面

总结

android教程
脚本网站
android studio

脚本宝典总结

以上是脚本宝典为你收集整理的Android EditText长按菜单中分享功能的隐藏方法全部内容,希望文章能够帮你解决Android EditText长按菜单中分享功能的隐藏方法所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。