Android开发笔记(一百一十九)工具栏ToolBar

Toolbar

在前面的博文《Android开发笔记(二十)顶部导航栏》中,我们学习了ActionBar的用法,可是ActionBar着实是不怎么好用,比如文字风格不能定制、图标不能定制,而且还存在低版本的兼容性问题,所以实际开发中大家还是不倾向使用ActionBar。为此,Android提供了加强版的工具栏控件即Toolbar,因为Toolbar继承自ViewGroup,而且可在布局文件中像其它布局视图一样使用,所以灵活性大大的提高了。既然Android都与时俱进了,那我们也不能落后,现在就来好好学学Toolbar的用法。

导入android-support-v7-appcompat

Toolbar包含在android-support-v7-appcompat.jar包中,但app工程还不能直接使用这个jar包,因为v7-appcompat是一个完整的工程,jar包里面大量引用了工程中的图片资源,所以我们要先把v7-appcompat导入为一个库工程,然后app工程再引用这个库工程。具体步骤如下所示:

1、SDK的Extra组件中的“Android Support Library”要更新到最新版本。

2、把v7-appcompat导入为一个库工程,v7-appcompat的源路径是sdk\extras\android\support\v7\appcompat。

3、把project.properties中的target改为23(注意库工程和app工程都要改),不然会出现如下错误:

Error:Error retrieving parent for item: No resource found that matches the given name ‘android:TextAppearance.Material.Widget.Button.Inverse‘.

Error:Error retrieving parent for item: No resource found that matches the given name ‘android:Widget.Material.Button.Colored‘.

4、删除values-v11与values-v14下面的styles.xml(注意库工程和app工程都要删),不然编译报错:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.exmtoolbar/com.example.exmtoolbar.MainActivity}: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

在项目中引入Toolbar

因为Toolbar与ActionBar都占着顶部导航栏的位置,所以要想引入Toolbar就得先关闭ActionBar啦,具体步骤如下所示:

1、在styles.xml中定义一个不包含ActionBar的风格样式

    <style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/blue_light</item>
        <item name="colorPrimaryDark">@color/blue_light</item>
        <item name="colorAccent">@color/blue_light</item>
    </style>

2、修改AndroidManifest.xml,把application节点的android:theme属性值改为第一步定义的风格,如android:theme="@style/AppBaseTheme"

3、页面布局文件的根节点改为LinearLayout,且为vertical垂直方向;然后增加一个Toolbar元素,因为Toolbar本质是个ViewGroup,所以也可在它下面添加别的控件。下面是个布局例子片段:

    <android.support.v7.widget.Toolbar
        android:id="@+id/tl_head"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

4、Activity代码改为继承AppCompatActivity,注意早期的v7包是没有AppCompatActivity的,所以前面才说要先把v7包更新到最新版本。

5、同样修改AndroidManifest.xml,给该activity节点补充属性值android:theme="@style/AppBaseTheme"

6、回到Activity代码,获取一个Toolbar对象,并调用setSupportActionBar方法设置默认的导航栏为当前的Toolbar。

Toolbar的常用方法

Toolbar比ActionBar灵活,主要便是它提供了多个方法来修改控件风格,下面是Toolbar的常用方法:

setLogo : 设置工具栏图标。

setTitle : 设置标题文字。

setTitleTextAppearance : 设置标题的文字风格。

setTitleTextColor : 设置标题的文字颜色。

setSubtitle : 设置副标题文字。副标题在标题下方。

setSubtitleTextAppearance : 设置副标题的文字风格。

setSubtitleTextColor : 设置副标题的文字颜色。

setNavigationIcon : 设置导航图标。导航图标在工具栏图标左边。

setNavigationOnClickListener : 设置导航图标的点击监听器。

setOverflowIcon : 设置溢出菜单的按钮图标。

showOverflowMenu : 显示溢出菜单图标。

hideOverflowMenu : 隐藏溢出菜单图标。

dismissPopupMenus : 关闭已弹出的菜单。

SearchView

v7包在带来Toolbar的同时,也带来了一个加强版的SearchView。有关原SearchView的使用说明参见《Android开发笔记(二十)顶部导航栏》,新旧两个SearchView的用法其实大同小异,当然新版的功能会更强大些,下面是android.widget.SearchView与android.support.v7.widget.SearchView的主要区别:

