众所周知,在使用ActionBar的时候,一堆的问题:这个文字能不能定制,位置能不能改变,图标的间距怎么控制神马的,由此暴露出了ActionBar设计的不灵活。为此官方提供了ToolBar,并且提供了supprot library用于向下兼容。Toolbar之所以灵活,是因为它其实就是一个ViewGroup,我们在使用的时候和普通的组件一样,在布局文件中声明。

(1)ToolBar的引入

既然准备用ToolBar,首先看看如何将其引入到app中。

1)隐藏原本的ActionBar

隐藏可以通过修改我们继承的主题为:Theme.AppCompat.Light.NoActionBar,当然也可以通过设置以下属性完成:

<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>

我们这里选择前者:

    <style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">

        <!-- customize the color palette -->
<item name="colorPrimary">@color/material_blue_500</item>
<item name="colorPrimaryDark">@color/material_blue_700</item>
<item name="colorAccent">@color/material_green_A200</item> </style>
2)在布局文件中声明
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"> <android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent" /> <android.support.v7.widget.GridLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" app:useDefaultMargins="true"
app:columnCount="3"> <TextView
android:text="First Name:"
app:layout_gravity="right" /> <EditText
android:ems="10"
app:layout_columnSpan="2" /> <TextView
android:text="Last Name:" app:layout_column="0"
app:layout_gravity="right" /> <EditText
android:ems="10"
app:layout_columnSpan="2" /> <TextView
android:text="Visit Type:" app:layout_column="0"
app:layout_gravity="right" /> <RadioGroup app:layout_columnSpan="2"> <RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Business" /> <RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Social" /> </RadioGroup> <Button
android:text="Ok"
app:layout_column="1" /> <Button
android:text="Cancel"
app:layout_column="2" /> </android.support.v7.widget.GridLayout> </LinearLayout>

ok,这里我们也贴出来上面图片的效果的xml,使用GridLayout实现的,有兴趣的可以研究下。可以看到我们在布局文件中定义了ToolBar。

3)代码中设定
public class MainActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar);
setSupportActionBar(toolbar);
}

ok,基本就是先隐藏ActionBar,然后在布局文件中声明,最后代码中设定一下。现在看一下效果图:

可以看到我们的ToolBar显示出来了,默认的Title为ToolBar,但是这个样式实在是不敢恭维,下面看我们如何定制它。

(2)定制ToolBar

首先给它一个nice的背景色,还记得前面的colorPrimary么,用于控制ActionBar的背景色的。当然这里我们的ToolBar就是一个普通的ViewGroup在布局中,所以我们直接使用background就好,值可以为:?attr/colorPrimary使用主题中定义的值。

ToolBar中包含Nav Icon , Logo , Title , Sub Title , Menu Items 。

我们可以通过代码设置上述ToolBar中的控件:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.id_toolbar); // App Logo
toolbar.setLogo(R.mipmap.ic_launcher);
// Title
toolbar.setTitle("App Title");
// Sub Title
toolbar.setSubtitle("Sub title"); setSupportActionBar(toolbar);
//Navigation Icon
toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp);
}

可选方案当然如果你喜欢,也可以在布局文件中去设置部分属性:

 <android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
app:title="App Title"
app:subtitle="Sub Title"
app:navigationIcon="@drawable/ic_toc_white_24dp"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="?attr/colorPrimary"/>

至于Menu Item,依然支持在menu/menu_main.xml去声明,然后复写onCreateOptionsMenuonOptionsItemSelected即可。

可选方案也可以通过toolbar.setOnMenuItemClickListener实现点击MenuItem的回调。

  toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
});

效果图:

关于字体的样式,可以在布局文件设置属性app:titleTextAppearanceapp:subtitleTextAppearance或者代码setTitleTextAppearancesetSubTitleTextAppearance设置。

4、实战

简单介绍了Toolbar以后呢,我们决定做点有意思的事,整合ToolBar,DrawerLayout,ActionBarDrawerToggle写个实用的例子,效果图如下:

ok,简单处理了下横纵屏幕的切换。接下来看代码实现。

  • 大致思路

整体实现还是比较容易的,首先需要引入DrawerLayout(如果你对DrawerLayout不了解,可以参考 
Android DrawerLayout 高仿QQ5.2双向侧滑菜单),然后去初始化mActionBarDrawerToggle,mActionBarDrawerToggle实际上是个DrawerListener,设置mDrawerLayout.setDrawerListener(mActionBarDrawerToggle);就已经能够实现上面点击Nav Icon切换效果了。当然了细节还是挺多的。

我们的效果图,左侧菜单为Fragment,内容区域为Fragment,点击左侧菜单切换内容区域的Fragment即可。关于Fragment的知识,可以查看:Android Fragment 你应该知道的一切

  • 布局文件 
    activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:background="#ffffffff"
