Android系统设计采用代码和布局分离的设计模式,因此在设计Android应用程序时需要遵循该设计模式。
“把非代码资源(如图片和字符串常量)和代码分离开来始终是一种很好的做法。”---《Android4高级编程》
为什么要将资源和代码进行分离?
Android支持各种资源与代码的分离,从简单的字符串和颜色这样的值到更复杂的资源,例如:图片(drawable)、动画、主题和菜单。通过将资源分离开来,可以使它们变得更加容易维护、更新和管理。这也可以让你通过轻松地定义多种可选的资源值来支持国际化需求,以及包含不同的资源来支持硬件的变化,特别是屏幕尺寸和分辨率的变化。当一个应用程序启动时,不需要编写一行代码,Android就会自动地选择正确的资源值。资源树中包含对应于各种可选的硬件配置、语言和位置的值。此外,还可以根据屏幕尺寸和方向来改变布局,根据屏幕密度改变图片,根据用户的语言和国家定制文本提示(这些改变都是系统根据改变而自动变更的)。
如何创建分离的资源?
Android应用程序中都包含哪些可分离的资源?
所有的应用程序资源都存储在项目层次中的res文件夹下。在这个文件夹中,每一种可用的资源类型都存储在各自的子文件夹中。
如上图所示,drawable-hdpi、layout、raw、values等应用程序分别包含了应用程序图标、布局、可用资源和默认的字符串资源定义。
上述三种文件夹:drawable-hdpi、drawable-lhdpi、drawable-mhdpi、drawable-xhdpi分别对应的是不同密度的显示屏。
每种资源类型存储在不同的文件夹中,包括:简单的值、Drawable、颜色、布局、动画、样式、菜单、XML文件(包括searchable)和原始资源。当构建应用程序的时候,这些资源会被尽可能高效地编译和压缩,并包含到应用程序包中。在这个过程中,还创建了R类文件,包含了对加入到项目中的每一个资源的引用。因此,可以在代码中引用该资源。
1. 简单的值
支持的简单值包括字符串、颜色、尺寸、样式和字符串数组或整型数组。所有的简单值都存储在res/values文件夹下的XML文件中。
每一个XML文件中,可以使用标签来说明存储的每一个值得类型,如:
存储颜色的colors.xml文件,使用以下节点内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="label_text">#ff2d2d2d</color>
<color name="textColorPrimary">#ff2d2d2d</color>
定义颜色时,使用#符号来指定颜色值。
存储尺寸的dimens.xml文件,使用以下节点内容:
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
可以用于创建像边界和制图高度这样的布局常量。常见单位:dp非密度制约的像素(一般用于宽高),sp缩放比例无关的像素(一般用于字体大小)。允许使用相对比例来定义尺寸,以适应不同的屏幕分辨率和密度,简化不同硬件上的缩放。
存储字符串的strings.xml文件,使用以下节点内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">传感器</string>
<string name="play">播放测试音乐</string>
<string name="app_name">FactoryTest</string>
字符串有助于维护应用程序内部的一致性,而且可以更容易地实现国际化。Android支持简单的文本样式,可以使用HTML标签让部分文本字符串变为粗体、斜体或带有下划线...还可以为字符串定义复数形式,比如:
<plurals name="test_plurals">
<item quantity="one"> one music song</item>
<item quantity="other"> %d music songs</item>
</plurals>
存储字符串数组的arrays.xml文件,使用以下节点内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="wifi_signal">
<item>弱</item>
<item>一般</item>
<item>较强</item>
<item>强</item>
</string-array>
存储样式的styles.xml文件,使用以下节点内容:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="TopTitleTextStyle">
<!-- Note: must be dp to fit in status bar -->
<item name="android:textSize">20sp</item>
<item name="android:textStyle">normal</item>
</style>
样式资源可以指定视图所使用的属性值,从而使应用程序保持一个一致的用户界面体验。主题和样式资源最常见的用途是用来存储应用程序的颜色和字体。
2. Drawable资源
Drawable资源包括位图和NinePatch图片。所有的Drawable都作为单独的文件存储在res/drawable文件夹下。此处支持JPG和GIF文件,但是PNG是更好的位图格式。
3. 布局资源
布局资源可以让你在XML文件中设计用户界面的布局,而不是在代码中构建它们,从而可以把表示层从业务逻辑中分离出来。布局可以用来定义任何可是组件(包括Activity、Fragment和Widget)的用户界面。一旦在XML中进行了定义,就必须把布局填充inflate到用户界面中。在Android中,使用布局在XML文件中创建自己的屏幕是一种最佳实践。布局和代码的分离可以让你为不同的硬件配置创建优化的布局。
每一个布局定义都存储在res/layout文件夹下的一个单独的文件中。
4. 动画资源
Android支持三种类型的动画:属性动画;视图动画;帧动画。
属性动画几乎可以用来为任何东西生成动画。每个属性动画都存储在项目的res/animator文件夹下的一个单独的XML文件中。
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="alpha"
android:valueFrom="0.0"
android:valueTo="1.0" />
每一个视图动画都存储在项目的res/anim文件夹下的一个独立的XML文件中。一个动画可以定义为:alpha、scale、translate和rotate四种属性。
<?xml version="1.0" encoding="utf-8"?>
<!-- 设置动画集合 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator" >
<rotate
android:duration="1000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="500"
android:toDegrees="360" />
<scale
android:fromXScale="1.0"
android:toXScale="0.0" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0" />
<translate
android:fromXDelta="0.0"
android:toXDelta="1.0" />
</set>
逐帧动画可以用来创建Drawable的序列,每个Drawable都会在视图的背景中持续一段时间。每个Drawale存储在res/drawable文件夹中,并使用文件名来作为资源ID。
5. 菜单
创建菜单资源并使用XML设计菜单布局,而不是在代码中构建菜单。每个菜单定义都存储在res/menu文件夹下的一个单独的文件中,每个文件都只包含一个菜单。在Android中,使用XML定义惨淡是一种最佳设计实践。
在Android应用程序中定义了相关的资源,那如何使用这些资源?
除了可以使用自定义的资源之外,Android平台提供了多个系统资源供在应用程序中使用。既可以在应用程序代码中直接使用这些资源,也可以在其他资源中引用这些资源。
根据使用资源的不同位置,可以将方式分为以下几种:
如何在代码中使用资源?
可以在代码中使用静态R类来访问资源。当需要一个资源本身的实例时,就需要使用辅助的方法来把它们从资源表中提取出来。在应用程序中,资源表被表示为Resource类的一个实例。这些辅助方法将在应用程序的当前资源表中执行查找,不是静态的。
Resources res = getResources();
ObjectAnimator animator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.anim.ani_demo);
String content = res.getQuantityString(R.plurals.test_plurals, 0, 0);
Resources类为每一个可用的资源类型包含了getter,并且通常是通过传递的资源实例ID来起作用。
如何在资源内部引用另一个资源?
使用@符号,就可以在一个资源中引用另一个资源。
如何使用系统资源?
在代码中使用系统资源的方法和使用自己的资源的方式相似。不同的是,要使用android.R类中使用的本地Android资源,而不是使用引用程序特定的R类。
如何在当前的主题中引用样式?
主题是保证引用程序UI一致性的非常好的方法。Android中使用?android:而不是@来作为想要使用的资源的前缀。
<TextView
android:id="@+id/tv_acc_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center_horizontal"
android:text="Demo"
android:textColor="?android:textColor"
android:textSize="18sp" />
其使用方法,如上所示;但实践中发生了崩溃。
这种技术可以创建出随当前主题改变而改变的样式,而不必对每一个单独的样式资源进行修改。
上述讲述了很多关于资源的创建和使用,还有没有其他的使用场景?
其中就有一个:为不同的语言和硬件资源创建资源。
Android系统实现了为特定的语言、位置和硬件配置创建不同的资源值,Android将会在运行时使用其动态资源选择机制自动地从这些值中做出选择。指定可选的资源值是通过在res文件夹下使用并行的目录结构来实现的,并且使用连接符来分隔指定你所支持的情况的限定符。
如上所示,values-fr和values-fr-rCA代表的是法语选项和加拿大地区的法语选项。
在定制资源时有哪些可选的限定符?
1. Mobile Country Code和Mobile Network Code
2. 语言和地区
3. 屏幕像素密度,像素密度以点每英寸(dpi)来表示。分别表示ldpi、mdpi、hdpi和xhdpi来指定低120dpi、中160dpi、高240dpi和极高320dpi像素密度的资源。可以为不想缩放的位图资源指定nodpi,以便支持精确的屏幕密度。与其他资源类型不同的是,Android在选择资源时不要求精确匹配;当选择合适的文件夹时,会选择与设备的像素密度最接近的匹配,并相应地缩放产生的Drawable。
4. 屏幕大小,可选值有small、medium、large和xlarge。
对于任何资源类型,都可以指定多个限定符,并把它们用连字符隔开。任何组合都是允许的,但是它们必须按照上面列表的顺序进行排列,而且每一个限定符都不能超过一个值。
当Android在运行的过程中检索一个资源的时候,它会从可用的可选资源中选出最佳的匹配。如果在给定设备上没有找到资源匹配,那么有用程序将会在尝试访问该资源时抛出一个异常。要想避免出现这种情况,应该总是在不包含限定符的文件夹中为每个资源类型包含一个默认值。
Android能够在运行时匹配并获取最佳资源,是否能够更改这些资源配置?
Android支持运行时更改语言、位置和硬件,是通过终止和重启Activity来实现上述功能的。这会强制重新评估Activity中使用的资源的分辨率,并为新的配置选择最合适的资源值。
要想让Activity可以监听运行时配置更改,需要向它的manifest节点中添加一个android:configChanges属性,并说明希望对哪些配置更改进行处理。
可监听的设备更改有如下几种:
1. keyboardHideen,显示或者隐藏键盘或其他输入机制;
2. orientation,屏幕在纵向和横向之间旋转;
等等。
可以选择处理多个想要处理的事件,只要用管道符(|)分隔它们。
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
这个Activity会捕获到屏幕改变的事件,添加android:configChanges属性可以阻止由于特定配置改变而造成的重启,而不会再次执行onCreate(),转去执行onConfigurationChanged()。