接触Android一年了,自学了不久就到公司里实习了,在公司的项目毕竟还是模块级的,很多Android基础知识平常接触不到。最近想想通过读一些书,如《疯狂Android讲义》、《Android群英传》、《Android开发艺术探索》才全面了解并掌握一些基础知识,查缺补漏,全面进阶。
1.View的回调事件
onFinishInflate():当应用从XML布局文件加载到该组件并利用它来构建界面之后,该方法被回调。
onMeasure():调用该方法来检测View组件以及它所包含的所有子View的大小。
onLayout():当该组件需要分配其子组件的位置、大小时,该方法被回调。
onSizeChange():当该组件大小被改变时,该方法被回调。
onDraw():当该组件要绘制它的内容时回调该方法进行绘制。
onKeyDown():当某个键被按下时触发该方法。
onKeyUp():当某个键被松开时触发该方法。
onWindowFocusChanged():当该组件得到、失去焦点时触发该方法。
onAttachedToWindow(): 当把该组件放入某个窗口时触发该方法。
onDetachedWindow():当把该组件从某个窗口分离时触发该方法。
onWindowVisibilityChanged():当该组件的可见性发生变化时触发该方法。
2.布局单位
px(pixel,像素):每个px对应手机屏幕的一个点。
dip或dp(device independent pixel, 设备独立像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。随着屏幕密度的改变,dp与px的换算会发生改变。ppi =√ ̄(屏幕宽像素平方+ 屏幕高像素平方)/屏幕对角线英寸。dp约等于ppi,但会取一个相对好计算的值,如120,160,240,320,480。
几种dpi对应的ppi的值:(1)ldpi (low) ~120dpi,mdpi x 0.75(2)mdpi (medium) ~160dpi,android 基准 ppi, 1dp=1px(3)hdpi (high) ~240dpi,mdpi x 1.5(4)xhdpi (extra-high) ~320dpi,mdpi x 2(5)xxhdpi (extra-extra-high) ~480dpi,mdpi x 3(6)xxxhdpi (extra-extra-extra-high) ~640dpi,mdpi x 4
sp(scaled pixel,比例像素):
3. ImageView的scaleType
matrix:使用matrix的方式缩放。
fitXY:对图片横向、纵向独立缩放,使得该图完全适应ImageView,比例有可能会改变。
fitStart:保持宽高比缩放,直到该图片能完全在ImageView中显示,缩放后图片显示在ImageView左上角。
fitCenter:保持宽高比缩放,直到该图片能完全在ImageView中显示,缩放后图片显示在ImageView中间。
fitEnd:保持宽高比缩放,直到该图片能完全在ImageView中显示,缩放后图片显示在ImageView右下角。
center:把图片放到ImageView中间,不进行任何缩放。
centerCrop:保持宽高比缩放图片,使得图片能完全覆盖ImageView,只要图片的最短边能显示出来即可。
centerInside:保持宽高比缩放图片,以使得ImageView能完全显示图片。
4.AdapterView及其子类
AdapterView继承自ViewGroup,本质是容器。AdapterView显示的多个列表项来自于Adapter,调用setAdapter即可。ListView、GridView、Spinner、Gallery等AdapterView都是容器,而Adapter负责提供每个列表项组件,AdapterView负责采取合适的方式显示这些列表项。
5.GridView
GridView和ListView有一样的父类AbsListView,GridView和ListView很相似。而ListView只显示一列,GridView显示多列。GridView也需要通过Adapter来提供显示的数据。
6.ExpanableListView
ExpanableListView是ListView的子类,它把列表项分为几组,每组又可能包含多个列表项。ExpanableListView与普通的ListView类似,它的数据由ExpanableListAdapter提供。关键要实现几个方法:(1)getGroupCout():该方法返回包含的组列表项的数量。(2)getGroupView():该方法返回的View对象用来承载组列表项。(3)getChildCount():返回特定组所包含的子列表项的数量。(4)getChildView():该方法返回的View对象用于承载子列表项。
7.Spinner
Spinner是一个列表选择框,弹出一个菜单供用户选择。Spinner和Gallery都继承AbsSpinner,AbsSpinner继承AdapterView,因此它也有AdapterView的特征,即通过Adapter设置数据。
8. Gallery
Spinner是一个垂直的列表选择框,Gallery是一个水平的列表选择框。Spinner的作用是供用户选择,而Gallery则允许用户通过拖动来查看上一下、下一个列表项。
9.SeekBar
拖动条。SeekBar与ProgressBar类似,只是进度条采用颜色填充表明进度完成的程度,而拖动条则通过滑块的位置来标识数值,如调节音量。可以通过:android:thumb 指定一个drawable对象,该对象作为自定义滑块。
10.RatingBar
星级评分条。RatingBar通过星星表示进度。几个常见的属性:(1)android:isIndicator,是否允许用户改变,true为不允许。(2)android:numStars,总共有多少个星级。(3)android:rating,设置该星级评分条默认星级。(4)android:stepSize,设置每次最少要改变多少星级。
11.ViewAnimator
ViewAnimator是一个基类,继承了FrameLayout,ViewAnimator可以在View切换时表现出动画效果。
12.ViewSwitcher
ViewSwitcher继承了FrameLayout,代表了视图切换组件,可以将多个View层叠在一起,每次只显示一个组件。当程序控制从一个View切换到另一个View的时候,ViewSwitcher支持指定动画效果。为了给ViewSwitcher添加多个组件,一般通过调用ViewSwitcher的setFactory为之设置ViewFactory,并由ViewFactory为之创建View即可。
13.基于回调的事件传播
几乎所有基于回调的事件处理方式都有一个boolean的返回值,该返回值用于标识该处理方法是否能够完全处理该事件。如果回调方法返回true,表明该方法已经完全处理该事件,该事件不会再继续传播下去。如果事件处理的回调返回false,表明该处理方法并未完全处理该事件,该事件会传播下去。
14.Handler与Looper
Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。
1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal
2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
3.Looper使一个线程变成Looper线程。
handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。一个线程可以有多个Handler,但是只能有一个Looper!
Android的主线程也是一个looper线程(looper在Android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,worker thread执行完任务后使用handler发送消息通知activity更新UI。
Message:Handler接收和处理的消息对象。
Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读取到消息之后就把消息交给发送该消息的Handler进行处理。UI线程系统已经初始化Looper对象,直接创建Handler即可往UI线程发送消息、处理消息。在自己创建的线程中,需要自己创建一个Looper对象,并启动它。Looper的prepar()、loop()方法用于初始化线程的Looper对象以及开始循环从MessageQueue中取出Message。Looper的prepar()首先会校验该线程是否只有一个Looper对象,否则会抛出异常。然后调用构造函数,初始化Looper,创建Looper的MessageQueue。Looper的loop方法会一直循环从MessageQueue中取出Message,通过调用dispatchMessage来回调handleMessage,让与之关联的线程进行处理。
MessageQueue:消息队列,它采用FIFO的方式管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象。
15.SurfaceView
一种就是使用普通View的canvas画图,还有一种就是使用专门的SurfaceView的canvas来画图。两种的主要是区别就是可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。前面一种适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的;而后一种主要用在游戏,高品质动画方面的画图。
16.LayoutInflater
public View inflate(int resource, ViewGroup root, boolean attachToRoot)
1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。
2. 如果root不为null,attachToRoot设为true,则会在加载的布局文件的最外层再嵌套一层root布局。
3. 如果root不为null,attachToRoot设为false,则root参数失去作用。
4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。
17.AsyncTask
AsyncTask是一个Android SDK中轻量级的异步任务类,它在线程池中执行后台任务,把执行进度和执行结果返回给主线程,并在主线程更新UI,AsyncTask实质上是对Thread和Handler的封装,通过AsyncTask能够更方便地在执行后台任务的过程中和结束后实现更新UI操作。AsyncTask是一个抽象类,它需要被实现后才能正常使用,子类必须要复写doInBackground方法,如果需要在执行完后台任务后更新UI,则需要实现onPostExecute方法。
执行AsyncTask很简单,先实例化,然后调用excute方法。通过查看sDefaultExecutor的代码发现,AsyncTask默认自己维护一个静态的线程池,而该线程池只允许同时执行一个线程,也就是说,不管多少个AsyncTask,只要是调用execute()方法,都是共享这个默认进程池的,你的任务必须在之前的任务执行完以后,才能执行。可以理解为,默认情况下,所有的AsyncTask在一个独立于UI线程的线程中执行,任务需要排队,先execute的先执行,后面的只能等。
通过查阅官方文档发现,AsyncTask首次引入时,异步任务是在一个独立的线程中顺序地执行,也就是说一次只能执行一个任务,不能并行地执行,从1.6开始,AsyncTask中引入了线程池,支持同时执行5个异步任务,也就是说同时只能有5个线程运行,超过的线程只能等待,等待前面的线程某个执行完了才被调度和运行。换句话说,如果一个进程中的AsyncTask实例个数超过5个,那么假如前5个都运行很长时间的话,那么第6个只能等待机会了。这是AsyncTask的一个限制,而且对于2.3以前的版本无法解决。如果你的应用需要大量的后台线程去执行任务,那么你只能放弃使用AsyncTask,自己创建线程池来管理Thread。
18.Activity四种加载模式
Activity有四种加载模式:standard(默认), singleTop:Task顶单例模式, singleTask:Task内单例模式和 singleInstance:全局单例模式。Android采用Task来管理多个Activity,当我们启动一个应用时,Android就会为之创建一个Task,然后启动这个应用的入口Activity。我们可以把Task理解为Activity栈,Task以栈的形式来管理Activity,先启动的Activity在栈底,后启动的被放在栈顶。
standard:Activity的默认加载方法,即使某个Activity在 Task栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中。例如:现在栈的情况为:A B C D,在D这个Activity中通过Intent跳转到D,那么现在的栈情况为: A B C D D 。此时如果栈顶的D通过Intent跳转到B,则栈情况为:A B C D D B。此时如果依次按返回键,D D C B A将会依次弹出栈而显示在界面上。
singleTop:如果某个Activity的Launch mode设置成singleTop,那么当该Activity位于栈顶的时候,再通过Intent跳转到本身这个Activity,则将不会创建一个新的实例压入栈中。例如:现在栈的情况为:A B C D。D的Launch mode设置成了singleTop,那么在D中启动Intent跳转到D,那么将不会新创建一个D的实例压入栈中,此时栈的情况依然为:A B C D。但是如果此时B的模式也是singleTop,D跳转到B,那么则会新建一个B的实例压入栈中,因为此时B不是位于栈顶,此时栈的情况就变成了:A B C D B。
singleTask:采用这种加载模式的Activity在同一个Task内只有一个实例,当系统采用这种模式启动目标Activity时有以下三种情况:如果将要启动的目标Activity不存在,系统将会创建目标Activity的实例,并将它加入Task栈顶;如果将要启动的目标Activity已经位于栈顶,此时与singleTop模式相同;如果将要启动的目标Activity已经存在、但没有位于栈顶,系统将会把位于该Activity上面的所以Activity移出栈,使得目标Activity处于栈顶。
singleInstance:在这种加载模式下,系统无论从哪个Task中启动目标Activity,只会创建一个目标Activity,并会使用一个全新的栈来装载该Activity。
Intent的常用Flag参数:
FLAG_ACTIVITY_CLEAR_TOP:同singleTask。
FLAG_ACTIVITY_SINGLE_TOP:同singleTop。
FLAG_ACTIVITY_NO_HISTORY:同singleInstance。
FLAG_ACTIVITY_NEW_TASK:例如现在栈1的情况是:A B C。C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK 标记,如果D这个Activity在Manifest.xml中的声明中添加了Task affinity,并且和栈1的affinity不同,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。如果D的Task affinity默认没有设置,或者和栈1的affinity相同,则会把其压入栈1,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK 标记效果是一样的了。注意如果试图从Service启动一个activity,比如从一个service中启动一个activity,则intent要加FLAG_ACTIVITY_NEW_TASK 标记。
19.Fragment
将Fragment添加到Activity有两种方式,一是在布局文件中直接引入,二是通过FragmentTransaction对象的add()方法来添加Fragment。Activity的getFragmentManager可返回FragmentManager,调用beginTransaction()方法即可开启并且返回FragmentTransaction对象。
Fragment调用getActivity()方法即可返回它所在的Activity。
Activity获取它所包含的Fragment:调用Activity关联的FragmentManager的findFragmentById即可。Activity向Fragment传递数据:在Activity中创建Bundle,调用Fragment的setArguments()方法即可。
Fragment生命周期:
onAttach():当该Fragment被添加到Activity时被回调。
onCreate():创建Fragment时被回调。
onCreateView():每次创建、绘制该Fragment的view组件时被回调,返回要显示的view组件。
onStart():启动Fragment时被回调。
onResume():恢复Fragment时被回调。
onPause():暂停Fragment时被回调。
onDestroyView():销毁该Fragment的view时被回调。
onDestroy():销毁Fragment时被回调。
onDetach():将该Fragment从Activity重被删除、被替换完成时回调该方法。
20.Intent
Intent对象大致包括Component、Action、Category、Data、Type、Extra和Flag7种属性。
Component用于明确指定需要启动的目标组件。常用的Intent构造函数public Intent(Context cxt, Class