2014-09-29 14:49:33 farsight2009 阅读数 13832


图书简介:

《android应用程序开发与典型案例》共23章,内容包含两大部分。第一部分是android程序设计基础,在介绍android环境搭建以及android系统基本控件和组件后,详细介绍了android系统应用编程中典型的技术,比如,android中的图形图像、多媒体编程、gps定位与地图编程等;第二部分是android程序ui设计,从手机软件的交互设计谈起,介绍了android用户界面设计原则和方法、android用户界面设计哲学等,并给出了具体建议。

《android应用程序开发与典型案例》是大学院校嵌入式技术专业、电子信息类其他专业的专业课程教材,也可供高等及中等职业技术院校使用。


完整版下载:

http://download.csdn.net/detail/u014170843/7961105


电子版效果截图:



图书目录:

上篇 android应用程序设计基础 
第1章 android基本概念 
1.1 android简介 
1.2 android平台特性 
1.3 android系统架构
1.3.1 linux内核(linux kernel)
1.3.2 android核心库(libraries)
1.3.3 android运行时环境(android runtime)
1.3.4 android应用程序框架(application framework)
1.3.5 android应用程序 
1.4 android开发框架 
1.4.1 应用方面 
1.4.2 数据存储 
1.4.3 网络访问方面 
1.4.4 开发流程 
1.5 oms简介 
1.5.1 ophone介绍 
1.5.2 widget介绍
1.6 本章小结 
第2章 android开发环境搭建 
2.1 android 开发环境的安装与配置 
2.1.1 安装jdk和配置java开发环境 
2.1.2 eclipse的安装 
2.1.3 sdk和adt的安装和配置 
2.2 创建第一个android应用 
2.3 在模拟器上运行程序
2.4 在手机上运行程序 
2.5 本章小结 
第3章 程序设计基础 
3.1 android程序框架 
3.1.1 android项目目录结构 
3.1.2 android应用解析 
3.2 android程序ui设计 
3.3 java语言在android程序中的使用 
3.3.1 interface的使用
3.3.2 abstract class的使用 
3.3.3 interface与abstract class的区别 
3.3.4 for循环的使用 
3.3.5 map类的使用 
3.3.6 integer与string之间的转换 
3.3.7 synchronized同步关键字
3.3.8 new的使用 
3.4 本章小结 
第4章 android生命周期 
4.1 程序生命周期
4.2 android组件 
4.3 activity生命周期 
4.3.1 全生命周期 
4.3.2 可视生命周期 
4.3.3 活动生命周期 
4.4 activity启动模式 
4.4.1 standard标准模式 
4.4.2 singletop
4.4.3 singletask 
4.4.4 singleinstance 
4.5 程序调试
4.5.1 logcat命令行工具 
4.5.2 devtools开发调试工具 
4.6 本章小结
第5章 用户界面开发 
5.1 用户界面基础 
5.2 界面布局 
5.2.1 线性布局(linearlayout) 
5.2.2 框架布局(framelayout) 
5.2.3 表格布局(tablelayout) 
5.2.4 相对布局(relativelayout) 
5.2.5 绝对布局(absolutelayout) 
5.3 界面控件 
5.3.1 textview 和 edittext 
5.3.2 button 和 imagebutton 
5.3.3 checkbox 和 radiobutton 
5.3.4 spinner 
5.3.5 listview 
5.3.6 tabhost 
5.4 菜单
5.4.1 选项菜单 
5.4.2 子菜单 
5.4.3 上下文菜单(context menu) 
5.5 界面事件 
5.5.1 按键事件 
5.5.2 触摸事件 
5.6 自定义样式和主题 
5.7 9patch 
5.8 本章小结 
第6章 组件间通信 
6.1 intent对象及其属性 
6.1.1 intent的action属性 
6.1.2 intent的data属性 
6.1.3 intent的type属性 
6.1.4 intent的category属性 
6.1.5 intent的extras属性
6.1.6 intent的componentname属性 
6.2 系统标准activityaction应用 
6.2.1 启动activity 
6.2.2 获取activity返回值 
6.3 intent过滤器 
6.3.1 注册intent过滤器 
6.3.2 intent解析 
6.4 广播消息 
6.5 本章小结 
第7章 数据存储与访问 
7.1 简单存储 
7.1.1 sharedpreferences 
7.1.2 示例 
7.2 文件存储 
7.2.1 内部存储 
7.2.2 外部存储 
7.2.3 资源文件 
7.3 数据库存储 
7.3.1 sqlite简介 
7.3.2 手动建立数据库 
7.3.3 代码建库(sqliteopenhelper)
7.3.4 数据操作(sqlitedatabase) 
7.4 数据共享(contentproviders) 
7.4.1 创建contentprovider 
7.4.2 使用数据提供者 
7.5 本章小结
第8章 多媒体开发
8.1 android系统提供内置的媒体格式 
8.2 在窗体布局上——videoview 
8.3 音频及视频播放——mediaplayer 
8.2.1 从源文件中播放 
8.3.2 从文件系统中播放 
8.3.3 从网络中播放 
8.3.4 音频播放示例 
8.3.5 视频播放示例 
8.4 音/视频的录制 
8.5 camera照相 
8.6 本章小结 
第9章 定位与地图 
9.1 位置服务 
9.1.1 android location api 
9.1.2 gps定位步骤 
9.2 google地图应用
9.2.1 申请地图密钥 
9.2.2 使用google地图 
9.2.3 使用overlay 
9.3 本章小结
第10章 android中的图形图像 
10.1 在android中访问图片 
10.1.1 使用图片文件创建drawable对象 
10.1.2 使用xml文件定义drawable属性 
10.1.3 bitmap和bitmapfactory 
10.2 android中的动画 
10.2.1 tween动画 
10.2.2 frame动画 
10.3 动态图形绘制 
10.3.1 动态图形绘制类简介 
10.3.2 动态图形绘制的基本思路 
10.3.3 绘制几何图形 
10.4 图形特效 
10.4.1 使用matrix实现旋转、缩放和平移 
10.4.2 使用shader类渲染图形 
10.5 本章小结 
第11章 android ndk开发 
11.1 android ndk简介 
11.2 ndk编译环境 
11.3 ndk开发示例 
11.4 本章小结 
第12章 android特色开发 
12.1 app widget 
12.1.1 app widget的生命周期 
12.1.2 建立android app widget 
12.2 传感器 
12.2.1 android中的传感器分类 
12.2.2 android中的传感器功能 
12.3 本章小结
第13章 android网络编程 
13.1 android网络通信基础 
13.1.1 标准java接口 
13.1.2 apache接口
13.1.3 android网络接口
13.2 http通信 
13.2.1 httpurlconnection接口
13.2.2 httpclient接口 
13.3 socket通信 
13.3.1 socket传输模式 
13.3.2 socket编程原理 
13.3.3 socket编程实例 
13.3.4 socket与http通信的区别 
13.4 本章小结 
第14章 android应用开发流程
14.1 应用规划及架构设计 
14.1.1 客户端功能点概述 
14.1.2 服务器端功能点概述 
14.2 实现ui 
14.3 数据操作和存储 
14.4 实现多页面跳转 
14.5 实现各个模块逻辑处理
14.5.1 登录注册模块的实现 
14.5.2 个人中心模块的实现 
14.5.3 发布约会模块的实现 
14.5.4 查看好友模块的实现 
14.6 完善应用细节 
14.7 应用测试和发布 
14.8 本章小结 
下篇 android应用程序ui设计 
第15章 android交互式界面设计概述 
15.1 交互设计概念 
15.1.1 交互设计的科学范畴及定义 
15.1.2 交互设计的特征 
15.2 用户图形界面设计概念 
15.2.1 图形界面的分类 
15.2.2 图形界面设计原则简述 
15.2.3 界面是用户的唯一感知通道 
15.2.4 交互设计与图形界面设计 
15.3 交互式图形界面设计新趋势 
15.3.1 手持移动设备的产品特点 
15.3.2 注重界面设计中的情感因素 
15.3.3 以用户为中心的界面设计 
15.4 交互式界面设计过程 
15.4.1 人机交互工程师的必备素质 
15.4.2 交互设计流程 
15.5 本章小结 
第16章 android用户界面设计原则 
16.1 android ui 设计特点 
16.1.1 特点一:慎用常驻通知栏 
16.1.2 特点二:界面无须返回按钮 
16.1.3 特点三:分享机制更加优越 
16.1.4 特点四:处理缓存文件 
16.1.5 特点五:退出无须确认 
16.1.6 特点六:默认检查更新 
16.1.7 特点七:关注焦点状态 
16.1.8 特点八:提示转存到存储卡 
16.2 android ui设计原则及相关事项 
16.2.1 android ui设计思想 
16.2.2 ui设计关键事项 
16.2.3 ui元素管理 
16.3 ui设计中资源的使用 
16.4 本章小结 
第17章 android ui设计过程与哲学 
17.1 android用户界面基础 
17.2 android 界面设计过程要点 
17.2.1 布局(layout)
17.2.2 组件(widget) 
17.2.3 菜单(menu) 
17.2.4 界面事件 
17.2.5 高级ui设计简述 
17.3 android ui设计的四个要点 
17.3.1 开放源码和ui的一致性 
17.3.2 支持多种互动模式 
17.3.3 公告管理 
17.3.4 支持无缝的互动 
17.4 android优秀界面设计哲学准则简述 
17.4.1 关注用户群体特点 
17.4.2 显示用户关注的内容 
17.4.3 适当的用户反馈 
17.4.4 符合逻辑的行为模式 
17.4.5 容错性 
17.5 本章小结 
第18章 必须了解的android ui框架特性 
18.1 android布局常用的公共属性 
18.2 android ui中的六大布局对象 
18.2.1 框架布局 
18.2.2 线性布局 
18.2.3 表格布局
18.2.4 绝对布局 
18.2.5 相对布局 
18.2.6 网格布局 
18.3 9-patch图片格式 
18.4 本章小结 
第19章 android widget常用控件设计
19.1 控件基础
19.1.1 创建widget控件实例 
19.1.2 控件事件处理 
19.2 常用控件介绍 
19.2.1 文本框(textview)
19.2.2 按钮(button) 
19.2.3 编辑框(edittext)
19.2.4 单项选择(radiogroup)
19.2.5 多项选择(checkbox)
19.2.6 下拉列表(spinner)
19.2.7 自动完成文本 
19.2.8 日期时间选择器 
19.2.9 进度条(progressbar) 
19.2.10 拖动条 
19.3 菜单 
19.3.1 选项菜单 
19.3.2 子菜单 
19.3.3 快捷菜单 
19.4 视图(imageview) 
19.4.1 图片视图 
19.4.2 网络视图 
19.4.3 卷轴视图 
19.5 本章小结 
第20章 android ui设计模式 
20.1 ui设计模式基础 
20.1.1 移动设备界面设计趋势 
20.1.2 ui设计模式概述 
20.2 android ui设计模式组件 
20.2.1 dashboard仪表板 
20.2.2 action bar操作杆 
20.3 android ui设计技巧 
20.3.1 使用include标签包含共享ui设计 
20.3.2 自定义样式和主题
20.3.3 渐变效果实现
20.3.4 自定义按钮使用效果 
20.4 本章小结
第21章 android软件图标
21.1 pc平台下的软件图标 
21.1.1 png格式 
21.1.2 ico格式 
21.1.3 bmp格式 
21.1.4 tiff格式 
21.1.5 gif格式 
21.1.6 jpeg格式 
21.1.7 svg格式 
21.2 android平台下的软件图标 
21.2.1 图标要具有可识别性
21.2.2 图标要与环境相协调
21.2.3 图标要体现差异性
21.2.4 图标要有统一的风格 
21.3 本章小结 
第22章 android ui图标设计 
22.1 android 图标类型 
22.1.1 启动图标 
22.1.2 菜单图标 
22.1.3 状态栏 
22.1.4 对话图标 
22.1.5 标签图标 
22.1.6 列表视图图标 
22.2 屏幕密度概述 
22.3 android 图标设计风格 
22.3.1 交互性 
22.3.2 现代性 
22.3.3 标志性 
22.3.4 触感与质感 
22.4 本章小结
第23章 android平板电脑应用程序的设计
23.1 平板电脑概述
23.2 android平板电脑与ipad 
23.2.1 两者差异 
23.2.2 相同之处 
23.3 设计模式 
23.4 平板电脑布局策略 
23.4.1 使用碎片和多窗格视图 
23.4.2 方向策略 
23.5 关于动画 
23.5.1 xml中定义动画 
23.5.2 java code中定义动画 
23.6 从例子中学习 
23.7 在线资源 
23.8 本章小结 
附录a 知识点测评答案

