轉載請聲明出處(http://www.cnblogs.com/linguanh/)
目錄
1,測試設備介紹
2,開源項目richeditor及CrossWalk的選擇
3,遇到的bug及其解決方法
4,附加功能彩蛋。
1,測試設備介紹----------------------
測試的機型有 魅藍note2-api 22,小米2A-api 16,三星galaxy I9152-API 17.
上述機型均通過測試,針對它們各自產生的bug我會在第二大點處介紹。
2,開源項目richeditor及CrossWalk的比較---------------------------
關於richeditor,它是一個算是很不錯的webView富文本編輯器,git鏈接:https://github.com/wasabeef/richeditor-android
優點:
1,是輕量級,功能較豐富
2,豐富的功能:
前進、返回、粗體、斜體、字號修改、背景顏色、字體顏色、圖片及超鏈接插入,其中圖片不含有其它功能,例如沒有帶有點擊看大圖,刪除等。
3,接口豐富,嵌入和調用極其方便。
缺點:
兼容性差、bug多、二次開發極難!體現在:
1,在上面所列機型里面都有一個共同的bug,插入圖片后,如果通過 javaScript 設置點擊事件,在第一次進入該頁面的時候,所有webView圖片的點擊都能響應,此時如果用戶點擊返回,finish當前頁面,再次進入該頁面后,所有圖片點擊事件失效,這個bug我無法解決,詭異地毫無人性,嘗試過注銷jsResult,但是無效,手動銷毀webView及撤銷等所有緩存設置都沒效。是么時候再有效?退出當前APP,重新進入就有效,然后往事重現。
2,在小米2A-api 16上測試,無法刪除通過軟鍵盤刪除鍵刪除圖片標簽,這個問題很粗!還一個是,如果你需要在接口 OnTextChange 里面loadUrl的話,那么就會,每輸入一次鍵值,每輸入一個字符,軟鍵盤隱藏一次,點擊再彈起,輸入一個字符又隱藏,簡直毀三觀。
3,因為它的所有實現,幾乎都是javaScript 注入,你要改,必須要會點javaScript,可能會一點還不夠。
接下來是CrossWalk,它和上面的不同,它不是一個僅僅只是重寫一個 WebView 那么簡單,它是獨立出來的一個瀏覽器,下載等所有在他們官網:https://crosswalk-project.org/ ,看到這,你或許心里默想,這明明講的是文本編輯器,突然變成瀏覽器了?留意我上面說到 richeditor 所產生到的一些bug,richeditor 是基於android自帶瀏覽器上面搞的,早期版本內核是webkit,后來是 Chrome,bug的產生有可能就是內核搞得鬼,或者是手機生產商自己改的sdk結的果,而 CrossWalk 統一了它們而不失兼容型,所以,值得一試。
使用方法很簡單,我們只需要把 richeditor 里面繼承的 WebView 改為 CrossWalk 的XWalkView 即可,修改下對應的函數。
優點:
1,流暢度明顯提高,javaScript 兼容提高;
2,自動修復了 小米2A-api 16 無法刪除圖片標簽的問題;
3,自動修復了 小米2A-api 16 ,如果在onTextChange處loudUrl,每輸入一次鍵值,每輸入一個字符,軟鍵盤隱藏一次的問題;
4,使用簡單,只需要引入下載好的 library
缺點:
1,太臃腫,官網的穩定版本,整個library 20多M,或者更大,編譯后APK 40多M (致命點);
2, 和richeditor 第一點bug相同,進入,退出,再進入,所有點擊事件"撲街"。
3,這個更是奇葩,導致我直接放棄使用它。無法嵌套在 ScrollView 里面,只能設置固定高度,而且超過后,無法滾動。
4,因為也是使用 js,這個就不說了,要改你得會。
3,遇到的bug及其解決方法---------------------------
所有遇到的bug,請看上面第二點。它們解決,待我喝口水,詳細道來......
richeditor 的bug解決
1,richeditor 在所上面三種機子上面體現出的,在第一次進入該編輯頁面的時候,所有webView圖片的點擊都能響應,此 時如果用戶點擊返回,finish當前頁面,再次進入該頁面后,所有點擊事件失效。
解決:
放棄javaScript 的點擊注入,重寫webView的onTouch實現完美點擊,代碼示例如下:
1 RE.insertImage = function(url, alt,ran, w, h) { 2 //var html = '<img src="' + url + '" alt="' + alt + '" id="' + ran +'"width="'+w+'"'+'height="'+h+'"'+ 'onclick="RE.showDialog('+ran+')" />'; //這是js點擊注入 3 var html = '<img src="' + url + '" alt="' + alt + '" id="' + ran +'"width="'+w+'"'+'height="'+h+'" />'; 4 RE.insertHTML(html); 5 }
上面使用如果使用 js 注入的點擊,第一次進入頁面點擊便響應 RE.showDialog(),退出再進入就再也沒效了。

1 mEditor.setOnTouchListener(new View.OnTouchListener() { 2 @Override 3 public boolean onTouch(View v, MotionEvent event) { 4 WebView temp = (WebView) v; 5 WebView.HitTestResult mResult = temp.getHitTestResult(); 6 if(mResult==null){ 7 Log.d("zzzzz","mResult==null"); 8 if(mEditor.getHitTestResult()==null){ 9 Log.d("zzzzz","mEditor mResult==null"); 10 } 11 }else { 12 final int type = mResult.getType(); 13 switch (type) { 14 case WebView.HitTestResult.ANCHOR_TYPE: 15 case WebView.HitTestResult.SRC_ANCHOR_TYPE: 16 //點擊的是鏈接 17 break; 18 19 case WebView.HitTestResult.IMAGE_TYPE: 20 case WebView.HitTestResult.IMAGE_ANCHOR_TYPE: 21 case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE: 22 Log.d("zzzzz", "hit image"); 23 String strUrl = mResult.getExtra(); // 獲取該 img標簽里面的 src 24 // Class s = mResult.getClass(); 25 if(strUrl==null){ 26 Log.d("zzzzz", "image src is null"); 27 }else{ 28 Log.d("zzzzz", "fucking success "+strUrl); 29 showDeleteDialog(strUrl); // 30 } 31 //點擊的是圖片 32 temp.clearFocus(); 33 mEditor.clearFocusEditor(); 34 return true; 35 default: 36 Log.d("zzzzz", "hit white"); 37 //點擊的是空白處 38 break; 39 } 40 } 41 Log.d("zzzzz","show me the fucking info"); 42 return false; 43 } 44 });
上面的折疊代碼是我的onTouch 重寫例子,注意里面的注釋。這樣就能捕獲webView 里面的圖片點擊事件,並獲取對應的 圖片的url。
2,在小米2a-api 16上面,在onTextChange借口處loudUrl(),每輸入一次鍵值,每輸入一個字符,軟鍵盤隱藏一次的問題。
解決:
使用java大招------反射,因為這個是在是難,源碼在我解決這些東西的過程中是肯定有看的了,百度也不能停,順便分享個 android 源碼的鏈接,在線查看 http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/
引入我下面的這個類,然后使用。

1 public class PrivateApiBridgeMode { 2 private static final int EXECUTE_JS = 194; 3 4 Method handler; 5 Object webViewCore; 6 boolean initFailed; 7 8 @SuppressWarnings("rawtypes") 9 private void initReflection(WebView webView) { 10 Object webViewObject = webView; 11 Class webViewClass = WebView.class; 12 try { 13 Field f = webViewClass.getDeclaredField("mProvider"); // mProvider 是WebView的一個借口成員,我們找到它 14 f.setAccessible(true); // 設置可訪問 15 webViewObject = f.get(webView); // 拿到對象 16 webViewClass = webViewObject.getClass(); // webView 類 17 } catch (Throwable e) { 18 Log.d("zzzzz","initReflection Throwable "+e.toString() ); 19 } 20 21 try { 22 Field f = webViewClass.getDeclaredField("mWebViewCore"); 23 f.setAccessible(true); 24 webViewCore = f.get(webViewObject); 25 if (webViewCore != null) { 26 handler = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class); 27 handler.setAccessible(true); 28 } 29 } catch (Throwable e) { 30 initFailed = true; 31 } 32 } 33 34 public void onNativeToJsMessageAvailable(WebView webView,String js) { 35 if (handler == null && !initFailed) { 36 initReflection(webView); 37 } 38 if (handler != null) { 39 Message execJsMessage = Message.obtain(null, EXECUTE_JS, js); // 阻斷 40 try { 41 handler.invoke(webViewCore, execJsMessage); 42 } catch (Throwable e) { 43 Log.d("zzzzz","onNativeToJsMessageAvailable Throwable "+e.toString() ); 44 } 45 } 46 } 47 }
使用對比,webView.loadUrl(String)
換為 new PrivateApiBridgeMode ().onNativeToJsMessageAvailable(this,String);
3,小米2A-api 16上測試,無法刪除通過軟鍵盤刪除鍵刪除圖片標簽
請看下面的第四點,彩蛋....
CrossWalk的bug解決
1,太臃腫的問題,這個只能這樣搞:http://blog.csdn.net/recall2012/article/details/47319653
2,進入,退出,再進入,所有圖片點擊事件失效的解決方法同 richeditor。
3,無法嵌套在 ScrollView 里面,只能設置固定高度,而且超過后,無法滾動。
本人不才,遇到這個bug的時候,我已心力交瘁,直接放棄它了,建議,能不套 ScrollView的,就別套吧.....
4,附加功能彩蛋---------------------------
彩蛋一:上面我講到了,點擊圖片顯示大圖,因為能拿到 src,你還可以保存,發送,上傳,等等。唯有一個不行,此乃便是刪除圖片,如果它不是有進入,返回,再進入會導致圖片 img 的 onClick 功能失效的情況,那么我就可能通過為img 標簽設置 id,來對應刪除。
例如:
<img src="'" id="id" onclick=" RE.delete(插入時就加入個整數作為id) " />
我上面的例子是可能通過在 js 注入的時候為標簽添加參數的,那么我完全可以添加個 id(大一點的隨機數),刪除的時候就執行下面的 js
1 RE.deleteImage = function(id) { 2 // obj.parentNode.removeChild(obj); 3 document.getElementById(id).parentNode.removeChild(document.getElementById(id)); 4 }
上面的代碼結合,用戶點擊就相應 RE.delete() 然后 回調 java接口,java接口獲取到 id,再傳入到 RE.deleteImage() 成功刪除圖片。你不用懷疑,因為我成功過。
由於,進入,返回,再進入會導致圖片 img 的 onClick 功能失效,所以我們可以這樣做:
利用臨時文件夾的方法,使用圖片路徑對應id來對應查找刪除,這里要注意了,圖片路徑對應id,我們可以采用 HashMap<String,Integer>,String是圖片路徑,Integer是我上面說到的id, 如果你懂了,那么到這里,你是會發現一個問題的,那么就是,用戶加入同一張圖片多次就完了,無法一一對應,String作為key直接被覆蓋,那么為了防止用戶可能輸入同一張圖片多次,就是路徑相同的情況,所以我們要建臨時文件,退出再刪除,占用了點 CPU 時間,那么怎么建文件呢。例子如下:
1 int ran = (int) (1 + Math.random() * (1000000 - 1 + 1)); // 隨機數id 2 if(text.contains("file:///storage/sdcard0/Tencent/QQ_Images/8012cccb6d78121.jpg")){ // 同一張圖片出現了 3 String path = "/storage/sdcard0/Tencent/QQ_Images/8012cccb6d78121.jpg"; // 重復的圖片的路徑 4 if(copyfile( // 復制到臨時文件夾 5 new File(path), 6 new File("/storage/sdcard0/bcImageCache/"), // 臨時文件夾 7 "/temp"+ran+".jpg" // 該重復圖片的臨時名字 8 )){ 9 Log.d("zzzzz","copy success"); 10 }else{ 11 Log.d("zzzzz","copy faild"); 12 } 13 hs.put("file:///storage/sdcard0/bcImageCache/"+"temp"+ran+".jpg",ran); // hashMap 加數據 14 mEditor.insertImage("file:///storage/sdcard0/bcImageCache/"+"temp"+ran+".jpg", "wrong",ran, 100, 100);
// 插入的是臨時復制的圖片 15 }else{ // 沒重復,正常插入 16 hs.put("file:///storage/sdcard0/Tencent/QQ_Images/8012cccb6d78121.jpg",ran); 17 mEditor.insertImage("file:///storage/sdcard0/Tencent/QQ_Images/8012cccb6d78121.jpg", "wrong",ran, 100, 100); 18 }
彩蛋二:監聽 WebView 在被編輯的時候的所有按鍵事件,然后做你的事情(我曾試過通過它監聽刪除鍵來刪除圖片,faild)。

1 // 重寫該函數 2 @Override 3 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 4 return new myInputConnection(super.onCreateInputConnection(outAttrs),true); 5 } 6 7 private class myInputConnection extends InputConnectionWrapper { 8 9 public myInputConnection(InputConnection target, boolean mutable) { 10 super(target, mutable); 11 } 12 13 @Override 14 public boolean sendKeyEvent(KeyEvent event) { 15 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) { 16 if (deleteKeyListener != null) { 17 deleteKeyListener.onDeleteClick(); // 執行刪除鍵的事件接口 18 return true; 19 } 20 }else{ 21 Log.d("zzzzz","fuck key"); 22 } 23 return super.sendKeyEvent(event); 24 } 25 26 @Override 27 public boolean deleteSurroundingText(int beforeLength, int afterLength) { 28 if (beforeLength == 1 && afterLength == 0) { 29 return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, 30 KeyEvent.KEYCODE_DEL)) 31 && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, 32 KeyEvent.KEYCODE_DEL)); 33 } 34 return super.deleteSurroundingText(beforeLength, afterLength); 35 } 36 } 37 38 private OnDeleteKeytListener deleteKeyListener; 39 40 public void OnDeleteKeytListener(OnDeleteKeytListener delKeyEventListener) { 41 this.deleteKeyListener = deleteKeyListener; 42 } 43 44 public interface OnDeleteKeytListener { 45 void onDeleteClick(); 46 }
好了,就這么多,喜歡就點個頂吧,一次總結這么多,不容易,讓更多人看到,開發不易啊!