使用Anko创建快400%的布局
原文:https://medium.com/@vergauwen.simon/400-faster-layouts-with-anko-da17f32c45dd#.ehmantke1
我用Anko已经有了一段时间了,很好奇Anko提供了哪些优势。所以做了一些性能测试。
我打算把一个比较高级的布局迁移到Anko。这个布局包含了一个RelativeLayout,里面有17个ImageView(全是用的.9图片),一个SurfaceView作为取景器,两个普通的TextView。仍然是一个比较干净的布局,因为没有嵌套。
为什么Anko性能更优?
XML布局是在运行时解析的。也就是说XML需要从资源文件中获取,然后XmlPullParser需要解析所有的元素并一个一个的创建它们。还要解析元素的属性,然后设置。这个过程非常繁重,那么到底浪费了多长时间在上面呢?
我在4个老旧的设备上运行了测试,虽然老但又是每个安卓开发者都需要处理的设备。我们在所有的设备上运行了大约4次 DevMetrics。结果发现性能差距居然达到了350%-600%的程度。
随着对设计/动画以及性能的需求日益增加,我认为大家都对应用的速度非常关心,所以为什么要使用Anko就不用多说了吧。
除了布局变快了之外,我们现在构建布局是在运行时,所以可以加上一些逻辑判断。让我们用Anko创建一个master-detail的示例:
class MainActivity : AppCompatActivity() {
private var toolBar: Toolbar? = null
private var container: ViewGroup? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
coordinatorLayout {
fitsSystemWindows = true
appBarLayout {
toolBar = toolbar {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f
}.lparams(width = matchParent, height = actionBarSize())
}.lparams(width = matchParent)
container = frameLayout()
.lparams(width = matchParent, height = matchParent) {
behavior = AppBarLayout.ScrollingViewBehavior()
}
}
}
}
MainActivity.kt hosted with ❤ by GitHub
就如你看到的那样,用Anko写布局跟xml没有什么两样。除了布局构建上的优势之外,我们可以做兼容性检查,根据OS的版本设置elevation,而不是在布局目录中写两个xml。
对于提取MainActivity中的的元素,Anko提供了AnkoComponent来解决这个问题,但是你还是需要使用findViewById和类型转换。不过这也是可以轻松解决的。
我们创建一个提供了一个bind 和一个unbind方法的接口。类似于使用Butter Knife来绑定与解绑。
interface ViewBinder<in T> {
fun bind(t: T) : View
fun unbind(t: T)
}
ViewBinder.kt hosted with ❤ by GitHub
现在我们可以轻松的从前面的布局中提取出元素到MainLayout.kt class。
class MainLayout : ViewBinder<MainActivity> {
override fun bind(mainActivity: MainActivity): View =
mainActivity.UI {
coordinatorLayout {
fitsSystemWindows = true
appBarLayout {
mainActivity.toolBar = toolbar {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f
}.lparams(width = matchParent, height = actionBarSize())
}.lparams(width = matchParent)
mainActivity.container = frameLayout()
.lparams(width = matchParent, height = matchParent) {
behavior = AppBarLayout.ScrollingViewBehavior()
}
}
}.view
override fun unbind(mainActivity: MainActivity) {
mainActivity.container = null
mainActivity.recycView = null
}
}
MainLayout.kt hosted with ❤ by GitHub
public class MainActivity extends AppCompatActivity {
LinearLayout container;
RecyclerView recycView;
FrameLayout detailContainer;
private MainLayout mainLayout = new MainLayout();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(mainLayout.bind(this));
...
}
}
现在我们的activity看起来是不是干净多了呢?除了调用了setContentView(mainLayout.bind(this))之外没有什么特别之处,它将设置content view并把view和变量绑定。就如你看到的那样,无需findViewById和类型转换。
Runtime layouts
如果你想在构建布局的时候加入逻辑,你肯定会喜欢这个东西。
configuration(orientation = Orientation.LANDSCAPE, smallestWidth = 700) {
recyclerView {
init()
}.lparams(width = widthProcent(50), height = matchParent)
frameLayout().lparams(width = matchParent, height = matchParent)
}
fun <T : View> T.widthProcent(procent: Int): Int =
getAppUseableScreenSize().x.toFloat().times(procent.toFloat() / 100).toInt()
RuntimeLayouts.kt hosted with ❤ by GitHub
让我们分析一下上面的代码,anko提供了一个语法友好的configuration用于在运行时检查配置。可以检查screenSize, density, language, orientation, fromSdk, sdk, uiMode, nightMode, rightToLeft 以及 smallestWidth。因此这里的layout DSL只有在设备为横向、横向宽度是700的时候才会运行。
我们还可以轻松计算出大小,这里将在运行时计算出屏幕宽度并把内容宽度设置为屏幕宽度的50% 。
总结
Anko提供了几种解决传统xml布局构建方式的办法。它绕开了xml布局方式的所有开销。你无需处理findViewById或者类型转换,而且在运行时构建布局可以让你添加想要的逻辑,让布局更动态。所有这些只需要花很小的代价
这里所提到的代码都可以在github的项目中找到。