2019-08-04 18:42:20 Test_tju 阅读数 191

地图样式

Android的Maps SDK允许完全定制地图的外观。您可以选择由我们的制图师设计的Mapbox的核心样式之一,或者通过调整地图的颜色、图标和字体来创建自定义地图样式,以匹配您的应用程序的UI或公司的品牌。

有两种方法可以定制地图的外观:使用Mapbox Studio创建自定义地图样式并将其加载到应用程序中,或者在运行时使用Android的Maps SDK更新地图特性。

其中,使用运行时样式,您可以实时动态地更改映射的外观和感觉。根据一天的时间调亮或变暗地图,根据用户的活动个性化图标和地图颜色,动态切换语言,或者根据用户的喜好增加标签的大小,以提高可读性。

Style对象

style对象是指在应用程序中使用的 Mapbox map style。Sources, layers, images 通过 map 样式出现在地图上,因此它们是通过 style 对象添加和检索的,而不是添加到实际的 MapboxMap 对象中。使用 style 有三种方法:

  • default Mapbox style found in the Style class constants
  • custom map style URL from a Mapbox account
  • raw style JSON string or referenced style JSON via asset:// or path://

如果样式加载失败或设置了无效的样式URL,map视图将变为空白。一个错误消息将被记录在 Android logcat 和 MapView 中。将触发OnDidFailLoadingMapListener回调。

