分类 原创 • 文章 下的文章

本文介绍了本人接触过的、试用过的、使用过的一些针对Android平台的第三方服务平台,涵盖了从开发、测试、发布、运营的整个流程,了解了这些平台,肯定会对你的APP开发起到很大的简化作用,可以大大提高开发效率。

推送

极光推送

极光推送是我接触的最早的推送平台,那还是2011年,当时国内基本上就这么一家提供推送服务,并且这家公司也从最开始的小团队逐渐成长为了大公司。
该平台支持针对Android、iOS、WinPhone的消息推送,服务端支持多种语言,后台控制台界面也相对比较友好。
极光推送的特色在于可以支持多种推送方式,可以把用户按照标签分类,并且针对标签推送。
不足之处在于复杂网络情况下,推送有时候不及时。

网址:https://www.jpush.cn/

个推

在使用了极光推送一段时间后,2013年开始,大量的推送平台开始出现了,其中个推是我个人认为很不错的一个选择。
该平台只支持Android、iOS的推送,但是相比较激光推送,其稳定性较好。

网址:http://www.getui.com/

友盟

友盟最开始是做数据统计分析的,好像是从2013年开始提供了推送服务。

网址:http://www.umeng.com/

百度云

相比较于极光、个推等中小型公司,百度的服务我个人认为是最可靠的,但是其垃圾的后台界面让我不能忍受。

网址:http://developer.baidu.com/cloud/push

云巴

云巴是从极光推送分裂出来的,个人没有试用过,不过基于极光的技术,应该是有保障的。

网址:http://yunba.io/

华为推送

号称是电信级别的推送能力,但是本人一向对华为的服务不感冒。

网址:http://developer.huawei.com/push

小米推送

如果你的应用小米用户居多,那么必然要使用小米推送,系统级别的推送可以给用户更好的体验。

网址:http://dev.xiaomi.com/doc/?page_id=1670

 

以上是我接触过的推送平台,基本上针对开发者都是免费服务,并且使用方法也是大同小异,具体选择什么平台,需要针对各自的实际情况进行测试之后再做选择。

 

测试

Android版本多,机型多,所以兼容性测试显得尤其重要。国内也有几家公司提供了云测试服务,主要集中于针对APP的功能测试、性能测试和兼容性测试。

Testin云测

Testin云测试平台是一个基于真实终端设备环境,基于自动化测试技术的7x24云端服务。Testin在云端部署了300多款1000多部测试终端,并开放这些智能终端给全球移动开发者进行测试,开发者只需在Testin平台提交自己的App应用,选择需要测试的网络、机型,便可进行在线的自动化测试,无须人工干预,自动输出含错误、报警等测试日志、UI截图、内存/CPU/启动时间等在内的标准测试报告。支持Android与iOS,它的业务也较为全面。

网址:http://www.testin.cn/

百度云测试MTC

MTC是百度云面向移动和web开发者提供的服务,能够满足一般的测试需求,包括当前的热门机型,还支持云端客户端回放。它还提供一个云众测服务,就是开放者上传App,百度提供给用户下载测试,然后将反馈收集返回给开发者,这在国外是一种比较火的方式,不过目前貌似没有做起来。

网址:http://mtc.baidu.com/

易测云

易测云由我的老东家东软集团出品,是一个专业为移动APP产品提供适配测试、性能测试、遍历测试、功能测试等多种服务的真机自动化云测试平台,主要为所有移动APP产品的开发者和测试者、以及需要定制化服务的企业级用户,提供安全、专业、高效、易用的自动化云测试服务;强大的录制脚本插件;详细实用的测试报告;以及简单人性化的操作体验。

网址:http://www.yiceyun.com/

 

本人在实际的项目中用的是Testin,它的报表功能确实不错,值得推荐。

统计

APP发布之后,需要对用户数、使用时长、启动次数等做统计,相信这也是不少产品经理的主要KPI了,我这里介绍两个提供统计服务的平台。

