控件 -- ActionBar

一、概念

在Android 3.0中,ActionBar是一个非常重要的交互元素,ActionBar取代了传统的标题栏,在程序运行中一直置于顶部,对于Android平板设备来说,屏幕更大,标题栏使用ActionBar来设计可以展示更多丰富的内容,方便操控。

二、使用

1.添加ActionBar

在Android 3.0及更高的版本中,Activity中都默认包含有ActionBar。

2.取消ActionBar

manifest设置:

<activity android:theme="@android:style/Theme.NoTitleBar">

代码设置:

ActionBar actionBar = getActionBar();  
actionBar.hide();  //隐藏

actionBar.show();  //显示

如果使用一个主题(theme)来移除Activity上的ActionBar,那么窗口将不再会有ActionBar,因此在运行时也就没有办法来添加ActionBar,调用getActionBar()方法会返回null值。

3.添加Action按钮

ActionBar可以根据应用程序当前的功能来提供与其相关的Action按钮,这些按钮都会以图标或文字的形式直接显示在ActionBar上。当然,如果按钮过多,ActionBar上显示不完,多出的一些按钮可以隐藏在overflow里面(最右边的三个点就是overflow按钮),点击一下overflow按钮就可以看到全部的Action按钮了。
(1)定义menu资源

//menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/user_p"
        android:icon="@drawable/icon_user_p"
        android:showAsAction="always"
        android:title="用户"/>
    <item
        android:id="@+id/write_p"
        android:icon="@drawable/icon_write_p"
        android:showAsAction="always"
        android:title="发布"/>
    <item
        android:id="@+id/favo_p"
        android:icon="@drawable/icon_favo_p"
        android:showAsAction="never"
        android:title="收藏"/>

</menu>

这里我们通过<item>标签定义了三个Action按钮。
<item>标签中又有一些属性,其中:
id:是该Action按钮的唯一标识符;
icon:用于指定该按钮的图标;
title:用于指定该按钮可能显示的文字(在图标能显示的情况下,通常不会显示文字);
actionViewClass:用于指定一个构建视窗所使用的布局资源;
showAsAction:用于指定按钮显示的位置。

showAsAction主要有以下几种值可选:
ifRoom:会显示在Item中,但是如果已经有4个或者4个以上的Item时会隐藏在溢出列表中,当然个数并不仅仅局限于4个,依据屏幕的宽窄而定。
never:永远不会显示,只会在溢出列表中显示,而且只显示标题,所以在定义item的时候,最好把标题都带上。
always:无论是否溢出,总会显示。
withText:示意ActionBar要显示文本标题,ActionBar会尽可能显示这个标题,但是如果图标有效并且受到ActionBar空间的限制,文本标题有可能显示不全。
collapseActionView:声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开,否则这个操作视窗在默认的情况下是可见的,并且即便在用于不适用的时候,也要占据操作栏的有效空间,一般要配合ifRoom一起使用才会有效果。

title中的内容通常情况下只会在overflow中显示出来,ActionBar中由于屏幕空间有限,默认是不会显示title内容的。但是出于以下几种因素考虑,即使title中的内容无法显示出来,我们也应该给每个item中都指定一个title属性:
●当ActionBar中的剩余空间不足的时候,如果Action按钮指定的showAsAction属性是ifRoom的话,该Action按钮就会出现在overflow当中,此时就只有title能够显示了。
●如果Action按钮在ActionBar中显示,用户可能通过长按该Action按钮的方式来查看到title的内容。

(2)重写Activity的onCreateOptionsMenu()方法
当Activity启动的时候,系统会调用Activity的onCreateOptionsMenu()方法来取出所有的Action按钮,我们只需要在这个方法中去加载一个menu资源,并把所有的Action按钮都定义在资源文件里面就可以了。

@Override 
public boolean onCreateOptionsMenu(Menu menu) {  
    MenuInflater inflater = getMenuInflater();  
    inflater.inflate(R.menu.menu_main, menu);  
    return super.onCreateOptionsMenu(menu);  
}  

