久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

Android EditText長(zhǎng)按菜單中分享功能的隱藏方法

Android EditText控件是經(jīng)常使用的控件,下面這篇文章主要給大家介紹了關(guān)于Android中EditText長(zhǎng)按菜單中分享功能的隱藏方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有

常見(jiàn)的EditText長(zhǎng)按菜單如下

oppo

小米

需求是隱藏掉其中的分享/搜索功能,禁止將內(nèi)容分享到其他應(yīng)用。

最終解決方案

這里先說(shuō)下最終解決方案

像華為/oppo等手機(jī),該菜單實(shí)際是谷歌系統(tǒng)的即沒(méi)有改過(guò)源代碼,像小米的菜單則是自定義,該部分的源代碼改動(dòng)過(guò)。
兩方面修改:

1.谷歌系統(tǒng)自帶的 通過(guò) EditText.setCustomSelectionActionModeCallback()方法設(shè)置自定義的選中后動(dòng)作模式接口,只保留需要的菜單項(xiàng)

代碼如下


 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
 //只保留需要的菜單項(xiàng) 
 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.小米等手機(jī)自定義菜單無(wú)法進(jìn)行隱藏,可以是分享、搜索等功能失效,即在BaseActivity的startActivityForResult中進(jìn)行跳轉(zhuǎn)攔截,如果是調(diào)用系統(tǒng)的分享/搜索功能,則不允許跳轉(zhuǎn)


 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//跳轉(zhuǎn)到瀏覽器
 && action != Intent.ACTION_SEARCH//搜索
 } ?: false
 }

如果以上不滿(mǎn)足要求,只能通過(guò)自定義長(zhǎng)按菜單來(lái)實(shí)現(xiàn)自定義的菜單欄。

解決思路(RTFSC)

分析源碼菜單的創(chuàng)建和點(diǎn)擊事件

既然是長(zhǎng)按松手后彈出的,應(yīng)該在onTouchEvent中的ACTION_UP事件或者在performLongClick中,從兩方面著手
先看perfomLongEvent EditText沒(méi)有實(shí)現(xiàn) 去它的父類(lèi)TextView中查找


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

 ···省略部分代碼
 return handled;
 }

可看到調(diào)用了 mEditor.performLongClick(handled)方法


Editor.java

 public boolean performLongClick(boolean handled) {
 if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY)
 && mInsertionControllerEnabled) {
 final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
 mLastDownPositionY);//獲取當(dāng)前松手時(shí)的偏移量
 Selection.setSelection((Spannable) mTextView.getText(), offset);//設(shè)置選中的內(nèi)容
 getInsertionController().show();//插入控制器展示
 mIsInsertionActionModeStartPending = true;
 handled = true;
 ···
 }
 if (!handled && mTextActionMode != null) {
 if (touchPositionIsInSelection()) {
 startDragAndDrop();//開(kāi)始拖動(dòng)
 ···
 } else {
 stopTextActionMode();
 selectCurrentWordAndStartDrag();//選中當(dāng)前單詞并且開(kāi)始拖動(dòng)
 ···
 }
 handled = true;
 }
 if (!handled) {
 handled = selectCurrentWordAndStartDrag();//選中當(dāng)前單詞并且開(kāi)始拖動(dòng)
 ···
 }
 }

 return handled;
 }

從上面代碼分析

1.長(zhǎng)按時(shí)會(huì)先選中內(nèi)容 Selection.setSelection((Spannable) mTextView.getText(), offset)

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

3.開(kāi)始拖動(dòng)/選中單詞后拖動(dòng) startDragAndDrop()/ selectCurrentWordAndStartDrag()

看著很像了

看下第二步中展示的內(nèi)容


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;
 }

實(shí)際是InsertionHandleView 執(zhí)行了show方法。  查看其父類(lèi)HandlerView的構(gòu)造方法


 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實(shí)際上是PopWindow的View。 即選中的圖標(biāo)實(shí)際上是popwidow
看源碼可看出HandleView有兩個(gè)實(shí)現(xiàn)類(lèi) InsertionHandleView  和SelectionHandleView 由名字可看出一個(gè)是插入的,一個(gè)選擇的 看下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);
 ···
 }
 ···
 }
 }

里面有一個(gè)updateSelection更新選中的位置,該方法會(huì)導(dǎo)致EditText重繪,再看show方法的getPositionListener().addSubscriber(this, true )

getPositionListener()返回的實(shí)際上是ViewTreeObserver.OnPreDrawListener的實(shí)現(xiàn)類(lèi)PositionListener
重繪會(huì)調(diào)用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;
 }

調(diào)用了positionListener.updatePosition方法, positionListener這個(gè)實(shí)現(xiàn)類(lèi)對(duì)應(yīng)的是HandlerView

重點(diǎn)在HandleView的updatePosition方法,該方法進(jìn)行popWindow的顯示和更新位置

看一下該方法的實(shí)現(xiàn)


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]);
 }
 } 
 ···
 }
 }

到此我們知道選中的圖標(biāo)即下面紅框內(nèi)的實(shí)際上popWindow展示

