記一次使用 android 自帶 WebView 作富文本編輯器之API、機型的兼容及各類奇葩bug的解決

轉載請聲明出處(http://www.cnblogs.com/linguanh/)html

 

目錄java

1,測試設備介紹android

2,開源項目richeditor及CrossWalk的選擇git

3,遇到的bug及其解決方法github

4,附加功能彩蛋。web

 

1,測試設備介紹----------------------api

     測試的機型有 魅藍note2-api 22,小米2A-api 16,三星galaxy I9152-API 17.瀏覽器

     上述機型均經過測試,針對它們各自產生的bug我會在第二大點處介紹。緩存

 

2,開源項目richeditor及CrossWalk的比較---------------------------app

     關於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         });
View Code

                上面的摺疊代碼是個人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 } 
View Code

              使用對比,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     }
View Code

 

 

好了,就這麼多,喜歡就點個頂吧,一次總結這麼多,不容易,讓更多人看到,開發不易啊!

相關文章
相關標籤/搜索