Android开发笔记(二十)顶部导航栏ActionBar

时间:2022-10-11 03:24:09

标题栏ActionBar

ActionBar是在Android3.0之后引入的,所以Android2.x之前的版本不能直接使用ActionBar。现在ActionBar广泛用做APP的顶部导航栏,它在布局上主要分为三部分:左边是返回区域,包括logo、返回箭头、左侧标题等等;右边是菜单区域,放的是溢出菜单OverflowMenu的各菜单项;中间是条件区域,包括导航项Navigation(下拉列表与标签切换)、搜索框SearchView,以及可自定义的定制视图CustomView。


显示或者关闭ActionBar的方式有如下几种:
1、在AndroidManifest.xml中给activity设置无标题栏的主题,就关闭ActionBar。否则就显示
<activity android:theme="@android:style/Theme.Holo.NoActionBar"> 
2、在styles.xml的当前主题中加入一个项android:windowNoTitle,为true时表示关闭ActionBar;为false表示显示
<item name="android:windowNoTitle">true</item>
3、在Activity代码的setContentView之前加入下面代码,就关闭ActionBar。否则就显示
requestWindowFeature(Window.FEATURE_NO_TITLE);
4、在Activity代码中获得ActionBar实例,调用该实例的hide方法表示关闭ActionBar;调用show方法表示显示
ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.hide(); //隐藏ActionBar
actionBar.show(); //显示ActionBar
}


ActionBar的基本设置

ActionBar其上各控件的开关设置说明如下:
setDisplayUseLogoEnabled : 是否在左侧返回区域显示logo,默认显示
setDisplayHomeAsUpEnabled : 是否在左侧返回区域显示返回箭头,默认不显示
setDisplayShowTitleEnabled : 是否在左侧返回区域显示左侧标题,默认显示APP名称
setTitle : 设置左侧标题的文本
setBackgroundDrawable : 设置ActionBar的背景图像
setDisplayShowCustomEnabled : 是否在中间条件区域显示定制视图
setNavigationMode : 设置导航项的样式,NAVIGATION_MODE_STANDARD表示不显示,NAVIGATION_MODE_LIST表示显示下拉列表,NAVIGATION_MODE_TABS表示显示标签切换。默认不显示


setDisplayOptions : 设置显示的选项参数,以“|”连接,各参数与设置函数的对应关系如下:
DISPLAY_USE_LOGO : 对应setDisplayUseLogoEnabled
DISPLAY_SHOW_HOME : 无对应函数,该参数表示需要显示左侧返回区域
DISPLAY_HOME_AS_UP : 对应setDisplayHomeAsUpEnabled
DISPLAY_SHOW_TITLE : 对应setDisplayShowTitleEnabled
DISPLAY_SHOW_CUSTOM : 对应setDisplayShowCustomEnabled


溢出菜单OverflowMenu

OverflowMenu其实就是把选项菜单OptionsMenu搬到了页面右上方,具体使用方法与Menu是一样的,所以就不多说了。下面列一下几个注意点:
1、菜单项的布局定义中,要把showAsAction属性设置好。该属性的取值类型主要有:
ifRoom : 如果ActionBar右侧有空间,则该项直接显示在ActionBar上面,不再放入溢出菜单。
never : 从不在ActionBar上直接显示,一直放在溢出菜单里面。
always : 总是在ActionBar上显示。
withText : 如果能在ActionBar上显示,则除了显示该项的图标,还要显示该项的文字说明。
collapseActionView : 不常用,而且比较麻烦,若有兴趣可自行查阅资料。
2、Android手机一般都有物理按键,按下物理按键的菜单键,有的手机在顶部显示选项菜单而不是在右上角显示,有的手机干脆不显示任何菜单(常见于Android4.2.2以下系统)。所以为了兼容不同手机不同系统,我们需要对溢出菜单做特殊处理,将物理按键加以屏蔽,强制显示OverflowMenu。
3、放入溢出菜单的菜单项,Android默认不在菜单文字左侧显示图标,就算在菜单布局文件中设置了android:icon也不管用。所以要想在菜单列表中显示左侧图标,需要调用MenuBuilder的setOptionalIconsVisible方法(MenuBuilder在Android内核中,未开放出来,只能通过反射机制来调用)。




导航项Navigation

使用导航项需要在ActionBar中将其设置为具体模式(setNavigationMode),目前ActionBar支持两种导航模式:
1、NAVIGATION_MODE_LIST: 表示采用下拉列表模式;
2、NAVIGATION_MODE_TABS: 表示采用标签切换模式;
下拉列表模式的使用方法类似Spinner,也要设置列表文本的ArrayAdapter与监听器。不同的是Spinner的监听器继承自OnItemSelectedListener,而Navigation的监听器继承自OnNavigationListener。
标签切换模式在实际开发中用得不多,类似效果一般采用底部标签栏或者ViewPager实现。


