应用资源是指应用中与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。在Android项目中的每一项资源,SDK构建工具都会定义一个唯一的整形ID,可以通过ID对资源进行应用。提供与源代码分离的资源的其中一个最重要优点在于,您可以提供针对不同设备配置的备用资源。
1、应用资源分组
项目res/目录下支持的资源目录
目录 | 资源类型 |
---|---|
animator/ | 定义属性动画的xml文件 |
anim/ | 定义渐变动画的XML文件 |
color/ | 定义颜色状态列表的XML文件 |
drawable/ | 位图文件(.png、.9.png、.jpg、.gif) |
values/ | 包含字符串、整型数和颜色等简单值的 XML 文件。 |
mipmap/ | 适用于不同启动器图标密度的可绘制对象文件。 |
layout/ | 用于定义用户界面布局的XML文件 |
menu/ | 用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。 |
raw/ | 要以原始形式保存的任意文件,要以原始的InputStream打开这些资源 |
xml/ | 可以在运行时通过调用 Resources.getXML() 读取的任意 XML 文件。各种 XML 配置文件(如可搜索配置)都必须保存在此处 |
values/目录下的资源
其他 res/ 子目录中的 XML 资源文件是根据 XML 文件名定义单个资源,而 values/ 目录中的文件可描述多个资源。对于此目录中的文件,<resources> 元素的每个子元素均定义一个资源。例如,<string> 元素创建 R.string 资源,<color> 元素创建 R.color 资源。
目录 | 资源类型 |
---|---|
arrays.xml | 用于资源数组(类型化数组) |
color.xml | 颜色值 |
dimens.xml | 尺寸值 |
string.xml | 字符串值 |
styles.xml | 样式 |
2、备用资源
几乎每个应用都应提供备用资源以支持特定的设备配置。 例如,对于不同的屏幕密度和语言,应分别包括备用可绘制对象资源和备用字符串资源。 在运行时,Android 会检测当前设备配置并为应用加载合适的资源。
- 在res/中创建一个以resources_name + config_qualifier形式命名的新目录。
- resources_name: 是相应默认资源的目录名称
- qualifier: 是指定要使用这些资源的各个配置的名称
- 将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。
例如hdpi限定符表示该目录中的资源适用于屏幕密度较高的设备。其中每个可绘制对象目录中的图像已经针对特定的屏幕密度调整大小,但是文件名完全相同。这样一来用于图像资源的ID始终相同,但是Android系统会通过将设备配置信息与资源目录名称中的限定符进行比较,选择最符合当前设备的各个资源版本。详细可见官方文档
限定符命名规则
- 单组资源可以指定多个限定符,并使用短划线分隔。
- 不能嵌套备用资源目录。
- 命名中的值不区分大小写。
- 对于每种限定符类型,仅支持一个值。
将备用资源保存到以这些限定符命名的目录中之后,Android 会根据当前设备配置在应用中自动应用这些资源。 每次请求资源时,Android 都会检查备用资源目录是否包含所请求的资源文件,然后查找最佳匹配资源。 如果没有与特定设备配置匹配的备用资源,则 Android 会使用相应的默认资源(一组用于不含配置限定符的特定资源类型的资源)。
以下为系统决定使用哪个资源文件夹的逻辑
3、处理运行时变更
有些设备配置可能在运行时发生变化。此时,Android系统会重启正在运行的Activity(先后调用onDestroy()和onCreate()方法)。重启行为旨在通过利用与新设备配置匹配的备用资源自动重新加载您的应用,来帮助它适应新配置。对于Activity中数据可以在销毁时通过onSaveInstanceState()进行保存,重启时通过onRestoreInstanceState()恢复数据,但是这样的处理方式成本高昂,会给用户留下糟糕的使用体验。
在配置变更期间保留对象
如果 Activity 因配置变更而重启,则可通过保留 Fragment 来减轻重新初始化 Activity 的负担。此片段可能包含对您要保留的有状态对象的引用。
当 Android 系统因配置变更而关闭 Activity 时,不会销毁您已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。
- 扩展Fragment类并声明对有状态的对象的引用
- 在创建片段后调用saveRetainInstance(boolean)
- 将片段添加到Activity中
- 重启Activity后,使用FragmentManager检索片段
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
使用 FragmentManager 将片段添加到 Activity。在运行时配置变更期间再次启动 Activity 时,您可以获得片段中的数据对象。
public class MyActivity extends Activity {
private RetainedFragment retainedFragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
retainedFragment = (RetainedFragment) fm.findFragmentByTag(“data”);
// create the fragment and data the first time
if (retainedFragment == null) {
// add the fragment
retainedFragment = new RetainedFragment();
fm.beginTransaction().add(retainedFragment, “data”).commit();
// load the data from the web
retainedFragment.setData(loadMyData());
}
// the data is available in retainedFragment.getData()
...
}
@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
retainedFragment.setData(collectMyLoadedData());
}
}
自行处理配置更新
如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免重启,则可声明 Activity 将自行处理配置变更,这样可以阻止系统重启 Activity。要声明由 Activity 处理配置变更,请在清单文件中编辑相应的 <activity> 元素,以包含 android:configChanges 属性以及代表要处理的配置的值。可以在该属性中声明多个配置值,通过“|”字符分隔这些配置值。
当其中一个配置发生变化时,Activity不会重启。而是,Activity会收到一个对onConfigrationChanged()的调用。这个方法会有一个Configration对象的参数指定对新设备的配置,可以通过对Configration中的字段确定新配置。调用此方法时,Activity 的 Resources 对象会相应地进行更新,以根据新配置返回资源,这样,您就能够在系统不重启 Activity 的情况下轻松重置 UI 的元素。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
}
}
网友评论