STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

移动App之经验总结(一)

时间过得真快,转眼间自己花时间做App已经一个多月了。在这一个多月的时间里,自己掌握的东西和了解的东西都会一一介绍给大家。

在使用前端技术开发移动App前,首先给大家简单介绍下移动App可以是下面的任意一类:

  • Web App(网页应用)
  • Hybrid App(混合式应用)
  • Native App(原生应用)

三类移动App简介及各自优缺点

Web App:使用纯前端技术,HTML、CSS、JavaScript去绘制和操作页面,使用媒体查询、响应式布局去适应不同的设备,比如:使用手机浏览器访问到的网站(官网、博客等)、原生应用里面嵌套的页面(微信公众平台、手机QQ的QQ空间等)
优点:跨平台、开发成本低、维护更新简单
缺点:体验性差、不能安装
特点:必须依赖浏览器

Native App:使用原生代码做的移动App,Android使用Java语言进行开发,IOS使用Object-C或Swift语言进行开发,比如:去App Store下载的大部分App,应用中心的大部分App等
优点:体验性高、可以安装
缺点:不跨平台,开发成本高,维护更新复杂
特点:必须下载安装

如果前端人员也想做一款App并发布到AppStore、安装应用市场上面的话,难道还要去学习下Java和Object-C?
No,使用前端技术做Hybrid App实现啦。

Hybrid App:使用前端技术负责界面展示,使用原生代码进行移动设备调用,比如:爱班级App、Telkomsel Moovigo等
优点:一套前端代码适配所有Androd手机和IOS手机App,可以安装、开发成本低、维护更新简单
缺点:性能问题,不同终端不同设备产生的兼容性bug


开发App前的准备工作

因为苹果大部分东西都是闭源的,就连IOS的开发环境必须用到OS X系统(不建议使用虚拟机、黑苹果),所以下面介绍前端开发手机App需要做以下准备:
IOS

  • 一台Mac电脑
  • 一部苹果设备(IPhone、ITouch)
  • 一个苹果账号ID
  • 一个苹果开发者账号 688元/年
    Android
  • 安装JDK并配置环境变量
  • 安装Android SDK或Adt-bundle
  • 一部安卓设备
  • Android File Transfer for mac
    编码及编译工具
  • Xcode、Sublime Text、Eclipse
  • Cordova

App产品的组成部分

前面讲到的都是硬件或软件工具的搭建,接下来讲解下一款App产品的组成部分:
1.产品构思:这个阶段需要一些有想法的人。
这个App的作用是什么?
给用户带来哪些价值?
用户的哪些需求可以满足?

2.产品设计:这个阶段需要美工和研发人员。
包括功能设计和UI设计,功能设计侧重于产品的功能;UI设计包括App的界面和美化。

3.产品实现:这个阶段需要web前端、后台及DBA等研发人员。
包括开发框架和开发工具的选择,平台接口和页面的编写,服务器搭建、数据库搭建等。

4 产品上架:这个阶段需要对各个平台发布了如指掌的人。
包括注册各个平台的账号发布产品,使用苹果开发者账号,提供必要的上架资料,将App提交应用到App Store;使用谷歌账号提交到Google Play商城等

5.产品推广:这个阶段需要销售、营销、SEO、运维等服务人员。
包括开发一个自己的网站、微信公众号、微博、博客,也可以打广告,百度推广等。

可见,对于一个人做App是多么大的挑战,明知山有虎,偏向虎山行,我就是这样的人。下面根据以上五点,讲解下我对App的付出。


1.产品构想

当我想自己做一款App出来的时候,我的第一步想的是我该做哪类的App呢?如今成千上万的App层出不穷,竞争也是相当的激烈,如果自己找到的需求或思路与其他App相同,那么一定会死掉。我应该找到用户的一个痛点,然后服务于他们,那么我的产品才会有市场。