二者在调用时的区别:

1、菜单布局文件中,旧SearchView的写法是android:actionViewClass="android.widget.SearchView",而新SearchView的写法是app:actionViewClass="android.support.v7.widget.SearchView"

2、代码中获取SearchView对象,新控件还可通过v7类MenuItemCompat的getActionView方法来获取。

SearchView searchView = (SearchView) MenuItemCompat.getActionView(menuItem);

二者在功能上的区别:

1、编辑框其实是个SearchAutoComplete控件,该控件在旧SearchView中是隐藏的,在新SearchView中是开放的,所以我们可随意修改v7编辑框的显示风格。

2、基于上一点,新控件可取到SearchAutoComplete的对象,因此我们可给该对象注册自动完成的字符串适配器,在用户输入文字时,界面会自动弹出符合搜索条件的关键词列表;

3、setAppSearchData方法在旧SearchView中是隐藏的,在新SearchView中是开放的,所以旧控件只能传递搜索文本给结果页面,而新控件允许传递其他的额外信息给搜索结果页面。

Toolbar运行问题处理集锦

更换导航栏还是存在一些兼容问题的,下面是博主发现的几个情况及其解决办法:

1、溢出菜单的菜单项已经设置为android:showAsAction="ifRoom",但即使工具栏上还有空间,该菜单项也不会显示在工具栏上。解决办法:

在菜单布局文件的menu根节点增加属性xmlns:app="http://schemas.android.com/apk/res-auto",然后把android:showAsAction="ifRoom"改为app:showAsAction="ifRoom"。

2、溢出菜单列表在菜单文字左侧显示图标的方法,使用ActionBar时正常,使用Toolbar时反而不会显示图标了。解决办法:

ActionBar的featureId是8,Toolbar的featureId是108,所以在图标显示方法内部,要同时判断这两个数值,而不能像以前那样仅仅判断Window.FEATURE_ACTION_BAR。修改之后的图标显示方法如下:

    //显示OverflowMenu的Icon
	public static void setOverflowIconVisible(int featureId, Menu menu) {
		//ActionBar的featureId是8,Toolbar的featureId是108
        if (featureId%100 == Window.FEATURE_ACTION_BAR && menu != null) {
            if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
                try {
                    Method m = menu.getClass().getDeclaredMethod(
                            "setOptionalIconsVisible", Boolean.TYPE);
                    m.setAccessible(true);
                    m.invoke(menu, true);
                } catch (Exception e) {
                    Log.d(TAG, e.getMessage());
                }
            }
        }
    }

3、代码中调用getActionView方法获取SearchView对象时,发现取到的SearchView为空。解决办法:

把菜单布局文件里的android:actionViewClass="android.support.v7.widget.SearchView"改为app:actionViewClass="android.support.v7.widget.SearchView"。

下面是新版Toolbar与SearchView的使用截图:

下面是新版Toolbar与SearchView的使用代码示例:

import java.util.Date;

import com.example.exmtoolbar.util.Utils;