友盟

友盟是老牌的提供应用统计分析的平台了,本人一直在用,数据及时并且各种报表比较完备,强烈推荐。

网址:http://www.umeng.com/

魔方

基本上提供了友盟一样的功能,不过先入为主,二者只能选择一个,我肯定选择友盟。

网址:http://www.imofan.com/

 

应用数据统计,使用友盟是第一选择。

即时通讯

随着微信的火爆,以及各种APP都要加入社交功能,2014年开始,提供即时通讯服务的平台一下子就涌现出了好多家。

环信

环信是其中最热门的一家公司了,融资、免费,在百度投放了大量广告。
其实现了类似微信的单聊,群聊,发语音,发图片,发位置等功能,并且300万以下注册用户完全免费,这个意思就是针对大多数APP是完全免费的。
他们提供的Demo程序本人试用过,效果确实不错,值得推荐。

网址:http://www.easemob.com/

融云

“多年专注于移动互联网即时通讯,提供稳定可靠的 App 沟通能力”,这是他们的口号。
但是移动互联网才几年,微信才几年,一家口号就明显夸大其词的公司,个人不信赖。

网址:http://www.rongcloud.cn/

 

社交分享

社交分享其中最重要的是两个功能:第三方登陆和一键分享。

友盟

友盟号称提供"APP开发及运营一站式解决方案",这其中肯定会包含社交分享的功能。

网址:http://www.umeng.com/

sharesdk

这个应该算是国内最出名的移动端社交分享SDK了,很可靠,一直在用。

网址:http://mob.com/

短信云

短信云主要提供发短信的功能,尤其是APP用户注册需要验证手机号码的时候,必用。

sharesdk

现在大优惠,基本上不用付费哦,而且经过博主实测,很及时,很好用。sharesdk的服务果然值得信赖。

网址:http://mob.com/

邮差云

这家是传统的短信提供商转型的产品,没使用过,明显的缺乏互联网(免费)思维,不推荐。

网址:http://www.smslinkapi.com/

后台baas

Baas,后端即服务。如果你对服务端编程没什么经验的话,Baas可以提供基础的服务端功能。

bmob

很奇怪的一家公司,国内算是起步很早的,但是一直发展缓慢,从去年开始又有比较大的动作了,而且官网提供了Demo,很不错。

网址:http://bmob.cn/

LeanCloud

这家公司更奇怪,以前叫做AVosCloud,不过因为域名带了AV两个字母,经常被屏蔽,就改为现在的域名了。这家我现在在使用中,推荐。

网址:https://leancloud.cn/

云存储

云存储提供文件存储服务,尤其是在前两年瀑布流盛行的时候,提供图片存储服务。

七牛

由技术大牛创业的一家公司,商务营销做得也不错,而且经常有优惠,推荐。博主也在使用。

网址:http://www.qiniu.com/

通过 https://portal.qiniu.com/signup?code=3lfl9s1wivioe 这个链接注册可以给我增加流量哦。

又拍

比较早的图片存储服务商,对图片的存储/处理都有很好的支持,博主一直在用。

网址:https://www.upyun.com/index.html

广告

admob

Google的服务,点击率还不错,不像国内很多厂商会有扣量,第一选择。

有米

国内做的比较好的了,不过我是不待见国内这些做移动广告的。

等等..

应用平台

由于国内上不了Google Play,滋生了很多应用分发市场,最盛的时候估计有上千家,不过随着时间的推移,慢慢就只剩下巨头们了。

360

360凭借流氓手段在电脑上占领了很大市场,所以对于手机端也很有优势。下载量大,审核快,推荐。

http://dev.360.cn/

应用宝

腾讯的应用宝很奇怪,感觉应该是量很大,但是博主的应用在应用宝的下载量明显不如360。

http://open.qq.com/

百度

百度可谓是一个巨头,旗下百度手机助手、91、安智,都是量很大的市场,而且只要提交百度,会自动同步到安智、91,值得推荐。