修改样式

如果想加载一个全新的地图样式,就必须以编程方式更改地图样式URL。使用样式URL作为参数调用mapboxMap.setStyle():

mapboxMap.setStyle(new Style.Builder().fromUrl(uniqueStyleUrl), new Style.OnStyleLoaded() {
	@Override
	public void onStyleLoaded(@NonNull Style style) {
		// 自定义地图样式已经加载,现在已经准备好地图
		
	}
});

Mapbox 默认样式

Mapbox提供了六种专业设计的地图样式:

  1. Mapbox Streets
  2. Outdoor
  3. Light and Dark
  4. Satellite
  5. Satellite Streets
  6. Traffic

Maps SDK的样式类具有默认Mapbox样式URL的 “私有静态最终字符串 private static final String” ,因此当您必须通过项目中的setStyle()方法或其他方法传递样式URL时,可以方便地引用这些样式:mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded()

检索地图图层

检索地图图层是实时调整地图视觉效果的第一步。每个地图样式都有一堆地图图层,每个图层都有一个id,如 landuse, settlement-label, natural-point-label, and building。

Layer singleLayer = mapboxMap.getStyle().getLayer(UNIQUE_LAYER_ID);

可以在Mapbox Studio中查看地图样式的图层z索引顺序和图层id。

查看此信息的另一种方法是,一旦地图加载到设备上,就将每个层ID打印到Android logcat,如下:

mapView.getMapAsync(new OnMapReadyCallback() {
@Override
	public void onMapReady(@NonNull final MapboxMap mapboxMap) {

		mapboxMap.setStyle(Style.MAPBOX_STREETS, new Style.OnStyleLoaded() {
			@Override
			public void onStyleLoaded(@NonNull Style style) {
			
				// 将每个层ID打印到Android logcat
				for (Layer singleLayer : mapboxMap.getStyle().getLayers()) {
					Log.d(TAG, "onMapReady: layer id = " + singleLayer.getId());
				}
				
			}
		});
	}
});

一旦有了单独的映射层,就可以使用 data-driven styling 和表达式来调整该层的属性。例如,下面是如何使用 runtime styling 将水层的蓝色更改为更深的蓝色:

button.setOnClickListener(new View.OnClickListener() {
@Override
	public void onClick(View view) {
		mapboxMap.getStyle().getLayer("water").setProperties(PropertyFactory.fillColor(Color.parseColor("#004f6b")));

	}
});

样式相关的事件

更改地图的样式将触发“地图事件”。有关事件以及如何在地图样式加载完成后在地图上显示或重新显示数据的更多信息,请阅读地图更改事件文档



地图注释

Android 的 Mapbox Maps SDK 提供了几种不同的方法来标记一个点、创建一个圆、在多个点之间添加一条线或绘制一个多边形。通常,可以在地图的顶部或者在某些情况下,在地图本身中绘制这些对象。

注意:在针对 Android 的 Mapbox Maps SDK 的 7.0.0 发行版中,该页面的大部分代码都被弃用了。不再维护 Polygon、Polyline 和 Marker 等类。这也意味着不应该使用 PolygonOptions 和 PolylineOptions 之类的类。最后,这还意味着不应该使用 addPolygon()、addPolyline() 或 addMarker() 等方法。