import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity  {

	private final static String TAG = "MainActivity";
	private TextView tv_desc;
	private String[] mFormatArray = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd",
			"yyyy年MM月dd日HH时mm分ss秒", "yyyy年MM月dd日"};
	private String mFormat = mFormatArray[0];
	private Date mNowTime = new Date();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv_desc = (TextView) findViewById(R.id.tv_desc);

        Toolbar tl_head = (Toolbar) findViewById(R.id.tl_head);
        tl_head.setBackgroundResource(R.color.blue_light);
        tl_head.setLogo(R.drawable.ic_launcher);
        tl_head.setTitle("标题");
        tl_head.setSubtitle("副标题");
        tl_head.setNavigationIcon(R.drawable.ic_back);
        setSupportActionBar(tl_head);
	}

	private void initSearchView(Menu menu) {
		MenuItem menuItem = menu.findItem(R.id.menu_search);
		SearchView searchView = (SearchView) MenuItemCompat.getActionView(menuItem);
	    if(searchView == null){
	        Log.d(TAG, "Fail to get SearchView.");
	    } else {
	    	//新旧SearchView公用代码开始
	    	searchView.setIconifiedByDefault(true);
	    	searchView.setSubmitButtonEnabled(true);
	        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
	        ComponentName cn = new ComponentName(this, SearchResultActvity.class);
	        SearchableInfo info = searchManager.getSearchableInfo(cn);
	        if(info == null){
	            Log.d(TAG, "Fail to get SearchResultActvity.");
	        }
	        searchView.setSearchableInfo(info);
	    	//新旧SearchView公用代码结束

	        sac_text = (SearchView.SearchAutoComplete) searchView.findViewById(R.id.search_src_text);
	        sac_text.setTextColor(Color.WHITE);
	        sac_text.setHintTextColor(Color.WHITE);

			searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
				@Override
				public boolean onQueryTextSubmit(String query) {
					return false;
				}

				@Override
				public boolean onQueryTextChange(String newText) {
					doSearch(newText);
					return true;
				}
			});

	        Bundle bundle = new Bundle();
	        bundle.putString("hi", "hello");
	        searchView.setAppSearchData(bundle);
	    }
	}

	private SearchView.SearchAutoComplete sac_text;
	private String[] hintArray = {"ab", "abc", "abcde", "abHtp", "aaeet", "aab"};
	private void doSearch(String text) {
		if (text.indexOf("a") == 0) {
			ArrayAdapter<String> adapter = new ArrayAdapter<String>(
					this, R.layout.list_auto, hintArray);
			sac_text.setAdapter(adapter);
	        sac_text.setOnItemClickListener(new OnItemClickListener() {
				@Override
				public void onItemClick(AdapterView<?> parent, View view,
						int position, long id) {
					TextView tv_item = (TextView) view;
					sac_text.setText(tv_item.getText());
				}
	        });
		}
	}

    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
    	//显示菜单项左侧的图标
        Utils.setOverflowIconVisible(featureId, menu);
        return super.onMenuOpened(featureId, menu);
    }  

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		//对搜索框做初始化
		initSearchView(menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		if (id == android.R.id.home) {
			finish();
		} else if (id == R.id.menu_refresh) {
			mNowTime = new Date();
			tv_desc.setText("当前刷新时间: "+Utils.getFormatDateTime(mNowTime, mFormat));
			return true;
		} else if (id == R.id.menu_about) {
			Toast.makeText(this, "这个是工具栏的演示demo", Toast.LENGTH_LONG).show();
			return true;
		} else if (id == R.id.menu_quit) {
			finish();
		}
		return super.onOptionsItemSelected(item);
	}

}

点此查看Android开发笔记的完整目录

时间: 2024-10-05 04:28:13

Android开发笔记(一百一十九)工具栏ToolBar的相关文章

Android开发笔记(八十八)同步与加锁

同步synchronized 同步方法 synchronized可用来给方法或者代码块加锁,当它修饰一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.这就意味着,当两个并发线程同时访问synchronized代码块时,两个线程只能是排队做串行处理,另一个线程要等待前一个线程执行完该代码块后,才能再次执行synchronized代码块. 使用synchronized修饰某个方法,该方法便成为一个同步方法,在同一时刻只能有一个线程执行该方法.可是,synchronized的锁机制太

Android开发笔记(一百零九)利用网盘实现云存储

网盘存储 个人开发者往往没有自己的后台服务器,但同时又想获取app的运行信息,这就要借助于第三方的网络存储(也叫网盘.云盘.微盘等等).通过让app自动在网盘上存取文件,可以间接实现后台服务器的存储功能,同时开发者也能及时找到app的用户信息. 曾几何时,各大公司纷纷推出免费的个人网盘服务,还开放了文件管理api给开发者调用,一时间涌现了网盘提供商的八大金刚:百度网盘.阿里云.华为网盘.腾讯微云.新浪微盘.360云盘.金山快盘.115网盘.可是好景不长,出于盈利.监管等等因素,各大网盘开放平台要

Android开发系列(二十九):使用ContextMenu创建上下文菜单

