Support Libraries v22.1.0

优质
小牛编辑
127浏览
2023-12-01

Support Libraries v22.1.0

22 Apr 2015

好久不见了啊大家~ 你可能听说了我们已经发布 22.1.0 support libraries 的这个消息, 这可能是目前为止我们对 support library 改动最大的一次更新。

在我们开讲前,建议先读一下 Ian Lake 的这篇官方 blog,里面列出了 这次更新中所有的新特性。

这篇文章我将重点讲解内部运行的方式和原因,尤其是我完成的部分 (因为我非常清楚了解这些)。


AppCompat

先从 AppCompat 说起吧,我们在这个版本中对它做了很大的更新。首先,它的重构...


Refactoring

在之前的版本中进入 AppCompat 唯一的入口 ActionBarActivity 已经被我们抛弃了。 也就是说你以后只能使用一组 Activity 视图层,再也不能像 PreferenceActivity 那样使用它了。

我们现在提取出了所有内部内容,并将它们暴露给一个单一委托 API , AppCompatDelegate 。AppCompatDelegate 可以从 任何提供了 Window.Callback 接口的 Android 对象中构造, 例如 Activity 或 Dialog 的子类。你可以使用它的静态方法 create) 创建它。

如果你创建了一个委托,你需要在每次调用它提供的接口时回调到它。 (例如 onCreate()),不过这很简单,可以提取到一个基类中。

最终,你可以给所有 Activity 的子类附加任何你想要的 AppCompat 的功能。

如果你打算使用 AppCompatDelegate ,我强烈建议你有空看一眼 AppCompatActivity 的源码。这是一个(极端的)例子介绍了怎样整合 AppCompatDelegate。

大部分人不需要这层自定义,可以像原来那样使用 ActionBarActivity 那样使用 AppCompatActivity 这个类就好。


Dialogs

上面刚刚提到了 Dialog ,你应该也想到了我们还加入了什么。在完成重构工作后, Dialog 很自然就是我们下一步工作对象。实际上从 decor-setup 角度来看, Activity 和 Dialog 这里有一些小的不同。

这意味着我们终于解决了自 v21 以来 AppCompat 最大的需求: material styled dialogs (balalalala~)。

我们现在有了新的 AppCompatDialog 类, 你应该在在引用(or 关联) Theme.AppCompat.Dialog 时使用它。

最后,AppCompat 现在也有了它自己的 AlertDialog 实现,来方便构建 material 样式的 AlertDialog。只要使用 android.support.v7.app.AlertDialog 就好了,它会自己处理细节部分。

有一点要注意,AppCompat 的 AlertDialog 没有实现框架版本所做的一切。 它只暴露了在这个 ‘material 世界’ 中有价值的部分。( (╯°□°)╯︵ ┻━┻ )


android:theme

在进入这部分前,请先阅读这篇文章 Theme vs Style, 了解下将要讲解内容的基础。

在 AppCompat v21 里,我们提供了一个快速方便的方法设置设置 Toolbar 的主题,使用 app:theme

在 v22.1.0 里,我们扩充了它的功能,现在你可以给你的 layout 里的任意视图 设置主题了。只要使用 android:theme 这个属性就好了,可以在 compat 和 framework 之间无缝地切换功能。

最好的一点要数它会自动继承父视图的 theme ,并且兼容所有 API v11 以上的设备。 一个例子:

<Toolbar
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <!-- This TextView inherits its theme from the parent Toolbar -->
    <TextView android:text="I'm light!" />

</Toolbar>

对于运行 API v10 甚至更老的设备来说,你也可以使用 android:theme 属性, 不过它不会继承父视图 theme 。这就意味着你要么重新考虑你的布局,要么 为每一个子视图都设置上 android:theme 属性。(这样做效率真的很低)

如果你感兴趣的话,开启继承父类 theme 的方法在这里 LayoutInflater.Factory2


Widgets

如果读过了 Ian 的文章,你可能看到了和 控件着色 (tinting widgets)相关的内容 (这里还有一些新的内容)。

很好,不过关于这点还有一个变化:我们不会再修改 该平台主题的默认 widget 样式了。也就是说如果你使用这个 widget 的 AppCompat 实现(无论显式还是隐式的),那么你在 v21 版本之前的设备上只能获取到 material 样式。在实践中你应该不会看到有什么不同, 因为我们会自动插入适当的 AppCompat 的实现。