如果计划向地图添加任何图标、文本、行或多边形,请查看Android的Mapbox注释插件。它简化了注释,并为显示数据提供了额外的灵活性。

1.Source和Layer

将数据源和映射层一起使用是在Mapbox map上显示数据的最有效选项。这种组合还可以让您更好地控制以下内容:

  • 表示单个点的图标
  • 多条线段
  • 多边形

有关更多信息,请参见上面的数据驱动样式。您可以探索用于创建注释的源和层组合。下面,您将发现关于显示注释的更简单(和更低性能)方法的信息。

2.标记Marker

当标识地图上的一个点时,标记是有用的。SDK 附带一个默认的标记图标,可以根据您的特定需求进行配置。 api 可以选择将此图标更改为您希望的任何位图图像。要为您的地图创建一个标记,您只需要提供一个 LatLng 位置,该位置定义了标记将放置在地图上的位置。调用 mapboxMap.addMarker() 将标记添加到映射中。

mapboxMap.addMarker(new MarkerOptions()
	.position(new LatLng(48.85819, 2.29458))
	.title("Eiffel Tower"));

除了提供位置之外,还可以添加显示在信息窗口内的标题和代码片段。当用户点击标记时显示信息窗口,当用户点击信息窗口外时关闭信息窗口。

对于多个地点标记:或者正在从GeoJSON文件中加载它们,那么可以使用mapboxmap . addMarkers() 添加标记列表。

移除标记

Mapbox Android SDK 提供了两种删除标记的方法。如果希望删除特定的标记,在传递要删除的标记对象时使用mapboxMap.removeMarker()
如果希望删除所有标记,请调用mapboxMap.clear()方法。注意,这还将删除添加到映射中的所有折线和多边形。

自定义标记icon

您可以通过使用 IconFactory 对象并将其传递给 Marker 来指定自定义 icon。如果在创建标记时没有指定图标,则使用默认标记图标。该标记的锚定将在中心,这意味着如果您有一个带有点的图标,您将需要添加填充到图像的底部。将自定义标记图像放在项目的 drawable 文件夹中,并注意其文件名。在下面的示例中,自定义图标的图像文件名为 blue_mark.png:

// Create an Icon object for the marker to use
IconFactory iconFactory = IconFactory.getInstance(MainActivity.this);
Icon icon = iconFactory.fromResource(R.drawable.blue_marker);

// Add the marker to the map
mapboxMap.addMarker(new MarkerViewOptions()
	.position(new LatLng(-37.821648, 144.978594))
	.icon(icon));

捕获事件标志

Android的 Mapbox Maps SDK 提供了一个方便的监听器,当用户点击一个标记时就可以捕获。默认情况下,所有标记都带有一个 onMarkerClick 事件监听器,用于显示和隐藏信息窗口。您可以覆盖这个默认事件监听器,并使用 setOnMarkerClickListener 方法设置您自己的事件监听器。

要显示带有已单击标记标题的 toast 消息,请使用 setOnMarkerClickListener 侦听一个单击事件,最后调用toast.maketext()。要防止同时显示 toast 消息和信息窗口,请在末尾返回true。示例如下:

mapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
  @Override
  public boolean onMarkerClick(@NonNull Marker marker) {
    // 用选定标记的标题显示toast
    Toast.makeText(this, marker.getTitle(), Toast.LENGTH_LONG).show();
    return true;
  }
});

更新标记

如果您打算更新标记而不是完全删除它,SDK 提供了一些更新方法。使用这些代码意味着更少的样板代码和性能的提高,因为只是在更新标记。

例如,使用这些更新 api,您可以使用 ValueAnimator 创建动画标记。您可以找到用于更新标记对象引用中的标记位置或图标位图的 api。示例:

// 修改标记marker的位置
marker.setPosition(new LatLng(-37.822884, 144.981916));

// 更新marker的图标
marker.setIcon(icon);

3.多段线和多边形

在地图中添加一条线或一个多边形就像添加一个标记。由于这些对象的性质,会公开不同的 api,比如多边形颜色或线宽。将所有 LatLng 对象捆绑在一个列表中,然后使用 addAll() 方法将它们传递进来,而不是放在一个位置。

在地图上画多段线时,需要注意,第一个点和最后一个点的坐标应该是重合的:

mapboxMap.addPolyline(new PolylineOptions()
	.addAll(points)
	.color(Color.parseColor("#3bb2d0"))
	.width(2));

在画多边形时,起点和终点同样需要重合:

List<LatLng> polygonLatLngList = new ArrayList<>();

polygonLatLngList.add(new LatLng(45.522585, -122.685699));
polygonLatLngList.add(new LatLng(45.534611, -122.708873));
polygonLatLngList.add(new LatLng(45.530883, -122.678833));
polygonLatLngList.add(new LatLng(45.515369, -122.678489));
polygonLatLngList.add(new LatLng(45.506346, -122.702007));
polygonLatLngList.add(new LatLng(45.522585, -122.685699));

mapboxMap.addPolygon(new PolygonOptions()
.addAll(polygonLatLngList)
.fillColor(Color.parseColor("#3bb2d0")));

使用线条和填充图层
要使用上面代码中的 addPolyline() 或 addPolygon() 方法,需要拥有一个 LatLng 对象列表,这些对象表示行或多边形区域。

正如本页面顶部所解释的,使用 source 和 layer 可以使您更灵活地在地图上显示地理数据。使用 LatLng 对象列表,您可以创建一个 featurecall 并使用该 featurecall 创建一个 GeoJsonSource。

  • 将 GeoJsonSource 提供给一个线性层,以显示您将通过 addPolyline() 绘制的线
  • 将 GeoJsonSource 提供给一个填充层,用于显示通过 addPolygon() 绘制的区域