我认为好的产品应该确定使用人群,该人群会用手机App才行,那么反过来问,会用手机App的人群有哪些呢?于是我确定自己做的App的第一点:__针对的人群是80、90、00后__。接着思考,在这些人群里面他们还需要怎样的App呢?带着这个问题,我把移动App产品从衣食住行耍五个方面思考我需要做个怎样的App出来。
1.衣
关于“衣”的产品,比如:淘宝、京东、亚马逊、唯品会…敢跟马云、刘强东他们比吗?于是乎我放弃这方面需求。
2.食
关于“食”的产品,比如:美团、饿了么、大众点评,美团与饿了么都在大战,都在为自己做推广与优惠来抢占市场,这场PK我就不参与啦!于是乎我也放弃这方面需求。
3.住
关于“住”的产品,比如:百度地图、携程、艺龙、58同城、赶集网、搜淘网,除了上面提到的,其实关于住的产品还有很多很多,比如还有每个酒店的产品等,于是乎我也放弃这方面需求。
4.行
关于“行”的产品,比如:去哪儿网、12306、途牛网、驴妈妈、穷游,除了上面提到的,其实关于行的产品还有很多,而且去哪儿网和12306的用户量碾压其他产品,实力如此强大,何必和它们抢呢?于是乎我也放弃这方面需求。
5.耍
关于“耍”的产品,其实有很多种类,比如我想去旅游,那么关于“行”的产品足够满足你了;我想看视频,那么各种视频App产品(土豆、优酷、腾讯、爱奇艺、搜狐、芒果、乐视、暴风)足够满足你了,所以“耍”的产品很广泛。

我在想大部分上班族除了节假日放假才会去旅游,那么平时周六、周日去旅游的话时间肯定不够,只能在“本地玩”,好,想到这,我准备做一款“__本地玩__”App产品。


App产品的特点及简介

产品名:一起玩吧(待定)
域名:play8b.com
简介:一款能一起玩的产品
详介:通过使用该App,可以找到附近想一起约来玩的年轻人,可以一起健身、一起运动、一起唱歌、一起跳舞、一起吃饭、一起打牌、一起看书、一起逛街、一起冒险、一起逛公园、一起LOL,只要是健康的,什么都可以一起。
显性需求:帮助大家在本地与陌生人一起玩
隐性需求:以玩会友,人这辈子真心朋友不多,希望通过该App能让大家找到真心朋友,同性也好、异性也罢。
特点:功能简单、安全


2.产品设计

当自己想做的产品确定好后,下一步就是设计。
产品设计首先需要原型,制作原型的常用工具有Axure、Mockup Builder等,这些工具都是重量级的,我想给大家介绍下我使用的原型工具—–墨刀。
我使用墨刀做的引导页及首页,详情可以点击一起玩吧
该工具很好用,非常适合做移动端的原型,具体的实现在这里就不一一细说啦。

对于无UI设计经验的我,用到的图片是从网上Copy的,只是借用,等功能完善差不多的时候,会把图片都改掉的,所以请放心,不会出现版权问题的哦。

产品设计的原型图与自己真正做的产品图对比:
墨刀做的首页:
首页


真正做的首页:
首页


墨刀做的选择城市:
选择城市


真正做的选择城市:
选择城市

尽管原型图与真正做App的图有差异,但是原型图的借鉴是必不可少的。因为原型设计出来才知道可不可行,以免给第三步产品实现填坑。


3.产品实现(着重介绍该内容)

当产品原型有个大概的体系后,就进入了下一步最最重要的环节:产品实现。在这个阶段,你可以想尽任何办法去实现和开发原型,想要高效率、少走弯路地开发产品,你可以挑选已有的任何框架、选择有用的技术及服务,更可以挑选别人已做好的半成品。
因为有些框架(公司自主研发的)不是开源且涉及到版权等问题,在这里我就不一一说明,只要记住一句话:产品实现你可以不折手段。

当框架选择好后,接下来做的是将每个模块的每个功能点进行细化,即你需要把每个功能点详细罗列出来,然后好挨个实现。
我当时想既然是个完整的app,模块必不可少的肯定有引导页,主页,选择城市,因此我在这一个多月的时间做了以下功能:

1.P图(android与ios):

  • 制作app的icon图标
  • 制作splash screen启动界面图片
  • 制作引导界面图片

p图

2.引导页:

  • 左右滑动图片及小点点切换
  • 最后一页,点击“马上体验”进入主页
  • 引导页只能进入一次

foot

3.首页:

  • 首次进入定位城市
  • 点击“城市名”进入选择城市页面
  • 页面的banner
  • 页面的响应式布局

home

4.选择城市:

  • 进入页面后定位城市
  • 点击“返回图标”返回到首页
  • 点击“选择城市title”返回到最上面
  • 点击“城市名”可切换城市
  • 调用ajax获取数据绘制页面
  • 上下滑动锚点到达对应位置
  • 向下滑动进行下拉分页
  • 输入值进行搜索城市

