第一章Android 体系与架构
底层通过最快的C语言保证效率,上层使用Java简单、快速进行开发。
Android大致分为四层,Linux内核层,库和运行时、Framwork层和应用层。Android的体系架构鼓励系统组件重用,共享组件间的数据,并且定义组件间的访问权限控制。
Linux层。包含了Android系统的核心服务,包括硬件驱动,进程管理、安全系统,等等。
Dalvik与ART。Dalvik包含了一整套的Android运行环境虚拟机。特点是运行时再编译。Android 5.版本开始,ART模式已经取代了Dalvik,ART采用的是安装时进行编译,以后运行时就不用编译了。
Framwork
Standard libraries 这里包含的是Android中的一些标准库,标准库,就是在开发者在开源环境中可以使用的开发库。
Application
1.3.1 Android四大组件如何协同工作
Activity作为人机交互的第一界面,负责向用户展示信息和处理结果,而这些信息的来源,可以是通过资源获取,也可以通过Content Provider来获取其他应用的信息,或是Service从后台计算,下载、处理的结果,当然也可以是通过BroadCastReceiver获取到的广播信息。同时,Android系统还提供了一个信使——Intent,作为信息传递的载体。组件与组件之间通过Intent来通信、传递信息、交换数据、正是通过这样一种方式,四大组件形成了各自独立而又紧密联系的关系,让整个Android系统活了起来。
1.3.2 应用运行上下文对象
Android系统的上下文对象,即在Context中,为我们封装了这样一个语境。Activity,Service,Application都是继承自Context。
1、创建Application
2、创建Activity
3、创建Service
不难发现,创建Context的时机就是在创建Context的实体类的时候。
1、当应用程序第一次启动时,Android系统都会创建一个Application对象,同时创建Application Context,所有的组件都共同拥有这样一个Context对象,这个应用上下文对象贯穿整个应用进程的生命周期,为应用全局提供了功能和环境支持。
2、创建Activity和Service组件时,系统也会给它们提供运行的上下文环境,即创建Activity实例,Service实例的Context对象。
对Makfile的解释:一个像Android这样的大型工程,它的源文件不计其数,不同的功能、模块,按类型分别防止在不同的目录中,这些模块通常会有一个叫Makefile的文件来进行管理,它定义了一系列的规则来指定模块,哪些文件需要编译,以及这些文件该按照怎样的顺序去编译。甚至,它还可以配置跟复杂的功能操作,比如定义编译规则,打包规则等,因为makeFile就像一个shell脚本,不仅可以使用自己的语法,也能调用操作系统的命令。
Makefile最大的好处就是自动化编译,同时还可以做到可控制的编译。Android通过makefile来描述Android各个组件间的联系并指导它们进行自动化编译。
1.4.2 Android系统目录
1、/system/app/
这里面放的一些系统的App
2、/system/bin/
这里面主要放的是Linux自带的组件
3、/system/build.prop
这里记录的是系统的属性信息
4、/system/fonts/
系统字体存放目录root后可下载TTF格式字体替换原字体
5、/system/framework/
系统的核心文件、框架层
6、/system/lib/
存放几乎所有的共享库(。so)文件
7、/system/media/
该目录是用来保存系统提示音,系统铃声。其中system/media/audio/目录,这里面保存着Android系统默认的铃声,alarms目录是闹铃提醒,notification目录是短信和提示音,ringtones目录是来电铃声,而ui目录则是一些界面音效。
8、/system/usr/
该目录用来保存用户的配置文件,如键盘布局、共享、时区文件等。
9、/data/app/
data目录包含了用户的大部分数据信息,其中,/data/app/这个目录包含了用户安装的App或升级的App。
10、/data/data/
这个目录应该是开发者访问的最多的目录了,这里包含了App的数据信息,文件信息,数据库信息等,以包名的方式来区分各个应用。
11、/data/system
这个目录包含了手机的各项系统信息
12、/data/misc/
这个目录保存了大部分的WIFI,VPN信息。
Android studio中的Project就相当于Eclipse里面的Workspace,而Androidstudio中的Module就相当于Ecliose里面的project就可以了。
Android studio 选择editor标签,在other中选择勾选show quick doc on mouse move 选项。
在Editor标签中选择Code Completion选项,在Code Sensitive completion里面选择None,即提示不区分大小写。
Android Build Tools是Android SDK的Build 工具,经常会有更新,建议保持最新,它对应着Gradle中Module下的Build.gradle文件中的buildToolVersion属性。
/system/core/toolbox和\framework\base\cmds 就是我们所有ADB命令和Shell命令的来源了。
Android 控件结构
ViewGroup控件作为父控件可以包含多个View控件,并管理其包含的View控件。
通过ViewGroup,整个界面上的控件形成了一个树形结构,上层控件负责下层子控件的测量与绘制,并传递交互事件。
通常在Activity中使用findViewbyId()方法,就是在控件树中以树的深度优先遍历来查找对应元素。在每棵控件树的顶部,都有一个ViewParent对象,这就是整棵树的控制核心,所以的交互管理事件都由它来统一调度和分配,从而可以对整个视图进行整体控制。
1、每个Activity都包含一个Window对象,在Android中Window对象通常由PhoneWindow来实现。
2、PhoneWindow将一个DecorView设置为整个应用窗口的根View
3、DecorView作为窗口界面的顶层视图,封装了一些窗口操作的通用方法。换句话说,DecorView将要显示的具体内容呈现了PhoneWindow上。
4、这里所有的View的监听事件,都通过windowManagerService来进行接收,并通过Activity对象来回调响应的onClickListener。显示上,它将屏幕分成两部分,一个是TitileView,一个是ContentView。
第二层装载一个LinearLayout,作为ViewGroup,这一层的布局结构会根据对应的参数设置不同的布局。
在代码中,当程序在onCreate()方法中调用setContentView()方法后,ActivityManagerService会回调onResume()方法,此时系统才会把整个decorView添加到PhoneWindow中。并让其显示出来,从而最终完成界面的绘制。
3.2 View的测量
Android系统给我们提供了一个设计短小精悍却功能强大的类——MeasureSpec类。MeasureSpec是一个32位的Int值,其中高2位为测量的模式,低30位为测量的大小。
测量模式分为三种:
1、EXACTLY。精确值模式
2、AT_MOST。即最大值模式。当控件属性指定为wrap_content时,控件大小一般随着控件的子控件或内容的变化而变化,此时控件的尺寸只要不超过父控件允许的最大尺寸即可。
3、UNSPEIFIED。不指定大小。通常在绘制自定义View时才会使用。
View类默认的onMeasure()方法只支持EXACTLY模式。控件可以响应你指定的具体宽高值或者match_parent属性。
而如果让自定义View支持wrap_content属性,那么就必须重写onMeasure()方法来指定wrap_content时的大小。
3.3 View的绘制
当测量好一个View之后,我们就可以简单地重写onDraw()方法,并在canvas对象上来绘制所需要的图形。
当创建一个Canvas对象时,为什么要传进去一个bitmap对象呢?传进去的bitmap与通过这个bitmap创建的Canvas画布是紧紧联系在一起的,这个过程我们称之为装载画布。这个bitmap用来存储所有绘制在Canvas上的像素信息。
我们可以使用Canvas的绘制API,然后让View重绘,从而显示改变之后的bitmap。
3.4 ViewGroup测量
ViewGroup在测量时通过遍历所有子View,从而调用子View的Measure方法获得每一个子View的测量结果。
3.5ViewGroup的绘制
ViewGroup使用dispatchDraw()方法来绘制其子View,其过程同样是通过遍历所有子View,并调用子View的绘制方法来完成绘制工作。
3.6自定义View
在View中通常有以下一些比较重要的回调方法
1、onFinishInflate(),从xml加载组件后回调
2、onSizeChanged(),组件大小改变时回调
3、onMeasure();回调该方法进行测量
4、onLayout():回调该方法进行测量。
5、onTouchEvent():监听到触摸事件时回调。
通常有以下三种情况来实现自定义的控件
1、对现有控件进行拓展。
2、通过组合来实现新的控件
3、重写View来实现全新的控件
3.6.1 对现有控件进行拓展
程序调用super.onDraw(canvas)方法来实现原生控件的功能,但是在调用super.onDraw()方法之前和之后,我们都可以实现自己的逻辑,分别在系统绘制文字前后,完成自己的操作。
//在回调父类方法前,实现自己的逻辑,对TextView来说即是在绘制文本内容前
super.onDraw(canvas)
//在回调父类方法后,实现自己的逻辑,对TextView来说即是在绘制文本内容后
3.6.2 创建复合控件
3.6.2.1 定义属性
我们在代码中通过
标签声明了使用自定义属性,并通过name属性来确定引用的名称。最后,通过标签来声明具体的自定义属性,比如在这里定义标题文字的字体,大小,颜色,并通过format属性来指定属性的类型。
在确定好属性后,就可以创建一个自定义控件——TopBar,并让它继承自ViewGroup,从而组合一些需要的控件。
3.6.2.2组合控件
1、xmlns:android="http://schemas.android.com/apk/res/android"
这行代码就是在指定引用的名字控件xmlns,即xml namespace。这里指定了名字空间为”android“,因此在接下来使用系统属性的时候,才可以用”android:“来引用Android的系统属性。
2、xmlns:custom="http://schemas.android.com/apk/res-auto"
如果要使用自定义的属性,那么就需要创建自己的名字控件,在Android studio中,第三方控件都使用如下代码来引入名字空间。
使用自定义的View与原生的View最大的区别就是在申明控件时,需要指定完整的报名,而在应用自定义的属性时,需要使用自定义的xmlns名字。
3.6.3重写View来实现全新的控件
3.7 自定义ViewGroup
ViewGroup存在的目的就是为了对其子View进行管理,为其子View添加显示、响应的规则。自定义ViewGroup通常需要重写onMeasure()方法来对子View进行测量,重写onLayout()方法来确定子View的位置,重写onTouchEvent()方法来增加响应事件。
3.8 事件拦截分析
触摸事件就是捕获触摸屏幕后产生的事件。
当点击一个按钮,通常会产生两个或三个事件。
1、按钮按下,这是事件一
2、如果不小心滑动一点,这就是事件二
3、当手抬起,这是事件三。
我们的触摸事件就是一个,到底该分给谁呢?同一个事件,子View和父ViewGroup都有可能想要进行处理,因此,这产生了事件拦截。
事件的传递顺序是(InterceptTouchEvent):
总经理(MyViewGroupA)->部长(MyViewGroupB)->你(View)。事件传递的时候,先执行dispathchTouchEvent()方法,再执行onInterceptTouchEvent()方法。
事件的处理顺序是:
你(View)->部长(MyViewGroupB)->总经理(MyViewGroupA)。事件处理都是执行onTouchEvent()方法。
事件传递的返回值非常容易理解:true,拦截,不继续;False,不拦截,继续流程。
事件处理的返回也类似:true,处理了,不用审核了;False,给上级处理。
初始情况下,返回值都是false。
第四章 ListView使用技巧
4.1 ListView常用优化技巧
4.1.1 使用ViewHolder模式提高效率
4.1.2 设置项目间分隔线
android:divider="android:color/darker_gray"
android :dividerHeight="10dp"
4.1.3 取消ListView的Item点击效果
android :listSelector =#00000000
4.1.5 设置listView需要显示在第几项
listView.setSelection(N)
实现平移滑动
listview.smoothScrollBy(distance,duration)
listview.smoothScrollByOffset(offset)
listview.smoothScrollToPosition(index)
4.1.6 动态修改ListView
mData.add("new");
mAdapter.notifyDataSetChanged();
4.1.7 遍历ListView中的所有item
for(int i=0;i
}
4.1.8 处理空ListView
当listView列表中无数据时,ListView不会显示任何数据或提示,按照完善用户体验的需求,这里应该给以无数据的提示。listView提高了一个方法:setEmptyView()。
当用户没有做手指抛动的状态时,这个方法只会回调2次,否则会回调3次,差别就是手指抛动这个状态。
通常情况下,我们会在这个方法中通过不同的状态来设置一些标志Flag,来区分不同的滑动状态,供其他方法处理。
4.2.1 具有弹性的ListView
listview中有一个控制滑动到边缘的处理方法。overScrollBy方法。里面有一个参数maxOverScrollBy,注释中写道,Number of piexls to overscroll by in either direction along the Y axis 。只要修改这个参数的值,就可以让ListView具有弹性了。
4.2.2 自动显示、隐藏布局的ListView
4.2.3 聊天ListView
4.2.4 动态改变ListView