Hello,大家吼,吾就是那個挖坑不止的郭小喵,不管有沒有想我(˶‾᷄ ⁻̫ ‾᷅˵)的,這次就分享快速實現一個自定義WebView的小控制元件吧,效果如下圖,廢話不多說,我們直接開擼吧。
(PS : ╮(╯▽╰)╭如果你翻一翻我過去的文章,你會發現90%的開場白,前幾個問候語都是一樣的。)
一、前言
** 戳這裡可以去DEMO,來吧 **
相信剛接觸android不久的同志們,在面對產品提出的 :
“自定義WebView頁面中,長按文字的彈出選項、點選選擇後,分享、轉發、收藏選擇文字”複製程式碼
這樣的需求時,第一反應大部分是:這是系統行為,如果實現需要在web端實現。
但是web端實現的侷限性太大,曾經也有過監聽系統貼上板,在使用者點選複製的時候實現其他的邏輯,但是這樣使用者體驗不好,所以自定義WebView中長按的彈出選單,並在點選時返回選中文字的小控制元件閃亮登場┏ (^ω^)=。
二、自定義長按彈出選單
這一步實現其實很簡單,首先建立一個CustomActionWebView繼承系統WebView,然後重寫下面兩個方法。
這兩個方法會在使用者長按選擇web文字時,在彈出選單前被呼叫。它們之間的區別在於,第一個方法的選單彈出方式,指定了預設的type。我們並不關係彈出的item型別是什麼,我們只需要攔截下來ActionMode,然後返回我們自己的自定義ActionMode即可。
@Override
public ActionMode startActionMode(ActionMode.Callback callback) {
ActionMode actionMode = super.startActionMode(callback);
return resolveActionMode(actionMode);
}
@Override
public ActionMode startActionMode(ActionMode.Callback callback, int type)
ActionMode actionMode = super.startActionMode(callback, type);
return resolveActionMode(actionMode);
}複製程式碼
這裡我們所做的事是:
- 1、把原本的actionMode物件儲存到mActionMode中。
- 2、清空原本actionMode中的MenuItem。
- 3、新增我們自定義的item到actionMode中。
- 4、重定義每個menuItem的點選事件。
- 5、在點選事件中通過執行js,獲取選中文字。
- 6、通過上面儲存的 mActionMode,釋放彈出選單(不釋放會記憶體洩漏)。
- 7、返回新填充的actionMode給系統。
/**
* 處理item,處理點選
* @param actionMode
*/
private ActionMode resolveActionMode(ActionMode actionMode) {
if (actionMode != null) {
final Menu menu = actionMode.getMenu();
mActionMode = actionMode;
menu.clear();
for (int i = 0; i < mActionList.size(); i++) {
menu.add(mActionList.get(i));
}
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
menuItem.setOnMenuItemClickListener(new Item.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
getSelectedData((String) item.getTitle());
releaseAction();
return true;
}
});
}
}
mActionMode = actionMode;
return actionMode;
}複製程式碼
三、獲取選中文字
光自定義選單,拿不到選中文字也沒意義,那麼如何獲取選中文字呢?這裡如果不轉個彎,還真會在南牆在撞死( ̄^ ̄)ゞ,所以,我們偉大的js就出現了。
首先,我們自定義一個介面,用於監聽js方法,其中@JavascriptInterface
是關鍵的所在, 在callback中獲取js端返回的資料。
然後將這個介面,在CustomActionWebView中add進去(一般是在初始化和頁面載入完成時都add一次),並指定js端呼叫的介面名稱為“ JSInterface”。(ps:別忘了開始webview的js允許哦。)
public void linkJSInterface() {
addJavascriptInterface(new ActionSelectInterface(this), "JSInterface");
}
···
/**
* js選中的回撥介面
*/
private class ActionSelectInterface {
CustomActionWebView mContext;
ActionSelectInterface(CustomActionWebView c) {
mContext = c;
}
@JavascriptInterface
public void callback(final String value, final String title) {
if(mActionSelectListener != null) {
mActionSelectListener.onClick(title, value);
}
}
}複製程式碼
最後在點選時,通過執行js來獲取web中選中的文字。在上面自定義選單中第5項,點選menu時,執行下方js程式碼,便可以把選中的item和文字,回撥到上面的介面中的callback。
熟悉js的小夥伴已經看出來吧:
- 其實就是定義了一個js的function,然後在webview中執行這個方法。
- 定義的這個名為function getSelectedText()的js方法,有兩個變數:txt和title。
- title是從原生中傳入的item名字,txt是通過window去獲取web中選中的文字。
- 最後回到上面我們註冊的js方法名JSInterface,通過它的callback方法,將文字和name返回到原生程式碼callback中。
- 根據版本不同,執行js方法的介面也不一樣。
/**
* 點選的時候,獲取網頁中選擇的文字,回掉到原生中的js介面
* @param title 傳入點選的item文字,一起通過js返回給原生介面
*/
private void getSelectedData(String title) {
String js = "(function getSelectedText() {" +
"var txt;" +
"var title = "" + title + "";" +
"if (window.getSelection) {" +
"txt = window.getSelection().toString();" +
"} else if (window.document.getSelection) {" +
"txt = window.document.getSelection().toString();" +
"} else if (window.document.selection) {" +
"txt = window.document.selection.createRange().text;" +
"}" +
"JSInterface.callback(txt,title);" +
"})()";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript("javascript:" + js, null);
} else {
loadUrl("javascript:" + js);
}
}複製程式碼
四、最後
既然自定義item實現了,點選和選擇文字返回也實現了,在callback中,你就可以愉悅的收藏,或者分享你所選中的文字啦(◐‿◑),操作一氣呵成,有沒有被驚豔到呢?
如果感興趣的,可以下載demo看下,同時CustomActionWebView也封裝好了遠端依賴,歡迎使用。