选择城市

尽管只有简简单单的三个页面,但是里面的业务逻辑和功能点还是挺多的,当功能点确定后,就需要找到每个功能点的解决方案或具体实现。

在接下来的章节里,我会一一介绍我是怎么实现上面说到的每个功能点的。


P图

1.IOS图标、图形尺寸详解:
高清晰度的iPhone和iPod touch(单位:像素)
启动影像:640x960
APP图标:114x114
App Store商店:1024x1024
Spotlight搜索小图标:58x58
文档图标:44x58
Web Clip图标:114x114
工具栏和导航栏图标:约40x40
标签栏的图标:约60x60
报刊杂志:最长的边缘1024像素(最小)

iPhone和iPod touch(单位:像素)
启动影像:320x480
APP图标:57x57
App Store商店:1024x1024
Spotlight搜索小图标:29x29
文档图标:22x29
Web Clip图标:57x57
工具栏和导航栏图标:约20x20
标签栏的图标:约30x30
报刊杂志:最长的边缘1024像素(最小)

icon

2.Android图标、图形尺寸详解:
icon

Splash尺寸大小:(单位:像素)
320X480、480X800、720X1280

3.修改App名字所在位置
Android:res/values/strings.xml里面
IOS:打开Xcode,选中项目后点击Info,键名为”Bundle display name”


引导页

功能点:左右滑动图片及小点点切换
实现原理:强行将4个背景图div放在同一行,注册touch相关事件改变容器的平移坐标位置,小点点用css3绘制,添加个css样式,切换时添移该样式即可。
核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
#slides{
white-space: nowrap;
font-size: 0;
-webkit-transform: translate3d(0px,0,0);
}
.circle{
box-sizing:border-box;
border-radius: 5px;
border: solid #ccc 3px;
}
.current{
border: solid #fff 3px;
}
1
2
3
4
5
6
slideObj.on('touchstart', slideStart).on('touchend', slideEnd).on('touchmove', slide);
...
slideObj.css({'-webkit-transform':'translate3d(' + pixelOffset + 'px,0,0)'});
...
$('.circle').removeClass('current');
$('.circle:nth-child('+currentSlide+')').addClass('current');

功能点:最后一页,点击“马上体验”进入主页
实现原理:“马上体验”按钮其实是图片的一部分,于是可以在“马上体验”字上面弄一个同等大小的div,默认是隐藏的,只有当用户滑到最后一个div时将其显示出来,添加个隐藏css样式,切换时添移该样式即可。
效果图:
foot

核心代码:

1
2
3
.displayNone{
display: none;
}
1
2
3
4
5
6
7
8
9
if(currentSlide == 3){
if(slideBtn.hasClass('displayNone')){
slideBtn.removeClass('displayNone');
}
}else{
if(!slideBtn.hasClass('displayNone')){
slideBtn.addClass('displayNone');
}
}

功能点:引导页只能进入一次
实现原理:用户安装app后首先进入引导页,如果用户点击了“马上体验”,则以后都不会再进入引导页,而直接进入首页,如果用户没点击则还会进入引导页,于是需要一个永久标识符去判断即可。
核心代码:

1
2
3
4
5
6
7
8
9
10
11
//初始化时第一个加载的panel
if(localStorage.getItem('isFirstShow')){
$.ui.defaultPanel = 'index';
}else{
$.ui.defaultPanel = 'foot';
}
...
//进入首页时
if(!localStorage.getItem('isFirstShow')){
localStorage.setItem('isFirstShow','true');
}

首页

功能点:首次进入定位城市
实现原理:用户在首次进入主页时存一个永久标识符且使用百度SDK定位即可。
核心代码:

1
2
3
4
5
6
if(localStorage.getItem('user_city')){
//获取城市名到页面
}else{
//通过百度SDK获取用户位置并存储到localStorage
$.wwIndex.locateMyPos();
}

功能点:点击“城市名”进入选择城市页面
实现原理:对包在“城市名”和向下图标的div注册click事件即可。
核心代码:过于简单,所以无


功能点:页面的banner
实现原理:以前封装的banner,所以直接拿来用就可以了。大致原理就是监听window.onresize事件获取不同设备屏幕的宽度和高度,计算出每张图片div的宽度及包图片的容器的宽度,使用相对、绝对布局、改变left值即可。
核心代码:封装后压缩混淆了js导致不可读,所以无


