基于Android平台的个人日历的设计与开发

基于Android平台的个人日历的设计与开发

摘要

个人日历是基于Android平台开发的,运行在Android手机平台的个人日程管理软件。本文是对基于Android平台的个人日历的设计与开发。

首先,本文介绍了软件开发的背景与特点。然后,本文介绍了软件所使用的关键技术,包括Android、fragment、service、activity。再然后,介绍日历的设计与开发,包括系统需求分析、功能设计、框架设计、界面设计、数据层设计以及各部分功能的实现。最后本文对全文进行总结,总结设计实现过程中遇到的问题、系统的不足、优势与自己的收获。

关键词:个人日历、Android、日程管理

The designationand development of personal calendar based on the Android platform

Abstract

Personal calendar is a personal schedule management software developed on the Android platform, running on the Android platform. This paper is to design and develop of personal calendar based on the Android platform.

First, this paper introduces the background and characteristics of software development. Then, this paper introduces the key technologies of software use, including Android, fragments, service, activity. Then, introduces the design and development of calendar, including system requirements analysis, functional design, framework design, interface design, the data layer design and the realization of the function of each part. Finally this paper summarizes the full text, summarizes the problems in the implementation process of design, advantages and disadvantages of the system and the deficiency of the harvest of their own.

Keyword: Personal calendar, Android, schedule management

目录

1.绪论51.1.课题背景51.2.研发现状51.3.软件研究意义51.4.研究内容与方法62.相关技术与知识72.1.开发平台Android72.2.MVC开发模式72.3.局部界面fragment72.4.Web service82.5.Localservice82.7.线程92.8.开发平台介绍93.个人日历的设计103.1.需求分析103.2.功能设计123.3.数据层设计153.4.界面布局设计204.个人日历的实现234.1.数据层实现234.2.界面实现354.3.Web服务434.4.本地服务464.5.日程同步484.6.屏幕旋转后的Activity数据保存504.7.Activity向Fragment传递数据504.8.Fragment之间通信515.总结535.1.遇到的问题与解决535.2.软件的优势与不足545.3.本章小结546.总结55参考文献56致谢56


  1. 绪论

    1. 课题背景
      在现代的生活中,人们每天要处理的事务越来越多。如何有效的管理自己每天的日程安排,已经变得越来越重要。[1]尤其在团队工作中,更需要相互协调以提高整体效率。[2] 在生活中中重要事件多半以电话、短信、邮件的方式发送,
      手机中的通讯录、短信以及其余重要数据的丢失,影响恐怕就难以估量了,由于那意味着会丢掉一些重要的短信。[3] 因此日程管理软件,一直深受人们的喜爱 。但是 ,传统的日历工具设计和功能千篇一律,仅能管理自己的日程。
      本软件可以定时或实时从后台数据库中通过Web Services下载与个人有关的日历信息,并能够根据设定的提醒方式和提醒时间自动提醒用户注意相关日程安排。
    2. 研发现状
      1. 国内外Android软件现状
        Android平台是Google公司推出的基于Linux系统一款移动操作平台。虽然只推出没有几年但发展迅速,尤其在国外Android更是呼声很高,占据了相当大的市场份额。中国是最大的消费大国,由于3G网络的推广更加推动了智能手机的发展,这几年个打手机场上都对Android工程师需求量很大。未来几年Android发展会更为迅猛,它将不再只是个手机平台,他将进入平板、电视、车载系统等。
      2. 日历软件发展现状
        目前许多日历软件,他们可以实现各种各样的功能,如今日运势、纪录日程、黄历、吉日查询、法定节假日查询、生理周期日历、星座查询、生肖查询、阴阳转换等。外表也更加美观,各式各样的更能满足人们对日历软件的需要。有些还提供各种美观的小插件,是日历使用更加方便美观。不过这些软件均仅能管理个人日程,不能获得其他管理软件的日程。
    1. 软件研究意义
      Android平台是Google公司推出的基于Linux系统一款移动操作平台。该平台具有开放性、不受运营商束缚、有丰富的硬件选择、不受限制的开发和与谷歌应用可以无缝结合等优势,现在已渐成为主流的智能手机平台。鉴于Android平台仍是一个较新的开发平台,仍有很大的发展空间,国内外很多运营商和软件公司都急需该平台的开发人员。通过本次毕业设计可以很好的锻炼学生实践能力,同时也为学生就业创造条件。
    2. 研究内容与方法
      1. 研究内容
        个人日历后台软件系统主要实现用户个人日历和企业日历数据的存储、手机日历端、PC日历端与后台系统日历项的同步、OA及项目管理等系统与日历系统的接口等功能。通过后台系统,用户可以随时将手机中的日历信息保存到后台数据库中,也可以随时同步后台数据库中与自身相关的日历信息;OA及项目管理系统中的日程安排也能够自动同步到后台系统和手机端。
        手机日历客户端主要实现企业日历信息的月视图、周视图、日视图、日历列表、新增日历、节假日、日历同步等功能。手机日历客户端软件可以定时或实时从后台数据库中通过Web Services下载与个人有关的日历信息,并能够根据设定的提醒方式和提醒时间自动提醒用户注意相关日程安排。用户也可以在手机上新建日程或工作安排,并将其发送到服务器端,然后通过后台管理软件自动同步到相关人员的手机上,实现工作安排的自动通知和自动提醒。
      2. 研究方法

本系统用软件工程化思想和方法开发软件,对软件进行了软件计划、需求分析、设计和实现。

  1. 需求分析
    根据实际情况获得需求信息并进行总结,提出软件的功能。
  2. 软件设计
    根据需求分析进行框架设计、界面设计、数据库设计。
  3. 软件实现

根据设计文档实现软件。


  1. 相关技术与知识
    1. 开发平台Android
      Android是一个基于Linux的操作系统。自推出以来Android平台迅猛发展,占据了巨大的市场份额,而且发展速度越来越快。目前Android手机销量约占全球三分之一,成为世界上最大的Android手机市场。
      Android的开发语言是Java。从全球来看从事Java编程的人数一直占据着第一的位置。而iPhone应用的开发语言是Object C,从事Object C编程的人才刚刚步入前十。并且Android上手容易,经过几个月的培训即可掌握安卓的开发技巧,并且安卓手机较为便宜,平台免费。
      在中国Android拥有很好的学习环境和许多开发社区,其中一些惊颤举办安卓开发者活动,一起交流技术和经验,并且免费发放学习资料更课题了解到做什么养的安卓应用可以赚钱,是开发者不在迷茫。。
    2. MVC开发模式