(3)重写Activity的onOptionsItemSelected()方法

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {  
        case R.id.user_p:  
            Toast.makeText(this, "你点击了“用户”按键!", Toast.LENGTH_SHORT).show();  
            return true;  
        case R.id.write_p:  
            Toast.makeText(this, "你点击了“发布”按键!", Toast.LENGTH_SHORT).show();  
            return true;  
        case R.id.favo_p:  
            Toast.makeText(this, "你点击了“收藏”按键!", Toast.LENGTH_SHORT).show();  
            return true;  
        default:  
            return super.onOptionsItemSelected(item);  
    }  
}

4.通过ActionBar图标进行导航

ActionBar actionBar = getActionBar();  
actionBar.setDisplayHomeAsUpEnabled(true); 

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) { 
        case android.R.id.home:  
            finish();
            return true; 
        ...
    }
}

5.添加ActionView

ActionView是一种可以在ActionBar中替换Action按钮的控件,它可以允许用户在不切换界面的情况下通过ActionBar完成一些较为丰富的操作。比如说,你需要完成一个搜索功能,就可以将SeachView这个控件添加到ActionBar中。
(1)定义menu资源
为了声明一个ActionView,我们可以在menu资源中通过actionViewClass属性来指定一个控件:

<item
    android:id="@+id/action_search"
    android:actionViewClass="android.widget.SearchView"
    android:showAsAction="always"
    android:title="搜索"/>

(2)重写Activity的onCreateOptionsMenu()方法

@Override 
public boolean onCreateOptionsMenu(Menu menu) {  
    MenuInflater inflater = getMenuInflater();  
    inflater.inflate(R.menu.main, menu);  
    MenuItem searchItem = menu.findItem(R.id.action_search);  
    SearchView searchView = (SearchView) searchItem.getActionView();  
    // 配置SearchView的属性  
    ...
    return super.onCreateOptionsMenu(menu);  
}  

有些程序可能还希望在ActionView展开和合并的时候显示不同的界面,其实我们只需要去注册一个ActionView的监听器就能实现这样的功能:

@Override 
public boolean onCreateOptionsMenu(Menu menu) {  
    MenuInflater inflater = getMenuInflater();  
    inflater.inflate(R.menu.main, menu);  
    MenuItem searchItem = menu.findItem(R.id.action_search);  
    searchItem.setOnActionExpandListener(new OnActionExpandListener() {  
        @Override 
        public boolean onMenuItemActionExpand(MenuItem item) {  
            Log.d("TAG", "on expand");  
            return true;  
        }  
          
        @Override 
        public boolean onMenuItemActionCollapse(MenuItem item) {  
            Log.d("TAG", "on collapse");  
            return true;  
        }  
    });  
    return super.onCreateOptionsMenu(menu);  
}  

6.添加ActionProvider

和ActionView有点类似,ActionProvider也可以将一个Action按钮替换成一个自定义的布局。但不同的是,ActionProvider能够完全控制事件的所有行为,并且还可以在点击的时候显示子菜单。
(1)定义menu资源
为了添加一个ActionProvider,我们需要在<item>标签中指定一个actionViewClass属性,在里面填入ActionProvider的完整类名。我们可以通过继承ActionProvider类的方式来创建一个自己的ActionProvider,同时Android也提供好了几个内置的ActionProvider,比如说ShareActionProvider。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item 
        android:id="@+id/action_share" 
        android:actionProviderClass="android.widget.ShareActionProvider" 
        android:showAsAction="ifRoom" 
        android:title="分享" /> 
    ...
</menu>

(2)重写Activity的onCreateOptionsMenu()方法

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu_main, menu);
    MenuItem shareItem = menu.findItem(R.id.action_share);
    ShareActionProvider provider = (ShareActionProvider) shareItem
        .getActionProvider();
    provider.setShareIntent(getDefaultIntent());
    return super.onCreateOptionsMenu(menu);
}