功能点:页面的响应式布局
实现原理:
疑点一.因为不同手机设备尺寸不同,如:iphone4s:320X460,魅蓝note2:360X615,所以四行三列的分类Logo需要写算法。
疑点二:border边框属性在android上无效,所以只能用背景色充当边框,留1px的缝隙出来即可。
疑点三:整个主页页面由头部、面板、底部导航构成,头部和底部高度不变,计算出四行三列的分类Logo的高度和,剩余的高度让banner填满即可。虽然这样不怎么好看,待后期再考虑啦。还要注意的是头部高度,ios比android要多20px。
效果图:
魅蓝note2的效果
魅蓝note2


iPhone4s的效果
iphone4s

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//总高度,是苹果设备则少20px
cHeight = os.ios ? cfg.cHeight - 20 : cfg.cHeight;
...
//每个方块宽度和高度
var girdWandH=cfg.cWidth/4-1;
//第二行中间合并两个方块后div的宽度
var imgWidth=(cfg.cWidth/4)*2-1;
//banner高度=总高度-头部高度-底部导航高度-四行三列高度
var bannerH=cfg.cHeight-43-48-(cfg.cWidth/4)*3;
...
//设置第二行中间合并两个方块后div的高度和宽度
$('.logoCenter').css({'width':imgWidth+'px','height':girdWandH+'px'});
//设置每个方块的高度和宽度,且向右向上1px把背景色显示出来当边框
$.('.blk').css({'width': girdWandH+'px', 'height': girdWandH+'px','margin-right':'1px','margin-top':'1px'});

选择城市

功能点:进入页面后定位城市
实现原理:与首页“首次进入定位城市”功能点相同,使用百度SDK定位即可,唯一区别是首页页面定位城市只会执行一次,选择城市页面定位城市会执行多次,如进入选择城市页面后返回到主页,又进入,又返回主页,又进入,还是可以定位。
核心代码:与“首次进入定位城市”代码相同,但无判断。


功能点:点击“返回图标”返回到首页、点击“选择城市title”返回到最上面、上下滑动锚点到达对应位置、点击“城市名”可切换城市
实现原理:即进行事件的注册与绑定,前一个实现可用history锚点,中间两个实现可用scrollTo,最后一个实现可获取li的value改变即可。
核心代码:较简单,无