http://developer.baidu.com/mobile

豌豆荚

豌豆荚是独立于巨头的一家平台,最近一直要转型,而且频频传出一些不好的消息,个人不看好。

http://open.wandoujia.com

小米

小米应用市场凭借小米手机的火爆,也算是一家巨头了。

http://dev.mi.com/

其他

其他想木蚂蚁、安卓、EOE、机锋等等,基本上不做考虑了。

一键提交多平台

有了那么多应用市场,如果要一个一个去提交审核的话,也是需要花费不少时间的。一键提交平台就是只要在这个平台提交了,会推送到其他各个平台。

zhuamob

目前博主只知道这一家,仅仅是试用过,如果你有这方面的需求的话,不妨试试。

快速开发

快速开发指的是利用一些现有的工具、框架等,可以快速搭建、开发APP。

apicloud

号称“积木拼装”式的开发跨平台App"。貌似是提供了一个基于Eclipse的IDE环境,而且有配套的云端服务,还有模块化的可复用的模板,一切看起来都很美好。有兴趣的可以尝试。

juhe.cn

聚合数据,提供了很多API,包括天气、身份证、菜谱等等,很全的数据。强烈推荐。

 

以上这些是我试用过的或者接触过的服务平台,其实每个平台也都提供了多种服务,大家如果能活用这些服务的话,相信会对APP开发带来很大便利。

本文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn

大道三千,何以证道?

最近有私信、邮件给我咨询一些职业生涯规划的同学,我在这里以过来人的身份给大家一些建议。

任何行业,任何职位,无论高低,无论大小,都可以分为广博、精深两个方向。

精深自然指的是在某一领域造诣深厚,当然也不会是对别的技术一无所知,否则就成了独腿人,岂能走得远?
广博自然指的是涉及面广,知识丰富,对各种技术都有所涉猎,当然不会是浅尝辄止的接触,所谓的全栈程序员自然属于这一类型,我本身也是这一类型的。

精深

首先说一下精深,作为Android程序员,所必需掌握的基础知识:

0. Java核心编程

Android的开发语言是Java,所以Java是作为一名Android程序员所必须掌握的。
我这里推荐几本书,Think in Java(Java编程思想)、Core Java(Java核心技术)、Effective Java,看过这三本书,你的Java技术就相当有保障了。

1. Android基础知识

Android四大组件及生命周期
Layout布局方式
各种控件的使用方式
Activity间传值、Activity与Fragment间传值
不同分辨率的界面适配
事件及回调机制
本地数据存储
HTTP访问网络
Widget的使用
针对各个Android版本的适配

2. 进阶

adb命令的使用
Activity、Service、Broadcaster互相调用
通知栏Notification的使用
Alarm的使用
SMS的使用
线程的使用
Handler消息机制
AsyncTask异步调用机制
Intent、Intent-filter的使用
使用自定义style、theme、drawable等方式美化界面
动画效果的使用
硬件调用,摄像头、录音、录像、地理位置
APP版本升级、本地数据库版本升级
音频、视频处理
自定义布局、界面
WebView的使用以及WebView和本地代码间通讯
各种第三方类库的使用
各种第三方平台的使用

3. 高阶

2D/3D图形应用
传感器的使用
蓝牙、NFC等的使用
性能优化
NDK的使用
Framework层修改
自定义ROM
适配Android系统到不同的硬件设备

我个人认为,
基础阶段可以制作出能用的功能简单的APP、这是一个普通开发人员所必须掌握的;
进阶阶段可以制作出好用的能吸引人的APP、这是一个技术负责人所必须掌握的;
高阶阶段则可以制作出MIUI这样的系统级别的框架了;更可以进军眼下火热的智能家居、智能眼镜行业了。这是一个技术总监所应具有的素质。

 

广博

如果你要是想走广博这条路,那么会有很多新奇的、好玩的技术等着你。