Android应用是基于模型控制器视图(Model-View-Controller,简称MVC)的架构模式进行设计的。 在MVC设计模式中,应用的任何对象归根结底都是模型对象、视图对象以及控制对象中的一种。

  1. 模型对象存储着应用的数据和业务逻辑。模型类通常被用来映射与应用相关的一些事物。模型对象不关心用户界面,它存在的唯一目的就是存储和管理应用数据。
  2. 视图对象需要知道如何绘制界面、如何处理用户和界面的互动。以往的经验法则表明,只要是屏幕上可以看得的就是视图对象。
  3. 控制对象包含了应用的逻辑单元,是联系视图与模型对象的纽带。控制对象被设计用来响应由视图对象触发的各类事件,此外还用来管理模型对象与视图层间的数据流动。
  4. 局部界面fragment
    自从Android3.0引入fragment概念之后,fragment被解释为碎片或者片段。一个fragment是用户界面的一个区域,可以再一个Activity中使用多个fragment来组合成一个多面板界面,也可以在不同的Activity中利用同一个fragment。可以把fragment理解为一个模块化部分,他有自己的生命周期、可以独立的接受用户的操作,并可以在Activity运行过程中添加或删除一个fragment。
    管理用户界面的fragment又称为UI fragment。它也可以自己产生布局文件。 fragment视图包含了用户会用到的一些的可视化UI元素。
  5. Web service
    Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式操作的应用程序。[1]
    Web Service技术, 能使得运行在不同平台上的不同应用不用借助第三方软件或硬件就可相互交换数据。更具Web Service规范发布的应用之间, 不管他们是用什么语言开发的、运行在什么平台上的都能相互交换数据。Web Service减少了应用接口的花费。
  6. Localservice
    Service是安卓四大组件之一,它是可以长时间在后台运行并且需要提供用户接口的应用组件。在启动一个应用的服务后即使在启动其他的应用之前的服务依旧会运行。并且其他非service组件也可以与service绑定并进行交互,甚至允许多线程交互。本地服务不是独立的进程,它依附在主进程上这样可以节省资源。
    Service有两种形式:
    启动形式:通过调用startService()方法启动服务,通过这种方式启动的service即使启动它的Activity已经被销毁它也会一直在后台运行。但是它只能执行单一的操作不能返回结果给调用者。通过调用stopService()来停止服务。
    绑定形式:通过调用bindService()方法启动服务,通过这种方式启动的服务会为它所绑定的组件一个交互接口,通过这个接口应用组件可以与service交互、请求结果、甚至跨进程通信。但是当组件被销毁后service也就销毁了。通过调用unbindService()停止服务。
  7. SQLite数据库
    SQlite是一种轻量级的、功能强大关系数据库,它用于完成实现结构化的数据存储和各种复杂的数据处理。
    SQlite数据库支持多数的SQL92标准,最大支持数据到2TB。他没有服务进程,但是它所包含的数据库、表等所有数据已经被放入Android系统平台的底层库文件,因此我们可以直接通过相关的API来调用,从而实现数据库的各种功能。
  8. 线程
    线程是进程内部执行代码的实体,他是CPU调度资源的最小单元,他没有自己独立的资源,他只有自己的执行堆栈和局部变量,所以线程不能独立执行不许依附在一个进程上。
    Thread:重写其run()方法,在其中实现我们需要线程所要完成的任务。在Activity中实例化并通过start()运行线程。在线程运行过程中如果需要更新UI可通过handler发消息给主线程。
    AsyncTask:他是一种简单实现后台运行事物的方式。他的任务在UI县城之外运行,而回调方法是在UI线程中执行,这样就可以有效的避免了使用Handler所带来的麻烦。AsyncTask是一个抽象类,要使用它必须继承这个类并重写其中的方法。
  9. 开发平台介绍
    1. 开发工具
      Eclipse,adt,Visual Studio2010,SQL Server 2008
    2. 开发语言
      Java,C#
    3. 测试平台
      Android手机,Win7

  1. 个人日历的设计
    1. 需求分析
      1. 对功能的规定

本软件要求将办公自动化平台(OA)系统中涉及日程的项目推送到手机终端软件中。其基本的数据相关流程如图3-1-1所示。
图 3-1-1 日历系统结构图

  1. 其中“手机日历”模块完成在手机终端(Android平台)中的显示日历、新建日历等功能,并提供手动/定时与中心数据库同步日历的功能。

  2. 中心数据库模块完成从OA系统、项目管理系统中抽取与日期相关的日程项,并整理为标准的日历格式后向手机端推送日历,要求使用Web服务的形式提供接口。

  3. 对性能的规定

    1. 精度
  4. 时间戳精确到1毫秒;

  5. 周视图、日视图日程项的时间精度为15分钟。

  6. 提前提醒的时间精度为1分钟,即不支持小于1分钟的时间提醒;

  7. 时间特性要求

  8. 手机向中心服务器读写数据网络超时响应时间:10秒,若超过10秒没有取回数据则退出;

  9. 手机端本地数据库查询超时:3秒;

  10. 面之间切换延时:不超过2秒;

  11. 灵活性
    本软件系统前台手机端与后台服务器端采用Web服务方式通信,可以减少防火墙的配置工作以及具有良好的可扩充性。
    手机端软件与后台系统之间通过Web Services进行通信,只要接口信息不发生改变,手机端界面和功能的变化不影响后台系统的开发和使用。

  12. 输入输出要求
    后台服务器端提供的Web服务的参数类型包括字符串和整型两类,结果以自定义类对象列表的形式返回。

  13. 数据管理能力要求
    中心数据库采用SQL Server2008数据库,有很强的数据存储和管理能力,完全能够胜任个人日历系统的数据要求。另外,Web服务发布在IIS上,其并发访问由IIS负责,多人同时进行同步不存在问题。

  14. 故障处理要求

本系统可能在下列情况下出现故障,待相关问题解决后,本系统能够重新正常运行:

  1. 用户手机不能正常联网时,会影响到日历信息上传和同步的实时性;
  2. 安装中心数据库的计算机出现故障或网络连接不畅时,会影响系统的稳定运行;
  3. 运行环境规定
    1. 设备

设备需求情况将分别从前台手机端和后台服务端说明:

  1. Android设备运行要求:
    支持Android2.2平台及以上,运行内存大于512M,系统存储空间大于128M(用于存数据库),屏幕分辨率大于800*400,具备联网功能的手机。
  2. 服务器运行要求:

需要一台性能较好的服务器(操作系统Windows 2003 Server,内存4G、硬盘512G、双网卡),安装数据库服务器和IIS服务器,其上运行后台同步程序和Web服务。

  1. 支持软件

服务器端开发所用到的支持软件包括:

  1. Windows 2003 Server
  2. Microsoft SQL Server2008
  3. 功能设计
    1. 总体框架设计

个人日历系统包含手机端(Android)和服务器端两部分组成。

系统的总体结构图如下图所示:

信息系统的内网通过SOA应用服务器的80端口连接到InterNet网,再通过无线加密通信将信息送到Android手机上。

SOA是一种面向服务的软件架构,是当前先进的软件结构。SOA对外以80为服务端口,易于配置防火墙。SOA架构借用现有的HTTP应用服务器的并发控制,可靠、安全。

用一览表及框图的形式说明本系统的系统元素(各层模块、子程序、公用程序等)的划分,扼要说明每个系统元素的标识符和功能,分层次地给出各元素之间的控制与被控制关系.

Android端手机软件的程序结构图为:

Android程序由手机界面显示程序、手机服务程序两部分组成。手机主程序完成主日历视图、新建日程视图、日程查看视图、假期查看视图;手机后台服务程序则完成状态栏提醒、桌面插件更新、后台数据更新等功能。

手机主视图界面由月视图、周视图、日视图、三个视图组成。这三个视图共享一个Android Activity,当用户切换视图时动态生成视图单元格。

