一 说几点
当下移动开发主要实现方式有传统的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¢er_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 }
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 }
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 });
IOS实现:亲测ios是有返回值
hybirdWebView.RegisterInvokeJsFunctionAgent((s,action) =>
{
string jsInvokeStr = string.Format("javascript: {0}", s);
Control.EvaluateJavaScript(jsInvokeStr, (rs, error) =>
{
if (action!=null)
action(rs.ToString());
});
});
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>
链接: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截图