但是首先,你要达到Android进阶水准,否则就不要自称是Android程序员了。
当你达到了进阶水准后,你的兴趣可能就不仅仅止于Android,可能你对iOS有兴趣,对服务端开发有兴趣,对Web开发有兴趣,对HTML5开发有兴趣,对设计有兴趣,对游戏开发有兴趣,等等等等,我这里对每一个方面都做一些简单介绍吧。

iOS

iOS和Android就是一对儿双胞胎,很多人在做了一段时间的Android开发后,或者做了一段时间的iOS开发后,都想学学对方的技术。
个人认为iOS的学习曲线较Android要高,学习成本也较大,苹果三件套是必备的,不过iOS开发真的很火呀,而且开发出来的APP美观度比Android不知道高到哪里去了。

服务端

Android APP是客户端,那么对于大多数的APP来说,是需要服务端提供服务、数据之类的,那么现今流行的服务端有哪些语言呢?
首推Java,Java的框架多呀,开发快呀,资料多呀,像SSH、Spring MVC、Jfinal、ofBiz呀,这些都是很流行的框架,我这里推荐Jfinal,典型的快速开发框架。
其他像Python的Django、Ruby on Rails、ThinkPhp等都是动态语言Web框架的经典,可以根据各自兴趣着重学习一种。
最不推荐的就是.Net,虽然VS很强大,WebService也很不错,但是个人认为不像Java那么规范,也不像动态语言那么快速,最不喜欢.Net框架,个人偏好而已。

前端

我把前端技术定义为HTML(5)、CSS(3)、JS等这些技术的合并体,网页所最终呈现出来的效果都是由我们的前端工程师完成的。
而且现在有非常多的前端框架,像Bootstrap、JQuery UI、Semantic UI、NEJ、Pure UI、Amaze UI等等,都是现在很流行的框架,大家可以学习学习。

设计

我认为的设计分为交互设计、视觉设计,
视觉设计肯定要掌握Photoshop之类的设计工具,并且有一定的美感(这个就仁者见仁了),
交互设计是现在设计中很需要强调的一部分,很多网站、APP外观看起来也不错,但是实际使用就感觉有些别扭,不尽如人意,那就是交互设计有缺陷。
一个经典问题:弹出框的[确定][取消]按钮,iOS默认确定在右,Android默认确定在左,那么如果你是一个交互设计师,你的APP需要同时在两个平台发布,这个按钮你应该如何设计呢?

游戏

现在游戏也是多种多样,页游、端游、手游一波一波的,我这里只对手游有过了解,所以只说说这一部分。
手游现在国内最火的框架就是cocos2d了,支持多种平台,而且有很多的资源可以参考;
另外像Unity、OGEngine、AndEngine、LGame、也都是不错框架。

等你掌握了以上这些技术之后,你就是一个典型的全栈工程师了,从设计到开发到运营你都可以胜任,这个时候,你就可以跟老板说:我可以干五个人的活,只要三个人的工资了。
更可以在家Soho了,作为自由职业者,或者创业也都是不错的选择的。

看到这里,你肯定已经有了自己的选择了。

我个人的看法是,走自己的路,让别人说去吧。

本文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn

相信已经有一些同学已经对怎么开发Android应用有了一些感觉了,在一些网友发给我的工程中有很多也都加入了自己个性化的东西,这样很好。

现在我们回过头来看看我们都学到了哪些知识。

 

第一天

万事开头难,我们在这一天里搭建了基础的开发环境,并且做出了第一个页面,俗称“Hello,world”。

 

第二天

我们初步了解了布局文件,并且使用了第三方库-xUtils进行了http访问,调用了百度天气API并且把天气信息以文本的形式展现出来了。

 

第三天

我们较为深入的学习了常见的各种布局,并且重点学习了ListView 的使用。

 

第四天

天气信息终于能够以列表的形式展示了,并且还有天气小图标哦。这里有一个重要的类:BaseAdapter,一定要熟练掌握这个类,会经常用到。

 

第五天