日历信息获取过程:

  1. 分析现有的信息服务系统,找到工作项子系统;
  2. 分析工作项子系统的数据库,设计查询日历项的存储过程(SQL编写);
  3. 将存储过程映射为WebService(C#或者Java编写);
  4. 将Web服务部署在应用服务器上(IIS或者Tomcat);
  5. 移动设备通过Web服务读写日历信息。

  1. 功能列表

    1. 导航栏设计
      导航栏由8个圆形按钮构成,他们的功能分别为查看月视图、查看周视图、查看日视图、将日期跳到今天、查看假期、添加日程、同步日程。
    2. 月视图设计
      该视图在整个屏幕上提供整月的信息查看,并且可左右滑动显示上一月和下一月的日期信息,该视图的最小操作单位为天,字体随手机分辨率自动设定。
      月视图分为三部分:头部日期标识、月主视图、日程列表。
      头部日期标识分为两部分:日期显示、农历阳历转换
      月主视图:上方为周一至周日导航;下方显示一个月的日单元格,日单元格可以显示公历和可选显示农历,单元格有日程项则显示一个标记,单击日单元格显示当日日程项,双击或者长按日单元格可以在当日新建一个日程项。用户可以根据日单元格的不同来判断是否为今天(有圆圈)、假期(蓝色)、所选中的日期(绿色)、是否有日程安排(右上方红色三角)、非本月(灰色)。
      日程列表:显示所点击日期的日程信息(主题、时间、地点、是否完成),并支持上下滑动查看日程,点击可产看详细信息。
    3. 周视图设计
      该视图提供整周日程的查看功能,并且可左右滑动显示上一周和下一周的日期信息,字体随手机分辨率自动设定。
      周视图分为两部分头部日期标识、周主视图。
      头部日期标识分为两部分:日期显示、农历阳历转换
      周主视图:上方为周一到周日导航,点击可跳转到显示那天的日视图;下方显示一周七天、每天24小时的日程信息和当日的全天日程。单击小时单元格可查看当时日程,双击或者长按小时单元格可新建一个日程项。
    4. 日视图设计
      该视图提供一天的日程查看功能,并且可左右滑动显示上一周和下一周的日期信息,字体随手机分辨率自动设定。
      日视图分为两部分:日期标识、日主视图。
      头部日期标识分为两部分:日期显示、农历阳历转换
      日主视图:显示一天的全天任务、每天24小时的日程信息,单击小时单元格可查看当时日程,双击或者长按小时单元格可新建一个日程项。不同紧急程度的任务使用不同的背景颜色。日程项显示主题、地点。每小时的任务太多,单元格太小、则尽量显示更多的文字。
    5. 列表视图设计
      列表视图提供从今天开始的100条日历,支持上下滑动查看前后的日程项。日程项之间使用日历时间栏区分,日历时间栏要求显示年月日和星期。默认显示从今天开始的100个日程,并提供主题关键字检索功能。点击一个日程项可产看详细信息。
    6. 新建、查看、修改、删除日程
      可以新建一个日程;查看一个日程的详细信息,修改或者删除日程。
    7. 假期
      可查看从今天开始的一年的假期。
    8. 同步功能
      可通过web srvice同步日程新到本地数据库。分为手动同步和自动同步两种。
    9. 菜单
  2. 设置:可以设置Web服务地址、同步数据的频率、网络超时时间。个人的姓名和编号的注册和注销。

  3. 退出:退出程序。

  4. 桌面插件
    该视图提供一个桌面查看,便于用户快捷查看今天开始的10条日程,点击后可进入本软件主程序。

  5. 功能衔接

  6. 打开本软件主程序显示月视图,点击导航栏的“月”按钮可由其他视图跳转回月视图;点击导航栏“周”按钮可跳转到周视图;点击周视图的星期导航可跳转到日视图,点击导航栏“日”按钮可跳转到日视图。

  7. 点击导航栏“+”按钮可添加日程,长按或双击月视图的日单元格、周视图和日视图的小时单元格可添加日程。

  8. 点击导航栏“三”按钮,可进入列表视图,查看今天开始的日程。

  9. 点击月视图的日程列表和列表视图的日程项可进入查看日程详细信息界面,在查看页面可点击“删除”按钮删除个人日历信息,点击“修改”按钮修改日程信息。

  10. 按菜单键弹出菜单。

  11. 数据层设计
    1. 数据库设计

  12. 手机端sqilite数据库:
    本系统中,设计了表:

表名:ColorConfig(配置表)

字段名

类型

说明

约束

id

Integer

本地数据库索引ID

自增、主键

isUse

Integer

是否使用该配置

userID

String

用户ID

userName

String

用户名

userStatus

String

用户状态

默认值:状态未知

NAMESPACE

String

webservice的默认名字空间

默认值:http://tempuri.org/

timeOut

int

web服务的网络超时时间

默认值:5000

imei

String

手机字串标识

WEBSERVICE_URL

String

web服务网址

WorkListDataBarBarColor

int

列表项中 日期栏的背景色

默认值:Color.DKGRAY

Calendar_WeekBgColor

int

周视图背景颜色

默认值:Color.WHITE

Calendar_DayBgColor

int

日视图背景颜色

默认值:Color.WHITE

isHoliday_BgColor

int

假期单元格颜色

默认值:0xFF6CAAD9

unPresentMonth_FontColor

int

非本月日期单元格字体颜色

默认值:Color.GRAY

isPresentMonth_FontColor

int

本月日期单元格字体颜色

默认值:Color.BLACK

isToday_BgColor

int

几天单元格背景颜色

默认值:0xFFD3D3D3

special_Reminder

int

提醒三角形颜色

默认值:Color.RED

Calendar_WeekFontColor

int

星期视图字体颜色

默认值:Color.BLACK

DayHourCellFontCell

int

日视图小时字体大小

默认值:16

FontSize

int

自动字体大小

默认值:25

FontSizeTitleSmall

int

小字体大小

默认值:12

FontSizeTitleBig

int

标题字体大小

默认值:20

DayHourHeight

int

日视图单元格高度

默认值:35

toShowLunar

Boolean

是否显示农历

默认值:false

MaxQueryCntOfWorkItemList

int

查询最多的项目数

默认值:20

该表是程序运行过程中常用到的一些变量的配置的存储。

表名:CalendarItem(日程表)

字段名

类型

说明

约束

id

Integer

数据库自建ID

自增、唯一、非空

CalendarId

String

日历ID

主键

YearID

int

年,用于重复项计算

默认值:-1

MonthID

int

月,用于重复项计算

默认值:-1

WeekID

int

周,用于重复项计算

默认值:-2

DayID

int

日,用于重复项计算

默认值:-1

HourOfDay24

int

小时,用于重复项计算

默认值:-1

Topic

String

日程主题

StartTime

String

日程开始时间

EndTime

String

日程结束时间

IsWholeDay

String

是否为整天任务

默认值:“否”

Place

String

日程地点

Repeat

Integer

是否重复(0表示不重复,1表示按天重复,2表示按周重复,3表示按月重复,4表示按年重复)

默认值:-1

Remind

Integer

提醒时间,设计单位为 (分钟)

默认值:-2

CreatTime

String

创建时间

Creator

String

创建者

SendEmail

String

是否发送邮件提醒

默认值:“否”

CompletedTime

String

完成时间

CalendarType

String

日历类型,暂定为整数类型(自建日历,企业日历)

AlterTime

String

最后更新时间(初始值等于创建时间,用于同步)

Note

String

备注

IsCompleted

String

是否完成

Priority

String

紧急程度

默认值:一般

Charger

String

负责人

PersonList

String

相关人

IsDeleted

String

是否删除

haveReminded

int

是否被提醒过,不需要和远程同步,0: 没有提醒过,1:已经提醒

该表用于存储日程信息,以CalendarId为主键,存储时按照StartTime、EndTime、ID排序。

表名:UpdateHistory(更新历史)

字段名

类型

说明

约束

id

Integer

自建ID

主键

updateID

String

更新历史ID

AlterTime

String

最后同步时间

默认值:2000/01/01 00:00:00

Click_CI_ID

String

界面点击的日程项ID值

该表用来保存更新历史,用于同步日程信息使用。

  1. 后台服务数据库的结构:

本系统中,设计了表:Table_UserManager(人员表)、Table_CalendarManage (日历表) 、Table_User_CalendarRelation (人员、日历关系表)来存储日历系统中涉及到的相关人员、日历项及日历项涉及到的人员等信息。各表的结构定义分别如下:

表Table_UserManager(人员表)

字段名

类型

说明

是否允许空

UserId

int

人员编号

Name

varchar(60)

姓名

SubCompany

varchar(60)

分部名称

Department

varchar(60)

部门名称

Job

varchar(60)

岗位名称

Email

varchar(60)

电子邮箱

RegisterTime

datetime

修改时间

Status

int

状态

IMIE1

varchar(300)

手机1标识

IMIE2

varchar(300)

手机2标识

IMIE3

varchar(300)

手机3标识

表Table_CalendarManage(日历表)

字段名

类型

说明

是否允许空

CalendarId

varchar(30)

日历ID,主键

Topic

varchar(60)

主题

StartTime

datetime

起始时间

EndTime

datetime

结束时间

IsWholeDay

varchar(2)

是否整天(是,否)

Place

varchar(60)

地点

Repeat

int

是否重复(0表示不重复,1表示按天重复,2表示按周重复,3表示按月重复,4表示按年重复)

Remind

int

提前提醒时间(以分钟为单位)

CreatTime

datetime

创建时间

Creator

varchar(50)

创建者

SendEmail

varchar(2)

是否发送邮件(是,否)

IsCompleted

varchar(2)

是否完成(是,否)

CompletedTime

datetime

完成时间

Source

varchar(50)

日历来源(OA、项目管理)

CalendarType

varchar(50)

日历类型(自建日历,企业日历)

AlterTime

datetime

最后更新时间(初始值等于创建时间,用于同步)

Priority

varchar(10)

优先级(紧急、报警、一般)

Charger

varchar(50)

负责人

Note

varchar(200)

备注

IsDeleted

varchar(2)

是否删除(是,否)

Examinor

varchar(10)

审核人

ExaminAdvice

varchar(200)

审核意见

ExaminTime

datetime

审核时间

表Table_User_CalendarRelation(人员、日历关系表)

字段名

类型

说明

是否允许空

UserId

int

人员编号

CalendarId

varchar(30)

日历Id

AlterTime

datetime

修改时间

IsDeleted

varchar(2)

是否删除(是,否)

注:表Table_CalendarManage和表Table_User_CalendarRelation之间通过字段CalendarId来关联,是一对多的关系。生成日历时,先在日历表Table_CalendarManage中插入日历信息记录,然后再在表Table_User_CalendarRelation中生成人员的关联信息。删除日历时,先从表Table_User_CalendarRelation中删除有关记录,当不存在与CanlendarId关联的人员信息时,将日历记录从Table_CalendarManage中删除。

  1. 数据缓冲
    月视图的每个单元格类中都有一个ArrayList用于保存当天的CalendarItem(日程信息),这样打开月视图就把整个月的日程信息缓冲到了内存中。模型层新增一个CalendarItemLab对象,该对象是一个数据缓冲池用来存储CalendarItem。点击日单元格时将ArrayList赋值给CalendarItemLab的ArrayList,之后对CalendarItem的查看均使用CalendarItemLab。
  2. 界面布局设计

日视图

桌面插件

  1. 个人日历的实现
    1. 数据层实现
      1. 数据库实现
        1. 手机端数据库:

Android使用ORMLite操作数据库

  1. 使用ormlite-android-4.41.jar和ormlite-core-4.41.jar两个jar包,将这两个包导入到Lib文件夹中。

  2. 存储的数据对象实体。
    代码如下:
    UpdateHistory 类
    @DatabaseTable
    public class UpdateHistory implements Serializable {
    /**
    * UID
    */
    private static final long serialVersionUID = 5273337865132837182L;/**
    * SqlLite自增ID
    */
    @DatabaseField(generatedId = true)
    Integer id = 0;
    /**
    * 最后同步时间
    */
    @DatabaseField
    String updateID = “”;
    /**
    * 最后同步时间
    */
    @DatabaseField
    String AlterTime = “2000/01/01 00:00:00”;
    /**
    * 界面点击的日程项 ID值
    */
    @DatabaseField
    String Click_CI_ID = “”;
    //一系列的get set方法
    //省略
    }
    CalendarItem类
    /**
    * 日历系统中的 日程项 基础类,包含了日程项所有的数据信息和数据库结构,以后可以新增日程项的查询、匹配等功能
    * @author GW
    */
    @DatabaseTable
    public class CalendarItem implements Serializable {
    /**
    * UID
    */
    private static final long serialVersionUID = -7368152766202427185L;

    }
    /**
    * SqlLite 自建ID
    */
    @DatabaseField(generatedId = true)
    Integer id = 0;
    /**
    * 远程数据库中心,日历ID,主键
    */
    @DatabaseField(index = true)
    String CalendarId = “”;
    /**
    * 主题
    */
    @DatabaseField
    String Topic = “”;
    /**
    * 起始时间
    */
    @DatabaseField(index = true)
    String StartTime = “”;
    /**
    * 结束时间
    */
    @DatabaseField(index = true)
    String EndTime = “”;
    //按照设计的数据库表写,并注释(此处部分省略)
    //一系列的set get 方法 省略
    }
    ColorConfig 类
    @DatabaseTable
    public class ColorConfig implements Serializable{
    /**
    * UID
    */
    private static final long serialVersionUID = 8216762882467739109L;
    /**
    * 本地数据库主索引ID
    */
    @DatabaseField(generatedId = true)
    Integer id = 0;
    /**
    * 是否正在使用该配置
    */
    @DatabaseField
    Integer isUse = 1;
    @DatabaseField
    public String userID = “”;
    @DatabaseField
    public String userName = “”;
    //按照设计的数据库表写,并注释(此处部分省略)
    //一系列的set get 方法 省略
    }
    注:@DatabaseTable 指定类对应的表名,@DatabaseField 对应的字段名,
    字段名注释方法
    cloumnName:指定字段名,不指定则变量名作为字段名
    canBeNull:是否可以为null
    dataType:指定字段的类型
    defaultValue:指定默认值
    width:指定长度
    id:指定字段为id
    generatedId:指定字段为自增长的id,不能id,generatedIdSequence通用
    foreign 指定这个字段的对象是一个外键,外键值是这个对象的id
    useGetSet:指定ormlite访问变量使用set,get方法默认使用的是反射机制直接访问变量
    throwIfNull,如果空值抛出异常
    persisted:指定是否持久化此变量,默认true
    unique:字段值唯一
    uniqueCombo整列的值唯一
    index:索引
    uniqueIndex 唯一索引
    foreignAutoRefresh 外键值,自动刷新
    foreignAutoCreate 外键不存在时是否自动添加到外间表中

  3. 需要数据DatabaseHelper类,来创建及管理数据库。 DatabaseHelper类继承OrmLiteSqliteOpenHelper,并在覆盖实现onCreate, onUpgrade, close等方法。创建删除数据库应该使用TableUtils工具类的方法.。

  4. 构造数据访问接口Dao类。
    代码如下:
    public class DatabaseHelper extends
    com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper {
    //数据库名字
    private static final String DATABASE_NAME = “KD.db”;
    /**
    * 当数据库表结构修改时,增加该数值即可。
    * 数据库版本号
    */
    private static final int DATABASE_VERSION = 47;
    private Dao<CalendarItem, Integer> calItemDao;
    private Dao<ColorConfig, Integer> configDao;
    private Dao <UpdateHistory, Integer> updateDao;

    public DatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION,
    R.raw.ormlite_config);
    }
    //创建数据库时调用 onCreats方法
    @Override
    public void onCreate(SQLiteDatabase arg0, ConnectionSource arg1) {
    // TODO Auto-generated method stub
    try {
    TableUtils.createTable(connectionSource, CalendarItem.class);
    TableUtils.createTable(connectionSource, ColorConfig.class);
    TableUtils.createTable(connectionSource, UpdateHistory.class);
    } catch (SQLException e) {
    Log.e(DatabaseHelper.class.getName(), “Unable to create datbases”,e);
    }
    }
    //数据库版本号变化时调用 onUpgrade 方法
    @Override
    public void onUpgrade(SQLiteDatabase sqliteDatabase,
    ConnectionSource connectionSource, int oldVer, int newVer) {
    // TODO Auto-generated method stub
    try {
    TableUtils.dropTable(connectionSource, CalendarItem.class, true);
    TableUtils.dropTable(connectionSource, ColorConfig.class, true);
    TableUtils.dropTable(connectionSource, UpdateHistory.class, true);
    onCreate(sqliteDatabase, connectionSource);
    } catch (SQLException e) {
    Log.e(DatabaseHelper.class.getName(),
    "Unable to upgrade database from version " + oldVer+ " to new " + newVer, e);
    }
    }
    /**
    * 获取用户信息接口DAO
    * @return
    * @throws SQLException
    */
    public Dao<User, Integer> getUserDao() throws SQLException {
    if (userDao == null) {
    userDao = getDao(User.class);
    }
    return userDao;
    }
    /**
    * 获取 日程项 信息接口DAO
    * @return
    * @throws SQLException
    */
    public Dao<CalendarItem, Integer> getCalItemDao() throws SQLException {
    if (calItemDao == null) {
    calItemDao = getDao(CalendarItem.class);
    }
    return calItemDao;
    }
    /**
    * 获取 配置项 的Dao
    * @return
    * @throws SQLException
    */
    public Dao<ColorConfig, Integer> getConfigDao() throws SQLException {
    if (configDao == null) {
    configDao = getDao(ColorConfig.class);
    }
    return configDao;
    }
    /**
    * 返回最后更新的时间
    * @return
    * @throws SQLException
    */
    public Dao<UpdateHistory, Integer> getUpdateHistoryDao() throws SQLException {
    if (updateDao == null) {
    updateDao = getDao(UpdateHistory.class);
    }
    return updateDao;
    }
    }

  5. 构造数据库操作类DbAdapter。在构造函数中先使用getHelper()获得数据库DatabaseHelper,然后使用getDao()获得数据操作接口。在析构函数通过releseHelper()释放数据库,再通过Dao类的方法操作数据。这样就可以使用DbAdapter的方法操作数据,更加方便快捷。