2011-12-31 13:52:25 hzbingyue 阅读数 639
 

课程描述:
    本课程以某行业企业中的实际应用需求为基础,针对在Android系统下的中的开发技术进行讲解。
    本课程基础部分讲解以一个简单的订单管理功能为核心,详细讲解Android基础开发知识,开发环境,调试,UI技术和程序界面控件,移动存储sqllite等技术。
在高级应用课程中主要讲解高级API的使用,包括常见的Android地图及GPS定位应用,以及Android视频API的使用,并且使用这些功能对原有订单系统进行完善,
课程案例实战部分最后以一个较大的移动CRM系统案例对整个Android开发技术在实际项目中的设计和开发应用进行讲解,将上两部分中开发的订单系统嵌入到本实战案例当中,并且加入客户拜访,客户信息管理,销售线索管理等其它功能模块的设计和开发思路。

购买地址:http://www.china-pub.com/3501161

作者简介
张萌

    北京市工业大学软件工程硕士,曾担任亿美软通系统架构师、担任G-NET软件维护主管、北京用友软件[集团]有限公司高级工程师等职务。
 
    7年的软件设计开发和4年的项目管理经验,一直致力于在J2EE和.NET领域内的构建企业应用系统及移动应用设计开发工作, 曾主持设计完成多个重点国家事业单位内部信息管理系统和企业CRM系统的开发实施。近年来致力于在Android平台下的移动应用上的产品设计和项目实施。在对企业信息化系统, 以及移动应用开发方面有充足的实施经验,在J2EE和.Net相关技术和实施管理有丰富的经验和对各类开源项目和技术具有浓厚的兴趣。具有多年的系统架构设计管理经验。个人技术能力主要集中在J2EE和Android架构下的应用开发。

目录

第一部分 基础知识

第1讲:android介绍及知识

第2-13讲:android 开发环境应用及helloworld应用程序开发与调试技术

第14-19讲:android 应用构成及系统架构详解

第20-21讲:android 平台资源管理

第22-29讲:android ui技术详解
android界面开发概述

第30-32讲:android sqllite数据库技术与android移动存储

第33-34讲:android 系统安全与权限

第35-39讲:第一部分总结案例-订单管理系统android客户端

第二部分 高级应用

第40-41讲:android地图定位应用

第42-43讲:android视频采集和播放

第44-45讲:android与web应用服务的集成

第46讲:第二部分总结案例--订单管理系统中的多媒体控制及与服务器交互

第三部分 案例实战---移动crm系统的设计与实现

第47讲:移动crm架构设计

第48-53讲:移动crm的开发实现详解

 

2015-03-21 19:37:28 smbroe 阅读数 5490

最近在帮老师做一个项目,类似于景点通的App手机应用,我们是要精细化一些室内的地图,室内的地图采用的是自己的一套定位机制,所有室内地图也要自己来实现,参考了网上一些例子,考虑到效率的问题,最后决定使用SurfaceView来进行地图绘制,实现的功能有:

  1. 双击放大
  2. 多点触摸放大
  3. 地图拖拽
  4. 添加地图标记
    效果图一张:

代码思路

1.处理缩放和拖拽事件
在这里我利用了Matrix类提供的图片操作方法去进行图片的缩放和平移处理,关于该方面的知识可以参考
Android开发–利用Matrix进行图片操作

2.双击放大
为了实现双击放大,在这里我们MyMap类中设置了一个成员变量lastClickTime用来记录上一次点击屏幕的时间(点击屏幕的时间值可以通过MotionEvent的getEventTime方法去获得,单位是ms),如果当前点击事件的时间与上次点击事件的时间差值小于300ms则执行放大事件。

3.多点触摸放大
通过MotionEvent中的方法来获得两个触摸点之间的距离大小, 如下:

//计算两个触摸点的距离
private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return (float) Math.sqrt(x * x + y * y);
}

利用一个变量oldDist表示前一次两个触摸点的距离,利用一个oldRate表示前一次的缩放,在onTouchEvent方法中move的情况下不断更新当前缩放mCurrentScale = oldRate * (newDist / oldDist);

4.地图拖拽
利用一个PointF变量mapCenter表示当前地图中心的位置在手机屏幕上的坐标,当拖拽事件发生时通过手指移动的距离来不同更新mapCenter的值,并在draw方法中利用Matrix类操作图片

matrix.postTranslate(mapCenter.x - mBitmap.getWidth() / 2, mapCenter.y - mBitmap.getHeight() / 2);

5.添加地图标记
编写一个MarkObject类来表示地图标记,在该类之下存储了标记的Bitmap对象,该标记相对于整张地图的位置,以及点击标记的回调事件的处理。
在MyMap类中利用一个List变量markList来记录所有已经添加的地图标记。
1)处理标记随拖拽和缩放事件而改变位置:这里主要是根据mapCenter的点来进行计算,具体的计算大家可以参考代码;
2)处理点击事件:在onTouchEvent方法中up情况时,遍历markList中的MarkObject进行判断当前触摸点是否被包含在当前的标记区域中;

6.异步线程绘图方法
首先,感谢 Tiger_老虎 朋友的热心提醒:现将draw方法修改如下,将原来每一次开启一个新的线程重新绘制地图的方式弃用,修改代码为:

首先在构造方法中开启一个新的DrawThread处理绘图事件,之后当我们每一次需要重写绘图时通过Handler传递绘图消息,接着在DrawThread去处理该消息,调用绘图方法进行异步绘图。

参考代码

MyMap类:

