主要讲解应用程序锁涉及的主要元素,包括资源、组件、事件处理、应用风格、数据适配等。
1.布局文件
布局文件通常由根布局和子布局构成,在布局文件的应用程序中,会涉及不同的控件和目标环境。布局文件看似简单,实则需要很多技巧和经验,本节仅介绍布局加载、密度和一些特殊标签的使用。
(1)加载布局
通过setContextView方法可以隐式地加载布局文件,如果希望显示地加载布局文件,可以在Activity中通过getLayoutInflater方法来获取LAYOUT_INFLATER_SERVICE服务将布局文件实例化。另外也可以获取已有LayoutInflater对象的副本来实例化布局文件。
通过getLayoutInflater方法实例化布局文件:View demo=getLayoutInflater().Inflate(R.layout.demo, null);
通过系统服务实例化布局文件:LayoutInflater inflater=(LayoutInflater)context.getSystemServiceContext.LAYOUT_INFLATER_SERVICE); View demo=inflater.Inflater(R.layout.demo, null);
通过已有LayoutInflater对象的副本实例化布局文件:LayoutInflater inflater=from(context); View demo=inflater.Inflater(R.layout.demo, null);
(2)密度的逻辑
在构建布局文件时,自然会涉及像素的问题,在Android中,有dp、px、dip等几种单位,其中最常用的是dip。
dip的设置与分辨率无关,但与屏幕密度有关。在默认情况下,LDPI的密度为120,系数为0.75;MDPI的密度为160,系数为1.0;HDPI的密度为240,系数为1.5;XHDPI的密度为320,系数为2.0。所谓密度即单位平方英寸中含像素的数量。
px与dip的关系如下:px=(int)(dip*density+0.5f)
(3)特殊标签
在Android布局文件中,除了普通的UI控件标签外,还有几个特殊标签,他们是viewStub、reuqestFocus、merge和include。
1)viewStub标签
viewStub标签实际上是一个特殊的控件,在默认情况下,其所包含的控件是不可见的,并且不占用任何内存空间,开发者可以通过setVisibility和inflater加载viewStub标签包含的布局。
viewStub标签所包含的布局是通过android:layout属性引用的外部布局文件。
2)requestFocus标签
requestFocus标签0可以使相应的UI控件获得焦点,在使用时,用在UI控件标签内部。
3)merge标签
merge标签在优化UI布局中具有重要的作用,可以减少多余或额外的层级。在应用中,merge标签主要用于优化根布局或者在使用viewStub标签和include标签时减少引用冗余。需要注意的时,merge标签仅能作为布局文件的根布局,如果希望通过布局加载器(LayoutInflater)加载以merge标签为根布局的布局文件,需要使用View inflate(int resource, ViewGroup root, boolean attachToRoot)指定一个ViewGroup作为其容器。并且要设置attachToRoot为true。
4)include标签
include标签的作用类似于C语言中的include关键字,用于引用外部布局文件,起到复用布局文件的作用。
(4)可用空间的分配
在布局文件中,如何使编写的布局文件能够尽可能地适配不同的目标环境。虽然Android为实现目标环境适配进行了诸多考虑,可以精确指明布局文件适配的分辨率、密度、横竖屏等,但是没有人喜欢针对同一个界面编写多个布局文件,因为这样的维护成本过于巨大,所以应尽可能地在布局文件本身的结构上下工夫。
笔者的经验是尽可能灵活地运用LinearLayout和RelativeLayout等布局控件。对于LinearLayout来说,关键在于其android:layout_weight属性的灵活运用,RelativeLayout则相对简单些。
2.值文件
值文件是Android应用必需的资源文件,其承担着应用的UI配置和文字显示工作。值文件包括字符串文件、字符串数组文件、配置文件、整数数组文件、维度文件、属性文件、颜色文件、风格文件、主题文件、表示符文件等,其中英文目录为res\value。
(1)字符串文件
字符串是最常用的值资源,其常用的设置方法:<string name="edit_event_from_lable">From</string>
如果该字符串资源不必本地化,则需要设置其translatable属性为false,具体设置方法:<string name="voice_mode_off" translatable="false">2</string>
在加载资源的过程中,会以资源的ID为线索进行加载,Android不允许在同一名字空间中出现相同的ID。
在Android中,资源分系统资源和应用资源两种,这两种资源在应用中引用的方式略有差异。系统资源并不全部对应用层开发,对应用层开放的系统资源在frameworks\base\core\res\res\values\目录下的public.xml中定义。不对应用层开发的系统资源的加载方式:getString(com.android.internal.R.string.using)
在Android中,文件特效目前仅支持加粗和斜体两种,实现加下划线的功能,则需要借组HTML语法,实现方法:textView.setText(Html.fromHtml("<u>"+helloStr+"</u>"))
语言的切换是通过资源管理器进行的,实现方法:Resource resource=getResources(); Configuration config=resource.getConfiguration(); DisplayMetrics dm=resource.getDisplayMetrice(); config.locale=Locale.SIMPLIFIED_CHINESE; resource.updateConfiguration(config, dm);
(2)字符串数组文件
字符串数组通常用于下拉框等场景,如果不希望该字符串数组随着语言环境的变化而变化,可以将字符串数组的translatable属性设为false。
(3)配置文件
配置文件在应用开发中并不常用,通常用于应用的设置等场景,配置文件的实现:
<resources>
<bool name="config_sf_limitedAlpha">false</bool>
<integer name="config_shortAnimTime">150</integer>
</resources>
在Java中配置文件的加载方法:mLimitedAlphaCompositing=context.getResource().getBoolean(com.android.internal.R.bool.config_sf_limitedAlpha);
(4)整数数据文件
整数数组和字符串数据的使用方法类似。
在Java中整数数组的加载方式:int[] tmpArray=getResource().getIntArray(com.android.internal.R.array.config_virtualKeyVibePattern);
(5)维度文件
在Java中维度文件的加载方法:Int w=getResource().getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
(6)属性文件
在Android中,属性文件通常由多个标签组成,
declare-styleable标签用于声明一个属性组,一个属性组可以由多个属性构成。
加载属性组的方法:TypeArray a=context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation); a.recycle();
eat-comment标签用于声明标签上的内容为注释。attr标签用于声明属性。一个属性包括属性名和属性格式两部分。
偏好值的实现分为enum、flag两种类型,其中flag表示以二进制方式表示偏好值。
(7)颜色文件
颜色的设置文件通常会放在框架层的资源目录中,对颜色属性进行引用而不是在应用层显示声明,有利于在系统层面保持UI的一致性。在颜色的声明中,采用的时ARGB的格式
<resource>
<color name="magic_flame">#00ffffff</color>
</resource>
在Java中颜色设置的加载方式:setBackgroundColor(getResources().getColor(R.color.candidate_background));
(8)风格文件
一个好的应用除了技术上应设计得优雅外(即代码具有高的可读性、可扩展性、复用性、性能),用户体验也非常重要(包括文字的用语和观感和交互设计),风格的设计决定了用户体验中文字和图片的效果。
(9)主题文件
主题文件在框架层比较常用,对于进行差异化工作的厂商而言,保持同一个主题是一个基本的UI需求,定义统一的风格文件供公司所有产品引用,是个不错的选择。
(10)标识符文件
标识符文件对于应用层开发者来说并不常见,但这并不意味着开发者完全接触不到。在使用系统提供的ListView、Tab等就会涉及。
3.创建菜单
根据应用场景的不同,菜单可以分为选项菜单(OptionsMenu)、上下文菜单(Context Menu)、子菜单(SubMenu)等3种,选项菜单是最普通的菜单(按Menu键弹出的菜单就是选项菜单),上下文菜单通常用于列表等场景中。
除了OptionsMenu、SubMenu、ContextMenu外。Android在构建菜单时还经常涉及MenuItem、MenuInflater等类。MenuInflater在加载XML资源文件时采用的解析器为XmlPullParser。出于性能方面的考虑,目前MenuInflater尚不支持加载未经处理的XML资源文件。
上下文菜单和子菜单不支持图标。菜单组和快捷键的概念在当前基于触摸屏的智能终端中并不常用。
(1)选项菜单
选项菜单时最常用的菜单形式,该菜单的实现通常由两种方式,一种是基于xml资源文件的实现,一种是通过Java代码实现,在通常情况下,推荐基于xml资源文件的实现。当加载的菜单项较多时,Android会自动将不能完全显示的菜单放置在More扩展选项菜单中,需要注意的是,在More扩展选项菜单中,无法显示图标。
1)通过xml资源文件实现选项菜单
<menu xmlns:android=http://schemas.android.com/apk/res/android>
<item andriod:id="@+id/menu_item_desk_clock"/>
</menu>
2)通过Java代码实现选项菜单
3)菜单组
(2)上下文菜单
上下文菜单也是Android中常用的一种菜单形式。需要注意的是,上下文菜单不支持快捷键和图标。上下文菜单可用于多种视图,但通常用于列表。通过registerForContextMenu注册上下文菜单或直接为视图安装监听器可以实现视图和上下文菜单的关联。
上下文菜单的创建可以分为Java实现和加载资源文件实现。在创建上下文菜单时,需特别注意ContextMenuInfo。ContextMenuInfo提供了菜单的附加信息,如选中的菜单项在整个菜单中的位置等。
还可以为上下文菜单设置标题。上下文菜单的标题支持图标形式。
(3)子菜单
子菜单的实现非常简单。子菜单不支持图标和二级子菜单。子菜单的执行在其父菜单的执行方法中进行,对于上下文菜单的子菜单,执行方法为onContextItemSelected对于选项菜单的子菜单,实现方法为onoptionItemSelected。
(4)弹出菜单
4.断言的处理
断言时Android应用中一个重要的部分,在断言中,可以加载字体、HTML文件、图片等。注意,断言中限制文件大小为1MB。同样,在res、raw下的资源文件也有这样的限制。对于超过限制的资源,系统在运行时会提出警告。代码实现如下:
asset(pid) Data exceeds UNCOMPRESS_DATA_MAX (2580997 vs 1048567)
System.err java.io.IOException
断言的管理是通过AssetManager进行的。获取断言的方法:AssetManager am=getAssets();
断言的加载模式有ACCESS_BUFFER、ACCESS_RANDOM、ACCESS_STREAMING、ACCESS_UNKNOWN等几种。在打开断言文件时,可以设置断言的加载模式,实现方法是:public final InputStream open(String fileName, int accessMode)
(1)加载字体
Android自带了sans、serif和monospace等3种字体,但系统默认的字体为sans,至于中文字体,默认的时DroidSansFallback.
TextView中,显式设置字体的方式为:<TextView android:typeface="serif"/>
有时,系统自带的字体并不能满足项目的需要,引入的字体通常放置在assets目录下,通过断言加载字体的方式:
TextView tv=(textView)findViewById(R.id.custom); Typeface face=Typeface.createFromAsset(getAssets(),"fonts/Clockopis.ttf"); tv.setTypeface(face);
在通常情况下,没有特殊需要不建议应用自定义的字体,因为这会消耗额外的空间。
(2)加载文件
通过断言,支持对多种文件(图片、文件、字体、XML设置网页等)的加载,根据场景的不同,其加载方式略有不同。
在断言中,加载文件的一个常用场景是基于JSP的HTML等进行的,主要用于基于WebView的应用,其Uri为file:///android_asset/*.*。
对文本资源的加载方式为:
InputStream is=getAssets().open("read_asset.txt"); int size=is.available(); byte[] buffer=new byte[size]; is.read(buffer); is.close();
String text=new String(buffer); TextView tv=(TextView)findViewById(R.id.text); tv.setText(text);
加载文件的默认方式为ACCESS_STREAMING。开发者可以通过断言的open()方法显示设置加载模式。
解析应用的APK包时,时解析的XML文件的断言加载:
try{
assmgr=new AssetManager();
int cookie=assmgr.addAssetPath(mArchiveSourcePath);
if(cookie!=0){
parser=assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
}
}
对于放置在raw目录下的文件,Android不会在对其进行加载时进行优化,这对一些特定文件而言非常重要。这类文件的加载方式非常简单。
加载图片文件的方法:
InputStream is=context.getResource().openRawResource(R.drawable.beach);
BitmapFactory.Options opts=new BitmapFactory.Option();
opts.inJustDecodeBounds=true;
Bitmap bm=BitmapFactory.decodeStream(is,null,opts);
5.Jar包和共享库
在Android应用开发中,Jar包和共享库非常常见,主要出现在一些移植和出于安全考虑的场景中。
(1)Jar包的加载
对于jar包,如果基于SDK在Eclipse中对其进行加载,方法非常简单,右击工程,在弹出的快捷菜单中选择Properties---Java Build Path----Libraries命名,然后选择Add JARs或Add External JARs命令即可,其中Add JARs命令表示添加工程目录下的Jar包,Add External JARs命令表示添加工程目录外的Jar包。
对于基于源代码进行编译的场景,Jar包的加载:LOCAL_PREBUILT_STATIC_JAVA_LIBRARISE := libarity:arity-2.1.2.jar。
(2)共享库的加载
对于so共享库的加载,基于SDK在Eclipse的场景中,同样非常简单,在相应的工程目录下创建libs\armeabi目录,直接将相应的so共享库放置在其下即可。
需要注意的是,在共享库发生更新时,Eclipse无法自动监听到这一变化,按快捷键F5进行工程刷新同样无法达到期望的效果,此时在处理方式为选择Project--Clean命令重新编译即可。
对于基于源代码进行编译的场景,需要先在Android.mk中进行共享库的配置,实现方法:LOCAL_JNI_SHARED_LIBRARIES :=gl2jni
为了使用相应的共享库,在Java文件中还需要进行相应的加载,实现方法:System.loadLibrary("gl2jni');
6.系统资源
UI设计涉及系统级别的字符串、动画、他、颜色、图片、属性、ID、布局、风格、主题、维度、数组、配置等资源的定义。系统资源的定义主要分布在frameworks\base\core\res\res目录下。
风格和主题是框架层与应用层在UI设计上的接口,其中应用层能引用的部分在Android中定义在public.xml中。为了新增的系统UI资源能被应用层引用,需要在public.xml中增加对该资源的声明。
风格与主题的主要区别在于:主题由风格构成。风格相对主题更着力于细处的描述和定义。在Android中,同样有风格和主题的存在,均以XML的方式定义,其中定义风格的文件为styles.xml,定义注意的文件为themes.xml。
在Android中,资源采用统一的标识符定义,对于属性资源,其标识符ID从0x01010000开始定义;对于ID资源,其标识符ID从0x01020000开始定义;对于风格资源,其标识符从0x01030000开始定义;对于字符串资源,其标识符从0x01040000开始定义;对于维度资源,其标识符从0x01050000开始定义;对于颜色资源,其标识符从0x01060000开始定义;对于数组资源,其标识符从0x01070000开始定义;对于图片资源,其标识符从0x01080000开始定义;对于动画资源,其标识符从0x010a0000开始定义。
在启动其他应用前,需要在zygote进程中将先加载的图片资源定义在arrays.xml中的preloaded_drawables数组中,将颜色资源定义在arrays.xml中的preloaded_color_state_lists数组中,状态栏加载的顺序定义在arrays.xml中的status_bar_icon_order数组中。
在Android应用的配置文件中AndroidManifest.xml中使用的属性是定义在attrs_manifest.xml中。
(1)颜色
在frameworks\base\core\res\res\values\目录下的colors.xml中定义了系统用到的一些颜色背景色、前景色、搜索、抽屉效果、键盘Tab等。在frameworks\base\core\res\res\colors目录下提供了颜色的具体实现。
(2)图片
在框架层,图片位于frameworks\base\core\res\res\drawable*目录下,其中图片资源设计得XML文件及与布局等无关的图片会放置在drawable目录下,而与布局有关的图片则分布在其他目录下。
针对LCD密度为240的图片位于drawable-hdpi目录下,而LCD密度为160且属于横屏场景的图片则位于drawable-land-mdpi目录。系统会先根据目标环境在特定目录下寻找资源,如果找不到则到drawable目录下寻找资源。
(3)动画
在框架层,动画位于frameworks\base\core\res\res\anim目录下。Android目前提供了多种动画可供选择,同时提供了淡入、淡出的不同效果。
淡入效果的实现:
<alpha xmlns:android=http://schemas.android.com\apk\res\android
android:interpolator="@anim/decelerate_interpolator"
android:fromAlpha=“0.0” android:toAlpha="1.0"
android:duration="@android:integer/config_longAnimTime"/>
淡出的效果实现:
<alpha xmlns:android=http://schemas.android.com\apk\res\android
android:interpolator="@anim/decelerate_interpolator"
android:fromAlpha=“1.0” android:toAlpha="0.0"
android:duration="@android:integer/config_mediumAnimTime"/>
适当利用框架层提供的动画,可以很好地提升用户体验。
(4)配置
在frameworks\base\core\res\res\values目录下的config.xml中定义了系统用到的一些配置信息。如动画持续时间、网络配置、LED、蓝牙、墙纸、SD卡等,在一般情况下,这些信息不需要调整。
(5)维度
在frameworks\base\core\res\res\values\目录下的dimens.xml中定义了缩略图、应用图标、Toast、状态栏、快速导航(fastscroll)等布局的一些宽和高信息。
(6)风格
在frameworks\base\core\res\res\values\目录下的styles.xml中定义了系统的风格,包括窗体、对话框、动画、部件、字体、Preference、MediaButton、ButtonBar等。
(7)主题
默认的系统主题定义位于frameworks\base\core\res\res\values\目录下的thenes.xml文件中。主题比风格多一个更高层次的视角去审视UI。目前Android提供了Activity、Dialog、Panel、InputMethod、SearchBar、Menu等的主题。