代码如下:

/**

* 管理底层的数据服务,比如从数据库刷新、写回数据库数据等

* @author GW

*/

public class DbAdapter {

private DatabaseHelper databaseHelper = null;

Context parent;

/**

* 构造函数

* @param context

*/

public DbAdapter(Context context) {

this.parent = context;

calItemList.clear();

databaseHelper = getHelper();

if (databaseHelper != null) {

try {

calItemDao = databaseHelper.getCalItemDao();

configDao = databaseHelper.getConfigDao();

updateHistoryDao = databaseHelper.getUpdateHistoryDao();

} catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

/**

* 析构函数

*/

@Override

protected void finalize() {

releseHelper();

}

/**

* 获取默认的配置信息

@return

*/

public ColorConfig getUsingConfig() {

try {

List config_list;

config_list = configDao.queryForEq(“isUse”, 1);

if (config_list.size() > 0) {

config = config_list.get(0);

else {

config = new ColorConfig();

newOrUpdateColorConfig(config);

}

catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return config;

}

public UpdateHistory getLastUpdateTime() {

try {

List update_list;

update_list = updateHistoryDao.queryForEq(“updateID”, “888”);

if (update_list.size() > 0) {

updateHistory = update_list.get(0);

else {

updateHistory = new UpdateHistory();

updateHistory.updateID = “888”;

newOrUpdateLastUpdateTime(updateHistory);

}

catch (SQLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return updateHistory;

}

}

Dao<T,V>

包含两个泛型,第一个泛型表DAO操作的类,第二个表示操作类的主键类型

主要方法:create:插入一条数据

createIfNotExists:如果不存在则插入

createOrUpdate:如果指定id则更新

queryForId:根据id查找

update 查找出数据

delte 删除数据

queryBuilder() 创建一个查询生成器:进行复杂查询

deleteBuilder() 创建一个删除生成器,进程复杂条件删除

updateBuilder() 创建修条件生成器,进行复杂条件修改

  1. 服务端数据库:
    使用SQL server2008图形界面方式建立数据库
    进入SQL server2008,右键“数据库”选择“新建数据库”输入输入数据库名然后点“确定”。再右键“表”选择“新建表”。然后输入每个属性的列名、数据类型、是否允许为空等。

表dbo.Table_CalendarManage 设计如下

表 dbo.Table_IMEI设计如下
表 dbo.Table_User_CalendarRelation设计如下

表dbo.Table_UserManage设计如下

  1. 数据缓冲池
    程序运行中将CalendarItem信息缓存到CalendarItemLab中,在应用中, CalendarItem数组对象将存储在一个单例里。 单例是特殊的java类,在创建
    实例时,一个类仅允许创建一个实例。应用能够在内存里存在多久,单例就能存在多久,因此将对象列表保存在单例里可保持CalendarItem数据的一直存在,不管activity、 fragment及它们的生命周期发生什么变化。
    要创建单例,需创建一个带有私有构造方法及get()方法的类,其中get()方法返回实例、setCalendarItems(ArrayList)方法可以设置单例的数据、getCalendarItem(String CalendarId)和deleteCalendarItem(String CalendarId)可以得到和删除CalendarItem数组对象中的一个CalendarItem。
    代码如下:
    public class CalendarItemLab {
    private ArrayList mCalendarItems;
    private static CalendarItemLab sCalendarItemLab;
    private Context mAppContext;
    private CalendarItemLab(Context appContext){
    mAppContext = appContext;
    mCalendarItems = new ArrayList();
    }
    public static CalendarItemLab get (Context c){
    if(sCalendarItemLab == null){
    sCalendarItemLab = new CalendarItemLab(c.getApplicationContext());
    }
    return sCalendarItemLab;
    }
    public void setCalendarItems(ArrayList CalendarItems){
    if(CalendarItems!= null) this.mCalendarItems = CalendarItems;
    else this.mCalendarItems=new ArrayList();
    }
    public void deleteCalendarItem(String CalendarId){
    for (CalendarItem c : mCalendarItems){
    if(c.getCalendarId().equals(CalendarId))
    mCalendarItems.remove©;
    }
    }
    public CalendarItem getCalendarItem(String CalendarId){
    for (CalendarItem c : mCalendarItems){
    if(c.getCalendarId().equals(CalendarId))
    return c;
    }
    return null;
    }
    }
  2. 界面实现
    1. 自定义控件MyView
      应用中的自定义控件月视图单元格DateWidgetDayCell、月视图头部DateWidgetDayHeader、日视图的DayHourCell、DayHourIndexCell、周视图的WeekDayHeader均继承自MyView。MyView继承自View类
      设置自定义控件大小和颜色:
      public MyView(Context context, int iWidth, int iHeight, ColorConfig config) {
      super(context);
      setFocusable(true);
      setLayoutParams(new LayoutParams(iWidth, iHeight));
      this.config = config;
      toDrawBox = true;
      fTextSize = config.FontSize;
      init(null, 0);
      }
      设置不透明渐变:
      public****void startAlphaAnimIn(View view) {
      AlphaAnimation anim = new AlphaAnimation(0.5F, 1);
      anim.setDuration(ANIM_ALPHA_DURATION);
      anim.startNow();
      this.startAnimation(anim);
      }
      根据变量重绘控件
      private****void init(AttributeSet attrs, int defStyle) {
      mExampleColor = config.Calendar_WeekFontColor;
      DefaultColor = config.Calendar_DayBgColor;
      // Set up a default TextPaint object
      mTextPaint = new TextPaint();
      mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
      mTextPaint.setTextAlign(Paint.Align.LEFT);
      // Update TextPaint and text measurements from attributes
      invalidateTextPaintAndMeasurements();
      }
      private****void invalidateTextPaintAndMeasurements() {
      mTextPaint.setTextSize(mExampleDimension);
      mTextPaint.setColor(mExampleColor);
      mTextWidth = mTextPaint.measureText(mMsgString);
      Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
      mTextHeight = fontMetrics.bottom;
      }
      绘制控件时调用
      @Override
      protected****void onDraw(Canvas canvas) {
      super.onDraw(canvas);
      //控件的绘制:使用Canvas Paint RectF绘制,代码省略
      }
    2. 导航栏MainTitleFragment
      在activity_calendar_my.xml中添加
      <FrameLayout
      android:id=“@+id/MainTitleFragmentContainer”
      android:layout_width=“match_parent”
      android:layout_height=“wrap_content” >


      新建MainTitleFragment.class,并设置超类为Fragment,早onCreate 方法中处理fragment要用到数据,在onCreateView方法中设置视图,R.layout._fragment_title_main是_MainTitleFragment的视图。Fragment.onCreateView(…)方法中的组件引用几乎等同于Activity.onCreate(…)方法的处理。
      在调用Fragment.onCreate(…)方法时,并不能生成fragment的视图。在Fragment.onCreate(…)方法中配置了fragment实例,通过另一个方法fragment.onCreateView()完成创建和配置fragment视图(如下所示):
      @Override
      public****void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      //数据准备
      }
      @Override
      public View onCreateView(LayoutInflater inflater,ViewGroup parent,
      Bundle savedInstanceState){
      View v = inflater.inflate(R.layout.fragment_title_main, parent,false);
      btn_month = (Button) v.findViewById(R.id.btn_month);
      //部分省略
      return View;
      }
      MyCalendarActivity继承FragmentActivity,在其中的
      FragmentManage fm = getSupportFragmentManager();
      Fragment Headfragment = fm
      .findFragmentById(R.id.MainTitleFragmentContainer);
      if (Headfragment == null) {
      Headfragment = new MainTitleFragment();
      fm.beginTransaction()
      .add(R.id.MainTitleFragmentContainer, Headfragment)
      .commit();
      }

运行结果:

    1. 头部日期标识MainDataBarFragment
      方法同上,不同的是Fragment不同

运行结果:

    1. 月、周、日视图

在月、周、日这三个试图中使用ViewPager,把一个Fragment作为一页,这样左右滑动就可以查看上一个和下一个。

  1. 首先使用自定义控件构建MonthMainFragment、WeekFragment、Dayfragment。

  2. 在MyCalendarActivity中以代码的方式定义并产生布局。FragmentManager要求任何用作fragment容器的视图都必须具有资源ID,ViewPager是一个fragment容器,因此,必须赋予其资源ID。在res/values目录下的XML文件中创建一个项目元素。创建一个名为res/values/ids.xml的Android XML资源文件,用以存储资源ID,并在其中新增一个名为viewPager的ID
    代码如下:

    <?xml version=_"1.0"_ encoding=_"utf-8"_?>

    <resources xmlns:android=http://schemas.android.com/apk/res/android>
    <item type=“id” name=“monthMainviewPager” />
    <item type=“id” name=“workItemPager” />

    以代码方式创建内容试图:
    mViewPager = new ViewPager(this);
    mViewPager.setId(R.id.monthMainviewPager);
    lin = (LinearLayout) findViewById(R.id.activity_calendar_my);
    lin.addView(mViewPager);

  3. 构建ViewPager数据:
    List mMList = new ArrayList();
    List mDList = new ArrayList();
    List mWList = new ArrayList();

  4. 使用FragmentStatePagerAdapter,它调用getItem(int)方法获取Fragment数组指定位置的Fragment,getCount()获得Pager总页数。并且他在运行时总是构建当前页、上一页、下一页三页,这样在滑动到下一页时就不会因为构建下一页而花费更多的时间。
    以月视图为例 代码如下:
    mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
    @Override
    public Fragment getItem(int arg0) {
    if (arg0 > mMList.size()) {
    return****null;
    else {
    return mMList.get(arg0);
    }
    }
    @Override
    public****int getCount() {
    return mMList.size();
    }
    });

    // 设置显示中间的一页
    mViewPager.setCurrentItem((int)(mMList.size()/2));

  5. ViewPager滑动事件监听器:
    mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
    public****void onPageScrollStateChanged(int arg0) {
    // TODO 在页面状态改变时的处理
    }
    public****void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO 页面正在滑动时的处理
    }
    public****void onPageSelected(int arg0) {
    // TODO 选中某页的处理
    }
    });//end of setOnPageChangeListener

  6. ViewPager页面刷新问题:

单纯的使用mViewPager.getAdapter().notifyDataSetChanged()并不能刷新页面。在调用notifyDataSetChanged()时,都会调用getItemPosition(Object object)方法,该方法会查看viewpager的所有item,为每个item返回一个状态值,若是POSITION_NONE,那么该item会调用destroyItem(ViewGroup container, int position, Object object)方法,然后重新生成,如果是POSITION_UNCHANGED,就不会重新生成,而默认返回值是POSITION_UNCHANGED,所以我们要想实现ViewPager页面刷新就要改写getItemPosition(Object object)方法。

解决办法:

  1. 先为每个数组对象设置ViewIndex
    // 设置显示顺序
    for (int i = 0; i < mMList.size(); i++) {
    mMList.get(i).viewIndex = i;
    }
  2. 然后在ViewPager OnPageChangeListener的onPageSelected中记录下当前页的位置position_Month
  3. 重写getItemPosition(Object object)代码,刷新当前三页即可:

@Override

public int getItemPosition(Object object) {

// 刷新前 现在 后共三页

if((position_Month -((MonthMainFragment)object).viewIndex)<1

&& (position_Month -((MonthMainFragment)object).viewIndex)>-1

return POSITION_NONE;

else return POSITION_UNCHANGED;

}

周视图和日视图构建方法同上

运行结果:

  1. 日程查看、修改、查询、新建
    构建方法同MainTitleFragment,日程修改和新建使用同一个Fragment

运行结果:

  1. 菜单的实现
    在MyCalendarActivity中重写onCreateOptionsMenu方法添加菜单,通过调用menu.add方法添加菜单项,menu.add方法的参数如下:
    第一个参数代表的是组概念。为了能更好的管理你的菜单按钮,我们将几个菜单项归为一组,。
    第二个参数代表的是项目编号。Item ID与菜单选项一一对应,这样在点选菜单项时可以得到唯一的item ID。
    第三个参数代表的是菜单项的显示顺序。按照数字的大小从小到大显示。如果是0则按照add的顺序显示菜单项 。
    第四个参数是菜单项显示的文字。
    /** 创建菜单 */
    public****boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 0, 0, config.getUserStatus());
    menu.add(0, 1, 1, “设置”);
    menu.add(0, 2, 2, “退出”);
    return****super.onCreateOptionsMenu(menu);
    }
    重写onOptionsItemSelected方法处理菜单响应。
    /** 菜单响应 */
    public****boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);
    switch (item.getItemId()) {
    // 退出
    case 2:
    MyCalendarActivity.this.finish();
    break;
    // 设置
    case 1:
    Intent i = new Intent();
    i.setClass(MyCalendarActivity.this, config_Activity.class);
    Bundle b = new Bundle();
    imei = getMIEI();
    b.putString(“imei”, imei);
    i.putExtras(b);
    startActivityForResult(i, 0);
    break;
    default:
    break;
    }
    return****true;
    }
  2. Web服务