进行了理论知识的补充,对Android的体系架构、项目结构有了初步认识。

 

第六天

有突破的一天,我们学习了百度定位的使用,并且学习了Preference这一经常会用到的本地数据存储方式。

 

第七天

理论知识进一步深入,对于Activity的生命周期有了更加深刻的理解。

 

第八天

这一天的内容很多很多,并且都很重要。主要是Activity间传值、Application的使用、本地数据库的使用、文本框事件监听,并根据我们对Activity生命周期的理解重构了代码。

 

第九天

主要学习了Fragment的使用,以及Fragment和ViewPager的搭配使用,可以做出左右滑动的界面哦。

 

第十天

这一天的主要工作就是打包、发布我们的天气预报APP了。

 

经过这十天的学习,相信很多同学都可以初步开发出一些简单的APP了,如果大家还有什么疑问、建议,都可以加入我们的Android学习群:44133900,我们一起来交流。

此系列文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn

本次是这个教程的最后一篇了,我们的APP开发基本上已经可以宣告完成了,接下来的工作就是如何发布推广运营了。

 

广告平台

古人云:兵马未动,粮草先行。我们身为APP开发者就需要考虑如何从APP盈利,目前通常的做法主要有APP收费、APP免费+内购及嵌入广告三种方式,我们这个简单的APP想让用户付费是比较有难度的,那么就只能通过嵌入广告的方式来赚取一点广告费了。

我经常使用的广告平台就是Admob了,不过Admob国内被屏蔽,但是广告依然可以展示,各位程序员一定要学习科学上网,否则。。。

话说回来,国内的广告平台也有很多,像百度、腾讯、360、有米、万普等等,都提供了很简单方便易用的服务,我们只需要按照官方文档一步步就可以加入广告了,这里我就不写这些内容了。例程里面也不会有含有广告的。

我这里简单列一下:

百度联盟:http://union.baidu.com/

腾讯广点通:http://e.qq.com/

360:http://dev.360.cn/mod/mediaV/intro

有米:http://www.youmi.net/

万普:http://www.waps.cn/

 

 

APK打包

下面,我们来看一下怎么发布APP。

总共分三步,打包、签名、上传,呵呵,简单吧,如果使用工具就会更加简单。

在Eclipse中右键点击项目,选择[Android Tools]->[Export Signed Application Package...],

QQ截图20150117142704

点击[Next],会出现选项,[Use existing keystore]和[Create new keystore],如果您以前有发布过Android APP经验话的,肯定有这个keystore文件。不过鉴于我们是第一次开发APP,这里我们就老老实实选择[Create new keystore]就可以了。

QQ截图20150128194315

输入密码之后,点击[Next],这个界面要填的内容非常多,其中有一项很重要:

Validity (years),这个代表了这个keystore的有效期,我们就填1000年就好了,也不要太多 :)。

其他项目均可以随便填写。

QQ截图20150117144511

然后继续[Next],这里就是我们的打包文件的路径了,

QQ截图20150128194438

点击[Finish],稍等几秒钟,我们第一个APP就开发完成了。

最后,我们把生成的apk文件安装到手机上试试看,是不是很有成就感!!!!!

 

就这样,我们的APP就开发完成了,赶快让它进入市场,接受大家伙的批评吧。

发布到应用市场

Android应用市场国内那是相当乱了,我们需要抓大放小,把我们的APP上传到几个大市场就可以了。

我通常的选择是百度、腾讯应用宝、360、豌豆荚、安智、木蚂蚁、应用汇,关于如何发布,请参考各大网站的介绍。

我这里简单列一下:

百度:http://developer.baidu.com/

应用宝:http://open.qq.com/

360:http://dev.360.cn/

豌豆荚:http://open.wandoujia.com/home

安智:http://dev.anzhi.com/

木蚂蚁:http://dev.mumayi.com/

应用汇:http://dev.appchina.com/

 

 