package com.example.maptest;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MyMap extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = MyMap.class.getSimpleName();

    private static final long DOUBLE_CLICK_TIME_SPACE = 300;

    private float mCurrentScaleMax;
    private float mCurrentScale;
    private float mCurrentScaleMin;

    private float windowWidth, windowHeight;

    private Bitmap mBitmap;
    private Paint mPaint;

    private PointF mStartPoint;
    private volatile PointF mapCenter;// mapCenter表示地图中心在屏幕上的坐标
    private long lastClickTime;// 记录上一次点击屏幕的时间,以判断双击事件
    private Status mStatus = Status.NONE;

    private float oldRate = 1;
    private float oldDist = 1;
    private float offsetX, offsetY;

    private boolean isShu = true;

    private DrawThread mDrawThread;

    private enum Status {
        NONE, ZOOM, DRAG
    };

    private List<MarkObject> markList = new ArrayList<MarkObject>();

    public MyMap(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        init();
    }

    public MyMap(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        init();
    }

    public MyMap(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        init();
    }

    private void init() {
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        // 获取屏幕的宽和高
        windowWidth = getResources().getDisplayMetrics().widthPixels;
        windowHeight = getResources().getDisplayMetrics().heightPixels;
        mPaint = new Paint();

        mStartPoint = new PointF();
        mapCenter = new PointF();
        mDrawThread = new DrawThread();// 开启异步线程绘图
        mDrawThread.start();
    }

    public void setBitmap(Bitmap bitmap) {
        if (mBitmap != null) {
            mBitmap.recycle();
        }
        mBitmap = bitmap;
        // 设置最小缩放为铺满屏幕,最大缩放为最小缩放的4倍
        mCurrentScaleMin = Math.min(windowHeight / mBitmap.getHeight(),
                windowWidth / mBitmap.getWidth());
        mCurrentScale = mCurrentScaleMin;
        mCurrentScaleMax = mCurrentScaleMin * 4;
        mapCenter.set(mBitmap.getWidth() * mCurrentScale / 2,
                mBitmap.getHeight() * mCurrentScale / 2);
        float bitmapRatio = mBitmap.getHeight() / mBitmap.getWidth();
        float winRatio = windowHeight / windowWidth;
        // 判断屏幕铺满的情况,isShu为true表示屏幕横向被铺满,为false表示屏幕纵向被铺满
        if (bitmapRatio <= winRatio) {
            isShu = true;
        } else {
            isShu = false;
        }
        draw();
    }

    /**
     * 为当前地图添加标记
     * 
     * @param object
     */
    public void addMark(MarkObject object) {
        markList.add(object);
    }

    /**
     * 地图放大
     */
    public void zoomIn() {
        mCurrentScale *= 1.5f;
        if (mCurrentScale > mCurrentScaleMax) {
            mCurrentScale = mCurrentScaleMax;
        }
        draw();
    }

    /**
     * 地图缩小
     */
    public void zoomOut() {
        mCurrentScale /= 1.5f;
        if (mCurrentScale < mCurrentScaleMin) {
            mCurrentScale = mCurrentScaleMin;
        }
        if (isShu) {
            if (mapCenter.x - mBitmap.getWidth() * mCurrentScale / 2 > 0) {
                mapCenter.x = mBitmap.getWidth() * mCurrentScale / 2;
            } else if (mapCenter.x + mBitmap.getWidth() * mCurrentScale / 2 < windowWidth) {
                mapCenter.x = windowWidth - mBitmap.getWidth() * mCurrentScale
                        / 2;
            }
            if (mapCenter.y - mBitmap.getHeight() * mCurrentScale / 2 > 0) {
                mapCenter.y = mBitmap.getHeight() * mCurrentScale / 2;
            }
        } else {

            if (mapCenter.y - mBitmap.getHeight() * mCurrentScale / 2 > 0) {
                mapCenter.y = mBitmap.getHeight() * mCurrentScale / 2;
            } else if (mapCenter.y + mBitmap.getHeight() * mCurrentScale / 2 < windowHeight) {
                mapCenter.y = windowHeight - mBitmap.getHeight()
                        * mCurrentScale / 2;
            }

            if (mapCenter.x - mBitmap.getWidth() * mCurrentScale / 2 > 0) {
                mapCenter.x = mBitmap.getWidth() * mCurrentScale / 2;
            }
        }
        draw();
    }

    public void onDestory() {
        if (mBitmap != null) {
            mBitmap.recycle();
        }
        for (MarkObject object : markList) {
            if (object.getmBitmap() != null) {
                object.getmBitmap().recycle();
            }
        }
        if (mDrawThread != null) {
            mDrawThread.mHandler.sendEmptyMessage(1);
        }
    }

    // 处理拖拽事件
    private void drag(MotionEvent event) {
        PointF currentPoint = new PointF();
        currentPoint.set(event.getX(), event.getY());
        offsetX = currentPoint.x - mStartPoint.x;
        offsetY = currentPoint.y - mStartPoint.y;
        // 以下是进行判断,防止出现图片拖拽离开屏幕
        if (offsetX > 0
                && mapCenter.x + offsetX - mBitmap.getWidth() * mCurrentScale
                        / 2 > 0) {
            offsetX = 0;
        }
        if (offsetX < 0
                && mapCenter.x + offsetX + mBitmap.getWidth() * mCurrentScale
                        / 2 < windowWidth) {
            offsetX = 0;
        }
        if (offsetY > 0
                && mapCenter.y + offsetY - mBitmap.getHeight() * mCurrentScale
                        / 2 > 0) {
            offsetY = 0;
        }
        if (offsetY < 0
                && mapCenter.y + offsetY + mBitmap.getHeight() * mCurrentScale
                        / 2 < windowHeight) {
            offsetY = 0;
        }
        mapCenter.x += offsetX;
        mapCenter.y += offsetY;
        draw();
        mStartPoint = currentPoint;
    }

    // 处理多点触控缩放事件
    private void zoomAction(MotionEvent event) {
        float newDist = spacing(event);
        if (newDist > 10.0f) {
            mCurrentScale = oldRate * (newDist / oldDist);
            if (mCurrentScale < mCurrentScaleMin) {
                mCurrentScale = mCurrentScaleMin;
            } else if (mCurrentScale > mCurrentScaleMax) {
                mCurrentScale = mCurrentScaleMax;
            }

            if (isShu) {
                if (mapCenter.x - mBitmap.getWidth() * mCurrentScale / 2 > 0) {
                    mapCenter.x = mBitmap.getWidth() * mCurrentScale / 2;
                } else if (mapCenter.x + mBitmap.getWidth() * mCurrentScale / 2 < windowWidth) {
                    mapCenter.x = windowWidth - mBitmap.getWidth()
                            * mCurrentScale / 2;
                }
                if (mapCenter.y - mBitmap.getHeight() * mCurrentScale / 2 > 0) {
                    mapCenter.y = mBitmap.getHeight() * mCurrentScale / 2;
                }
            } else {

                if (mapCenter.y - mBitmap.getHeight() * mCurrentScale / 2 > 0) {
                    mapCenter.y = mBitmap.getHeight() * mCurrentScale / 2;
                } else if (mapCenter.y + mBitmap.getHeight() * mCurrentScale
                        / 2 < windowHeight) {
                    mapCenter.y = windowHeight - mBitmap.getHeight()
                            * mCurrentScale / 2;
                }

                if (mapCenter.x - mBitmap.getWidth() * mCurrentScale / 2 > 0) {
                    mapCenter.x = mBitmap.getWidth() * mCurrentScale / 2;
                }
            }
        }
        draw();
    }

    // 处理点击标记的事件
    private void clickAction(MotionEvent event) {

        int clickX = (int) event.getX();
        int clickY = (int) event.getY();

        for (MarkObject object : markList) {
            Bitmap location = object.getmBitmap();
            int objX = (int) (mapCenter.x - location.getWidth() / 2
                    - mBitmap.getWidth() * mCurrentScale / 2 + mBitmap
                    .getWidth() * object.getMapX() * mCurrentScale);
            int objY = (int) (mapCenter.y - location.getHeight()
                    - mBitmap.getHeight() * mCurrentScale / 2 + mBitmap
                    .getHeight() * object.getMapY() * mCurrentScale);
            // 判断当前object是否包含触摸点,在这里为了得到更好的点击效果,我将标记的区域放大了
            if (objX - location.getWidth() < clickX
                    && objX + location.getWidth() > clickX
                    && objY + location.getHeight() > clickY
                    && objY - location.getHeight() < clickY) {
                if (object.getMarkListener() != null) {
                    object.getMarkListener()
                            .onMarkClick(object, clickX, clickY);
                }
                break;
            }

        }

    }

    // 计算两个触摸点的距离
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    private void draw() {
        // TODO Auto-generated method stub
        if (mDrawThread != null && mDrawThread.mHandler != null) {
            mDrawThread.mHandler.sendEmptyMessage(0);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            if (event.getPointerCount() == 1) {
                // 如果两次点击时间间隔小于一定值,则默认为双击事件
                if (event.getEventTime() - lastClickTime < DOUBLE_CLICK_TIME_SPACE) {
                    zoomIn();
                } else {
                    mStartPoint.set(event.getX(), event.getY());
                    mStatus = Status.DRAG;
                }
            }
            lastClickTime = event.getEventTime();
            break;

        case MotionEvent.ACTION_POINTER_DOWN:
            float distance = spacing(event);
            if (distance > 10f) {
                mStatus = Status.ZOOM;
                oldDist = distance;
            }
            break;

        case MotionEvent.ACTION_MOVE:

            if (mStatus == Status.DRAG) {
                drag(event);
            } else if (mStatus == Status.ZOOM) {
                zoomAction(event);
            }
            break;
        case MotionEvent.ACTION_UP:
            if (mStatus != Status.ZOOM) {
                clickAction(event);
            }

        case MotionEvent.ACTION_POINTER_UP:
            oldRate = mCurrentScale;
            mStatus = Status.NONE;
            break;
        default:
            break;
        }

        return true;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        draw();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // TODO Auto-generated method stub

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
    }

    public class DrawThread extends Thread {

        public Handler mHandler;

        @Override
        public void run() {
            // TODO Auto-generated method stub
            Looper.prepare();
            synchronized (this) {
                mHandler = new Handler() {// 当接收到绘图消息时,重新进行绘图

                    @Override
                    public void handleMessage(Message msg) {
                        // TODO Auto-generated method stub
                        super.handleMessage(msg);
                        switch (msg.what) {
                        case 1:// 销毁绘图线程
                            Looper.myLooper().quit();
                            break;
                        default:// 绘图
                            Canvas canvas = getHolder().lockCanvas();
                            if (canvas != null && mBitmap != null) {
                                canvas.drawColor(Color.GRAY);
                                Matrix matrix = new Matrix();
                                matrix.setScale(mCurrentScale, mCurrentScale,
                                        mBitmap.getWidth() / 2,
                                        mBitmap.getHeight() / 2);
                                matrix.postTranslate(
                                        mapCenter.x - mBitmap.getWidth() / 2,
                                        mapCenter.y - mBitmap.getHeight() / 2);
                                canvas.drawBitmap(mBitmap, matrix, mPaint);
                                for (MarkObject object : markList) {
                                    Bitmap location = object.getmBitmap();
                                    matrix.setScale(1.0f, 1.0f);
                                    // 使用Matrix使得Bitmap的宽和高发生变化,在这里使用的mapX和mapY都是相对值
                                    matrix.postTranslate(
                                            mapCenter.x - location.getWidth()
                                                    / 2 - mBitmap.getWidth()
                                                    * mCurrentScale / 2
                                                    + mBitmap.getWidth()
                                                    * object.getMapX()
                                                    * mCurrentScale,
                                            mapCenter.y - location.getHeight()
                                                    - mBitmap.getHeight()
                                                    * mCurrentScale / 2
                                                    + mBitmap.getHeight()
                                                    * object.getMapY()
                                                    * mCurrentScale);
                                    canvas.drawBitmap(location, matrix, mPaint);
                                }

                            }
                            if (canvas != null) {
                                getHolder().unlockCanvasAndPost(canvas);
                            }
                            break;
                        }
                    }

                };
            }
            Looper.loop();
        }

    }

}

