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
本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文连接。