private Intent getDefaultIntent() {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/*");
    return intent;
}

这里我们通过getDefaultIntent()方法来构建了一个Intent,该Intent会将所有可以共享图片的程序都列出来。

这个ShareActionProvider点击之后是可以展开的,有点类似于overflow的效果,这就是ActionProvider的子菜单。

7.添加导航Tabs

Tabs的应用可以算是非常广泛了,它可以使得用户非常轻松地在应用程序中切换不同的视图。而Android官方更加推荐使用ActionBar中提供的Tabs功能,因为它更加的智能,可以自动适配各种屏幕的大小。比如说,在平板上屏幕的空间非常充足,Tabs会和Action按钮在同一行显示,而如果是在手机上,屏幕的空间不够大的话,Tabs和Action按钮则会分为两行显示。

添加步骤:
(1)实现ActionBar.TabListener接口,这个接口提供了Tab事件的各种回调,比如当用户点击了一个Tab时,可以进行切换Tab的操作;
(2)为每一个想添加的Tab创建一个ActionBar.Tab的实例,并且调用setTabListener()方法来设置ActionBar.TabListener。除此之外,还需要调用setText()方法来给当前Tab设置标题;
(3)最后调用ActionBar的addTab()方法将创建好的Tab添加到ActionBar中。

//TabListener
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;

public class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    /** Constructor used each time a new tab is created.
     * @param activity  The host Activity, used to instantiate the fragment
     * @param tag  The identifier tag for the fragment
     * @param clz  The fragment's Class, used to instantiate the fragment
     */
    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
        // Check if the fragment is already initialized
        if (mFragment == null) {
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}

//Fragment1,Fragment2、Fragment3类似
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment1, container, false);
    }
}

//fragment1.xml,fragment2.xml、fragment3.xml类似
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Fragment1" />
</LinearLayout>

//MainActivity
private void initView() {
    // 提示getActionBar方法一定在setContentView后面
    ActionBar actionBar = getActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    // 添加Tab选项
    ActionBar.Tab tab = actionBar
            .newTab()
            .setText("澳门风云2")
            .setTabListener(
                    new TabListener<Fragment1>(this, "film1",
                            Fragment1.class));
    actionBar.addTab(tab);

    tab = actionBar
            .newTab()
            .setText("五十度灰")
            .setTabListener(
                    new TabListener<Fragment2>(this, "film2",
                            Fragment2.class));
    actionBar.addTab(tab);

    tab = actionBar
            .newTab()
            .setText("爸爸去哪儿2")
            .setTabListener(
                    new TabListener<Fragment3>(this, "film3",
                            Fragment3.class));
    actionBar.addTab(tab);
}

8.添加下拉列表导航

作为Activity内部的另一种导航(或过滤)模式,操作栏提供了内置的下拉列表。下拉列表能够提供Activity中内容的不同排序模式。

添加步骤:
(1)创建一个给下拉提供可选项目的列表,以及描画列表项目时所使用的布局;
(2)实现ActionBar.OnNavigationListener回调,在这个回调中定义当用户选择列表中一个项目时所发生的行为;
(3)用setNavigationMode()方法该操作栏启用导航模式;
(4)用setListNavigationCallbacks()方法给下拉列表设置回调方法。

//strings.xml
<string-array name="action_list">
    <item>Fragment1</item>
    <item>Fragment2</item>
    <item>Fragment3</item>
</string-array>

