Roman Guy的Android Trick系列文章笔记(二)

5.Speed up your Android UI

 

一些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>

 

 效果图如下:

Roman Guy的Android Trick系列文章笔记(二)

(一层一层的书架)

 

 

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标签,不过我们可以完全规避这个缺陷)。

 

7.Drawable mutations

 

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并不会出现如上所说的问题,应该有保护措施来保护系统资源不会被其它程序在运行时修改。

你可能感兴趣的:(android)