在Android平台调用Web Service需要依赖于第三方类库ksoap2,它是一个SOAP Web service客户端开发包。Web Service需要完成注册和注销用户、同步日程信息两个功能。

  1. 引入下载的ksoap2-android类库
  2. 编写WebServiceCall.class。访问web服务方法,使用公共属性request来设置属性信息:NAMESPACE:名字空间、WEBSERVICE_URL:web服务地址、METHOD_NAME:web服务方法名称。调用web服务hts.call(null, envelope), 结果:result = (Object) envelope.getResponse()。
    public class WebServiceCall {
    public String NAMESPACE;
    public String WEBSERVICE_URL;
    public String METHOD_NAME;
    public SoapObject request;
    public int TimeOutMS = 2000;
    public WebServiceCall(String NAMESPACE, String WEBSERVICE_URL, String METHOD_NAME, int timeOut) {
    this.NAMESPACE = NAMESPACE;
    this.WEBSERVICE_URL = WEBSERVICE_URL;
    this.METHOD_NAME = METHOD_NAME;
    TimeOutMS = timeOut;
    this.request = new SoapObject(NAMESPACE, METHOD_NAME);
    }
    public Object callWebMethod() throws IOException, XmlPullParserException {
    Object result = null;
    SoapSerializationEnvelope envelope = new S oapSerializationEnvelope(SoapEnvelope.VER10);
    // 设置bodyOut属性
    envelope.bodyOut = request;
    envelope.dotNet = true;
    envelope.setOutputSoapObject(request);
    HttpTransportSE hts = new HttpTransportSE(WEBSERVICE_URL,TimeOutMS);
    hts.debug = true;
    try {
    hts.call(null, envelope);
    result = (Object) envelope.getResponse();
    } catch (IOException e) {
    throw e;
    } catch (XmlPullParserException e) {
    throw e;
    }
    return result;
    }
    }
  3. 编写WSHelper,调用web服务更便捷。在WSHelper中写相应功能的web服务调用方法,首先新建WebServiceCall对象,再为WebServiceCall设置参数,然后使用WebServiceCall. callWebMethod()调用web服务。这样使用WSHelper对象的方法就可直接完成web服务的操作,不必再程序中配置调用web服务的各种参数。

