转载请注明出处:http://blog.youkuaiyun.com/forevercbb/article/details/50889282
由于ActionBar的局限性,Google引进Toolbar取代ActionBar,Toolbar可以更方便及更大程度的进行自定义。最近也是粗浅的使用了一下Toolbar,也遇到一些问题,这里分享一下Toolbar的正确使用姿势。
Toolbar的基本使用
1.调整styles.xml
<style name="AppBaseTheme" parent="Theme.AppCompat">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
使用Theme.AppCompat系列主题,如果应用是白色主题,可以使用Theme.AppCompat.Light.DarkActionBar
2.在布局文件中定义Toolbar控件,跟定义TextView一样,增加app命名空间,一般控件的属性如background、layout属性用android的,Toolbar特有的属性用app命名空间的。
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@drawable/toolbar_bg"
app:navigationIcon="@drawable/icon_back_new"
app:titleTextColor="#787878"
>
</android.support.v7.widget.Toolbar>
属性不多,通过属性名基本可以看懂作用。由于应用风格的一致,Toolbar会有较多的相同或相似,因此建议将Toolbar单独定义在一个layout里面,使用时通过include导入即可,既避免重复劳动,日后修改也比较方便。
Background可以设置toolbar的背景,navigationIcon定义返回按钮,只需要在布局文件中设置这个属性,就会出现返回按钮了,无需像Actionbar一样在代码中set个什么鬼的。
3.在代码中获取Toolbar,可以通过代码继续定义toolbar,最后调用setSupportActionBar(),就能用Toolbar替代ActionBar了。
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(R.string.music);
setSupportActionBar(toolbar);
这里需要注意,Activity需要继承v7包的AppCompatActivity才能调用setSupportActionBar()方法。同时Toolbar要导入v7包的,否则只有在API Level 21,也就是Android 5.0以上版本才能使用。
其它还有什么设置logo啊,title啊,subTitle啊,都太简单了,使用任何语言进行描述都显得苍白无力。
这里介绍一下Toolbar的一些自定义颜色属性。
colorPrimaryDark
状态栏背景色。
在 style 的属性中设置。
textColorPrimary
App bar 上的标题与更多菜单中的文字颜色。
在 style 的属性中设置。
App bar 的背景色
Actionbar 的背景色设定在 style 中的 colorPrimary。
Toolbar 的背景色在layout文件中设置background属性。
colorAccent
各控制元件(如:check box、switch 或是 radoi) 被勾选 (checked) 或是选定 (selected) 的颜色。
在 style 的属性中设置。
colorControlNormal
各控制元件的预设颜色。
在 style 的属性中设置
windowBackground
App 的背景色。
在 style 的属性中设置
navigationBarColor
导航栏的背景色,但只能用在 API Level 21 (Android 5) 以上的版本
在 style 的属性中设置
AppCompatActivity中的菜单使用问题
1.AppCompatActivity菜单不会加载Icon。
解决方案:
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
if (menu != null) {
if (menu.getClass() == MenuBuilder.class) {
try {
Method m = menu.getClass().getDeclaredMethod(“setOptionalIconsVisible”, Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
} catch (Exception e) {
}
}
}
return super.onPrepareOptionsPanel(view, menu);
}
2.使用onPrepareOptionsMenu()会重复加载菜单
解决方法:在onPrepareOptionsMenu方法中调用invalidateOptionsMenu()方法使原来填充的menu无效。
最后说一下setSupportActionBar()方法和getSupportActionBar()方法
setSupportActionBar的参数是Toolbar,而getSupportActionBar取回的却是ActionBar
public abstract void setSupportActionBar(Toolbar toolbar);
public abstract ActionBar getSupportActionBar();
跟踪一下源码,由setSupportActionBar()的注释可以知道,Toolbar to set as the Activity’s action bar,该方法实际将Toolbar设置成了Actionbar。但是Toolbar继承于GroupView,而ActionBar没有父类,又是怎么设置的呢。
继续跟踪,在AppCompatDelegateImplV7类找到setSupportActionBar()方法的实现,
public void setSupportActionBar(Toolbar toolbar) {
if (!(mOriginalWindowCallback instanceof Activity)) {
// Only Activities support custom Action Bars
return;
}
final ActionBar ab = getSupportActionBar();
if (ab instanceof WindowDecorActionBar) {
throw new IllegalStateException("This Activity already has an action bar supplied " +
"by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set " +
"windowActionBar to false in your theme to use a Toolbar instead.");
}
// Clear out the MenuInflater to make sure that it is valid for the new Action Bar
mMenuInflater = null;
ToolbarActionBar tbab = new ToolbarActionBar(toolbar, ((Activity) mContext).getTitle(),
mAppCompatWindowCallback);
mActionBar = tbab;
mWindow.setCallback(tbab.getWrappedWindowCallback());
tbab.invalidateOptionsMenu();
}
可以知道Toolbar到ActionBar的转换通过ToolbarActionBar类完成,跟进ToolbarActionBar类,可以看到ToolbarActionBar是ActionBar的子类,并且有个成员变量private DecorToolbar mDecorToolbar,
public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback callback) {
mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false);
mWindowCallback = new ToolbarCallbackWrapper(callback);
mDecorToolbar.setWindowCallback(mWindowCallback);
toolbar.setOnMenuItemClickListener(mMenuClicker);
mDecorToolbar.setWindowTitle(title);
}
在以上的代码中,toolbar作为参数完成成员变量mDecorToolbar的初始化。并在以下的代码中得到Toolbar的属性。
public ToolbarWidgetWrapper(Toolbar toolbar, boolean style,
int defaultNavigationContentDescription, int defaultNavigationIcon) {
mToolbar = toolbar;
mTitle = toolbar.getTitle();
mSubtitle = toolbar.getSubtitle();
mTitleSet = mTitle != null;
mNavIcon = toolbar.getNavigationIcon();
if (style) {
final TintTypedArray a = TintTypedArray.obtainStyledAttributes(toolbar.getContext(),
null, R.styleable.ActionBar, R.attr.actionBarStyle, 0);
final CharSequence title = a.getText(R.styleable.ActionBar_title);
if (!TextUtils.isEmpty(title)) {
setTitle(title);
}
final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle);
if (!TextUtils.isEmpty(subtitle)) {
setSubtitle(subtitle);
}
final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo);
if (logo != null) {
setLogo(logo);
}
final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon);
if (mNavIcon == null && icon != null) {
setIcon(icon);
}
final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator);
if (navIcon != null) {
setNavigationIcon(navIcon);
}
setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0));
final int customNavId = a.getResourceId(
R.styleable.ActionBar_customNavigationLayout, 0);
if (customNavId != 0) {
setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId,
mToolbar, false));
setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM);
}
final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
if (height > 0) {
final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams();
lp.height = height;
mToolbar.setLayoutParams(lp);
}
final int contentInsetStart = a.getDimensionPixelOffset(
R.styleable.ActionBar_contentInsetStart, -1);
final int contentInsetEnd = a.getDimensionPixelOffset(
R.styleable.ActionBar_contentInsetEnd, -1);
if (contentInsetStart >= 0 || contentInsetEnd >= 0) {
mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0),
Math.max(contentInsetEnd, 0));
}
final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0);
if (titleTextStyle != 0) {
mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle);
}
final int subtitleTextStyle = a.getResourceId(
R.styleable.ActionBar_subtitleTextStyle, 0);
if (subtitleTextStyle != 0) {
mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle);
}
final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0);
if (popupTheme != 0) {
mToolbar.setPopupTheme(popupTheme);
}
a.recycle();
// Keep the TintManager in case we need it later
mTintManager = a.getTintManager();
} else {
mDisplayOpts = detectDisplayOptions();
// Create a TintManager in case we need it later
mTintManager = TintManager.get(toolbar.getContext());
}
setDefaultNavigationContentDescription(defaultNavigationContentDescription);
mHomeDescription = mToolbar.getNavigationContentDescription();
setDefaultNavigationIcon(mTintManager.getDrawable(defaultNavigationIcon));
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(),
0, android.R.id.home, 0, 0, mTitle);
@Override
public void onClick(View v) {
if (mWindowCallback != null && mMenuPrepared) {
mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem);
}
}
});
}
由于Toolbar最后还是设置给了ActionBar,因此在v7包中,通过Activity的对象activity.getSupportActionBar()方法取得的ActionBar对象是能够正常使用并修改ActionBar属性的。