MarkObject类用于存储标记信息:

package com.example.maptest;

import android.graphics.Bitmap;
import android.graphics.Rect;

public class MarkObject {

    private Bitmap mBitmap;
    private float mapX;
    private float mapY;
    private MarkClickListener listener;

    public MarkObject() {

    }

    public MarkObject(Bitmap mBitmap, float mapX, float mapY) {
        super();
        this.mBitmap = mBitmap;
        this.mapX = mapX;
        this.mapY = mapY;
    }

    /**
     * @return the mBitmap
     */
    public Bitmap getmBitmap() {
        return mBitmap;
    }

    /**
     * @param mBitmap
     *            the mBitmap to set
     */
    public void setmBitmap(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
    }

    /**
     * @return the mapX
     */
    public float getMapX() {
        return mapX;
    }

    /**
     * @param mapX
     *            the mapX to set
     */
    public void setMapX(float mapX) {
        this.mapX = mapX;
    }

    /**
     * @return the mapY
     */
    public float getMapY() {
        return mapY;
    }

    /**
     * @param mapY
     *            the mapY to set
     */
    public void setMapY(float mapY) {
        this.mapY = mapY;
    }

    public MarkClickListener getMarkListener() {
        return listener;
    }

    public void setMarkListener(MarkClickListener listener) {
        this.listener = listener;
    }