本系列教程到此就算是结束了,大家一步步从零开始,做出了一个虽然简单但是完整的天气预报APP,也用到了Android里面最核心、也是开发项目过程中必须用到的Activity、数据库、HTTP、图片展示、数据转换等知识,这些知识还需要大家在实际的工作中慢慢加深理解才能熟练应用,才能开发出更好用的APP。

 

最后祝大家工作愉快,生活顺心。

 

写到这里,感觉我们的APP还是有很多缺陷,比如:

1. 界面真的很糟糕,有很大的美化余地

2. 第一次启动的时候,感觉比较慢

3. 选择城市的页面,肯定有更好的处理方法

4. 只显示天气是否太简单了?可以加入类似墨迹天气一样的穿衣指南、PM25等等

5. 能否每天早上7点钟自动播报天气?

等等等等

这些您可以作为之后的版本升级内容逐渐加进去哦。

如果您的天气预报APP真的上线了,请一定让我知道,我去给你点赞。等着大家的好消息!!!

 

此系列文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn

 

 

 

接着昨天的任务,我们今天实现左右滑动可以切换城市的功能。

这里就需要引入新的控件了,Android给我们提供了ViewPager,我们就使用这个,同时,显示天气的界面我们也不再使用Activity,而改为Fragment。

Fragment

Fragment可以认为是可复用的UI组件,有自己的布局和完整的生命周期,可以处理本身的事件,但是必须依存于Activity,不能脱离Activity而存在。

08162501-0d1530c0e072468b8b0a68c1c8913bce

 

可以看出来,Fragment的生命周期跟Activity非常相似,并且会随着Activity的销毁而销毁。

 

下面,我们来战。

首先,新建一个Fragment的子类,取名为WeatherFragment。

public class WeatherFragment extends Fragment
{
    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
    }

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        return super.onCreateView( inflater, container, savedInstanceState );
    }
}

这是用来显示天气的界面,而我们之前是直接在Activity中显示的,需要把这部分代码给移植到Fragment中。

这是个麻烦的过程,不过不要紧,慢慢来。

新建一个Layout,取名为frag_weather.xml,然后把activity_main.xml中的代码给复制过来,

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"  >

    <ListView
        android:id="@+id/weather_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" >
    </ListView>

</RelativeLayout>

 

我们让WeatherFragment使用新的Layout,再把MainActivity中关于天气的代码移植到WeatherFragment中,

public class WeatherFragment extends Fragment
{
    @ViewInject( R.id.weather_list )
    private ListView lstWeather;

    private WeatherAdapter adapter;
    private BaiduData data;

    private List<WeatherDataBean> datas;
    private String city;

    public void setCity( String city )
    {
        this.city = city;
    }

    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );

        datas = new ArrayList<WeatherDataBean>();
        adapter = new WeatherAdapter( getActivity(), datas );
    }

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState )
    {
        View view = inflater.inflate( R.layout.frag_weather, null );
        ViewUtils.inject( this, view );

        lstWeather.setAdapter( adapter );

        getWeather();

        return view;
    }

    private void getWeather()
    {
        HttpUtils http = new HttpUtils();

        RequestParams params = new RequestParams();
        params.addQueryStringParameter( "location", city );
        params.addQueryStringParameter( "output", "json" );
        params.addQueryStringParameter( "ak", "YknGmxIoPugT7YrNrG955YLS" );

        http.send( HttpMethod.GET, "http://api.map.baidu.com/telematics/v3/weather", params, new RequestCallBack<String>()
        {
            @Override
            public void onSuccess( ResponseInfo<String> responseInfo )
            {
                String weather = responseInfo.result;

                Gson gson = new Gson();
                data = gson.fromJson( weather, BaiduData.class );

                datas.clear();
                datas.addAll( data.getResults().get( 0 ).getWeather_data() );
                adapter.notifyDataSetChanged();

                Log.v( "onSuccess", data.toString() );
            }

            @Override
            public void onFailure( HttpException arg0, String arg1 )
            {
                Log.v( "onFailure", arg1 );
            }
        } );
    }
}

 