功能点:调用ajax获取数据绘制页面,向下滑动进行下拉分页、输入值进行搜索城市
实现原理:
疑点一:因为有几百上千个城市名,所以在网上找了个json文件,模拟从后台接口获取。
疑点二:返回后的数据不可能全部将其绘制到页面,如有1000个城市名就要绘制1000个li出来,对于移动web来说重排与重绘性能消耗是巨大的,曾试过一个办法,即显示前20个,剩余的都将其隐藏掉,结果手机卡顿了几秒,为了影响用户体验度,所以放弃了该想法。最终的解决方案是:缓存+前端分页+模板引擎
疑点三:当用户输入值进行搜索城市时,用户每打一个字,就会查询一次,很慢也很卡顿。最终的解决方案是:用户在输入时延迟800ms执行ajax查询数据。
核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<script id="JdotTplCitys" type="text/x-dot-template">
{{~ it.rlist :city }}
{{? city.pinyin == 'A'}}
<li data-group="#" class="mui-table-view-divider mui-group-list-group">定位城市</li>
<li class="mui-table-view-cell localCityLi">
<div id='localCityDiv' class="hotCityDiv"><i class="fa fa-spinner fa-spin"></i>定位中...</div>
</li>
<li data-group="$" class="mui-table-view-divider mui-group-list-group">热门城市</li>
<li class="mui-table-view-cell hotCityLi">
<div class="hotCityDiv">北京</div>
<div class="hotCityDiv">上海</div>
<div class="hotCityDiv">广州</div>
<div class="hotCityDiv">深圳</div>
<div class="hotCityDiv">成都</div>
<div class="hotCityDiv">武汉</div>
<div class="hotCityDiv">天津</div>
<div class="hotCityDiv">西安</div>
<div class="hotCityDiv">南京</div>
<div class="hotCityDiv">杭州</div>
<div class="hotCityDiv">重庆</div>
</li>
<li data-group="A" class="mui-table-view-divider mui-group-list-group">A</li>
{{?? city.pinyin == 'B'}}
<li data-group="B" class="mui-table-view-divider mui-group-list-group">B</li>
{{?? city.pinyin == 'C'}}
<li data-group="C" class="mui-table-view-divider mui-group-list-group">C</li>
{{?? city.pinyin == 'D'}}
<li data-group="D" class="mui-table-view-divider mui-group-list-group">D</li>
{{?? city.pinyin == 'E'}}
<li data-group="E" class="mui-table-view-divider mui-group-list-group">E</li>
{{?? city.pinyin == 'F'}}
<li data-group="F" class="mui-table-view-divider mui-group-list-group">F</li>
{{?? city.pinyin == 'G'}}
<li data-group="G" class="mui-table-view-divider mui-group-list-group">G</li>
{{?? city.pinyin == 'H'}}
<li data-group="H" class="mui-table-view-divider mui-group-list-group">H</li>
{{?? city.pinyin == 'J'}}
<li data-group="J" class="mui-table-view-divider mui-group-list-group">J</li>
{{?? city.pinyin == 'K'}}
<li data-group="K" class="mui-table-view-divider mui-group-list-group">K</li>
{{?? city.pinyin == 'L'}}
<li data-group="L" class="mui-table-view-divider mui-group-list-group">L</li>
{{?? city.pinyin == 'M'}}
<li data-group="M" class="mui-table-view-divider mui-group-list-group">M</li>
{{?? city.pinyin == 'N'}}
<li data-group="N" class="mui-table-view-divider mui-group-list-group">N</li>
{{?? city.pinyin == 'P'}}
<li data-group="P" class="mui-table-view-divider mui-group-list-group">P</li>
{{?? city.pinyin == 'Q'}}
<li data-group="Q" class="mui-table-view-divider mui-group-list-group">Q</li>
{{?? city.pinyin == 'R'}}
<li data-group="R" class="mui-table-view-divider mui-group-list-group">R</li>
{{?? city.pinyin == 'S'}}
<li data-group="S" class="mui-table-view-divider mui-group-list-group">S</li>
{{?? city.pinyin == 'T'}}
<li data-group="T" class="mui-table-view-divider mui-group-list-group">T</li>
{{?? city.pinyin == 'W'}}
<li data-group="W" class="mui-table-view-divider mui-group-list-group">W</li>
{{?? city.pinyin == 'X'}}
<li data-group="X" class="mui-table-view-divider mui-group-list-group">X</li>
{{?? city.pinyin == 'Y'}}
<li data-group="Y" class="mui-table-view-divider mui-group-list-group">Y</li>
{{?? city.pinyin == 'Z'}}
<li data-group="Z" class="mui-table-view-divider mui-group-list-group">Z</li>
{{??}}
<li class="mui-table-view-cell mui-group-list-item" data-label="{{=city.label}}">{{=city.name}}</li>
{{?}}
{{~}}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//前端分页
queryByPage:function(arr,pageNo,pageSize){
var pageTotal=arr.length % pageSize == 0 ? arr.length/pageSize : Math.floor(arr.length/pageSize)+1;
return {pageTotal:pageTotal,data:arr.slice((pageNo-1)*pageSize,pageNo*pageSize)};
}
//前端查询后分页
queryBySearch:function(arr,keyword){
var data=[];
for(var i in arr){
if(arr[i]['label'].toUpperCase().indexOf(keyword.toUpperCase()) != -1){
data.push(arr[i]);
}
}
return data;
}
...
//对执行ajax后缓存的data进行分页
var dataObj=$.feed.queryByPage(data,nSpage,20);
...
searchInput.addEventListener('input', function() {
var keyword = this.value;
clearTimeout(cfg.flag);
cfg.flag=setTimeout(function(){
self.search(keyword);//调用ajax查询数据
},800);
}, false);
...
//如果没有生成模板的方法,则先生成模板,再调用模板生成html
if(!$.isFunction(dotRenders.citys)) {
dotRenders.citys=doT.template(document.getElementById('JdotTplCitys').text);
}
dotRenders.citys(data);

总结

虽然看上去只有简简单单的三个页面,但是页面与页面的交互和细节功能都还没展示出来。因为我的app还没有做完,所以关于第四步“产品上架”与第五步“产品推广”的内容等以后再补充。
这些东西就先讲到这里,接下来的日子我会接着从第二步产品设计开始继续设计和完善自己的app,所以博客的更新又会延迟一段时间,请大家多多谅解。