定制视图CustomView

定制视图用于在ActionBar上显示一些个性化内容,比如说,ActionBar自带的标题文字位于左侧区域,而且也不能调整文字大小、颜色等等,如果我们想把标题文本挪到中间,还要设置文字样式的话,就得使用定制视图了。
使用定制视图需要在ActionBar中将其设置为可用(setDisplayShowCustomEnabled),同时要通过setDisplayOptions来设置DISPLAY_SHOW_CUSTOM。
定制视图的布局与普通布局一样,都在layout目录下,从布局文件中获取并修改完成视图后,调用ActionBar的setCustomView方法就完成了定制。
另外,更换左侧返回区域的返回箭头图标,可通过ActionBar的setIcon来实现。但该方法在Android4.4.2之后才支持,之前版本的系统仍然不支持定制左侧返回图标。


搜索框SearchView

搜索框有些复杂,实现步骤大致如下:
1、在菜单布局文件中定义搜索项:
    <item
android:id="@+id/menu_search"
android:orderInCategory="1"
android:icon="@drawable/ic_search"
android:showAsAction="ifRoom"
android:title="搜索"
android:actionViewClass="android.widget.SearchView" />

2、在res\xml目录下新建searchable.xml,设置搜索框的样式:
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/please_input"
android:inputType="text"
android:searchButtonText="@string/search" />

3、在AndroidManifest.xml中加入一个搜索结果activity的定义,需要指定action和meta-data,例如:
        <activity android:name=".SearchResultActvity" android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.SEARCH"/>
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
</activity>

4、在菜单代码中初始化搜索框,并关联搜索动作对应的activity(本例中的activity是SearchResultActvity)
private void initSearchView(Menu menu) {
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
if(searchView == null){
Log.d(TAG, "Fail to get SearchView.");
} else {
//设置搜索框默认自动缩小为图标
searchView.setIconifiedByDefault(true);
//设置是否显示搜索按钮。搜索按钮只显示一个箭头图标,Android暂不支持显示文本。
//查看Android源码,搜索按钮用的控件是ImageView,所以。。。
searchView.setSubmitButtonEnabled(true);
//设置搜索框内的默认显示的提示文本
//searchView.setQueryHint(getResources().getString(R.string.please_input));

SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
//关联搜索结果的activity
ComponentName cn = new ComponentName(this, SearchResultActvity.class);
//从activity中获取相关搜索信息,就是searchable的xml设置
SearchableInfo info = searchManager.getSearchableInfo(cn);
if(info == null){
Log.d(TAG, "Fail to get SearchResultActvity.");
}
//将activity的搜索信息与search view关联
searchView.setSearchableInfo(info);
}
}

5、编写搜索结果activity的代码,其中提取搜索关键字的代码片段如下:
        if(intent == null) {
return;
} else {
//如果是通过ACTION_SEARCH来调用,即如果通过搜索调用
if(Intent.ACTION_SEARCH.equals(intent.getAction())){
//获取搜索内容
String queryString = intent.getStringExtra(SearchManager.QUERY);
tv_search_result.setText("您输入的搜索文字是:"+queryString);
}
}


代码例子

下面是几个导航栏效果的代码例子
原生导航代码,包括溢出菜单和导航项
import java.util.Date;

import com.example.exmactionbar.util.Utils;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class OriginActivity extends Activity {
private static final String TAG = "OriginActivity";
private TextView tv_origin;
private String[] mDescArray = {"标点时间", "标点日期", "中文时间", "中文日期"};
private String[] mmFormatArray = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd",
"yyyy年MM月dd日HH时mm分ss秒", "yyyy年MM月dd日"};
private String mFormat = mmFormatArray[0];
private Date mNowTime = new Date();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_origin);
tv_origin = (TextView) findViewById(R.id.tv_origin);
getActionBar("原生页", R.drawable.actionbar_gradient_blue).show();
}

@SuppressLint("InflateParams")
private ActionBar getActionBar(String title, int bgId) {
ActionBar actionBar = this.getActionBar();
if (actionBar != null) {
//无论系统版本为何,无论有无物理按键,都强制显示选项菜单
Utils.forceShowOverflowMenu(this);
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(false);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE|ActionBar.DISPLAY_HOME_AS_UP);
if (bgId > 0) {
actionBar.setBackgroundDrawable(getResources().getDrawable(bgId));
}
actionBar.setTitle(title);
//允许在导航栏上显示下拉框,另一种NAVIGATION_MODE_TABS表示标签页切换
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this, R.layout.spinner_item, mDescArray);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
actionBar.setListNavigationCallbacks(adapter, new navigationListener());
} else {
Toast.makeText(this, "无法获得ActionBar", Toast.LENGTH_SHORT).show();
}
return actionBar;
}