public class WSHelper {

//配置项

ColorConfig config = new ColorConfig();

//数据管理器

DbAdapter dba = null;

//构造函数

public WSHelper() {

if (dba == null) {

dba = new DbAdapter();

config = dba.getUsingConfig();

}

}

//从服务器获取新的日程

public int ReadWorkItemFromServer(String LastUpdateTime, String imei) {

int ret = -1;

SoapObject result;

WebServiceCall wsCall = new WebServiceCall(config.NAMESPACE,

config.WEBSERVICE_URL, “GetCalendarByUA”, config.timeOut);

wsCall.request.addProperty(“IMEI”, imei);

wsCall.request.addProperty(“alterTime”, LastUpdateTime);

try {

result = (SoapObject) wsCall.callWebMethod();

ret = dba.InsertManyCIS(result);

} catch (Exception e) {

ret = -1;

}

return ret;

}

}

  1. 本地服务

系统启动本地服务时先调用onCreate()方法,再调用 onStart()方法,最后在关闭服务时调用 onDestroy()方法。这个过程和创建Activity的过程类似,在启动时调用onCreate()方法进行一次初始化。程序调用Context.startService()来打开服务,调用 Context.stopService()来中止服务。

  1. 本系统实现:
    新建LocalService.class继承Service类。
    重写onCreate()方法,在其中新建一个线程。threadDisable为线程运行控制变量,当threadDisable==true时线程可一直在后台运行。While循环中,cnt是分钟计数,每休眠一分钟查询是否有提醒的日程并通知栏提醒;根据colorconfig中的同步时间调用web服务同步数据。
    new Thread(new Runnable() {
    @Override
    public****void run() {
    while (!threadDisable) {
    try {
    // 休眠1分钟
    Thread.sleep(SleepMS);
    // cnt自加
    cnt = (cnt + 1) % 3600;
    //通知栏提醒
    showNotify();
    // 根据定时时间与后台数据库同步数据
    if (dba == null) {
    dba = new DbAdapter(LocalService.this);
    config = dba.getUsingConfig();
    }
    int minu = config.syncMinute;
    if((minu>0) && (cnt%minu == 0)){
    syscFromServer();
    }
    catch (InterruptedException e) {
    }
    }
    }
    }).start();
  2. 通知栏提醒实现

//获取状态通知栏管理,

//其中创建的 nm 对象负责“发出”与“取消” Notification。

NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

Notification nf = new Notification();

//设置通知的声音、图标、主题、标记

nf.defaults |= Notification.DEFAULT_SOUND;

nf.icon = R.drawable.ic_launcher;

nf.tickerText = “日历有提醒!”;

nf.flags |= Notification.FLAG_AUTO_CANCEL;

//设置点击通知页面跳转的数据、跳转的页面

b.putString(WorkItemCheckFragment.EXTRA_WORKITEM_ID, ciList.get(i).getCalendarId());

CalendarItemLab.get(this).setCalendarItems((ArrayList) ciList);

intent.setClass(LocalService.this,WorkItemCheckPagerActivity.class);

intent.putExtras(b);

pd = PendingIntent.getActivity(LocalService.this, 0,

intent, PendingIntent.FLAG_CANCEL_CURRENT);

//设置显示在通知下拉框中的信息

//参数分别为:Context,标题,内容,PendingIntent。

nf.setLatestEventInfo(LocalService.this, ciList.get(i)

.getTopic(), CalendarBuilder

.getHourMinuteString(ciList.get(i).getStartTime()),pd);

notifyList.add(nf);

//发送通知栏消息

nm.notify((int) ciList.get(i).notifyTime.getTimeInMillis(),nf);

  1. 日程同步

    1. 手动同步
      点击导航栏上的同步按钮,在点击事件处理中调用web服务同步数据并保存在本地数据库中。但是web服务同步数据时间会比较长,所以在此处启动一个线程处理web服务。
      新建一个AsyncTask,线程运行前调用onPreExecute()在其中启动一个对话框,设置同步标志is_downloading = true;后台运行时调用doInBackground(String… params),在其中调用web服务并返回结果UpdateCnt;运行结束后调用onPostExecute(String result),在其中关闭对话设置同步标志is_downloading =false并处理结果;当返回键按下时调用onCancelled()关闭对话框并设置同步标志is_downloading =false。
      //新建线程 运行线程
      wsTask = new WSTask();
      wsTask.execute();
      class WSTask extends AsyncTask<String, Integer, String> {
      // TODO 后台同步数据
      @Override
      protected****void onCancelled() {
      pupdateDialog.cancel();
      is_downloading = false;
      super.onCancelled();
      }
      @Override
      protected****void onPreExecute(){
      is_downloading = true;
      pupdateDialog = ProgressDialog.show(getActivity(), “请稍等”,“正在更新任务项…”);
      }
      @Override
      protected String doInBackground(String… params) {
      //mWsHelper.IMEIRegister(“姚鹏”, “73”, getMIEI());
      UpdateCnt = mWsHelper.ReadWorkItemFromServer(dba.getLastUpdateTime()
      .getAlterTime(), imei);
      dba.updataLastUpdateTime();
      return UpdateCnt+“”;
      }
      @Override
      protected****void onPostExecute(String result) {

      if (pupdateDialog != null) {
      pupdateDialog.dismiss();
      }
      if(result.equals(“-1”) || result.equals(“0”)){
      //Handler.post(runnableUi_xml_error);
      }else{
      //Handler.post(runnableUi_update_ui);
      myListener.updateViewPager();
      }
      is_downloading = false;
      super.onPostExecute(result);
      }
      }

    2. 自动同步
      在4.4章节中已介绍。

  2. 屏幕旋转后的Activity数据保存
    Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到以下状况时,如:Home键按下时、手机内存不足时,一个Activity会由系统销毁,这时Activity会调用onSaveInstanceState()保存数据。但是当用户在应用中按返回键去主动销毁一个Activity时,Activity不会调用onSaveInstanceState()方法,这是因为用户的行为表明要退出应用故不再需要存储Activity的状态。通常保存一些临时性的状态使用onSaveInstanceState(),而数据的持久化保存使用onPause()适。
    屏幕旋转时会调用onSaveInstanceState(),在其中保存一些临时数据(当前界面ID、选中的日期)
    @Override
    protected****void onSaveInstanceState(Bundle outState){
    outState.putInt(“cal_ui”, cal_ui);
    String temp = CalendarBuilder.Calendar2LongString(calSelected);
    outState.putString(“calselected”, temp);
    super.onSaveInstanceState(outState);
    }
    在Activity获取保存的临时数据需要在onCreate()方法中使用如下代码:
    if(savedInstanceState != null){
    cal_ui = savedInstanceState.getInt(“cal_ui”);
    Calendar tempCalendar = CalendarBuilder.longString2Calendar(savedInstanceState.getString(“calselected”));
    calSelected = (Calendar) tempCalendar.clone();
    }

  3. Activity向Fragment传递数据
    利用fragment argument。在Fragment中新建newInstance()方法,在newInstance中首先需创建Bundle对象。然后,使用Bundle限定类型的“ put”方法(类似于Intent的方法) ,将argument放到bundle中。附加bundle给fragment,需使用Fragment.setArguments(Bundle)方法。
    public****static MonthMainFragment newInstance(Calendar calstartdate,ColorConfig config,int cell_size) {
    Bundle args = new Bundle();
    args.putSerializable(CAL_START_DATE, calstartdate);
    args.putSerializable(CAL_CONFIG, config);
    args.putSerializable(CELL_SIZE, cell_size);
    MonthMainFragment fragment = new MonthMainFragment();
    fragment.setArguments(args);
    return fragment;
    }
    在Fragment中onCreate方法中通过getArguments().getSerializable();方法获取数据。
    @Override
    public****void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
    calStartDate = (Calendar) getArguments()
    .getSerializable(CAL_START_DATE);
    config = (ColorConfig) getArguments().getSerializable(CAL_CONFIG);
    cell_size = (Integer) getArguments().getSerializable(CELL_SIZE);
    if (dba == null) {
    dba = new DbAdapter(getActivity());
    config = dba.config;
    }
    }
    当CrimeActivity创建CrimeFragment时,应调用CrimeFragment.newInstance(UUID)方法。
    MonthMainFragment.newInstance((Calendar)(calTodayDate.clone()), config,cell_size)

  4. Fragment之间通信

  5. Fragment与它所属Activity通讯。首先在Fragment类中定义一个接口,并在它所属的Activity中实现接口。Fragment在它的onAttach()方法执行期间捕获该接口的实现,然后调用接口方法,这样便可以同Activity通信。
    //TODO fragment信息交换接口
    public****interface MainTitleClickListener
    {
    public****void setToToday();
    }
    private MainTitleClickListener myListener;
    @Override
    public****void onAttach(Activity activity)
    {
    super.onAttach(activity);
    myListener = (MainTitleClickListener) activity;
    }

  6. 在Activity中实现setToToday()接口类即可。这样Fragment就可以和Activity通信了。

  7. 在Fragment中定义public的方法,在Activity中通过FragmentManager. findFragmentById()方法可获得Fragment,在调用Fragment的public方法。这样就可以完成Activity与Fragment的通信。

