Xamainr 地图之webview初探

一 说几点

   当下移动开发主要实现方式有传统的Native以及新的混合开发想Rect.js,nodejs这些前段框架,其本质要么是原生控件来实现UI,要么html来实现UI。Xamarin其实也只是取巧而已,目的在于方便net开发者再学习java以及蛋疼的oc和不成熟的swift,好了废话不多说了。

 

二 xamarin地图实现及其问题

   gis作为软件领域基础性存在,比之传统表格列表等图形化展现数据优势。在app中由于国内国外墙原因一般都使用三方api,百度,高德,腾讯之类,其中高德自然是国内做得最专业的在线地图服务商,他们的api都很简单,所要注意一点就是坐标系,因为坐标系的原因常常标注一些地物要素对不上号。像传统老牌arcgis在移动领域其实也只是刷存在感。在xamarin中要开发地图应用自然的不得不使用三方,原因嘛android绑定了谷歌,ios嘛绑定了高德地图api功能又不够强大。怎么办的用高德,百度,腾讯,那么问题来了,xamarin使用三方库,这地方非常蛋疼,原因嘛xamarin其实将原生库元素据提取出来与c#语法映射,什么jar,.a ,.framework用很不成熟的sharpie工具反射,其实在这个过程中千丝万缕的牵涉到原生的oc姿势,不得不说非常蛋疼。即使你能够看懂官方英文,demo各种类型对应,问题还是会不少,不是缺个类就是函数签名对不上号,要么就是即使能is某个类型但as却编译不过。

 

三 面对问题怎么办?

  问题自然是要解决的,随着h5的完善webview不失为更好一种办法,说白了就是把网页嵌入到页面中用c#与js交互,在这里以百度js api为例。

  在xamarin.android中由于4.4版本以下浏览器内核存在天生渲染慢加载慢等不足,在xamarin.ios自8.0后增强优化wkwebview控件。

四 需求与实现

1 怎样把地图html页面嵌入到app页面,在这里xamarin为我们提供了很好的demo