这样我们就解决了已经使用 material 样式但是没有着色的问题。它出现在 widget 的平台实现使用了我们的样式,并且出现在不同的位置之中时,比如 Preferences 。

反之,你将会看到当前平台默认的样式 (Holo,etc)。 虽然这样看起来可能有点奇怪,不过这可比看一个空白未着色的 drawable 好多了。


Theme window features

现在 AppCompat 预测 窗口主题 flag 时会更严格 ,配合框架更密切。

背后的原因是为我们早些时候提到的 dialogs 提供支持。它们大量使用了 AppCompat 之前并没有重视的 windowNoTitle 标志。

升级到 v22.1.0 以后,你可能已经遇到过下面的异常:

IllegalArgumentException: AppCompat does not support the current theme features

我在这里回答了解决办法: http://stackoverflow.com/q/29790070/474997


v4

support libraries 的老祖宗 support-v4 还在继续增长,添加了一些新内容。

ColorUtils

ColorUtils 已经从 Palette 中移入到 support-v4里。 它包含了一些非常好的操作颜色的方法。比如,你可以计算在某个背景中, 最小的文本颜色 alpha 值:

int backgroundColor = ...;
int textColor = Color.WHITE;
float minContrastRatio = 4.5f; // We want a minimum contrast ration of 1:4.5

int minAlpha = ColorUtils.calculateMinimumAlpha(
        textColor, backgroundColor, minContrastRatio);

if (minAlpha != -1) {
    // There is an alpha value which has enough contrast, use it!
    return ColorUtils.setAlphaComponent(textColor, minAlpha);
}

还有很多方便的方法,比如颜色合成,计算亮度工具等等。更多信息可以 去看文档。


Drawable 着色

Lollipop 中加入的 Drawable 着色的方法非常好用,可以让你动态的处理着色资源。 在 v21 support library 中 AppCompat 有它自己的实现,现在我们将它移入到 support-v4 的 DrawableCompat 之中让大家都可以使用它。 知道它的工作方式很重要。

Drawable drawable = ...;

// Wrap the drawable so that future tinting calls work
// on pre-v21 devices. Always use the returned drawable.
drawable = DrawableCompat.wrap(drawable);

// We can now set a tint
DrawableCompat.setTint(drawable, Color.RED);
// ...or a tint list
DrawableCompat.setTintList(drawable, myColorStateList);
// ...and a different tint mode
DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_OVER);

需要注意下在调用完 DrawableCompat.wrap() 之后, 它的返回值和你赋值给它的那个并不是同一个东西。你应该使用 DrawableCompat.unwrap( ) 取出原始 Drawable。

在内部,我们将你的 Drawable 包裹在一个特殊的 ‘tint drawable’ (着色 drawable) 之中,它会根据指定的色彩自动更新 Drawable 的滤色器。 允许我们处理 ColorStateList 实例。


Palette

Palette 在这次发布中也获得了一些更新。首先,我们给它加入了新的 Builder 类来帮助获取 Palette 实例。我们发现 Palette 与日俱增的 ‘把手’ 和 设置 正在将它的 API 变得复杂难懂。 使用 Builders 可以显著缓解这个问题。

更加重要的是第二个改变,我们在很大程度上提升了 Palettes 的生成速度。 在生成 Palette 过程中最耗时的要数 色彩量化这一步,它会读取一张图片中的 所有像素点,并降低颜色深度到一个很小的色彩数 (通常是 16)。

在这次更新中,我们使用了传统的方式优化色彩量化的性能,比如更少的对象分配, 更合适的数据结构还有降低算法复杂度。成果很显著~

下面是一些测试数据,在一个使用 ART 的设备上性能大概提升 5 到 6 倍, 如果是使用 Dalvik 的设备,效果还会更加明显。

Device22.022.1.0Speedup
Nexus 655ms8ms~6x
Nexus 555ms11ms~5x
Nexus One1200ms120ms~10x

测试结果并不是很科学,只是给出一个近似的值,不过你懂这个意思的啦。

Cover photo:Scaffolding by Brett Weinstein