xmlns:app="http://schemas.android.com/apk/res-auto"> <!--app:subtitle="Sub Title"-->
<android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
app:title="App Title"
app:navigationIcon="@drawable/ic_toc_white_24dp"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="?attr/colorPrimary" /> <android.support.v4.widget.DrawerLayout
android:id="@+id/id_drawerlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"> <FrameLayout
android:id="@+id/id_content_container"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout> <FrameLayout
android:id="@+id/id_left_menu_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left"
android:background="#ffffffff"></FrameLayout> </android.support.v4.widget.DrawerLayout> </LinearLayout>

DrawerLayout中包含两个FrameLayout,分别放内容区域和左侧菜单的Fragment。

  • LeftMenuFragment
package com.zhy.toolbar;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView; /**
* Created by zhy on 15/4/26.
*/
public class LeftMenuFragment extends ListFragment { private static final int SIZE_MENU_ITEM = 3; private MenuItem[] mItems = new MenuItem[SIZE_MENU_ITEM]; private LeftMenuAdapter mAdapter; private LayoutInflater mInflater; @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); mInflater = LayoutInflater.from(getActivity()); MenuItem menuItem = null;
for (int i = 0; i < SIZE_MENU_ITEM; i++) {
menuItem = new MenuItem(getResources().getStringArray(R.array.array_left_menu)[i], false, R.drawable.music_36px, R.drawable.music_36px_light);
mItems[i] = menuItem;
}
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
} @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); setListAdapter(mAdapter = new LeftMenuAdapter(getActivity(), mItems)); } @Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id); if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.menuItemSelected(((MenuItem) getListAdapter().getItem(position)).text);
} mAdapter.setSelected(position); } //选择回调的接口
public interface OnMenuItemSelectedListener {
void menuItemSelected(String title);
}
private OnMenuItemSelectedListener mMenuItemSelectedListener; public void setOnMenuItemSelectedListener(OnMenuItemSelectedListener menuItemSelectedListener) {
this.mMenuItemSelectedListener = menuItemSelectedListener;
} }

继承自ListFragment,主要用于展示各个Item,提供了一个选择Item的回调,这个需要在Activity中去注册处理。

  • LeftMenuAdapter
package com.zhy.toolbar;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView; /**
* Created by zhy on 15/4/26.
*/
public class LeftMenuAdapter extends ArrayAdapter<MenuItem> { private LayoutInflater mInflater; private int mSelected; public LeftMenuAdapter(Context context, MenuItem[] objects) {
super(context, -1, objects); mInflater = LayoutInflater.from(context); } @Override
public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_left_menu, parent, false);
} ImageView iv = (ImageView) convertView.findViewById(R.id.id_item_icon);
TextView title = (TextView) convertView.findViewById(R.id.id_item_title);
title.setText(getItem(position).text);
iv.setImageResource(getItem(position).icon);
convertView.setBackgroundColor(Color.TRANSPARENT); if (position == mSelected) {
iv.setImageResource(getItem(position).iconSelected);
convertView.setBackgroundColor(getContext().getResources().getColor(R.color.state_menu_item_selected));
} return convertView;
} public void setSelected(int position) {
this.mSelected = position;
notifyDataSetChanged();
} } package com.zhy.toolbar; public class MenuItem { public MenuItem(String text, boolean isSelected, int icon, int iconSelected) {
this.text = text;
this.isSelected = isSelected;
this.icon = icon;
this.iconSelected = iconSelected;
} boolean isSelected;
String text;
int icon;
int iconSelected;
}

Adapter没撒说的~~提供了一个setSection方法用于设置选中Item的样式什么的。 
接下来看ContentFragment,仅仅只是一个TextView而已,所以代码也比较easy。

package com.zhy.toolbar;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView; /**
* Created by zhy on 15/4/26.
*/
public class ContentFragment extends Fragment { public static final String KEY_TITLE = "key_title";
private String mTitle; public static ContentFragment newInstance(String title) {
ContentFragment fragment = new ContentFragment();
Bundle bundle = new Bundle();
bundle.putString(KEY_TITLE, title);
fragment.setArguments(bundle);
return fragment;
} @Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { TextView tv = new TextView(getActivity());
String title = (String) getArguments().get(KEY_TITLE);
if (!TextUtils.isEmpty(title))
{
tv.setGravity(Gravity.CENTER);
tv.setTextSize(40);
tv.setText(title);
} return tv;
}
}

提供newInstance接收一个title参数去实例化它。

最后就是我们的MainActivity了,负责管理各种Fragment。

  • MainActivity
