一、五种布局:FrameLayout、LinearLayout、AbsoluteLayout、ReLativeLayout、TableLayout全都继承ViewGroup,各自特点及绘制效率对比。
FrameLayout(框架布局)
此布局是五种布局中最简单的布局,Android中并没有对child view的摆布进行绘制,这个布局中的所有控件都会默认出现在视图的左上角,我们可以使用“android:layout_margin”,”android:layout_gravity”等属性去控制子控件相对布局的位置。LinearLayout(线性布局)
一行只控制一个控件的线性布局,所以当有很多控件需要再一个界面中列出时,可以用LinearLayout布局。此布局有一个需要格外注意的属性:“android:orientation=’horizontal’|’vertical’”,当‘android:orientation=‘horizontal’’时,说明你希望将水平方向的布局交给‘LinearLayout’,其子元素的‘android:layout_gravity=right|left’等控制水平方向的gravity值都是被忽略的,此时LinearLayout中的子元素都是默认的按照水平从左向右来排,我们可以用‘android:layout_gravity=top|bottom’等gravity值来控制垂直展示gravity值来控制垂直展示AbsoluteLayout(绝对布局)
可以放置多个控件,并且可以自己定义控件的x,y位置- RelativeLayout(相对布局)
这个布局也是相对自由的布局,Android对该布局的child view的水平layout& 垂直layout 做了解析,由此我们可以FrameLayout的基础上使用标签或者Java代码对垂直方向,以及水平方向,布局中的views 任意的控制。
相关属性:
android:layout_centerInParent=”true|false”
android: layout_centerHorizontal=”true|false”
android: layout_alignParentRight=”true|false” - TableLayout(表格布局)
将子元素的位置分配到行或列中,一个TableLayout由许多的TableRow组成
二、Activity的生命周期
- 启动Activity:
onCreate()->onStart()->onResume(), Activity进入运行状态。 - Activity退居后台
当前Activity转到新的Activity界面或按Home键回到主屏:onPause()->onStop(),进入停止状态。 - Activity返回前台:
onRestart()->onStart()-onResume(),再次回到运行状态。 - Activity退居后台,且系统内存不足,系统会杀死这个后台状态的Activity(此时这个Activity引用仍然处在任务栈中,只是这个时候引用指向的对象已经为null),若再次回到这个Activity则会走onCreate()->onStart()->onResume()(将重新走一次Activity的初始化生命周期)
- 锁定屏与解锁屏只会调用onPause(),而不会调用onStop()方法,开屏后则调用onResume().
可以参考下面Activity的生命周期图。
三、Activity的四种启动模式
通过Activity的xml标签来改变任务栈的默认行为
- 使用android:launchMode=“standard|singleInstance|singleTask|singleTop”来控制Activity任务栈。
任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,以APP的包名来命名。
1. standard:标准模式,每次启动Activity都会创建一个新的Activity实例,并且将其压入任务栈栈顶,而不管这个Activity是否已经存在。Activity的启动三回调(onCreate()->onStart()->onResume())都会执行。 2. singleTop:栈顶复用模式(单一顶部模式),这种模式下,如果新Activity已经位于任务栈的栈顶,那么此时Activity不会被重新创建,所以它的启动三回调就不会执行,同时Activity的onNewIntent()方法会被回调,如果Activity已经存在但是不在栈顶,那么作用与standard模式一样。 3. singleTask:栈内复用模式(单一任务栈模式),创建这样的Activity的时候,系统会先确认它所需任务栈已经创建,否则先创建任务栈,然后放入Activity,如果栈中已经有一个Activity实例,那么这个Activity就会被调到栈顶,onNewIntent(),并且singleTask会清理在当前Activity上面所有的Activity(clear top) 4. singleInstance:加强版的singleTask模式(单一实例模式),这种模式的Activity只能单独位于一个任务栈内,由于栈内复用的特性,后续请求均不会创建新的Activity,除非这个独特的任务栈被系统。
Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面,可以认为一个ActivityRecord就是一个Activity栈。
四、Activity缓存方法
1)有a、b两个Activity,当从a进入b之后一段时间,可能系统会把a回收,这时候按back,执行的不是a的onRestart(),而是onCreate方法,a被重新创建一次,这是a中的临时数据和状态可能就丢失了。
2)可以用Activity中的onSaveInstanceState()回调方法保存临时数据和状态,这个方法一定会在活动被回收之前调用。方法中有一个Bundle参数,putString()、putInt()等方法需要传入两个参数,一个键一个值。数据保存之后会在onCreate中恢复,onCreate也有一个Bundle类型的参数。
示例代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里,当Acivity第一次被创建的时候为空
//所以我们需要判断一下
if( savedInstanceState != null ){
savedInstanceState.getString("anAnt");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("anAnt","Android");
}
- 1.onSaveInstanceState(Bundle outState)
当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:
1)当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则。
2)长按HOME键,选择运行其他的程序时。
3)按下电源按键(关闭屏幕显示)时。
4)从activity A中启动一个新的activity时。
5)屏幕方向切换时,例如从竖屏切换到横屏时。(如果不指定configchange属性)
在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以 onSaveInstanceState 一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。另外,需要注意的几点:
布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复。但是这种情况至于在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,将不会存储它的状态。
由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息时,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法。
- 由于onSaveInstanceState()方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在onPause()方法中,存储持久化数据(例如应该被存储到数据库中的数据)。
onSaveInstanceState()如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发。
- onRestoreInstanceState(Bundle outState)
- 至于OnRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的
- onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行
- 另外,onRestoreInstanceState的Bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。还有onRestoreInstanceState在onStart之后执行。
- 至于这两个函数的使用,给出示范代码(留意自定义代码在调用super的前或后):
- onRestoreInstanceState(Bundle outState)
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "Welcome back to Android");
// etc.
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}