//MainActivity
private ActionBar.OnNavigationListener mOnNavigationListener;
private String[] arry_list;
private void initView() {
    ActionBar actionBar = getActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    // //导航模式必须设为NAVIGATION_MODE_LIST
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

    // 定义一个下拉列表数据适配器
    SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
            R.array.action_list,
            android.R.layout.simple_spinner_dropdown_item);
    arry_list = getResources().getStringArray(R.array.action_list);
    mOnNavigationListener = new ActionBar.OnNavigationListener() {

        @Override
        public boolean onNavigationItemSelected(int position, long itemId) {
            Fragment newFragment = null;
            switch (position) {
                case 0:
                    newFragment = new Fragment1();
                    break;
                case 1:
                    newFragment = new Fragment2();
                    break;
                case 2:
                    newFragment = new Fragment3();
                    break;
                default:
                    break;
            }
            getFragmentManager()
                    .beginTransaction()
                    .replace(R.id.container, newFragment,
                            arry_list[position]).commit();
            return true;
        }
    };
    actionBar.setListNavigationCallbacks(mSpinnerAdapter,
            mOnNavigationListener);
}

9.自定义ActionBar样式

(1)使用主题
可以将主题应用到整个应用程序,也可以只应用于某个Activity。通过在AndroidManifest.xml文件中给<application>或<activity>标签指定android:theme属性就可以实现了。如果只想让ActionBar使用深色系的主题,而Activity的内容部分仍然使用浅色系的主题,可以通过声明以下主题来实现:

android:theme="@android:style/Theme.Holo.Light.DarkActionBar"

(2)自定义背景
如果想要修改ActionBar的背景,我们可以通过创建一个自定义主题并重写actionBarStyle属性来实现。这个属性可以指向另外一个样式,然后我们在这个样式中重写background这个属性就可以指定一个drawable资源或颜色,从而实现自定义背景的功能。

<style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
    <item name="android:actionBarStyle">@style/MyActionBar</item>
</style>

<style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
    <item name="android:background">#f4842d</item> //修改ActionBar的背景色
    <item name="android:backgroundStacked">#d27026</item> //修改Tabs的背景色
</style>

(3)自定义文字颜色

<style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">
    <item name="android:actionBarStyle">@style/MyActionBar</item>
    <item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item>
</style>

<style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">
    ...
    <item name="android:titleTextStyle">@style/MyActionBarTitleText</item>
</style>


<style name="MyActionBarTitleText" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
    <item name="android:textColor">#fff</item> //修改ActionBar的标题文字颜色
</style>

<style name="MyActionBarTabText" parent="@android:style/Widget.Holo.ActionBar.TabText">
    <item name="android:textColor">#fff</item> //修改Tabs的标题文字颜色
</style>

(4)自定义Tab Indicator
为了可以明确分辨出我们当前选中的是哪一个Tab项,通常情况下都会在选中Tab的下面加上一条横线作为标识,这被称作Tab Indicator。如果要自定义Tab Indicator,首先我们需要重写actionBarTabStyle这个属性,将它指向一个新建的Tab样式,然后重写background这个属性即可。需要注意的是,background必须要指定一个state-list drawable文件,这样在各种不同状态下才能显示出不同的效果。

步骤1:准备四张图片,分别用于表示Tab的四种状态:选中未按下、选中且按下、未选中未按下、未选中且按下,并创建state-list drawable文件。

//actionbar_tab_indicator.xml
<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
 
    <item  android:state_selected="false" 
          android:state_pressed="false" 
          android:drawable="@drawable/tab_unselected" /> 
    <item android:state_selected="true" 
          android:state_pressed="false" 
          android:drawable="@drawable/tab_selected" /> 
    <item android:state_selected="false" 
          android:state_pressed="true" 
          android:drawable="@drawable/tab_unselected_pressed" /> 
    <item android:state_selected="true" 
          android:state_pressed="true" 
          android:drawable="@drawable/tab_selected_pressed" /> 
 
</selector> 

步骤2:修改style.xml文件。

<resources> 
 
    <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> 
        ......  
        <item name="android:actionBarTabStyle">@style/MyActionBarTabs</item> 
    </style> 
      
    <style name="MyActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView"> 
        <item name="android:background">@drawable/actionbar_tab_indicator</item> 
    </style> 
 
</resources> 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容