LZ阅读的是中文翻译版本:http://hukai.me/android-training-course-in-chinese/basics/activity-lifecycle/recreating.html,试验机系统版本7.1。
1、Android入门基础
1.2 添加ActionBar
虽然早就被Toolbar取代了,但是我还是将ActionBar相关的东西看了一遍,并且实践了一下。
以下内容均支持2.1及以上版本系统。
首先,想要添加ActionBar,需要Activity继承AppCompatActivity,并且主题要继承自Theme.AppCompat,这样就会自动添加ActionBar。
为ActionBar添加UP按钮,在需要添加按钮的Activity清单文件中添加属性
android:parentActivityName="parentActivity"
在4.0及以下系统中需多加一个meta-data属性
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.MainActivity" />
在该Activity的onCreate方法中添加
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
这样,该Activity的ActionBar视图左侧就会出现一个回退箭头,点击它会退到上一个Activity。
为ActionBar添加图片或文字按钮,在res目录下新建menu目录,并创建menu.xml文件,在其中添加对应的按钮即可
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 搜索, 应该作为动作按钮展示-->
<item android:id="@+id/action_search"
android:icon="@drawable/ic_action_search"
android:title="@string/action_search"
android:showAsAction="ifRoom" />
<!-- 设置, 在溢出菜单中展示 -->
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:showAsAction="never" />
</menu>
其中,icon属性指明图片按钮的图片,title指明文字按钮的文字,如果有icon,就会显示为图片按钮,如果没有图片,就会显示文字按钮。showAsAction表示按钮显示的方式,ifRoom值表示如果有足够空间,就会显示,never表示该按钮永远不会显示,只会包含在三个点中。
在Activity中要重写onCreateOptionsMenu方法填充Menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// 为ActionBar扩展菜单项
getMenuInflater().inflate(R.menu.main_activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
为各按钮添加点击事件,重写onOptionsItemSelected方法
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 处理动作按钮的点击事件
switch (item.getItemId()) {
case R.id.action_search:
openSearch();
return true;
case R.id.action_settings:
openSettings();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
这样ActionBar上就会出现一个图片按钮,还有三个点,点击三个点会出现settings按钮,点击相应的图片或settings按钮,会响应相应的事件。
自定义ActionBar背景,文字颜色,这些都需要自定义主题来实现。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomActionBarTheme"
parent="@style/Theme.AppCompat.Light.DarkActionBar"
>
<item name="actionBarStyle">@style/MyActionBar</item>
<item name="actionBarTabTextStyle">@style/MyActionBarTabText</item>
<item name="actionMenuTextColor">@color/actionbar_text</item>
<item name="windowActionBarOverlay">true</item>
</style>
<style name="MyActionBar"
parent="@style/Widget.AppCompat.ActionBar"
>
<item name="background">@color/translucent</item>
<item name="titleTextStyle">@style/MyActionBarTitleText</item>
</style>
<style name="MyActionBarTitleText"
parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
>
<item name="android:textColor">@color/actionbar_text</item>
</style>
<style name="MyActionBarTabText"
parent="@style/Widget.AppCompat.ActionBar.TabText">
<item name="android:textColor">@color/actionbar_text</item>
</style>
</resources>
自定义ActionBar主题,必须要继承自Theme.AppCompat。
要实现自定义背景,需要自定义actionBarStyle,它的值MyActionBar继承自Widget.AppCompat.ActionBar,在MyActionBar中定义background的值,即为ActionBar的背景。
自定义ActionBar的字体,需要在MyActionBar中添加titleTextStyle属性,它的值是MyActionBarTitleText,继承自TextAppearance.AppCompat.Widget.ActionBar.Title,在其中定义android:textColor属性即可定义ActionBar的字体颜色。
其他的像actionBarTabTextStyle属性表示ActionBar下的tab的字体样式,actionMenuTextColor属性表示文字按钮的颜色。
windowActionBarOverlay属性表示是否叠加ActionBar,其值如果为true,表示叠加,即ActionBar不占用Activity高度,Activity顶端的内容会被ActionBar遮盖,如果要看其内容,可以设置ActionBar的背景为透明;默认结尾false,表示不叠加,ActionBar占用Activity高度,Activity内容显示在ActionBar下方。
1.3 兼容不同的设备
1.3.1 适配不同的语言
很简单,就是创建多个values文件夹,类似values-zh-rCN这样,后面的zh-rCN表示中国,这样的后缀有很多,需要哪个国家或地域,就创建这样的values文件夹即可。默认的values文件夹表示英文。
1.3.2 适配不同的屏幕
1.3.2.1 适配不同的屏幕尺寸
1、用wrap_content和match_parent代替固定宽高值
2、使用RelativeLayout
3、使用尺寸限定词,如:res/layout-large
4、使用最小宽度限定词,如:res/layout-sw600dp
5、使用布局别名:res/values-large/layout.xml
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
res/values-sw600dp/layout.xml
<resources>
<item name="main" type="layout">@layout/main_twopanes</item>
</resources>
这是因为3.2以前版本只能使用尺寸限定词,而最小宽度限定词只能在3.2及以上版本使用,所以用布局别名可以不用重复创建main_twopanes.xml文件。
6、使用方向限定词:
小屏幕,纵向,一个窗格 res/layout/onepane.xml
小屏幕,横向,一个窗格 res/layout/onepane.xml
7寸屏幕,纵向,一个窗格加ActionBar res/layout/onepane_with_bar.xml
7寸屏幕,横向,两个宽窗格加ActionBar res/layout/twopanes.xml
10寸屏幕,纵向,两个窄窗格加ActionBar res/layout/twopanes_narrow.xml
10寸屏幕,横向,两个宽窗格加ActionBar res/layout/twopanes.xml
电视,横向,两个宽窗格加ActionBar res/layout/twopanes.xml
这里有四种布局文件,且又有横向又有纵向,所以为了使用,可以定义以下文件
res/values/layouts.xml
<resources>
<item name="main_layout" type="layout">@layout/onepane_with_bar</item>
<bool name="has_two_panes">false</bool>
</resources>
res/values-sw600dp-land/layouts.xml
<resources>
<item name="main_layout" type="layout">@layout/twopanes</item>
<bool name="has_two_panes">true</bool>
</resources>
res/values-sw600dp-port/layouts.xml
<resources>
<item name="main_layout" type="layout">@layout/onepane</item>
<bool name="has_two_panes">false</bool>
</resources>
res/values-large-land/layouts.xml
<resources>
<item name="main_layout" type="layout">@layout/twopanes</item>
<bool name="has_two_panes">true</bool>
</resources>
res/values-large-port/layouts.xml
<resources>
<item name="main_layout" type="layout">@layout/twopanes_narrow</item>
<bool name="has_two_panes">true</bool>
</resources>
7、使用.9图片
1.3.2.2 适配不同的屏幕密度
1、使用dp和sp代替px
2、 提供可选择的图片,在不同的mipmap文件夹中各放置一份图片
1.4 管理Activity的生命周期
启动和销毁Activity
其中启动就是onCreate方法,通常在里面做一些初始化操作。
当在onCreate方法中创建了线程或者做了其他有可能导致内存泄漏的操作,则要在onDestroy中进行资源清理,杀死线程。
暂停和恢复Activity
暂停就是onPause,通常可以在里面做以下操作:
停止动画或其他正在运行的操作
提交在用户离开时期待保存的内容
释放系统资源,如广播、传感器等
恢复就是onResume,通常用来初始化在onPause里面释放掉的组件,并执行那些Activity每次进入Resumed state都需要的初始化操作。
停止与重启Activity
因为系统在Activity停止时会在内存中保存Activity的实例,所以有时不需要实现onStop,onRestart甚至是onStart方法,因为大多数的Activity相对比较简单,Activity会自己停止与重启,我们只需要使用onPause来停止正在运行的动作并断开系统资源链接。
当Activity调用onStop方法,Activity不再可见,并且应该释放那些不再需要的所有资源。一旦 Activity停止了,系统会在需要内存空间时摧毁它的实例。极端情况下,系统会直接杀死我们的app进程,并不执行Activity的onDestroy方法,因此我们需要使用onStop方法来释放资源,从而避免内存泄漏。
Activity已经停止后,Activity对象会保存在内存中,并在Activity Resume时被重新调用。我们不需要再回复到Resumed state状态前重新初始化那些被保存在内存中的组件。系统同样保存了每一个在布局中的视图的当前状态,如果用户在EditText组件中输入了text,它会被保存,因此不需要保存与恢复它。
当Activity从Stopped状态回到前台时,它会调用onRestart方法,系统再调用onStart方法,onStart方法会在每次Activity可见时都会被调用。onRestart方法则是只在Activity从Stopped状态恢复时才会被调用,因此我们可以使用它来执行一些特殊的恢复工作,请注意之前是被Stop而不是Destroy。
重新创建Activity
因为系统资源紧张而导致Activity的Destroy,系统会在用户回到这个Activity时有这个Activity存在过的记录,系统会使用那些保存的记录数据(描述了当Activity被Destroy时的状态)来重新创建一个新的activity实例。那些被系统用来恢复之前状态而保存的数据被叫做“instance state”,它是一些存放在Bundle对象中的key-value pairs。
你的Activity会在每次旋转屏幕时被destroyed与recreated。当屏幕方向改变时,系统会Destroy与Recreate前台的Activity,因为屏幕配置被改变,你的Activity可能需要另一些替代的资源(例如layout)。
默认情况下,系统使用Bundle实例来保存每一个View对象中的信息(例如输入EditText中的文本内容)。因此,如果Activity被destroyed与recreated,则layout的状态信息会自动恢复到之前的状态。然而,Activity也许存在更多你想要恢复的状态信息,例如记录用户Progress的成员变量。
为了使Android系统能够恢复Activity中的View状态,每个View都必须有一个唯一ID,由android:id属性定义。
为了保存更多的状态信息,可以复写onSaveInstanceState方法,例
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Caution:必须要调用onSaveInstanceState方法的弗雷实现,这样默认的弗雷实现才能保存视图的状态信息。
为了恢复Activity的状态信息,可以下onCreate中直接恢复
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
由于onCreate方法会在第一次创建新的Activity实例与重新创建之前被Destroy的实例时都会被调用,我们必须在尝试读取Bundle对象前检测它是否为null。若为null,系统则创建一个新的Activity实例,而不是恢复之前被Destroy的Activity。
也可以复写onRestoreInstanceState方法,因为此方法在onStart方法之后执行,系统仅仅会在需要恢复状态信息时才会调用之,因此不需要检查Bundle是否为null。
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Caution:与上面的保存状态信息方法一样,总是需要调用onRestoreInstanceState方法的父类实现,这样默认的父类实现才能恢复视图的状态信息。