然后,修改主页面activity_main.xml为:

<?xml version="1.0"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:id="@+id/viewGroup"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="30dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
    </LinearLayout>

</RelativeLayout>

这里我们引入了ViewPager,并且还有一个LinearLayout,其中ViewPager我们用来显示天气,Linearlayout用来作为指示器,表示我们当前所选城市的次序。

 

之后,修改我们主页面的代码,主界面现在的作用主要是两个:

1. 初次启动的时候,获取所在地城市

2. 处理切换Fragment的逻辑

3. 处理页面跳转/返回的逻辑

public class MainActivity extends FragmentActivity
{
    @ViewInject( R.id.viewPager )
    private ViewPager pager;

    @ViewInject( R.id.viewGroup )
    private LinearLayout layout;

    private MyAdapter mAdapter;
    private List<SelectCityBean> citys;

    private LocationClient mLocationClient;
    private BDLocationListener myListener;

    private List<ImageView> imageViews;

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        Log.v( "WeatherAPP", "onCreate" );

        ViewUtils.inject( this );

        imageViews = new ArrayList<ImageView>();

        citys = readCity();
        mAdapter = new MyAdapter( getSupportFragmentManager() );
        pager.setAdapter( mAdapter );
        pager.setOnPageChangeListener( new OnPageChangeListener()
        {
            @Override
            public void onPageSelected( int arg0 )
            {
                setTitle( citys.get( arg0 ).getCityName() + "天气" );
                setImageBackground( arg0 );
            }

            @Override
            public void onPageScrolled( int arg0, float arg1, int arg2 )
            {
            }

            @Override
            public void onPageScrollStateChanged( int arg0 )
            {
            }
        } );

        if( citys == null || citys.size() == 0 )
        {
            citys = new ArrayList<SelectCityBean>();
            initLocationClient();
            mLocationClient.start();
        }

