这几天再看Android的新控件,打算彻底的研究分析一下各个控件的特性,已经设计初衷,毕竟UI是为了用户交互,而通用UI更是为了应付更多的使用场景的。
那么就先从这个最简单和实用的Toolbar开始。如果使用的话那么一个APP90%的页面可能是需要顶部导航栏的,那么如果不自定义而使用官方推荐的控件,那么就是Toolbar了。但是不得不说在使用toolbar之前我还是使用自己写的一套顶部导航,毕竟自己写的修改和使用更方便,但是当遇到一些复杂页面的时候,就发现自己写的无法适配这些场景,因为当我们使用CollapsingToolbarLayout和CoordinatorLayout相结合的时候才发现实现复杂的事件分发和一些特效原生竟是如此的优雅简单!
话不多说我们首先先看一下Toolbar的官方解释。
Toolbar supports a more focused feature set than ActionBar. From start to end, a toolbar may contain a combination of the following optional elements:
简单翻译:Toolbar相对于ActionBar支持更多的特性,从开始到结束,一个toolbar可以包含以下这些元素:
1.A navigation button.导航按钮
This may be an Up arrow, navigation menu toggle, close,
collapse, done or another glyph of the app’s choosing. This button should always be used to access other navigational destinations within the container of the Toolbar and its signified content or otherwise leave the current context signified by the Toolbar. The navigation button is vertically aligned within the Toolbar’s minimum height, if set.
简单翻译:可以是一个向上的按钮、导航菜单开关、关闭、折叠或者一些app的选择控件。这个按钮需要一直用于连接其他的导航,或者指向其他的上下文,离开当前的上下文。这个按钮是垂直对齐于toolbar的最小高度,如果设置的话。
2.A branded logo image.一个LOGO图片。
3.A title and subtitle.一个标题或者子标题。
4.One or more custom views.(重点)一个或多个自定义View
在我们的日常开发当中经常遇见的情况就是当前封装的控件不能完全的适配我们的业务需求,那么我们就需要进行自定义View而toolbar恰好又支持这个方式去开发我们的导航栏,并且支持一个或多个,实在是很使用啊!
The application may add arbitrary child views
to the Toolbar. They will appear at this position within the layout. If a child view’s
{@link LayoutParams} indicates a {@link Gravity} value of
{@link Gravity#CENTER_HORIZONTAL CENTER_HORIZONTAL} the view will attempt to center
within the available space remaining in the Toolbar after all other elements have been measured.
简单翻译:应用可以在Toolbar上添加任意子View,他们会出现在layout设置的位置。
5.An {@link ActionMenuView action menu}.一个向下展示的菜单。
总结起来就是Toolbar有高度的可自定义性,可以追加很多我们常用的控件,也可以添加自定义视图。
理论不能脱离实践,那么接下来我们来撸码吧。
先借助一个比较好例子:
按照效果图,从左到右分别是我们前面提及到的 导航栏图标、App的logo、标题和子标题、自定义控件、以及 ActionMenu 。接着,我们来看下布局文件和代码实现。
首先,在布局文件 activity_tool_bar.xml 中添加进我们需要的 Toolbar 控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da">
<!--自定义控件-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clock" />
</android.support.v7.widget.Toolbar>
</LinearLayout>
注:这里只有一个TextView!
接着在 base_toolbar_menu.xml 中添加 action menu 菜单项。对照我们之前英文文档里面的menu。
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@id/action_search"
android:icon="@mipmap/ic_search"
android:title="@string/menu_search"
app:showAsAction="ifRoom" />
<item
android:id="@id/action_notification"
android:icon="@mipmap/ic_notifications"
android:title="@string/menu_notifications"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_item1"
android:title="@string/item_01"
app:showAsAction="never" />
<item
android:id="@+id/action_item2"
android:title="@string/item_02"
app:showAsAction="never" />
</menu>
最后到 ToolbarActivity 中调用代码拿到这 Toolbar 控件,并在代码中做各种setXXX操作。
public class ToolBarActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tool_bar);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);//设置导航栏图标
toolbar.setLogo(R.mipmap.ic_launcher);//设置app logo
toolbar.setTitle("Title");//设置主标题
toolbar.setSubtitle("Subtitle");//设置子标题
toolBar.setTitleTextColor(Color.parseColor("#112233"));//设置主标题颜色
toolBar.setSubtitleTextColor(Color.parseColor("#112233"));//设置子标题颜色
toolbar.inflateMenu(R.menu.base_toolbar_menu);//设置右上角的填充菜单
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
int menuItemId = item.getItemId();
if (menuItemId == R.id.action_search) {
Toast.makeText(ToolBarActivity.this , R.string.menu_search , Toast.LENGTH_SHORT).show();
} else if (menuItemId == R.id.action_notification) {
Toast.makeText(ToolBarActivity.this , R.string.menu_notifications , Toast.LENGTH_SHORT).show();
} else if (menuItemId == R.id.action_item1) {
Toast.makeText(ToolBarActivity.this , R.string.item_01 , Toast.LENGTH_SHORT).show();
} else if (menuItemId == R.id.action_item2) {
Toast.makeText(ToolBarActivity.this , R.string.item_02 , Toast.LENGTH_SHORT).show();
}
return true;
}
});
}
}
代码到此已经完成了 Toolbar 的基本使用,下面有几个代码里面需要注意的地方:
像我们看到的Toolbar 中支持的控件都提供响应的方法而且很简单直接的就可以调用。
如果你想修改标题和子标题的字体大小、颜色等,可以调用setTitleTextColor、setTitleTextAppearance、setSubtitleTextColor、setSubtitleTextAppearance 这些API;自定义的View位于 title、subtitle 和 actionmenu 之间,这意味着,如果 title 和 subtitle 都在,且 actionmenu选项 太多的时候,留给自定义View的空间就越小;
导航图标 和 app logo 的区别在哪?如果你只设置 导航图标( or app logo) 和 title、subtitle,会发现 app logo 和 title、subtitle 的间距比较小,看起来不如 导航图标 与 它们两搭配美观;
Toolbar 和其他控件一样,很多属性设置方法既支持代码设置,也支持在xml中设置
最后最重要的部分你可能说,这个控件不错但是还是不能满足我所有的需求啊!
比如说我们的上面的menu是可以动态改变的!那么就到了画重点的时候了。我们看Toolbar源码的时候发现他是个ViewGroup那么我们就可以在里面放布局让后自定义View了!
先上代码:
<android.support.v7.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:tag="(0,60)"
app:layout_collapseMode="pin"
>
<RelativeLayout
android:background="#70000000"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_marginLeft="200dp"
android:background="@mipmap/wm_focus_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:background="@mipmap/wm_focus_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/wm_focus_img1"
android:background="@mipmap/wm_focus_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"/>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
toolBar.setNavigationIcon(R.mipmap.arrow);
toolBar.setLogo(R.mipmap.player);
toolBar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
当我们都设置之后,我们会发现,虽然我们给RelativeLayout设置的是match_parent,但是Toolbar并不会给我们全部的可用空间,可用的可用空间是根据我们自己设置的其他属性而定的,Toolbar会优先于给自己的控件位置,然后才是分配给我们自定控件位置。
Toolbar 存在的坑
当我们在XML中使用初始化Toolbar的一些自定义属性的时候是不能已这种方式实现的虽然不会报错,但是在运行的时候并没有任何效果。
android:subtitle="456"
android:title="123"
这里的原因是toolbar是V7支持包下面的而不在Android下面。所以我们使用的时候相当于是使用一个自定义属性,这个时候我们需要给一个命名空间。
xmlns:app=”http://schemas.android.com/apk/res-auto”(比较通用的形式)
把所有用 android:xxx 设置无效的,都用 toolbar:xxx 设置即可生效。代码更改如下:
app:"456"
app:"123"
总结一下:当我们对于头的UI显示效果要求不是那么精确的时候我们可以使用Toolbar 快速的实现我们的标题栏的部署。当然也需要我们做一些二次封装,总之封装的越好我们的效率就越高。