在上一篇我们介绍了创建选项菜单,这里我们介绍下创建上下文菜单. 上下文菜单就是通过长按某一段文字,然后出来相应的菜单.就比如贴吧,当你长安某一楼层,就会弹出来一个"复制"这一楼层文字的菜单,点一下"复制",这段文字就保存在你手机的临时存储的地方了,可以在别的地方黏贴. 开发上下文菜单的步骤: 1.重写Activity的onCreateContextMenu(ContextMenu menu,View source,ContextMenu.Context MenuIn

Android开发笔记(一百一十八)自定义悬浮窗

WindowManager 在前面<Android开发笔记(六十六)自定义对话框>中,我们提到每个页面都是一个Window窗口,许多的Window对象需要一个管家来打理,这个管家我们称之为WindowManager窗口管理.在手机屏幕上新增或删除页面窗口,都可以归结为WindowManager的操作,下面是该管理类的常用方法说明: getDefaultDisplay : 获取默认的显示屏信息.通常用该方法获取屏幕分辨率,详情参见<Android开发笔记(三)屏幕分辨率>. addV

Android开发笔记(一百一十一)聊天室中的Socket通信

Socket通信 基本概念 对于程序开发来说,网络通信的基础就是Socket,但因为是基础,所以用起来不容易,今天我们就来谈谈Socket通信.计算机网络有个大名鼎鼎的TCP/IP协议,普通用户在电脑上设置本地连接的ip时,便经常看到下图的弹窗,注意红框部分已经很好地描述了TCP/IP协议的作用. TCP/IP是个协议组,它分为三个层次:网络层.传输层和应用层: 网络层包括:IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 传输层包括:TCP协议.UDP协议. 应用层包括:HT

【转】Android开发笔记(序)写在前面的目录

原文:http://blog.csdn.net/aqi00/article/details/50012511 知识点分类 一方面写写自己走过的弯路掉进去的坑,避免以后再犯:另一方面希望通过分享自己的经验教训,与网友互相切磋,从而去芜存菁进一步提升自己的水平.因此博主就想,入门的东西咱就不写了,人不能老停留在入门上:其次是想拾缺补漏,写写虽然小众却又用得着的东西:另外就是想以实用为主,不求大而全,但求小而精:还有就是有的知识点是java的,只是Android开发也会经常遇上,所以蛮记下来.个人的经

Android开发笔记(一百零八)智能语音

智能语音技术 如今越来越多的app用到了语音播报功能,例如地图导航.天气预报.文字阅读.口语训练等等.语音技术主要分两块,一块是语音转文字,即语音识别:另一块是文字转语音,即语音合成. 对中文来说,和语音播报相关的一个技术是汉字转拼音,想想看,拼音本身就是音节拼读的标记,每个音节对应一段音频,那么一句的拼音便能用一连串的音频流合成而来.汉字转拼音的说明参见<Android开发笔记(八十三)多语言支持>. 语音合成通常也简称为TTS,即TextToSpeech(从文本到语言).语音合成技术把文字

Android开发笔记(一百零六)支付缴费SDK

第三方支付 第三方支付指的是第三方平台与各银行签约,在买方与卖方之间实现中介担保,从而增强了支付交易的安全性.国内常用的支付平台主要是支付宝和微信支付,其中支付宝的市场份额为71.5%,微信支付的市场份额为15.99%,也就是说这两家垄断了八分之七的支付市场(2015年数据).除此之外,还有几个app开发会用到的支付平台,包括:银联支付,主要用于公共事业缴费,如水电煤.有线电视.移动电信等等的充值:易宝支付,主要用于各种报名考试的缴费,特别是公务员与事业单位招考:快钱,被万达收购,主要用于航空旅

Android开发笔记(一百一十三)测试工具

单元测试TestCase Android的sdk提供了对项目进行单元测试的功能,开发包的android.test下面便是专门用来单元测试的类.单元测试的作用是通过模拟文本输入和手势输入(如点击操作),从而让app自动执行一系列的操作,这样就能够检查程序是否运行正常. 下面是搭建测试工程的具体步骤: 1.首先当然你得有一个待测试的app工程,最简单的如带有一个编辑框的Hello World工程: 2.其次在ADT中创建测试工程,操作步骤为依次选择菜单"File"--"New&q