        showIndicator( 0 );
    }

    private void showIndicator( int position )
    {
        layout.removeAllViews();
        imageViews = new ArrayList<ImageView>();

        pager.setCurrentItem( position );

        for( int i = 0; i < citys.size(); i++ )
        {
            ImageView imageView = new ImageView( this );
            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( 20, 20 );
            lp.leftMargin = 5;
            imageView.setLayoutParams( lp );
            imageViews.add( imageView );
            if( i == position )
            {
                setTitle( citys.get( position ).getCityName() + "天气" );
                imageView.setBackgroundResource( R.drawable.page_indicator_focused );
            }
            else
            {
                imageView.setBackgroundResource( R.drawable.page_indicator_unfocused );
            }

            layout.addView( imageView );
        }
    }

    private void setImageBackground( int selectItems )
    {
        for( int i = 0; i < imageViews.size(); i++ )
        {
            if( i == selectItems )
            {
                imageViews.get( i ).setBackgroundResource( R.drawable.page_indicator_focused );
            }
            else
            {
                imageViews.get( i ).setBackgroundResource( R.drawable.page_indicator_unfocused );
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu( Menu menu )
    {
        super.onCreateOptionsMenu( menu );
        menu.add( Menu.NONE, Menu.FIRST + 1, 0, "添加城市" ).setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS );
        return true;
    }

    @Override
    public boolean onOptionsItemSelected( MenuItem item )
    {
        if( item.getItemId() == Menu.FIRST + 1 )
        {
            Intent intent = new Intent( getApplicationContext(), ChooseCityActivity.class );
            intent.putExtra( "key", "value" );
            startActivityForResult( intent, 99 );
        }

        return super.onOptionsItemSelected( item );
    }

    @Override
    protected void onActivityResult( int requestCode, int resultCode, Intent data )
    {
        super.onActivityResult( requestCode, resultCode, data );

        if( resultCode == RESULT_OK )
        {
            String city = data.getStringExtra( "selectedCity" );
            addCity( city );
        }
    }

    private void initLocationClient()
    {
        mLocationClient = new LocationClient( getApplicationContext() ); // 声明LocationClient类
        myListener = new MyLocationListener();
        LocationClientOption option = new LocationClientOption();
        option.setLocationMode( LocationMode.Hight_Accuracy );
        option.setIsNeedAddress( true );
        mLocationClient.setLocOption( option );
        mLocationClient.registerLocationListener( myListener );
    }

    @Override
    protected void onStop()
    {
        Log.v( "WeatherAPP", "onStop" );
        super.onStop();
        if( mLocationClient != null ) mLocationClient.stop();
    }

    @Override
    protected void onPause()
    {
        Log.v( "WeatherAPP", "onPause" );
        super.onPause();
    }

    @Override
    protected void onRestart()
    {
        Log.v( "WeatherAPP", "onRestart" );
        super.onRestart();
    }

    @Override
    protected void onResume()
    {
        Log.v( "WeatherAPP", "onResume" );
        super.onResume();
    }

    @Override
    protected void onStart()
    {
        Log.v( "WeatherAPP", "onStart" );
        super.onStart();
    }

    @Override
    protected void onDestroy()
    {
        Log.v( "WeatherAPP", "onDestroy" );
        super.onDestroy();
    }

    public class MyLocationListener implements BDLocationListener
    {
        @Override
        public void onReceiveLocation( BDLocation location )
        {
            String city = location.getCity();
            addCity( city );
        }
    }

    private void addCity( String city )
    {
        SelectCityBean cityBean = new SelectCityBean();
        cityBean.setCityName( city );
        saveCity( cityBean );

        if( citys == null ) citys = new ArrayList<SelectCityBean>();
        citys.add( cityBean );
        mAdapter.notifyDataSetChanged();
        showIndicator( citys.size() - 1 );
    }

    private void saveCity( SelectCityBean city )
    {
        DbUtils dbUtils = WeatherApplication.getInstance().getDbUtil();
        try
        {
            dbUtils.save( city );
        }
        catch( DbException e )
        {
        }
    }

    private List<SelectCityBean> readCity()
    {
        DbUtils dbUtils = WeatherApplication.getInstance().getDbUtil();
        try
        {
            return dbUtils.findAll( SelectCityBean.class );
        }
        catch( DbException e )
        {
            return null;
        }
    }

    public class MyAdapter extends FragmentStatePagerAdapter
    {
        public MyAdapter( FragmentManager fm )
        {
            super( fm );
        }

        @Override
        public Fragment getItem( int arg0 )
        {
            WeatherFragment fragment = new WeatherFragment();
            fragment.setCity( citys.get( arg0 ).getCityName() );
            return fragment;
        }

        @Override
        public int getItemPosition( Object object )
        {
            return POSITION_NONE;
        }

        @Override
        public int getCount()
        {
            if( citys == null ) return 0;
            return citys.size();
        }
    }
}

 

基本上面目全非了,跟之前的代码完全不一样了,这里面的主要变动为:

1. saveCity、readCity不再从Preference中获取数据了,而改为从数据库获取

2. 增加了MyAdapter以及相关的ViewPager的逻辑

 

这里还用到了一个新的SelectCityBean以及两个图片资源,

public class SelectCityBean
{
    private int id;
    private String cityName;

    public int getId()
    {
        return id;
    }

    public void setId( int id )
    {
        this.id = id;
    }

    public String getCityName()
    {
        return cityName;
    }

    public void setCityName( String cityName )
    {
        this.cityName = cityName;
    }
}

 

两个图片资源分别代表了当前城市以及其他城市,

page_indicator_focusedpage_indicator_unfocused

 

完成之后,运行来看看,我这边的效果是这样的:

device-2015-01-27-084239

 

 

你可以试着添加城市看看,是不是这样的效果。

 

今天的内容比较少,代码比较多,大家多多练习一下。

 

附件是本次的工程文件,点击下载 http://pan.baidu.com/s/1sj2V5fB 。

此系列文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn