1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
      <output id="hzk7v"></output>
    2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
    3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>

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

       更新时间£º2019年02月28日 16:06:00   作者?#33322;¹Ê来?   我要评论

      Android EditText控件是经常使用的控件£¬下面这篇文章主要给大家介绍了关于Android中EditText长按菜单中分享功能的隐藏方法£¬文中通过示例代码介绍的非常详细£¬对大家的学习或者工作具有一定的参考学习价值£¬需要的朋友们下面来一起学习学习吧

      常见的EditText长按菜单如下

      oppo

      小米

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

      最终解决方案

      这里先说下最终解决方案

      像华为/oppo等手机£¬该菜单?#23548;?#26159;谷歌系统的即没有改过源代码£¬像小米的菜单则是自定义£¬该部分的源代码改动过¡£
      两方面修改£º

      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()) {
       startDragAndDrop();//开始拖动
       ¡¤¡¤¡¤
       } 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;
       }

      ?#23548;?#26159;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?#23548;?#19978;是PopWindow的View¡£ 即选中的图标?#23548;?#19978;是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£¨£©返回的?#23548;?#19978;是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]);
       }
       } 
       ¡¤¡¤¡¤
       }
       }

      到此我们知道选中的图标即下面红框内的?#23548;?#19978;popWindow展示

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

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

      updateFloatingToolbarVisibility(ev)真相在这里£¬该方法进行悬浮菜单栏的展示
      经过进一步查找£¬可以看?#20132;?#35843;用下面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实现长图展开与收起效果

        这篇文章主要为大家详细介绍了Android实现长图展开与收起效果£¬具有一定的参考价值£¬?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
        2018-09-09
      • Android Theme以及解决启动黑屏的方法详解

        Android Theme以及解决启动黑屏的方法详解

        这篇文章主要给大家介绍了关于Android Theme以及解决启动黑屏的相关资?#24076;?#25991;中通过示例代码介绍的非常详细£¬对大家的学习或者工作具有一定的参考学习价值£¬需要的朋友们下面随着小编来一起学习学习吧
        2018-08-08
      • android使用多线程更新ui示例分享

        android使用多线程更新ui示例分享

        在Android平台中多线程应用很广泛£¬在UI更新¡¢游戏开发和耗时处理(网络通信等)等方面都需要多线程,下面是一个在线程中更新UI的代码
        2014-01-01
      • 创建Android库的方法及Android .aar文件用法小结

        创建Android库的方法及Android .aar文件用法小结

        本文给大家介绍了创建Android库的方法及Android中 .aar文件生成方法与用法详解£¬涉及到创建库模块操作步骤及开发注意事项£¬需要的朋友参考下吧
        2017-12-12
      • Android编程之SharedPreferences文件存储操作实例分析

        Android编程之SharedPreferences文件存储操作实例分析

        这篇文章主要介绍了Android编程之SharedPreferences文件存储操作方法,实例分析了SharedPreferences文件操作的相关技巧,非常具有实用价值,需要的朋友可以参考下
        2015-04-04
      • RecyclerView Adapter辅助类详解及示例代码

        RecyclerView Adapter辅助类详解及示例代码

        本文主要介绍RecyclerView Adapter辅助类的知识£¬这里整理了详细资料及简单示例代码£¬帮助大家学习这部分的内容£¬有兴趣的小伙伴可以参考下
        2016-09-09
      • Flutter Android端启动白屏问题的解决

        Flutter Android端启动白屏问题的解决

        Flutter 应用在 Android 端上启动时会有一段很明显的白屏现象£¬白屏的时长由设备的性能决定£¬设备性能越差£¬白屏时间越长¡£这篇文章主要介绍了Flutter Android端启动白屏问题的解决¡£?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
        2018-07-07
      • Android使用动画设置ProgressBar进度的方法

        Android使用动画设置ProgressBar进度的方法

        这篇文章主要为大家详细介绍了Android使用动画设置ProgressBar进度的方法£¬具有一定的参考价值£¬?#34892;?#36259;的小伙伴们可以参?#23478;?#19979;
        2018-01-01
      • Ubuntu Android源码以及内核下载与编译

        Ubuntu Android源码以及内核下载与编译

        本文主要介绍Android源码的下载和编译£¬这里整理了相关资料及如何下载和编译的详细步骤£¬有需要的小伙伴可以参考下
        2016-09-09
      • Android使用VideoView播放本地视频和网络视频的方法

        Android使用VideoView播放本地视频和网络视频的方法

        本文将讲解如何使用Android视频播放器VideoView来播放本地视频和网络视频£¬实现起来还是比较简单的£¬有需要的可以参考借鉴¡£
        2016-08-08

      最新评论

      3dÊÔ»úºÅÖвÊÍø

        1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
          <output id="hzk7v"></output>
        2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
        3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>

            1. <output id="hzk7v"><pre id="hzk7v"><address id="hzk7v"></address></pre></output>
              <output id="hzk7v"></output>
            2. <nav id="hzk7v"><i id="hzk7v"><em id="hzk7v"></em></i></nav>
            3. <listing id="hzk7v"><delect id="hzk7v"><em id="hzk7v"></em></delect></listing>