package com.zhy.toolbar;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Gravity; import java.util.List; public class MainActivity extends AppCompatActivity { private ActionBarDrawerToggle mActionBarDrawerToggle; private DrawerLayout mDrawerLayout; private Toolbar mToolbar; private LeftMenuFragment mLeftMenuFragment;
private ContentFragment mCurrentFragment; private String mTitle; private static final String TAG = "com.zhy.toolbar";
private static final String KEY_TITLLE = "key_title"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); initToolBar();
initViews(); //恢复title
restoreTitle(savedInstanceState); FragmentManager fm = getSupportFragmentManager();
//查找当前显示的Fragment
mCurrentFragment = (ContentFragment) fm.findFragmentByTag(mTitle); if (mCurrentFragment == null) {
mCurrentFragment = ContentFragment.newInstance(mTitle);
fm.beginTransaction().add(R.id.id_content_container, mCurrentFragment, mTitle).commit();
} mLeftMenuFragment = (LeftMenuFragment) fm.findFragmentById(R.id.id_left_menu_container);
if (mLeftMenuFragment == null) {
mLeftMenuFragment = new LeftMenuFragment();
fm.beginTransaction().add(R.id.id_left_menu_container, mLeftMenuFragment).commit();
} //隐藏别的Fragment,如果存在的话
List<Fragment> fragments = fm.getFragments();
if (fragments != null) for (Fragment fragment : fragments) {
if (fragment == mCurrentFragment || fragment == mLeftMenuFragment) continue;
fm.beginTransaction().hide(fragment).commit();
} //设置MenuItem的选择回调
mLeftMenuFragment.setOnMenuItemSelectedListener(new LeftMenuFragment.OnMenuItemSelectedListener() {
@Override
public void menuItemSelected(String title) { FragmentManager fm = getSupportFragmentManager();
ContentFragment fragment = (ContentFragment) getSupportFragmentManager().findFragmentByTag(title);
if (fragment == mCurrentFragment) {
mDrawerLayout.closeDrawer(Gravity.LEFT);
return;
} FragmentTransaction transaction = fm.beginTransaction();
transaction.hide(mCurrentFragment); if (fragment == null) {
fragment = ContentFragment.newInstance(title);
transaction.add(R.id.id_content_container, fragment, title);
} else {
transaction.show(fragment);
}
transaction.commit(); mCurrentFragment = fragment;
mTitle = title;
mToolbar.setTitle(mTitle);
mDrawerLayout.closeDrawer(Gravity.LEFT); }
}); } private void restoreTitle(Bundle savedInstanceState) {
if (savedInstanceState != null)
mTitle = savedInstanceState.getString(KEY_TITLLE); if (TextUtils.isEmpty(mTitle)) {
mTitle = getResources().getStringArray(
R.array.array_left_menu)[0];
} mToolbar.setTitle(mTitle);
} @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_TITLLE, mTitle);
} private void initToolBar() { Toolbar toolbar = mToolbar = (Toolbar) findViewById(R.id.id_toolbar);
// App Logo
// toolbar.setLogo(R.mipmap.ic_launcher);
// Title
toolbar.setTitle(getResources().getStringArray(R.array.array_left_menu)[0]);
// Sub Title
// toolbar.setSubtitle("Sub title"); // toolbar.setTitleTextAppearance(); setSupportActionBar(toolbar); //Navigation Icon
toolbar.setNavigationIcon(R.drawable.ic_toc_white_24dp);
/*
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
});*/ } private void initViews() { mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawerlayout); mActionBarDrawerToggle = new ActionBarDrawerToggle(this,
mDrawerLayout, mToolbar, R.string.open, R.string.close);
mActionBarDrawerToggle.syncState();
mDrawerLayout.setDrawerListener(mActionBarDrawerToggle); }
}

内容区域的切换是通过Fragment hide和show实现的,毕竟如果用replace,如果Fragment的view结构比较复杂,可能会有卡顿。当然了,注意每个Fragment占据的内存情况,如果内存不足,可能需要改变实现方式。 
对于旋转屏幕或者应用长时间置于后台,Activity重建的问题,做了简单的处理。

对了,写布局的时候,可以尽可能的去考虑 Material design 的规范。

5、参考资料

最新文章

  1. 浅谈 JS 创建对象的 8 种模式
  2. ibeacon的使用和应用场景简单示例
  3. 批量处理_cmd_matlab
  4. other
  5. Java Hibernate 之连接池详解
  6. install ubuntu16.04
  7. 基于moco的mock server 简单应用 来玩玩吧
  8. TP传输的两种模式
  9. Safari 浏览器模拟iPhone和其他浏览器
  10. vue中样式的典型操作(:class,:style)
  11. CMake support in Visual Studio
  12. ssm学习的第一个demo---crm(1)
  13. 三个UID
  14. 【Python】使用hashlib进行MD5和sha1摘要计算
  15. NDT(Normal Distributions Transform)算法原理与公式推导
  16. Android build.gradle
  17. pyDay11
  18. 改变VO中的sql
  19. php实现银联支付
  20. nexus 安装配置

热门文章

  1. Linux权限扩展
  2. oracle initialization or shutdown in progress问题解决步骤
  3. Mysql 性能调优之Memory 计算
  4. Trace文件过量生成问题解决
  5. Oracel数据库连接时出现:ORA-12518:监听程序无法分发客户机连
  6. iOS仿网易新闻栏目拖动重排添加删除效果
  7. Linux内核设计第三周——构造一个简单的Linux系统
  8. C语言 ---- 数组 iOS学习-----细碎知识点总结
  9. iOS开发UI篇—Modal简单介绍
  10. 将编码从GB2312转成UTF-8的方法汇总(从前台、程序、数据库)