记一次使用 android 自带 WebView 做富文本编辑器之API、机型的兼容及各种奇葩bug的解决

简介: 转载请声明出处(http://www.cnblogs.com/linguanh/)   目录 1,测试设备介绍 2,开源项目richeditor及CrossWalk的选择 3,遇到的bug及其解决方法 4,附加功能彩蛋。

转载请声明出处(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         });
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

 

 

好了,就这么多,喜欢就点个顶吧,一次总结这么多,不容易,让更多人看到,开发不易啊!

 

 

  

 

如果您认为这篇文章还不错或者有所收获,您可以通过扫描一下下面的支付宝二维码 打赏我一杯咖啡【物质支持】,也可以点击右下角的【推荐】按钮【精神支持】,因为这两种支持都是我继续写作,分享的最大动力


img_12e3f54d4d0f70f0eb14f20548e3d781.png
目录
相关文章
|
5月前
|
Linux Android开发 iOS开发
基于.Net开发的ChatGPT客户端,兼容Windows、IOS、安卓、MacOS、Linux
基于.Net开发的ChatGPT客户端,兼容Windows、IOS、安卓、MacOS、Linux
86 0
|
6月前
|
API Android开发 数据安全/隐私保护
解决android webview 加载http url 失败 net::ERR_CLEARTEXT_NOT_PERMITTED 错误
解决android webview 加载http url 失败 net::ERR_CLEARTEXT_NOT_PERMITTED 错误
228 0
|
2月前
|
XML Android开发 数据格式
安卓和webview交互
安卓和webview交互
25 0
|
4月前
|
JavaScript 前端开发 Android开发
android开发,使用kotlin学习WebView(详细)
android开发,使用kotlin学习WebView(详细)
120 0
|
4月前
|
设计模式 Oracle 关系型数据库
二十三种设计模式全面解析-适配器模式的妙用:异构数据库和不同版本API的完美兼容!
二十三种设计模式全面解析-适配器模式的妙用:异构数据库和不同版本API的完美兼容!
|
4月前
|
XML Android开发 数据格式
安卓和webview交互
安卓和webview交互
57 1
|
5月前
|
定位技术 Android开发
[√]Android webview的url scheme
[√]Android webview的url scheme
407 0
|
5月前
|
小程序 Android开发 iOS开发
在钉钉小程序中安卓无法打开webview
在钉钉小程序中安卓无法打开webview
114 1
|
6月前
|
JavaScript 前端开发 Android开发
Android AgentWeb WebView 与js交互总结
Android AgentWeb WebView 与js交互总结
182 0
|
9天前
|
缓存 前端开发 API
API接口封装系列
API(Application Programming Interface)接口封装是将系统内部的功能封装成可复用的程序接口并向外部提供,以便其他系统调用和使用这些功能,通过这种方式实现系统之间的通信和协作。下面将介绍API接口封装的一些关键步骤和注意事项。