1.前言javascript
身處在移動互聯網的今天,移動應用開發煊赫一時,身爲程序猿的咱們怎麼能錯過開發一款咱們本身的APP.本人算是一個基於.net的GIS開發入門者(立刻就大四啦), 暑假在學校參加GIS比賽有大把的時間,利用最近這兩天本身寫了一個跨平臺移動APP。功能比較簡單,之後我會慢慢完善的。爲何要跨平臺呢?大學期間主要學.net,而微軟不太給力啦,WP開發基本上已近死啦 .而從新學習Android開發比較吃力費時。因爲對HTML、JavaScript與CSS 等 Web 技術開發有所瞭解,最終選擇使用Cordova來作跨平臺移動應用。本次使用的技術主要有Cordova,Asp.net Mvc+EF,jquery mobile,bootstorap,百度地圖Javascript API等。功能css
2.功能 html
目前主要是實現基於位置的拍照,存儲,地圖的可視化顯示等,使用戶更好的管理本身的照片以及照片背後的空間地理位置和故事。你們能夠下載apk試用一下啊。java
1.登陸node
2.註冊jquery
3.主頁android
a.定位ios
b.照片位置點的地圖顯示,以及屬性窗口web
c.拍照上傳ajax
d.照片詳情展現
3.演示
安裝包獲取 權限獲取 安裝完成
GISAPP安裝完成 啓動屏 登陸
註冊 主頁 GPS獲權
位置顯示 查看已有照片信息 拍照頁
啓動攝像頭 提交照片信息 提交的信息反饋
照片詳情 照片詳情 待實現功能
4.實現
1.Cordova簡介
Cordova是貢獻給Apache後的開源項目,是從PhoneGap中抽出的核心代碼,是驅動PhoneGap的核心引擎。
Cordova提供了一組設備相關的API,經過這組API,移動應用可以以JavaScript訪問原生的設備功能,如攝像頭、麥克風等。
Cordova還提供了一組統一的JavaScript類庫,以及爲這些類庫所用的設備相關的原生後臺代碼。
Cordova支持以下移動操做系統:iOS, Android,ubuntu phone os, Blackberry, Windows Phone, Palm WebOS, Bada 和 Symbian。
具體介紹能夠參考官網:http://cordova.apache.org/
2.Cordova平臺環境配置
須要安裝的東西以下(android爲例)
1.配置JDK環境
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
安裝jdk-8u5-windows-x64.exe,
配置環境變量
JAVA_HOME = C:\Program Files\Java\jdk1.8.0_05
Path += %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;
CLASSPATH += %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
2.配置android-sdk環境
http://developer.android.com/design/downloads/index.html
解壓android-sdk.rar到D:\Android\
配置環境變量
ASDK_HOME = D:\Android\sdk
Path += %ASDK_HOME%\platform-tools;%ASDK_HOME%\tools;
3.配置Node環境
http://nodejs.org/download/
安裝node-v0.10.29-x64.msi
配置環境變量
NODE_HOME = C:\Program Files\Nodejs\
Path += %NODE_HOME%;
在cmd中運行npm install -g cordova(在線安裝)
3.數據庫設計
一個用戶表,一個照片表。
值得注意的是字段Location(位置)我使用的是Geometry類型存儲的,這是.net 4.5後新支持的字段類型,對應sqlserver中的geometry類型,之後咱們專門寫博客講這個東西的。
4.頁面構建以及服務端通訊
登陸註冊使用bootstorap作得響應式佈局以及後臺驗證明現
a.頁面搭建(登陸爲例)
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>MyHybirdApp</title> <link href="assets/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="assets/css/style.css" rel="stylesheet" /> </head> <body> <!-- 表單區域 --> <form id="form_login" class="form-main" method="get" autocomplete="off"> <h2>登 錄</h2> <hr /> <div class="avator"> <img id="myavator" src="assets/img/avatar.jpg" /> </div> <div class="message-box"></div> <!--.sr-only 能夠用於隱藏元素--> <label for="username" class="sr-only">Username</label> <input id="username" type="text" class="form-control input-lg input-group-top" placeholder="請輸入用戶名" required autofocus> <label for="password" class="sr-only">Password</label> <input id="password" type="password" class="form-control input-lg input-group-bottom" placeholder="請輸入密碼" required> <div class="checkbox"> <input id="remember" type="checkbox" value="remember-me" checked/> <label for="remember">記 住 我</label> <a class="link" href="register.html">注 冊</a> </div> <button id="btn_login" class="btn btn-lg btn-info btn-block">登 錄</button> </form> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>MyHybirdApp</title> <link href="assets/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="assets/css/style.css" rel="stylesheet" /> </head> <body> <!-- 表單區域 --> <form id="form_login" class="form-main" method="get" autocomplete="off"> <h2>登 錄</h2> <hr /> <div class="avator"> <img id="myavator" src="assets/img/avatar.jpg" /> </div> <div class="message-box"></div> <!--.sr-only 能夠用於隱藏元素--> <label for="username" class="sr-only">Username</label> <input id="username" type="text" class="form-control input-lg input-group-top" placeholder="請輸入用戶名" required autofocus> <label for="password" class="sr-only">Password</label> <input id="password" type="password" class="form-control input-lg input-group-bottom" placeholder="請輸入密碼" required> <div class="checkbox"> <input id="remember" type="checkbox" value="remember-me" checked/> <label for="remember">記 住 我</label> <a class="link" href="register.html">注 冊</a> </div> <button id="btn_login" class="btn btn-lg btn-info btn-block">登 錄</button> </form> </body> </html>
b.Ajax表單提交
咱們知道在傳統PC 瀏覽器端中,ajax請求受限於XMLHttpRequest沒法進行跨域請求,咱們可能須要藉助JSONP一類的幫手幫咱們解決,而在Cordova生成的Hybird App中不須要考慮這個問題。在上面的代碼中,get請求訪問的是一個位於遠端服務器中的一個服務(能夠是ashx通常處理程序,也能夠是一個MVC應用的action)。
<script src="assets/lib/jquery/jquery-1.11.0.min.js"></script> <script> $(function () { $('#btn_login').click(function () { var username = $.trim($("#username").val()); var password = $.trim($("#password").val()); if (username.length < 1) { alert("請輸入用戶名"); return false; } if (password.length < 1) { alert("請輸入密碼"); return false; } $.get('http://localhost:62383/UserInfo/Login?username=' + username + '&password=' + password + '', {}, function (data) { if (data.Message == "success") { window.location.href = "APP.html?username=" + data.UserName + "&id=" + data.Id; } else { alert(data.Message); } }); //click事件中加return false來阻止冒泡 return false; }); }); </script>
注意:登陸成功後回調函數中經過設置url進行登陸信息保存與傳遞。window.location.href = "APP.html?username=" + data.UserName + "&id=" + data.Id;
c.後臺驗證
[HttpGet] public ActionResult Login(string username, string password) { GISAppDBEntities1 db = new GISAppDBEntities1(); string message; if (string.IsNullOrEmpty(username)) { message = "請輸入用戶名"; return Json(new { Message = message }, JsonRequestBehavior.AllowGet); } if (string.IsNullOrEmpty(password)) { message = "請輸入密碼"; return Json(new { Message = message }, JsonRequestBehavior.AllowGet); } //密碼加密後對照 password = MD5Helper.EncryptString(password); UserInfo userInfo = db.UserInfo.Where(u => u.UserName == username && u.PassWord == password).FirstOrDefault(); if (userInfo == null) { message = "登陸失敗用戶名或密碼錯誤"; return Json(new { Message = message }, JsonRequestBehavior.AllowGet); } return Json(new { Message = "success", UserName = username, Id = userInfo.UId}, JsonRequestBehavior.AllowGet); }
主頁jquery mobile+百度地圖Javascript API
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> <title>JQueryApp 01</title> <!--網頁不容許放大縮小--> <meta content="width=device-width, initial-scale=1" name="viewport"> <link rel="stylesheet" href="assets/lib/mobile/themes/my-custom-theme.min.css" /> <link rel="stylesheet" href="assets/lib/mobile/themes/jquery.mobile.icons.min.css" /> <link href="assets/lib/mobile/jquery.mobile.structure-1.4.2.min.css" rel="stylesheet" /> <style> #allmap { position: absolute; background-color: #EEEEDD; height: 87%; width: 100%; padding: 0px; z-index: 0; left: 0px; } #wimg { width: 100%; position: absolute; bottom: 120px; z-index: 99; text-align: center; } #Warning { width: 120px; height:120px } .ui-btn { padding-bottom: 2px; } </style> </head> <body> <!--data-role屬性就是JQM中控件的概念--> <div data-role="page" data-theme="a"> <div data-fullscreen="false" data-position="fixed" data-role="header"> <h1 id="user"></h1> </div> <div data-role="ui-content" id="mapcontent"> <div id="allmap"> </div> <div id="wimg"> <img src="assets/lib/mobile/images/yujing.png" id="Warning"/> </div> </div> <div data-position="fixed" data-role="footer" data-fullscreen="false"> <div data-role="navbar"> <ul class="ui-grid-c" > <li class="ui-block-a"><a href="#" data-icon="location" data-iconpos="top" class="ui-btn-active" id="app">定位</a></li> <li class="ui-block-b"><a data-ajax="false" href="#" data-icon="heart" data-iconpos="top" id="main">主頁</a></li> <li class="ui-block-c"><a data-ajax="false" href="#" data-icon="comment" data-iconpos="top" id="news">資訊</a></li> <li class="ui-block-d"><a data-ajax="false" href="#" data-icon="gear" data-iconpos="top" id="setting">設置</a></li> </ul> </div> </div> </div> </body> </html> <script src="assets/lib/jquery/jquery-1.11.0.js"></script> <script src="assets/lib/mobile/jquery.mobile-1.4.2.js"></script> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=jSVkiBOGSsNh69VzlhaHReHVuZvWvzNA"></script> <script type="text/javascript"> //URL截取 var queryString = window.location.search.substr(1); var qureyArr = {}; var tempArr = queryString.split('&'); for (var i = 0; i < tempArr.length; i++) { var kv = tempArr[i].split('='); if (kv.length == 2) { qureyArr[kv[0]] = kv[1]; } else if (kv.length == 1) { qureyArr[kv[0]] = ""; } } var id = qureyArr.id; var username = decodeURI(qureyArr.username); //將用戶信息傳到其餘href設置 $("#main").attr("href", "main.html?id=" + id + "&username="+username); $("#news").attr("href", "news.html?id=" + id + "&username=" + username); $("#setting").attr("href", "setting.html?id=" + id + "&username=" + username); $("#user").text(username+" の 相 冊"); // 百度地圖API功能 var map = new BMap.Map("allmap"); //緯度 var lat; //經度 var lng; //地址 var address ; map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); // 添加帶有定位的導航控件 var navigationControl = new BMap.NavigationControl({ // 靠左上角位置 anchor: BMAP_ANCHOR_TOP_LEFT, // LARGE類型 type: BMAP_NAVIGATION_CONTROL_LARGE, // 啓用顯示定位 enableGeolocation: true }); var top_left_control = new BMap.ScaleControl({ anchor: BMAP_ANCHOR_TOP_LEFT }); map.addControl(top_left_control); map.addControl(navigationControl); // 添加定位控件 var geolocationControl = new BMap.GeolocationControl(); geolocationControl.addEventListener("locationSuccess", function (e) { // 定位成功事件 lat = e.point.lat; lng = e.point.lng; address += e.addressComponent.province; address += e.addressComponent.city; address += e.addressComponent.district; address += e.addressComponent.street; address += e.addressComponent.streetNumber; alert("當前定位地址爲:" + address); }); geolocationControl.addEventListener("locationError", function (e) { // 定位失敗事件 alert(e.message); }); map.addControl(geolocationControl); $(function () { var photos; //加載點 $.post("http://localhost:62383/photo/getphoto", { id: id }, function (data) { photos = data.photos; var x = photos.length; var myIcon = new BMap.Icon("assets/img/1432101726.png", new BMap.Size(32, 46)); for (var i = 0; i < photos.length; i++) { var marker = new BMap.Marker(new BMap.Point(photos[i].Lng, photos[i].Lat), { icon: myIcon }); // 建立標註 var content = photos[i].Title + "</br>" + photos[i].Address + "</br>" + photos[i].CreateDate + "</br>" + photos[i].Url; map.addOverlay(marker); // 將標註添加到地圖中 addClickHandler(content, marker); if (i == 0) { //默認縮放至第一點 map.centerAndZoom(new BMap.Point(photos[i].Lng, photos[i].Lat), 15); } } var opts = { width: 250, // 信息窗口寬度 height: 150, // 信息窗口高度 title: "圖片窗口", // 信息窗口標題 enableMessage: true//設置容許信息窗發送短息 }; function addClickHandler(content, marker) { marker.addEventListener("click", function (e) { openInfo(content, e); } ); } function openInfo(content, e) { var p = e.target; var point = new BMap.Point(p.getPosition().lng, p.getPosition().lat); var infoWindow = new BMap.InfoWindow(content, opts); // 建立信息窗口對象 map.openInfoWindow(infoWindow, point); //開啓信息窗口 } }); $("#Warning").click(function myfunction() { if (lat == null || lng == null) { alert("請先進行定位操做"); return false; } window.location.href = "edit.html?lat=" + lat + "&lng=" + lng + "&address=" + address + "&id=" + id + "&username=" + username; return false; }); }); </script>
5.調用手機硬件進行拍照
攝像頭的調用(只能使用手機調試,web端不能調試)
a.頁面
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta content="width=device-width, initial-scale=1" name="viewport"> <link rel="stylesheet" href="assets/lib/mobile/themes/my-custom-theme.min.css" /> <link rel="stylesheet" href="assets/lib/mobile/themes/jquery.mobile.icons.min.css" /> <link href="assets/lib/mobile/jquery.mobile.structure-1.4.2.min.css" rel="stylesheet" /> <link href="assets/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <title>發佈照片</title> <style> #photobg { width: 150px } #label { margin-top: 10px } #content { height: 200px !important } </style> </head> <body> <div data-role="page" data-theme="a"> <div data-fullscreen="false" data-position="fixed" data-role="header"> <h1>發佈照片</h1> </div> <div class="container"> <div class="row" style="margin-top: 20px"> <div class="col-xs-4" id="phototest"> <br /> <br /><p class="text-center text-muted">照片</p> </div> <div class="col-xs-8"> <img src="assets/img/photobg.png" class="img-thumbnail" id="photobg"/> </div> </div> <hr /> <div class="row"> <div class="form-group"> <label for="title" class="col-xs-2 control-label text-center text-muted" id="label">標題</label> <div class="col-xs-10"> <input type="text" class="form-control" id="title" name="title" placeholder="請輸入標題"/> </div> </div> </div> <div class="row"><div class="form-group"> <label for="content" class="col-xs-2 control-label text-center text-muted" id="label1">描述</label> <div class="col-xs-10"> <textarea class="form-control" id="content" rows="15" name="content" placeholder="請輸入詳細描述" ></textarea> </div> </div></div> <hr /> <div class="row" style="text-align: center"> <button class="btn" style="background: #449d44; color: #fff; width: 30%;" id="btn">提交</button> </div> </div> </div> </body> <script src="assets/lib/jquery/jquery-1.11.0.min.js"></script> <script src="assets/lib/mobile/jquery.mobile-1.4.2.js"></script> <script src="cordova.js"></script> <script> $(function () { //存儲圖片DATA_URL var photobg; //調用拍照插件 $('#photobg').click(function () { navigator.camera.getPicture(onSuccess, onFail, { quality: 50, destinationType: Camera.DestinationType.DATA_URL }); function onSuccess(imageData) { photobg = imageData; var image = document.getElementById('photobg'); image.src = "data:image/jpeg;base64," + imageData; } function onFail(message) { alert('Failed because: ' + message); } }); //URL截取 var queryString = window.location.search.substr(1); var qureyArr = {}; var tempArr = queryString.split('&'); for (var i = 0; i < tempArr.length; i++) { var kv = tempArr[i].split('='); if (kv.length == 2) { qureyArr[kv[0]] = kv[1]; } else if (kv.length == 1) { qureyArr[kv[0]] = ""; } } var username = decodeURI(qureyArr.username); $("#btn").click(function() { var title = $.trim($("#title").val()); var content = $.trim($("#content").val()); if (title.length < 1) { alert("請輸入標題"); return false; } $.post("http://localhost:62383/Photo/Add", { photobg: photobg, title: title, id:qureyArr.id, content: content, lat: qureyArr.lat, lng: qureyArr.lng, address: qureyArr.address }, function(data) { if (data.Message == "success") { window.location.href = "APP.html?username=" + username + "&id=" + qureyArr.id; } else { alert(data.Message); } }); }); }); </script> </html>
其中cordova.js就是cordova進行手機底層硬件訪問的js
destinationType: Camera.DestinationType.DATA_URL :這段是設置照片的返回類型爲dataurl,爲下面圖片上傳作準備
b.後臺提交
/// <summary> /// 添加照片 /// </summary> /// <param name="lat">緯度</param> /// <param name="lng">精度</param> /// <param name="address">地址</param> /// <param name="title">標題</param> /// <param name="content">描述</param> /// <param name="photobg">上傳圖片dataurl</param> /// <param name="id">用戶編號</param> /// <returns></returns> public ActionResult Add(string lat, string lng, string address, string title, string content, string photobg,int id) { //byte[] arr = Convert.FromBase64String(photobg); //using (MemoryStream ms = new MemoryStream(arr)) // { // Bitmap bmp = new Bitmap(ms); // bmp.Save("/images/" + DateTime.Now.ToFileTime() + ".jpg"); // } GISAppDBEntities1 db = new GISAppDBEntities1(); //url解碼 address= HttpUtility.UrlDecode(address); if (address.Contains("undefined")) { address = address.Replace("undefined",""); } string point = "POINT(" + lng + " " + lat + ")"; Photo photo = new Photo(); photo.Address = address; photo.Picture = ""; photo.CreateDate = DateTime.Now; photo.Content = content; photo.Title = title; photo.UserInfoUId = id; photo.Location = DbGeometry.PointFromText(point, 4326); db.Photo.Add(photo); if (db.SaveChanges() != 1) { return Json(new { Message = "服務器錯誤" }, JsonRequestBehavior.AllowGet); } return Json(new { Message = "success" }, JsonRequestBehavior.AllowGet); }
圖片的上傳
1.咱們知道對與form表單的提交,pc之間提交照片要使用<input type=」file」/>進行提交,可是咱們想在手機上提交照片怎麼辦呢?下面有兩種方式。
a.將圖片經過base64編碼生成dataurl進行傳輸,而後在後臺解碼生成圖片
b.cordova插件cordova-plugin-file-transfer 進行異步提交
爲了簡單我直接經過base64編碼生成dataurl進行傳輸,這種方法,
其餘功能實現請查看個人源碼,這裏就不過多介紹啦。
5.Cordova項目構建與打包示例
1.建立項目
在要創建項目的文件加下shift+右鍵,點擊在此處打開命令窗口,在控制檯中輸入
2.建立android平臺
3.添加訪問硬件的插件
4.項目導入到根目錄www文件夾裏面
將咱們開發的web網頁以及依賴的資源(圖片、css、js等)拷貝到此目錄下
5.配置根目錄下的config.xml
設置APP圖標和啓動屏
<?xml version='1.0' encoding='utf-8'?> <widget id="cn.sharegis.app" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>GISAPP</name> <description> A sample Apache Cordova application that responds to the deviceready event. </description> <author email="dev@cordova.apache.org" href="http://cordova.io"> Apache Cordova Team </author> <content src="login.html" /> <plugin name="cordova-plugin-whitelist" spec="1" /> <access origin="*" /> <allow-intent href="http://*/*" /> <allow-intent href="https://*/*" /> <allow-intent href="tel:*" /> <allow-intent href="sms:*" /> <allow-intent href="mailto:*" /> <allow-intent href="geo:*" /> <platform name="android"> <allow-intent href="market:*" /> <allow-intent href="market:*" /> <icon src="www/gps.png" density="ldpi" /> <icon src="www/gps.png" density="mdpi" /> <icon src="www/gps.png" density="hdpi" /> <icon src="www/gps.png" density="xhdpi" /> <splash src="www/screen.png" density="land-hdpi" /> <splash src="www/screen.png" density="land-ldpi" /> <splash src="www/screen.png" density="land-mdpi" /> <splash src="www/screen.png" density="land-xhdpi" /> <splash src="www/screen.png" density="port-hdpi" /> <splash src="www/screen.png" density="port-ldpi" /> <splash src="www/screen.png" density="port-mdpi" /> <splash src="www/screen.png" density="land-xhdpi" /> </platform> <preference name="SplashScreenDelay" value="2000" /> <platform name="ios"> <allow-intent href="itms:*" /> <allow-intent href="itms-apps:*" /> </platform> </widget>
6.編譯
6.注意事項
1.你一但選擇使用cordova插件來進行操做的話,你在電腦上就不能夠調試啦,必須手機操做。
2.手機調試時必定要將手機經過電腦發出的WiFi進行測試,保證手機與後臺服務通暢。我本身是使用的一臺阿里雲服務器,不須要考慮上述通訊問題.
3.你在web開發時url能夠不注意大小寫,可是cordova打包是區分大小寫的,例如index.html寫成Index.html就會出現異常。
4.手機的真機測試比較慢,因此儘可能將邏輯處理測試經過再打打包.節約時間。
7.總結
本次主要實現了登陸註冊以及基於GIS的照片管理,實現的功能比較簡單。你可下載一下APP試用一下,目前只有android版本,其餘平臺有興趣的話能夠本身打包。所有源碼連接在下面,若是有問題的能夠互相交流,不懂得地方能夠私信我。後面我還會添加一些新的功能,若是喜歡能夠關注一下我。
這個系列未完,待續。。。。。。。。。。。。。。。。。。。。。,期待您的關注
所有源碼:http://pan.baidu.com/s/1c2x9Q7Y
測試APP for android下載:http://pan.baidu.com/s/1c2wTSbe
做者:ATtuing
出處:http://www.cnblogs.com/ATtuing
本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。