Android隐藏EditText长按菜单中分享功能探索
常見的EditText長按菜單如下
oppo小米
需求是隱藏掉其中的分享/搜索功能,禁止將內容分享到其他應用。
最終解決方案
這里先說下最終解決方案
像華為/oppo等手機,該菜單實際是谷歌系統的即沒有改過源代碼,像小米的菜單則是自定義,該部分的源代碼改動過。
兩方面修改:
1.谷歌系統自帶的 通過 EditText.setCustomSelectionActionModeCallback()方法設置自定義的選中后動作模式接口,只保留需要的菜單項
代碼如下
2.小米等手機自定義菜單無法進行隱藏,可以是分享、搜索等功能失效,即在BaseActivity的startActivityForResult中進行跳轉攔截,如果是調用系統的分享/搜索功能,則不允許跳轉
override fun startActivityForResult(intent: Intent?,requestCode: Int) {if (!canStart(intent)) returnsuper.startActivityForResult(intent, requestCode)}@SuppressLint("RestrictedApi")@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)override fun startActivityForResult(intent: Intent?,requestCode: Int,options: Bundle?) {if (!canStart(intent)) returnsuper.startActivityForResult(intent, requestCode, options)}private fun canStart(intent: Intent?): Boolean {return intent?.let {val action = it.actionaction != Intent.ACTION_CHOOSER//分享&& action != Intent.ACTION_VIEW//跳轉到瀏覽器&& action != Intent.ACTION_SEARCH//搜索} ?: false} 復制代碼如果以上不滿足要求,只能通過自定義長按菜單來實現自定義的菜單欄。
解決思路(RTFSC)
分析源碼菜單的創建和點擊事件
既然是長按松手后彈出的,應該在onTouchEvent中的ACTION_UP事件或者在performLongClick中,從兩方面著手
先看perfomLongEvent EditText沒有實現 去它的父類TextView中查找
可看到調用了 mEditor.performLongClick(handled)方法
Editor.javapublic 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()
看著很像了
看下第二步中展示的內容
實際是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方法
看下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的方法
調用了positionListener.updatePosition方法, positionListener這個實現類對應的是HandlerView
重點在HandleView的updatePosition方法,該方法進行popWindow的顯示和更新位置
看一下該方法的實現
到此我們知道選中的圖標即下面紅框內的實際上popWindow展示
點擊選中的圖標可以展示菜單,看下HandleView的onTouchEvent方法
Editor.java ->HandleView@Overridepublic boolean onTouchEvent(MotionEvent ev) {updateFloatingToolbarVisibility(ev);···} 復制代碼updateFloatingToolbarVisibility(ev)真相在這里,該方法進行懸浮菜單欄的展示 經過進一步查找,可以看到會調用下面SelectionActionModeHelper的這個方法
SelectionActionModeHelper.javapublic 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.javavoid 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方法中
生成的菜單的方法populateMenuWithItems(menu)中,生成完菜單會執行自定義的回調getCustomCallback(), 看下該回調如何賦值。
在TextView中
因此我們可以在自定義回調的onCreateActionMode方法中,刪除不需要的菜單項。
但該方法對小米手機無效,小米手機的菜單展示,不是通過startActionMode來展示的。不過可以對菜單中的分享等功能進行禁止跳轉,解決方法看最上面
總結
以上是生活随笔為你收集整理的Android隐藏EditText长按菜单中分享功能探索的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JDK 是如何判断两个对象是否相同的?判
- 下一篇: 图解JavaScript原型链继承