本文旨在分享本身在 ToolBar 使用上的偷懒,没有较多代码,只是分享一种思路。git
这里指的 ToolBar 是泛指顶部的那个功能区域,不只仅局限于 Android 中的 ActionBar、ToolBar 。github
ToolBar 应该算是在项目中使用较为普遍的一个 View 了,它主要用于展现当前页面的标题、导航按钮及可能存在的扩展功能。app
下图就展现了一个具备一些基本属性的 ToolBar。布局
在 Android 中也不难实现,无非就是在布局文件声明出 ToolBar,而后在 Activity 使用它,为其设置一些内容,若是须要用到右侧的菜单,为其添加菜单便可。post
不过呢,国内的应用大多没有按照这种样式去设计,咱们公司也是如此。这里咱们不去评判它该是怎么样的,只是来讨论如何快速作出与设计师要求相同的效果。spa
QQ 的 ToolBar设计
小米短信的 ToolBar3d
我司产品的 ToolBarcode
遇到这种状况,无非就是经过自定义 View,作出一个相似 ToolBar 的 View,而后放在页面的最靠近状态栏的地方。而后每一个页面经过在布局文件中进行声明,再到相应的 Activity 或是 Fragment 中经过 findViewById 的形式找到再设置一些相关属性之类的。cdn
难吗?不难,就是有点恶心,每一个页面都得写一些重复代码,我是真的不想写啊。那就一块儿来偷个懒吧。
我不想在每一个 Activity 或是 Fragment 上都写那些恶心的代码,能不能少些一点?
通过观察我发现,大部分页面的 ToolBar 都是中间是标题,左侧是返回按钮,右侧可能为文字、图片或是什么都不显示。区别最大的就是底部的内容,那么我把底部的内容所有划分给 Fragment、Toolbar 所属的区域属于承载 Fragment 的 Activity。(以下图所示)
这样一来,开发者只须要在 Activity 中写,在 Activity 加载不一样的 Fragment 时由 Fragment 去更新当前的标题。当用户点击了左侧或右侧的功能按键,经过查找当前 Activity 中存在的 Fragment 列表,找出当前显示的 Fragment,把点击事件传入便可。
这样一来,既能实现了 UI 效果也少些了部分代码,挺好的。到这还没完,来思考一下这种方案的优缺点。
优势是不须要在每一个 Fragment 中声明并设置 ToolBar 了,在必定程度上减小了 ToolBar 声明与设置属性的次数。
不过呢这种方案的缺点也很明显,因为把 ToolBar 放在了 Activity,因此每次设置 ToolBar 的相关属性都必须通过 Activity,Activity 要保证其承载的每一个 Fragment 显示不出问题,就必须作到对每一个 Fragment 的状况作到兼容。好比,有个页面须要放置一张圆弧的背景图,此时这种方案作起来就会比较麻烦。
因此,总体来讲,只是把粗略的把重复代码的解决掉,可是又会带来某些兼容的问题。也不算多好,因此才又有了新的偷懒版本。
自从 Android Studio 大力推行 ConstraintLayout 以后,我就一直在使用,发现了能够利用 ConstraintLayout 动态设置 View 的约束从而达到设置 ToolBar 的效果。
具体来讲是这样的:
首先使用 Java 代码动态建立出 ToolBar 这个 View。紧接着为 ToolBar 设置约束条件,分别为:
这样一来,就能让 ToolBar 位于整个页面的顶端位置。
不过这样有一个致命的痛,就是会覆盖原有属于顶部的 View(closeToolView),解决这个问题也很简单,利用 ConstraintLayout 为顶端 View (closeToolView)设置一个 top_toBottomOf 这个属性,这个属性值固然就是 ToolBar 了。
怎么样,这个方案是否是挺简单的,不过,稍微想想就会发现,咱们在布局文件里不就这么写的吗?有什么稀奇的。
这个方案没什么稀奇的,只是把本来属于开发者写在布局文件中的内容,放在了 Java 代码中而已。
不过稍稍变通一下,咱们在页面的布局文件也就不须要写这么多的重复代码了,并且,能够建立出一个 ToolBarHelper 这个类,由这个类去完成上述的这些内容,如此一来,Activity 与 Fragment 的基类只须要去调用 ToolBarHelper 便可。
这样这个流程下来就算是完事了,不过你要是想用的话仍是得清楚他的不足之处:
根布局必须为 ConstraintLayout
这个很好理解,若是不是 ConstraintLayout 这个布局的话,后续全部的约束条件都不能使用;
顶部 View (closeToolView)的高度不能设置为 match_parent
若是顶部 View(closeToolView)的高度为 match_parent 的话,那么即便设置了 top_toBottomOf 顶部 View(closeToolView)的相对位置也不会发生改变。
关键的代码以下:
private fun addToolBar(root: View, closeToolView: View) {
val layoutParams = ConstraintLayout.LayoutParams(root.layoutParams)
layoutParams.width = ConstraintLayout.LayoutParams.MATCH_PARENT
layoutParams.height = 48.toPix()
toolView.layoutParams = layoutParams
toolView.id = View.generateViewId()
if (initTitle.isNotEmpty()) {
toolView.setTitle(initTitle)
}
if (root is ConstraintLayout) {
val relation = ConstraintSet()
checkId(root)
root.addView(toolView)
relation.clone(root)
relation.connect(toolView.id, ConstraintSet.START, root.id, ConstraintSet.START)
relation.connect(toolView.id, ConstraintSet.END, root.id, ConstraintSet.END)
relation.connect(toolView.id, ConstraintSet.TOP, root.id, ConstraintSet.TOP)
relation.applyTo(root)
val closeRelation = ConstraintSet()
closeRelation.clone(root)
closeRelation.connect(
closeToolView.id,
ConstraintSet.TOP,
toolView.id,
ConstraintSet.BOTTOM
)
closeRelation.applyTo(root)
}
}
复制代码
笔者作了一个简单的 Demo,并上传到了 GitHub,这个 Demo 仅用于演示 ToolBarHelper 的使用,后续可能包含 ToolBar 的点击相应及多样化的设置,没有什么特别的功能,其中 Fragment 的导航库使用的是 AndroidX 的 Navigation。
有什么问题或是状况,能够在评论或是 issues 中留言。😉
封面图:Photo by Amber Walker on Unsplash
推荐阅读:来学一波 Navigation