點(diǎn)擊選中的圖標(biāo)可以展示菜單,看下HandleView的onTouchEvent方法


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

updateFloatingToolbarVisibility(ev)真相在這里,該方法進(jìn)行懸浮菜單欄的展示
經(jīng)過(guò)進(jìn)一步查找,可以看到會(huì)調(diào)用下面SelectionActionModeHelper的這個(gè)方法


SelectionActionModeHelper.java

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

會(huì)啟動(dòng)一個(gè)叫TextClassificationAsyncTask的異步任務(wù),該異步任務(wù)最后會(huì)執(zhí)行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的注釋?zhuān)赩iew類(lèi)中,Start an action mode with the given type. 根據(jù)給的類(lèi)型,開(kāi)啟一個(gè)動(dòng)作模式,該模式是一個(gè)TYPE_FLOATING模式,菜單的生成就在TextActionModeCallback類(lèi)中
在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)中,生成完菜單會(huì)執(zhí)行自定義的回調(diào)getCustomCallback() , 看下該回調(diào)如何賦值。

在TextView中


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

因此我們可以在自定義回調(diào)的onCreateActionMode方法中,刪除不需要的菜單項(xiàng)。

但該方法對(duì)小米手機(jī)無(wú)效,小米手機(jī)的菜單展示,不是通過(guò)startActionMode來(lái)展示的。不過(guò)可以對(duì)菜單中的分享等功能進(jìn)行禁止跳轉(zhuǎn),解決方法看最上面

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)html5模板網(wǎng)的支持。

【網(wǎng)站聲明】本站部分內(nèi)容來(lái)源于互聯(lián)網(wǎng),旨在幫助大家更快的解決問(wèn)題,如果有圖片或者內(nèi)容侵犯了您的權(quán)益,請(qǐng)聯(lián)系我們刪除處理,感謝您的支持!

相關(guān)文檔推薦

這篇文章主要介紹了Android開(kāi)發(fā)之TabHost選項(xiàng)卡及相關(guān)疑難解決方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android開(kāi)發(fā)中TabHost選項(xiàng)卡的常見(jiàn)用法以及相關(guān)疑難問(wèn)題解決方法,需要的朋友可以參考下
這篇文章主要介紹了Android TabHost選項(xiàng)卡標(biāo)簽圖標(biāo)始終不出現(xiàn)的解決方法,涉及Android界面布局相關(guān)屬性與狀態(tài)設(shè)置操作技巧,需要的朋友可以參考下
這篇文章主要介紹了Android開(kāi)發(fā)之Notification手機(jī)狀態(tài)欄通知用法,結(jié)合實(shí)例形式分析了Android Notification手機(jī)狀態(tài)欄通知的常見(jiàn)函數(shù)、功能及使用技巧,需要的朋友可以參考下
這篇文章主要介紹了Android開(kāi)發(fā)實(shí)現(xiàn)模仿微信小窗口功能,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)微信風(fēng)格Dialog對(duì)話(huà)框窗口相關(guān)功能與布局操作技巧,需要的朋友可以參考下
這篇文章主要介紹了Android開(kāi)發(fā)之PopupWindow創(chuàng)建彈窗、對(duì)話(huà)框的方法,結(jié)合實(shí)例形式詳細(xì)分析了Android使用PopupWindow創(chuàng)建對(duì)話(huà)框相關(guān)操作技巧,需要的朋友可以參考下
這篇文章主要介紹了Android開(kāi)發(fā)之DatePickerDialog、TimePickerDialog時(shí)間日期對(duì)話(huà)框用法,結(jié)合實(shí)例形式分析了Android使用DatePickerDialog、TimePickerDialog顯示日期時(shí)間相關(guān)操作技巧,需要的朋友可以參考
主站蜘蛛池模板: 国产激情在线 | 免费美女网站 | 在线视频 欧美日韩 | 精品综合久久久 | 成年人黄色一级毛片 | 日韩在线一区二区三区 | 精精国产xxxx视频在线野外 | 欧美日韩综合一区 | 国产亚洲精品一区二区三区 | 日本一区二区三区视频在线 | 亚洲三级在线观看 | 免费成人在线网 | 日韩五月天 | 免费av电影网站 | 看av电影 | 日本免费视频在线观看 | 亚洲国产成人精 | 久久国产成人 | 精品福利一区 | 一区二区三区回区在观看免费视频 | 精品国产精品国产偷麻豆 | 日韩在线免费 | 国产 欧美 日韩 一区 | 日韩视频一区二区 | 亚洲午夜久久久 | 久久综合久色欧美综合狠狠 | 黄色片a级 | 羞羞视频网站免费看 | 在线看91 | 中文字幕在线一区二区三区 | 极品销魂美女一区二区 | 宅男伊人 | 三级成人在线观看 | 午夜精品网站 | 精品一区二区免费视频 | 中文字幕视频免费 | 亚洲欧美v| 一区二区三区四区免费在线观看 | 亚洲电影免费 | 亚洲精品久久久久久久久久久久久 | 欧美精品一二三 |