class navigationListener implements OnNavigationListener {
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
mFormat = mmFormatArray[itemPosition];
tv_origin.setText("当前格式时间: "+Utils.getFormatDateTime(mNowTime, mFormat));
return false;
}
}

@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);
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_origin.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);
}

}



定制导航代码,包括溢出菜单和定制视图
import com.example.exmactionbar.util.Utils;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class CustomActivity extends Activity {
private static final String TAG = "CustomActivity";
private TextView tv_custom;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom);
tv_custom = (TextView) findViewById(R.id.tv_custom);
getActionBar("定制标题", R.drawable.actionbar_gradient_red).show();
}

@SuppressLint("InflateParams")
private ActionBar getActionBar(String title, int bgId) {
ActionBar actionBar = this.getActionBar();
if (actionBar != null) {
//无论系统版本为何,无论有无物理按键,都强制显示选项菜单
Utils.forceShowOverflowMenu(this);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
actionBar.setDisplayShowCustomEnabled(true);
if (bgId > 0) {
actionBar.setBackgroundDrawable(getResources().getDrawable(bgId));
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
actionBar.setIcon(R.drawable.back_btn);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM|ActionBar.DISPLAY_SHOW_HOME);
} else {
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM|ActionBar.DISPLAY_SHOW_HOME|ActionBar.DISPLAY_HOME_AS_UP);
}
ActionBar.LayoutParams lp =new ActionBar.LayoutParams(
ActionBar.LayoutParams.MATCH_PARENT,
ActionBar.LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
View v_titlebar = this.getLayoutInflater().inflate(R.layout.action_bar_title, null);
actionBar.setCustomView(v_titlebar, lp);
TextView tv_title = (TextView) v_titlebar.findViewById(R.id.tv_title);
tv_title.setText(title);
} else {
Toast.makeText(this, "无法获得ActionBar", Toast.LENGTH_SHORT).show();
}

return actionBar;
}

@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);
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) {
tv_custom.setText("当前刷新时间: "+Utils.getNowDateTime("yyyy-MM-dd HH:mm:ss"));
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);
}
}



搜索导航代码,包括溢出菜单和搜索框
import com.example.exmactionbar.util.Utils;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;

public class SearchActivity extends Activity {
private static final String TAG = "SearchActivity";
private TextView tv_search;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
tv_search = (TextView) findViewById(R.id.tv_search);
getActionBar(this, "搜索页", R.drawable.actionbar_gradient_blue).show();
}

@SuppressLint("InflateParams")
public static ActionBar getActionBar(Activity act, String title, int bgId) {
ActionBar actionBar = act.getActionBar();
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(true);
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(false);
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE|ActionBar.DISPLAY_HOME_AS_UP);
if (bgId > 0) {
actionBar.setBackgroundDrawable(act.getResources().getDrawable(bgId));
}
actionBar.setTitle(title);
} else {
Toast.makeText(act, "无法获得ActionBar", Toast.LENGTH_SHORT).show();
}
return actionBar;
}

private void initSearchView(Menu menu) {
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
if(searchView == null){
Log.d(TAG, "Fail to get SearchView.");
} else {
//设置搜索框默认自动缩小为图标
searchView.setIconifiedByDefault(true);
//设置是否显示搜索按钮。搜索按钮只显示一个箭头图标,Android暂不支持显示文本。
//查看Android源码,搜索按钮用的控件是ImageView,所以。。。
searchView.setSubmitButtonEnabled(true);
//设置搜索框内的默认显示的提示文本
//searchView.setQueryHint(getResources().getString(R.string.please_input));

SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
//关联搜索结果的activity
ComponentName cn = new ComponentName(this, SearchResultActvity.class);
//从activity中获取相关搜索信息,就是searchable的xml设置
SearchableInfo info = searchManager.getSearchableInfo(cn);
if(info == null){
Log.d(TAG, "Fail to get SearchResultActvity.");
}
//将activity的搜索信息与search view关联
searchView.setSearchableInfo(info);
}
}

@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.search, 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_about) {
Toast.makeText(this, "这个是顶部导航栏的演示demo", Toast.LENGTH_LONG).show();
return true;
} else if (id == R.id.menu_quit) {
finish();
}
return super.onOptionsItemSelected(item);
}
}



下面是顶部导航栏三种方式的效果图
原生导航
Android开发笔记(二十)顶部导航栏ActionBar

定制导航
Android开发笔记(二十)顶部导航栏ActionBar

搜索导航
Android开发笔记(二十)顶部导航栏ActionBar


点击下载本文用到的顶部导航栏三种方式的代码



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