代码如下:

FragmentManager fm = getSupportFragmentManager();

Fragment MainDateBarfragment = fm

.findFragmentById(R.id.MainDateBarContainer);

((MainDateBarFragment) MainDateBarfragment).setTopDate(calShowDate);


  1. 总结

    1. 遇到的问题与解决
      1. ViewPager大小不能自适应
        本系统中多次使用ViewPager,但是他却有个小麻烦。ViewPager的大小总是填满整个屏幕的剩余部分,即使为它使用了android:height="wrap_content"这个属性也是没有效果的
        解决方法:通过LayoutParmas动态改变ViewPager的高度。计算View的大小并通过LayoutParmas设置给ViewPager。
        代码如下:
        lin = (LinearLayout) findViewById(R.id.activity_calendar_my);
        lin.addView(mViewPager);
        LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(childViewWeight, childViewHeight);
        lin.setLayoutParams(params);
      2. 调用系统通讯录并返回联系人的姓名、电话号码
  2. 打开系统通讯录
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
    startActivityForResult(intent, REQUEST_CONTACT);

  3. activity是以需返回结果的方式启动的,所以我们会调用onActivityResult(…)方
    法来接收一个intent。该intent包括了数据URI。该数据URI是一个指向用户所选联系人的定位符。创建了一条查询语句,要求返回全部联系人的显示名字。然后查询联系人数据库,获得一个可用的Cursor。因为已经知道Cursor只包含一条记录,所以将Cursor移动到第一条记录并获取它的字符串形式。该字符串即为联系人的姓名。

  1. 联系人应用返回包含在intent中的URI数据给父activity时,它会添加一个Intent.FLAG_GRANT_READ_URI_PERMISSION标志。该标志向Android示意,应用中的父activity可以使用联系人数据一次。这种方式工作的很好,因为,我们不需要访问整个联系人数据库,只需访问数据库中的一条联系人信息。
  2. Calendar类相关计算
    Calendar c = Calendar.getInstance(); //当前时间
    c.set(Calendar.DAY_OF_MONTH, 1);//设置为本月1日
    System.out.println(c.getTime()); //输出本月一号的date
    //获取本月一号是星期几
    System.out.println(c.get(Calendar.DAY_OF_WEEK));
    //设置为本月日历中的第一天
    c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH)- (c.get(Calendar.DAY_OF_WEEK) - 1));
    //输出本月日历显示的第一天的date
    System.out.println(c.getTime());
    //获取日历上显示的最后一天
    c.set(Calendar.DAY_OF_MONTH, c.get(Calendar.DAY_OF_MONTH) + 6 * 7 - 1);
    //输出日历上显示的最后一天的date
    System.out.println(c.getTime());
  3. 软件的优势与不足

