Xamainr 地圖之webview初探

一 說幾點

   當下移動開發主要實現方式有傳統的Native以及新的混合開發想Rect.js,nodejs這些前段框架,其本質要麼是原生控件來實現UI,要麼html來實現UI。Xamarin其實也只是取巧而已,目的在於方便net開發者再學習java以及蛋疼的oc和不成熟的swift,好了廢話很少說了。javascript

 

二 xamarin地圖實現及其問題

   gis做爲軟件領域基礎性存在,比之傳統表格列表等圖形化展示數據優點。在app中因爲國內國外牆緣由通常都使用三方api,百度,高德,騰訊之類,其中高德天然是國內作得最專業的在線地圖服務商,他們的api都很簡單,所要注意一點就是座標系,由於座標系的緣由經常標註一些地物要素對不上號。像傳統老牌arcgis在移動領域其實也只是刷存在感。在xamarin中要開發地圖應用天然的不得不使用三方,緣由嘛android綁定了谷歌,ios嘛綁定了高德地圖api功能又不夠強大。怎麼辦的用高德,百度,騰訊,那麼問題來了,xamarin使用三方庫,這地方很是蛋疼,緣由嘛xamarin其實將原生庫元素據提取出來與c#語法映射,什麼jar,.a ,.framework用很不成熟的sharpie工具反射,其實在這個過程當中千絲萬縷的牽涉到原生的oc姿式,不得不說很是蛋疼。即便你可以看懂官方英文,demo各類類型對應,問題仍是會很多,不是缺個類就是函數簽名對不上號,要麼就是即便能is某個類型但as卻編譯不過。html

 

三 面對問題怎麼辦?

  問題天然是要解決的,隨着h5的完善webview不失爲更好一種辦法,說白了就是把網頁嵌入到頁面中用c#與js交互,在這裏以百度js api爲例。java

  在xamarin.android中因爲4.4版本如下瀏覽器內核存在天生渲染慢加載慢等不足,在xamarin.ios自8.0後加強優化wkwebview控件。node

四 需求與實現

1 怎樣把地圖html頁面嵌入到app頁面,在這裏xamarin爲咱們提供了很好的demo

android實現代碼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實現代碼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實現:git

因爲android api問題在4.4如下只能傳參而沒有返回值,4.4以上使用相應方法(可是我試過了是沒有返回值的,緣由未知)github

 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是有返回值web

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裏面已經實現了swift

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截圖

 

 

相關文章
相關標籤/搜索