    public interface MarkClickListener {
        public void onMarkClick(int x, int y);
    }

}

注意问题

1.每次使用Matrix进行缩放时,均设置缩放中心为图片地图中心(这里是相对图片来说的,所以是
(mBitmap.getWidth() / 2, mBitmap.getHeight() / 2)的位置,而不是mapCenter;),这样在我们处理图片的缩放时mapCenter的位置不会改变,如果不这样做的话,处理mapCenter的位置变化十分困难。

2.为了避免不同分辨率的手机获得的图片高度,宽度不一致的情况,这里采用的是标记相对于图片的整体位置值,即标记在图片中的像素坐标除以图片的高或宽。

3.为了获得良好的用户体验,当我们拖拽图片离开了屏幕边缘的时候,应当重新设定mapCenter以避免这种情况;同时在处理缩放事件时也应当注意。

4.为了获得高效率,我们利用SurfaceView来做,并在异步线程中进行地图更新,关于SurfaceView的用法可以参考
Android开发–SurfaceView的基本用法

5.在surfaceDestroyed记得回收Bitmap资源。

源码下载

点击下载源码

2017-02-27 17:04:57 Small_Mouse0 阅读数 235

Android 大致可以分为四层架构,五块区域。


这里写图片描述

1.应用程序
  
  Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。


  
2.应用程序框架

  开发人员也可以完全访问核心应用程序所使用的API框架。该应用程序的架构设计简化了组件的重用;任何一个应用程序都可以发布它的功能块并且任何其它的应用程序都可以使用其所发布的功能块(不过得遵循框架的安全性限制)。同样,该应用程序重用机制也使用户可以方便的替换程序组件。

  隐藏在每个应用后面的是一系列的服务和系统, 其中包括;
  (1). 丰富而又可扩展的视图(Views),可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。
  (2). 内容提供器(Content Providers)使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据
  (3). 资源管理器(Resource Manager)提供 非代码资源的访问,如本地字符串,图形,和布局文件( layout files )。
  (4). 通知管理器 (Notification Manager) 使得应用程序可以在状态栏中显示自定义的提示信息。
  (5). 活动管理器( Activity Manager) 用来管理应用程序生命周期并提供常用的导航回退功能。
  有关更多的细节和怎样从头写一个应用程序,请参考 如何编写一个 Android 应用程序.
 


3.系统运行库(C/C++)

  程序库
  Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。以下是一些核心库:
  * 系统 C 库 - 一个从 BSD 继承来的标准 C 系统函数库( libc ), 它是专门为基于 embedded linux 的设备定制的。
  * 媒体库 - 基于 PacketVideo OpenCORE;该库支持多种常用的音频、视频格式回放和录制,同时支持静态图像文件。编码格式包括MPEG4, H.264, MP3, AAC, AMR, JPG, PNG 。
  * Surface Manager - 对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。
  * LibWebCore - 一个最新的web浏览器引擎用,支持Android浏览器和一个可嵌入的web视图。
  * SGL - 底层的2D图形引擎
  * 3D libraries - 基于OpenGL ES 1.0 APIs实现;该库可以使用硬件 3D加速(如果可用)或者使用高度优化的3D软加速。
  * FreeType -位图(bitmap)和矢量(vector)字体显示。
  * SQLite - 一个对于所有应用程序可用,功能强劲的轻型关系型数据库引擎。

  Android 运行库  
  Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。
  每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例。Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。 Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了优化。同时虚拟机是基于寄存器的,所有的类都经由JAVA编译器编译,然后通过SDK中 的 “dx” 工具转化成.dex格式由虚拟机执行。
  Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。
  


4.Linux 内核

  Android 的核心系统服务依赖于 Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。 Linux 内核也同时作为硬件和软件栈之间的抽象层。
这一层为Android 设备的各种硬件提供了底层的驱动,如显示驱动、音频驱动、照相机驱动、蓝牙驱动、Wi-Fi 驱动、电源管理等。

没有更多推荐了,返回首页