优势:

  1. 现在处于信息时代每天接受的信息太多。本软件可以把日程信息整理到日历上,使信息更加明朗。
  2. 使用了局部视图Fragment,使用fragment而不是activity进行UI管理,可绕开Android系统对activity规则的限制。依据应用和用户的需求,可结合使用fragment和activity来组织或重新组织用户界面,这样界面设计更加灵活。

不足:

  1. Fragment构建View过程中进行查询数据库,导致页面构建速度慢。
  2. 手机端数据库数据冗余较大,造成存储浪费。
  3. 总结
    本软件是基于Android平台的个人日历,是办公自动化软件的一部分。最终本软件完成了基本功能,运行流畅。
    1. 绪论中讨论了课题背景,技术发展研究概况,课题研究意义,课题研究 内容及方法。
    2. 第二章讨论了课题开发的相关技术,包括Android平台,局部视图Fragment,web服务,本地服务,MVC开发模式。并简单介绍了本软件的开发软件、开发语言、和运行平台。
    3. 第三章对系统做了简单的需求分析,得到用户的功能和界面需求。并设计软件的功能、界面、数据层。
    4. 第四章详细的介绍了个人日历软件的各个模块的设计和实现,包括界面的实现、数据层实现、web服务、本地服务、日程同步、屏幕旋转后Activity数据保存、Activity向Fragment传递数据、Fragment之间的通信。
    5. 第五章介绍了在开发过程中遇到的问题,解决方法,以及应用的优势与不足。由于之前并没有完整的完成一个系统的设计与实现,并且使用对于自己来说比较新的技术而增加了不少难度,但是在老师的帮助下完成了它。毕业设计大大提高了我对软件的整体架构的认识和软件编程能力。

参考文献

[1] Bill Phillips Brian Hardy等,Android编程权威指南[M]. 北京:人民邮电出版社,2014.

[2] 李刚等.疯狂Android讲义(第2版). 北京:电子工业出版社,2013.

[3] Zigurd Mednieks等.Android程序设计(第2版). 北京:机械工业出版社,2014

[4] 明日科技. Android从入门到精通.北京:清华大学出版社,2012

[5] (美)迈耶.Android 4 高级编程(第3版). 北京:清华大学出版社,2014

[6] 何孟翰.Google AndroidSDK开发实战演练[M].北京:人民邮电出版社,2012.

[7] 高凯等.Android智能手机软件开发教程[M].北京:国防工业出版社,2012.6.

[8] 徐炳文.佳木斯大学学报(自然科学版).基于J2ME的手机教务课表查询系统设计与实现[J/OL],2010-1(28).

[9] 徐炳文.天津师范大学学报(自然科学版).基于四层架构J2ME移动课表查询系统的开发设计 [J/OL],2011-4(31).

[10] 王中华等.宜春学院学报.Android手机理财软件的实际和实现 [J/OL],2011-12(33).

[11] Gasolin.深入浅出Android手持设备应用程序设计.北京:电子音像出版社,2010.

[12] 张新柱等.齐齐哈尔大学学报.基于Android手机终端信息查询系统的设计 [J/OL],2012-7(28).

[13] Marziah Karch.Android for work:productivity dor professionals[M].USA:
Apress.2010,97~114.

[14] 基于Android的课程表提醒程序.百度百科.http://wenku.baidu.com/view/83d0e6b969dc5022aaea0036.html

[16] 赵洛育,刘洪利. android开发实战经验. 北京:清华大学出版社,2012.

[17] 王金柱,王翔,闫秀华,张雪娇. android学习精要. 北京:清华大学出版社,2012

致谢

致谢

经过将近两个月的学习与实践,本次毕业设计也接近了尾声。通过毕业设计的锻炼,我不仅学到了很多关于android应用开发的知识还体会到在设计开发程序中应有的态度。当然这些知识经验的获得与老师和同学的热心帮助是分不开的。在这里我向他们表示真挚的感谢。 在这里首先要感谢我的导师王晓辉老师。导师的专业水平,治学严谨的态度和科学研究的精神深令我敬佩,并将积极 影响我以后的学习和工作。虽然老师平日工作繁忙,但是老师针对我的问题总是细心的指导,他给我提供了很多创意性的想法,打开了我的思路。

最后要感谢我的母校——华北电力大学,是母校给我们提供了优良的学习环 境。感谢那些曾给我授过课的每一位老师,是你们教会我专业知识。在此,我再 说一次谢谢!谢谢大家!

文章知识点与官方知识档案匹配,可进一步学习相关知识

云原生入门技能树首页概览16822 人正在系统学习中

项目源码

微信公众号

android项目源码/dw静态网页制作/学生网页

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值