一些Android程序需要从UI toolkit中一点点地挤出性能,不过确实有许多方法可以办到。在这篇文章中,你会发现如何加速你的绘制并感觉到activity启动时间的变化。这些技术都依赖于一个功能:window的background drawable.
window background这个概念有一点令人误解。当你在一个Activity中调用setContentView()来创建UI时,Android会把你所有view添加到Actitity的window中。这个window中不只包含你的那些view,还有一些是它人为你创建的。这里面最重要的一个是,在G1中的那个DecorView,在下面的hierarchy视图中高亮的:
DecorView实质上了window background的绘制,在Activity调用getWindow().setBackgroundDrawable()就可以改变DecorView的background的绘制。正如前面提到的,这个机制是跟当前Android的实现非常相关的,可能会在Android以后的版本中被改变。
我在Android2.3.3版本中测试了文中的示例,发现测试前后的帧数并未改变,所以我猜测,可能这个机制已经在这个版本改变了,我们不需要关心它。
文章的后半部分介绍了一个trick:
把bitmap drawable设置成背景以实现某些特定的功能。例如:
<bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/shelf_panel" android:tileMode="repeat" />
然后activity的theme来引用这个drawable:
<resources> <style name="Theme.Shelves" parent="android:Theme"> <item name="android:windowBackground">@drawable/background_shelf</item> <item name="android:windowNoTitle">true</item> </style> </resources>
效果图如下:
(一层一层的书架)
The same exact trick is used in the Google Maps application that ships with the T-Mobile G1. When the application is launched, the user immediately sees the loading tiles of MapView
. This is only a trick, the theme is simply using a tiled background that looks exactly like the loading tiles of MapView
.
Sometimes the best tricks are also the simplest so the next time you create an activity with an opaque UI or a custom background, remember to change the window’s background.
6.Android Layout Tricks #4: Optimize, Part 2
本文介绍了一个非常有用的的Widget:ViewStub。ViewStub可以被看作一个lazy include。它带来了所有<include />的功能并且还不会使一些不常使用的view污染你的UI。
ViewStub是一个轻量级的view。它没有dimension(尺寸),也不许要绘制任何东西,并且不以任何方式参与到布局中。这意味着ViewStub是一个非常廉价的,所以view层级中包含它也不会有大的开销。它的最好描述就是:lazy include。引用了ViewStub的layout会在你决定要inflate这些ui时inflate。
ViewStub的使用很简单:
<ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import" android:layout="@layout/progress_overlay" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" />
当你inflate ViewStub时,简单的调用它的inflate()方法就可以了。你也可以改变ViewStub的visibility来inflate:
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
来看一下效果图:
下面的Importing...视图就是由ViewStub inflate而来。
需要注意的是:stub被inflate后,它会被从view hierarchy中移除。因此,不需要长期持有它的引用,例如用类变量来引用一个ViewStub。
ViewStub在轻松编码和效率之间做到了非常好的妥协。你可以用它来替代在运行时inflate view并手动添加到你的view hierachy这样的事情。它唯一的缺陷是它目前不支持<merge />标签。(我在SDK2.3.3中做了测试,依然不支持merge标签,不过我们可以完全规避这个缺陷)。
Android的Drawable是极其有用的。Drawable是一个通常跟View有关联的插件化的图像容器。例如,BitmapDrawable可以被用来显示图片,ShapeDrawable用以显示图形或者梯度,等等。你甚至可以把它们组合起来来创建更复杂的效果图。
Drawables allows to easily customize the rendering of the widgets without subclassing them.(Drawable允许在不subclassing widget的情况下来轻松地自定义它们的效果。这句话好难翻译 =。=)。事实上,大量的Android默认程序和widget都使用了Drawable;在Android Framework核心库里面有700多个Drawable。因为Android中使用了大量的drawable,所以Android对它们的加载方式作了优化。例如,每当你创建一个Button,将从framework resources中加载一个新的drawable(android.R.drawable.btn_default)。这意味着所有app的button都使用新的drawable实例作为它们的背景。所以,所有的这些drawable都共享一个公共的状态(叫做constant state)。这个state的内容因你使用的drawable类型而不同,但是它通常包含了一个resource可以定义的所有的属性(properties)。以button为例,它的constant state包含了这个默认的bitmap。通过这种方式,所有跨app的button其实都是共享同一个bitmap,节省了很多内存。
下图展示了当给2个不同view指定相同的图片资源作为背景后的情况。你可以看到,两个drawable被创建出来,但是它们都共享了同样的constant state和bitmap:
共享state的特性非常棒地避免了内存浪费,但是当你要尝试改变drawable属性的时候会引入一些问题:当你改变了多个view所引用的drawable的constant state时,你会发现所有引用处都被更改了,这是因为你的改动改变了constant state,并且这个改变是不可逆的。如果你要避免这个问题,那么就需要调用Drawble的mutate()方法,它会创建一份constant state的拷贝,这样,修改当前drawable的属性就不会影响其它drawable。如下图所示:
mutate()返回drawable本身,并不创建新的drawable。
备注:引用android system的drawable并不会出现如上所说的问题,应该有保护措施来保护系统资源不会被其它程序在运行时修改。