android实现代码

  1 using System;
  2 using Android.Net.Http;
  3 using Android.OS;
  4 using Android.Webkit;
  5 using MobileOperation.Droid.Web;
  6 using MobileOperation.Views;
  7 using Xamarin.Forms;
  8 using Xamarin.Forms.Platform.Android;
  9 using View = Android.Views.View;
 10 using WebView = Android.Webkit.WebView;
 11 
 12 [assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
 13 namespace MobileOperation.Droid.Web
 14 {
 15     public class HybridWebViewRenderer : ViewRenderer<HybridWebView, Android.Webkit.WebView>, IDownloadListener, View.IOnLongClickListener
 16     {
 17         const string JavaScriptFunction = "function invokeCSharpAction(data){jsBridge.invokeAction(data);}";
 18 
 19 
 20 
 21         protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
 22         {
 23             base.OnElementChanged(e);
 24 
 25             if (Control == null)
 26             {
 27                 var webView = new Android.Webkit.WebView(Forms.Context);
 28                 webView.Settings.JavaScriptEnabled = true;
 29                 webView.SetDownloadListener(this);
 30                 SetNativeControl(webView);
 31             }
 32             if (e.OldElement != null)
 33             {
 34                 Control.RemoveJavascriptInterface("jsBridge");
 35                 var hybridWebView = e.OldElement as HybridWebView;
 36 
 37             }
 38             if (e.NewElement != null)
 39             {
 40 
 41                 Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
 42                 //Control.LoadUrl(string.Format("file:///android_asset/Web/{0}", Element.Uri));
 43                 InjectJS(JavaScriptFunction);
 44 
 45 
 46                 Control.Settings.JavaScriptEnabled = true;
 47                 Control.SetWebChromeClient(new GeoWebChromeClient());
 48                 Control.SetWebViewClient(new MyWebViewClient());
 49                 Control.SetNetworkAvailable(true);
 50                 Control.Settings.SetGeolocationEnabled(true);
 51                 Control.Settings.JavaScriptCanOpenWindowsAutomatically = (true);
 52                 
 53                 Control.Settings.SetAppCacheEnabled(true);
 54                 Control.Settings.AllowFileAccess=(true);
 55                 Control.Settings.DomStorageEnabled=(true);
 56                 Control.Settings.SetSupportZoom(false);
 57                 Control.Settings.SetSupportMultipleWindows(false);
 58                 Control.Settings.BuiltInZoomControls=(false);
 59                 Control.Settings.SetRenderPriority(WebSettings.RenderPriority.High);
 60 
 61                 Control.SetOnLongClickListener(this);
 62                 Control.ClearCache(true);
 63                 if ((int)Build.VERSION.SdkInt >= 19)
 64                 {
 65                     Control.Settings.LoadsImagesAutomatically=(true);
 66                 }
 67                 else
 68                 {
 69                     Control.Settings.LoadsImagesAutomatically=(false);
 70                 }
 71 
 72 
 73 
 74                 var hybirdWebView = e.NewElement;
 75                 hybirdWebView.RegisterInvokeJsFunctionAgent((s, action) =>
 76                 {
 77                     string jsInvokeStr = string.Format("javascript: {0}", s);
 78 
 79                     // 如果android运行版本高于4.4则调用该版本及其以上所支持的函数
 80                     if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
 81                     {
 82                         Control.EvaluateJavascript(jsInvokeStr, new ValueCallback(Control));
 83                     }
 84                     else
 85                     {
 86                         // todo 此处调用本身并不支持有返回值
 87                         Control.LoadUrl(jsInvokeStr);
 88                     }
 89 
 90                     //res  http://droidyue.com/blog/2014/09/20/interaction-between-java-and-javascript-in-android/
 91 
 92                     // todo 目前在android还无法实现有返回值
 93                     if (action != null)
 94                     {
 95                         action(string.Empty);
 96                     }
 97                 });
 98                 //Control.LoadUrl(string.Format("http://map.baidu.com/mobile/webapp/index.html"));
 99                 Control.LoadUrl(string.Format("http://192.168.50.148/baidu/index.html"));
100                 //Control.LoadUrl(string.Format("http://192.168.50.254"));
101                 //Control.LoadUrl(string.Format("http://map.baidu.com/mobile/webapp/search/search/qt=s&wd=atm&c=75&searchFlag=bigBox&version=5&exptype=dep&src_from=webapp_all_bigBox&src=0&nb_x=11577553.94&nb_y=3541989.14&center_rank=1/vt=map"));
102 
103 
104             }
105         }
106 
107         void InjectJS(string script)
108         {
109             if (Control != null)
110             {
111                 Control.LoadUrl(string.Format("javascript: {0}", script));
112             }
113         }
114 
115         public void OnDownloadStart(string url, string userAgent, string contentDisposition, string mimetype, long contentLength)
116         {
117             
118         }
119 
120         public bool OnLongClick(View v)
121         {
122             return true;
123             
124         }
125     }
126 
127     public class GeoWebChromeClient : WebChromeClient
128     {
129         public override void OnGeolocationPermissionsShowPrompt(string origin, GeolocationPermissions.ICallback callback)
130         {
131             //允许通过权限询问访问
132             callback.Invoke(origin, true, false);
133         }
134 
135         
136 
137     }
138 
139 
140     public class MyWebViewClient : WebViewClient
141     {
142         public override bool ShouldOverrideUrlLoading(WebView view, string url)
143         {
144             view.LoadUrl(url);
145             return true;
146         }
147         public override void OnPageFinished(WebView view, String url)
148         {
149             if (!view.Settings.LoadsImagesAutomatically)
150             {
151                 view.Settings.LoadsImagesAutomatically=(true);
152             }
153         }
154 
155 
156         public override void OnReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
157         {
158             handler.Proceed();
159         }
160 
161         public override void OnReceivedError(WebView view, ClientError errorCode, string description, string failingUrl)
162         {
163             base.OnReceivedError(view, errorCode, description, failingUrl);
164 
165 
166         }
167 
168         
169         
170 
171     }
172 
173     public class ValueCallback : IValueCallback
174     {
175 
176         private Android.Webkit.WebView webView;
177 
178         public ValueCallback(Android.Webkit.WebView wbView)
179         {
180             webView = wbView;
181         }
182 
183         public void OnReceiveValue(Java.Lang.Object value)
184         {
185 
186         }
187 
188         public System.IntPtr Handle
189         {
190             get { return new IntPtr(); }
191         }
192 
193         public void Dispose()
194         {
195 
196         }
197     }
198 
199 }
View Code

ios实现代码

 1 using System;
 2 using System.IO;
 3 using Foundation;
 4 using MobileOperation.iOS.WebCS;
 5 using MobileOperation.Views;
 6 using WebKit;
 7 using Xamarin.Forms;
 8 using Xamarin.Forms.Platform.iOS;
 9 
10 [assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
11 namespace MobileOperation.iOS.WebCS
12 {
13     public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
14     {
15         const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
16         WKUserContentController _userController;
17 
18         protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
19         {
20             base.OnElementChanged(e);
21 
22             if (Control == null)
23             {
24                 _userController = new WKUserContentController();
25                 var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
26                 _userController.AddUserScript(script);
27                 _userController.AddScriptMessageHandler(this, "invokeAction");
28 
29                 var config = new WKWebViewConfiguration { UserContentController = _userController };
30                 var webView = new WKWebView(Frame, config);
31                 SetNativeControl(webView);
32             }
33             if (e.OldElement != null)
34             {
35                 _userController.RemoveAllUserScripts();
36                 _userController.RemoveScriptMessageHandler("invokeAction");
37                 var hybridWebView = e.OldElement as HybridWebView;
38             }
39             if (e.NewElement != null)
40             {
41                 string fileName = Path.Combine(NSBundle.MainBundle.BundlePath, string.Format("Web/{0}", Element.Uri));
42                 Control.LoadRequest(new NSUrlRequest(new NSUrl(fileName, false)));
43 
44                 Control.LoadRequest(new NSUrlRequest(new NSUrl(string.Format("http://192.168.50.148/baidu/index.html"))));
45                 var hybirdWebView = e.NewElement;
46                 //Control.UIDelegate = new MyWKUIDelegate();
47                 hybirdWebView.RegisterInvokeJsFunctionAgent((s,action) =>
48                 {
49                     string jsInvokeStr = string.Format("javascript: {0}", s);
50                     Control.EvaluateJavaScript(jsInvokeStr, (rs, error) =>
51                     {
52                         if (action!=null)
53                             action(rs.ToString());
54                     });
55                 });
56 
57             }
58         }
59 
60         public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
61         {
62             Element.InvokeAction(message.Body.ToString());
63         }
64     }
65 
66     public class MyWKUIDelegate : WKUIDelegate
67     {
68         public override void RunJavaScriptAlertPanel(WKWebView webView, string message, WKFrameInfo frame, Action completionHandler)
69         {
70             base.RunJavaScriptAlertPanel(webView, message, frame, completionHandler);
71         }
72 
73         public override void RunJavaScriptTextInputPanel(WKWebView webView, string prompt, string defaultText, WKFrameInfo frame,
74             Action<string> completionHandler)
75         {
76             base.RunJavaScriptTextInputPanel(webView, prompt, defaultText, frame, completionHandler);
77         }
78 
79 
80         public override void RunJavaScriptConfirmPanel(WKWebView webView, string message, WKFrameInfo frame, Action<bool> completionHandler)
81         {
82             base.RunJavaScriptConfirmPanel(webView, message, frame, completionHandler);
83         }
84     }
85 
86 
87 }
View Code

 

 

2 c#与js如何交互:

c#调用js  android实现:

由于android api问题在4.4以下只能传参而没有返回值,4.4以上使用相应方法(但是我试过了是没有返回值的,原因未知)

 1  hybirdWebView.RegisterInvokeJsFunctionAgent((s, action) =>
 2                 {
 3                     string jsInvokeStr = string.Format("javascript: {0}", s);
 4 
 5                     // 如果android运行版本高于4.4则调用该版本及其以上所支持的函数
 6                     if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
 7                     {
 8                         Control.EvaluateJavascript(jsInvokeStr, new ValueCallback(Control));
 9                     }
10                     else
11                     {
12                         // todo 此处调用本身并不支持有返回值
13                         Control.LoadUrl(jsInvokeStr);
14                     }
15 
16                     //res  http://droidyue.com/blog/2014/09/20/interaction-between-java-and-javascript-in-android/
17 
18                     // todo 目前在android还无法实现有返回值
19                     if (action != null)
20                     {
21                         action(string.Empty);
22                     }
23                 });
View Code

IOS实现:亲测ios是有返回值

hybirdWebView.RegisterInvokeJsFunctionAgent((s,action) =>
                {
                    string jsInvokeStr = string.Format("javascript: {0}", s);
                    Control.EvaluateJavaScript(jsInvokeStr, (rs, error) =>
                    {
                        if (action!=null)
                            action(rs.ToString());
                    });
                });
View Code

js调用c#在demo里面已经实现了

3 粗线的问题

在2.0版本的百度地图由于其js与移动端双指缩放处理bug当地图添加一些标注后缩放到一定时候地图卡死,解决办法将地图版本降低到1.5版本,对于百度地图嘛自然是无语的,下面请看

1 <script type="text/javascript" src="http://api.map.baidu.com/api?v=1.5&ak=ak"></script>
View Code

链接:http://tieba.baidu.com/p/1724327638

 4 定位

在代码里面已经实现自然的需要添加权限,重写webclient控件,由于移动手机的浏览器内核一般都支持h5,所以只需要调用百度地图的定位api即可通过本质上调用浏览器定位api轻松实现定位

链接:http://developer.baidu.com/map/jsdemo.htm#i8_1

5 性能

 android webview的性能不咋个好,但是组织好html的渲染过程还是可以接受的

6截图

 

 

 

posted @ 2016-03-21 21:34  cuit_igis  阅读(1606)  评论(2编辑  收藏  举报