Action Bar

ActionBar 是标识用户位置的窗口功能,并提供用户操作和导航模式。在应用程序中使用ActionBar能够优雅的适应不同屏幕配置,为你的用户提供一个熟悉的界面。

Action Bar_第1张图片

注:ActionBar包括了[1]app icon,[2]two action items,和[3]action overflow。

Actionbar提供几个关键功能:
1.提供了一个专门的空间给你的应用程序一个标识和指示用户在应用程序中的位置。
2.以一个可预测的方式来突出和访问重要的动作行为。
3.在应用程序中支持导航和视图切换一致性。

想要了解更多的关于ActionBar的交互模式和设计指南,请查看ActionBar的设计指导。

ActionBar APIs 第一次是被增加到 Android API 11中,但是使用支持库中的ActionBar时,它们也能够兼容在 Android API 7 和以上中。
这份指导主要是关注怎样使用支持库中的ActionBar,但是如果你的应用程序仅仅支持Android 3.0 或以上时,你应该使用framework中的ActionBar APIs。
注意:确定从适当的包中导入的ActionBar类。
           如果支持API 11 以下的:
            import android.support.v7.app.ActionBar
            如果仅仅支持API 11 以上的:             
            import android.app.ActionBar

1.增加ActionBar

如上所述,这份指导关注的是怎样使用支持库中的ActionBar APIs。所以在你添加ActionBar之前,你必须设置你的项目appcompat v7 支持库。一旦你的工程中设置好了支持库,下面就是告诉你如何添加ActionBar:

1.新建一个Activity并继承ActionBarActivity。
2.在你的Activity中使用或者继承Theme.AppCompat 主题。
<activity android:theme="@style/Theme.AppCompat.Light" ... >

现在你的Activity运行在 API 7 或以上时就会包含了ActionBar。
 在API 11 或 以上
使用主题Theme.Holo作为默认主题,并且targetSdkVersion 和 minSdkVersion 属性设置为11 或者更高后,这时ActionBar会被包含在所有的Activity中。如果你不想在Activity中显示ActionBar,设置Activity的主题为
Theme.Holo.NoActionBar .

1.1 移除ActionBar

在运行时,通过调用hide() 方法,你可以隐藏ActionBar,例如:
ActionBar actionBar = getSupportActionBar();
actionBar.hide();

在API 11 或 以上
通过getActionBar() 方法获得ActionBar。

当ActionBar隐藏后,系统调整你的布局来填充现在可用的屏幕空间。你可以调用show()方式,让ActionBar重新显示。
意识到隐藏和移除ActionBar后,它会导致你的Activity重新布局,因为它会计算ActionBar所占用的空间。如果你的Activity经常需要隐藏和显示ActionBar,你可能希望启用覆盖模式。覆盖模式在你的Activity布局前面绘制出ActionBar,遮蔽顶端。这样,当ActionBar隐藏或重现的时候,你的布局仍然保持固定不变。在启用覆盖模式时,你需要为你的Activity创建一个自定义主题,然后设置   windowActionBarOverlay  为 true。想了解更过信息,查看下面的 设置ActionBar的样式。

1.2 使用Logo 替代 icon

通常系统会默认使用你的应用程序icon在ActionBar上,这个icon就是被指定在<application>或<activity>元素的icon属性上。然而,如果你还指定了logo属性,则ActionBar就会使用logo 图片。 一个logo图片通常要比icon图片更宽,但是不应该包含不必要的文字。

2.增加动作条目

ActionBar 为用户提供了与应用程序当前上下文相关的最重要的Action 条目。那些直接出现在动作栏上的图标和文本被称为动作按钮。那些不能适应动作栏或者不重要的Actions,都被隐藏在action overflow (动作溢出)按钮中。用户能够通过点击右侧的overflow按钮来显示其他操作的列表。
当你的Activity开始后,系统通过调用你Activity中的 onCreateOptionsMenu()方法来填充动作条目。使用这个方法来填充一个定义了所有动作条目的菜单资源。例如:

res/menu/main_activity_actions.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/action_search"
          android:icon="@drawable/ic_action_search"
          android:title="@string/action_search"/>
    <item android:id="@+id/action_compose"
          android:icon="@drawable/ic_action_compose"
          android:title="@string/action_compose" />
</menu>

然后在你的Activity中的onCreateOptionsMenu()方法中,填充菜单资源到给定的菜单中,菜单资源中的条目就会添加到动作栏中。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu items for use in the action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.main_activity_actions, menu);
    return super.onCreateOptionsMenu(menu);
}

想要将一个条目作为一个动作按钮来直接显示到动作栏中,应该在<item> 标签中包含 showAsAction="ifRoom",例如:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_search"
          android:icon="@drawable/ic_action_search"
          android:title="@string/action_search"
          yourapp:showAsAction="ifRoom"  />
    ...
</menu>
如果这里没有足够的空间来放置条目,那它将会出现在overflow 中。
如果你的菜单条目支持标题和图标(在title和icon属性中),然后动作条目默认只显示图标。但是如果你想显示文本标题,你可以在 showAsAction 属性中增加“withText”。例如:

<item yourapp:showAsAction="ifRoom|withText" ... />
注意:“withText”是暗示动作栏文本标题应该显示。动作栏在可能的情况下会显示标题,但是如果图标是可用的和动作栏空间不够的情况下也可能不会显示。

因为一下原因,你应该为每个条目定义一个标题,即使你不准备让这个标题出现在动作栏上:
1.如果动作栏没有足够的空间给这个动作条目,那么它将会以标题的形式出现在overflow中。
2.如果动作条目仅仅以图标形式出现,那么用户能够长按该条目来显示一个标题的提示。

你同样可以使用“always”来声明一个条目作为一个按钮总是出现。然而,你不应该用这个方法强制一个条目出现在动作栏中。这样做的话在小屏幕设备上创建布局时,可能导致一些问题。所以最好使用“ifRoom”来让条目出现在动作栏中,但是在空间不够的情况下,允许系统把它移动到overflow中。然后,如果该条目包含一个不能折叠并且始终可见并提供访问的关键功能的动作视图,那么就可能有必要使用“always”了。

2.1 处理动作条目上的点击事件

当用户按下一个动作,系统就会去调用你当前Activity中的 onOptionsItemSelected() 方法。通过这个方式使用菜单项,你能通过getItemId()识别按压的动作。这个返回的唯一ID,是由<item>标签的id属性提供的,因此你就可以执行对应适当的操作。例如:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle presses on the action bar items
    switch (item.getItemId()) {
        case R.id.action_search:
            openSearch();
            return true;
        case R.id.action_compose:
            composeMessage();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

注意:如果你是通过Fragment类中的onCreateOptionsMenu()方法,从fragment中填充菜单条目,当用户选择其中一个条目时,那么系统会调用fragment中的 onOptionsItemSelected()。然而,这个Activity会先得到一个机会处理这个事件,所以系统在调用fragment中的回调时,会首先调用Activity中的onOptionsItemSelected().为了确保这个Activity中的所有fragment同样有机会去处理回调,总是通过调用父类的默认行为,而不是当你不处理这个条目时返回false。

2.2 使用拆分动作栏

拆分动作条在屏幕底部提供了一个分离的栏去显示所有的动作条目,当Activity运行在一个狭小的屏幕中时。
分离动作条目,这种方式确保了在一个狭小的屏幕中提供一个合理的空间可用来显示所有你的动作条目,同时离开顶部的导航和标题元素。
当使用支持库去启用拆分动作栏时,你必须做两件事情:
1.在每个<activity>元素或者<application>元素中,增加uiOptions="splitActionBarWhenNarrow" 。这个属性仅被API 14 和以上的版本所理解(之前的老版本会忽略).
2.为了支持老版本,在每个<activity>元素中增加一个<meta-data> 元素作为子元素,同时表明相同的值 "android.support.UI_OPTIONS".例如:
<manifest ...>
    <activity uiOptions="splitActionBarWhenNarrow" ... >
        <meta-data android:name="android.support.UI_OPTIONS"
                   android:value="splitActionBarWhenNarrow" />
    </activity>
</manifest>

3. 使用应用icon向上导航

启用应用图标作为一个向上按钮,允许用户在屏幕间的层次关系上浏览你的应用。例如,如果屏幕A显示了一个项目列表,选择一个条目进入屏幕B,然后屏幕B应该包含这个向上按钮来返回屏幕A。
注意:向上导航有别于系统中的返回键导航。返回键是用来返回用户刚才最近打开的页面,它基本是基于页面间的时间关系,而不是应用的层级结构。
想要启用应用图标作为向上按钮,调用方法setDisplayHomeAsUpEnabled().例如:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_details);

    ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayHomeAsUpEnabled(true);
    ...
}

现在动作栏中的图标出现了up符号。然而,它默认是不做任何事情。如果你想当用户按下UP键后,去打开指定的Activity,你有两个选择:
3.1 在manifest文件中,指定parent activity:
当parent activity一直是同一个时,这是最好的选择。通过在manifest文件中声明parent activity,当用户按下UP button 时,那么动作栏会自行执行正确的动作。
在android 4.1 (API 16 ),你能够在<activity>元素的 parentActivityName 属性中声明这个parent activity。
为了支持老设备使用支持库,同样需要包含<meta-data>元素上指定 parent activity ,并且值为android.support.PARENT_ACTIVITY. 

<application ... >
    ...
    <!-- The main/home activity (has no parent activity) -->
    <activity
        android:name="com.example.myfirstapp.MainActivity" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name="com.example.myfirstapp.DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName="com.example.myfirstapp.MainActivity" >
        <!-- Parent activity meta-data to support API level 7+ -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>
</application>

一旦在manifest 文件中这样指定parent activity 和 用setDisplayHomeAsUpEnabled() 启用 UP button,那么你的工作完成了并且动作栏会正确的导航。

3.2 或者,在你的Activity中重写 getSupportParentActivityIntent() 和 onCreateSupportNavigateUpTaskStack() 方法。
这种方法适合于当前screen的parent activity不确定的情况。如果用户有多条路径进入到当前screen,这意味着当前screen有多个不同的parent activity。up button需要返回用户使用的那条路径对应的parent Activity。
当用户按下up button在你自己的app task里进行导航时,系统会调用getSupportParentActivityIntent(),你需要覆写这个方法,返回一个intent,指明合适的parent activity。如果在导航时打开的Activity会根据用户到达当前的位置不同而有所不同的话,那么你应该重写此方法以返回这个Intent 来开始适当的parent activity。
当用户按下activity上的up button,而调用activity的task不属于自己的activity 自己所属的app时,系统会调用onCreateSupportNavigateUpTaskStack()。你通过这个方法的TaskStackBuilder参数,构建一个合适的back stack。
即使你覆写了getSupportParentActivityIntent(),你也可以通过在manifest 文件里定义default parent activity来避免onCreateSupportNavigateUpTaskStack()的覆写。onCreateSupportNavigateUpTaskStack()的默认实现会根据mainfest文件里的parent activity声明,来合成一个合适的back stack。
说明:如果你的应用层次是通过一系列Fragment来实现的,up button的导航功能,则需要覆写onSupportNavigateUP(),进行Fragment之间的处理。通常会调用popBackStack()从back stack里弹出当前的Fragment。

4.增加一个动作视图

一个动作视图就是显示在动作栏中用来替代一个动作按钮的小部件。一个动作视图提供了快速访问丰富的动作,而不需要去改变activity和fragment,并不需要更换动作栏。例如,如果你有一个搜索动作,你可以新增一个动作视图嵌入在动作栏中的SearchView的部件里。

在声明一个动作视图时,需要在 actionLayout 或者 actionViewClass 属性中分别去指定一个布局资源或者部件类。比如:

<?xml version="1.0" encoding="utf-8"?>
<menuxmlns:android="http://schemas.android.com/apk/res/android"
     
xmlns:yourapp="http://schemas.android.com/apk/res-auto">
   
<itemandroid:id="@+id/action_search"
         
android:title="@string/action_search"
         
android:icon="@drawable/ic_action_search"
         
yourapp:showAsAction="ifRoom|collapseActionView"
         
yourapp:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>

注意在showAsAction属性中同样包含了"collapseActionView" 值。这是可选的用来声明这个动作视图应该被折叠成一个按钮。
如果你想去配置这个动作视图(比如添加监听事件),你可以在onCreateOptionsMenu()回调方法中进行。你可以获得这个动作视图的对象通过调用静态方法MenuItemCompat.getActionView() 。

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_activity_actions, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    // Configure the search info and add any event listeners
    ...
    return super.onCreateOptionsMenu(menu);
}

在 API 11 或者更高时:
通过在相应的MenuItem调用getActionView() ,得到这个动作视图
menu.findItem(R.id.action_search).getActionView()
4.1处理可折叠的动作视图

为了节省动作栏空间,你可以将你的动作视图折叠成一个动作按钮。当折叠了之后,系统可能将动作放在overflow按钮中,但是当用户选择它时,动作视图会一直显示在动作栏中。
因为系统扩展这个动作视图了,当用选择了这个动作时,你不需要在 onOptionsItemSelected()回调中响应它。
如果你需要根据你的动作视图的可见性来更新你的Activity,你可以接受回调方法,当这个aciton在处于扩展和折叠状态时,进行不同的操作。该回调方法定义在OnActionExpandListener 中,你可以通过设置setOnActionExpandListener来监听。
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.options, menu);
    MenuItem menuItem = menu.findItem(R.id.actionItem);
    ...

    // When using the support library, the setOnActionExpandListener() method is
    // static and accepts the MenuItem object as an argument
    MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
        @Override
        public boolean onMenuItemActionCollapse(MenuItem item) {
            // Do something when collapsed
            return true;  // Return true to collapse action view
        }

        @Override
        public boolean onMenuItemActionExpand(MenuItem item) {
            // Do something when expanded
            return true;  // Return true to expand action view
        }
    });
}
5.增加一个动作提供者

和动作视图一样,一个动作提供者可以使用自定义的布局来替换动作按钮。然后不像动作视图那样,一个动作提供者按下后,可以控制所有的动作行为,显示子菜单按钮。
要声明一个action provider,在item标签里定义actionProviderClass属性。通过继承ActionProveder 类,你可以创建自己的action provider。Android系统提供了一些预先实现好的action provider。例如ShareActionProvider,可以在action bar上直接共享数据给一些app。
由于每个ActionProvider类定义了自己的action behavior,你不需要在onOptionItemSelected()方法里监 听action。如果万一你需要在onOptionItemSelected()里监听action以同步处理其他action,则返回值必须返回false,这样action provider仍然可以接收到onPerformDefaultAction()回调函数,处理与它相关的action事件。
然而,如果action provider提供了action的子菜单,用户在选择子菜单里的项时,你的activity接收不到对onOPtionItemSelected()的调用。

5.1  使用 ShareActionProvider

要使用ShareActionProvider添加 一个"share" 动作,需要在<item>标签里定义actionProviderClass属性。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_share"
          android:title="@string/share"
          yourapp:showAsAction="ifRoom"
          yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
          />
    ...
</menu>
现在action provider控制了这个action项的显示和行为。但是你也必须为这个action项提供title属性,它在overflow里显示的时候需要。
剩下的事就是你定义一个需要共享的Intent。编辑你的onCreateOptionsMenu() 方法去调用MenuItemCompat.getActionProvider() 传递MenuItem参数。 然后在返回的ShareActionProvider上调用的setShareIntent()方法,向其传递一个ACTION_SEND intent。
你应当在onCreateOptionMenu()里调用setShareIntent()来初始化share action,但是由于用户的上下文也许改变,你必须重新调用setShareIntent()来更新你要发送的intent内容。例如:

private ShareActionProvider mShareActionProvider;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_activity_actions, menu);

    // Set up ShareActionProvider's default share intent
    MenuItem shareItem = menu.findItem(R.id.action_share);
    mShareActionProvider = (ShareActionProvider)
            MenuItemCompat.getActionProvider(shareItem);
    mShareActionProvider.setShareIntent(getDefaultIntent());

    return super.onCreateOptionsMenu(menu);
}

/** Defines a default (dummy) share intent to initialize the action provider.
  * However, as soon as the actual content to be used in the intent
  * is known or changes, you must update the share intent by again calling
  * mShareActionProvider.setShareIntent()
  */
private Intent getDefaultIntent() {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/*");
    return intent;
}

现在ShareActionProvider处理action项的所有用户交互,你不需要再onOtionItemSelected()回调函数函数里进行任何事件处理。
默认情况下,ShareActionProvider对于每个共享目标根据用户的使用频率保持一个次序。用户经常使用的共享目标列在下拉菜单的最前面。默认情况下,排序信息存储在一个命名为DEFAULT_SHARE_HISTORY_FILE_NAME的私有文件里。如果你只在一个action项里使用ShareActionProvider,你可以继续使用这个默认私有文件,而不需要做其他的事情。相反的,如果你想在多个action项里使用ShareActionProvider,你需要为每一个action项维护一个存储信息排序的文件(通过调用setShareHistoryFileName(),并提供一个xml file名,例如custom_share_history.xml)。

5.2 新建自定义的 action provider

创建你自己的action provider 允许你在独立的模块中复用和动态管理action项的行为,二不是在你的fragment 或activity中。可以参考之前的ShareActionProvider.
为一个不同的action项创建action provider,首先你需要继承ActionProvider类,实现它规定的回调函数。如下:
ActionProvider()
         在这个构造函数里,传递你的应用的context参数,应该保存起来在后续的会调函数会使用到它。
onCreateActionView(MenuItem)
        这里定义action项的action view。使用context来获得LayoutInflater ,然后填充你的action view 的布局资源文件,然后设置事件监听器。如下示例:
public View onCreateActionView(MenuItem forItem) {
    // Inflate the action view to be shown on the action bar.
    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
    View view = layoutInflater.inflate(R.layout.action_provider, null);
    ImageButton button = (ImageButton) view.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do something...
        }
    });
    return view;
}

onPerformDefaultAction()
        当在action overflow里选中一项menu项时,系统会调用这个函数,action provider会执行一个默认的行为操作。

然而,如果你的action provider提供了一个子菜单,通过onPrepareSubMenu()回调函数,子菜单会显示在actionbar里。所以,如果存在子菜单,onPerformDefaultAction()不会被调用。

6.添加导航标签

action bar提供的tabs使用户很容易的访问和切换app中的不同的view。ActionBar提供的标签是理想的,因为它们能够适应不同的屏幕尺寸。例如,当屏幕的宽度足够宽时,tabs显示在action bar上;当屏幕宽度比较窄时,tabs显示在一个独立的bar上(称为stacked action bar)。在一些案例中,android系统会以下拉菜单的方式显示你的tab项。
首先,你的布局里必须包含一个ViewGroup,在其中一个tab关联一个Fragment。确保ViewGroup有一个resource ID,在你的代码里需要使用它切换tab。另外,如果tab的内容填充了你的activity布局,则你的activities不需要自己的布局(你甚至不需要调用setContentView())。相反的,你可以将每个fragment设置在root view下,引用android.R.id.content ID。
一旦你确定了布局里的fragment显示,添加一个tab的基本流程为:
1. 实现ActionBar.TabListener接口。这个接口为tab events提供了回调函数,例如切换tab事件。
2.对于每一个要添加的tab,实例化一个ActionBar.Tab,通过调用setTabListener()设置ActionBar.TabListener。使用setText()设置tab的title(使用setIcon(),设置tab的icon,可选)。
3. 调用addTab()增加一个tab。

注意ActionBar.TabListener回调函数并没有定义tab与fragment之间的关联逻辑,仅仅是确定了哪个ActionBar.Tab被选中了。你必须自己定义每个ActionBar.Tab和对应的Fragment之间的关联逻辑。有很多方式定义这种逻辑,依赖于你的设计。
例如,你想为每个tab实现一个ActionBar.TabListener:
publicstaticclassTabListener<T extendsFragment>implementsActionBar.TabListener{
   
privateFragment mFragment;
   
privatefinalActivity mActivity;
   
privatefinalString mTag;
   
privatefinalClass<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
      */

   
publicTabListener(Activity activity,String tag,Class<T> clz){
        mActivity
= activity;
        mTag
= tag;
        mClass
= clz;
   
}

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

   
publicvoid onTabSelected(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);
       
}
   
}

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

   
publicvoid onTabReselected(Tab tab,FragmentTransaction ft){
       
// User selected the already selected tab. Usually do nothing.
   
}
}
警告:你一定不能再每个上面的callback里为fragment事务调用commit()---你自己调用,会抛出异常。你也不能将这些fragment事务添加到back stack。

在这个例子里,当某个tab被选中,listener简单的实现了attach一个fragment到activity布局(attach()),添加一个fragment到布局(add())作为android.R.id.content view group里的一个子项。当某个tab未被选中时,listener实现了detach()。

剩下的工作就是创建每个ActionBar.Tab,并将它添加到ActionBar。此外,你必须调用setNavigationMode(NAVIGATION_MODE_TABS)来使tabs可见。例如,下面的代码使用上述的listener添加了两个tabs:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Notice that setContentView() is not used, because we use the root
    // android.R.id.content as the container for each fragment

    // setup action bar for tabs
    ActionBar actionBar = getSupportActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    actionBar.setDisplayShowTitleEnabled(false);

    Tab tab = actionBar.newTab()
                       .setText(R.string.artist)
                       .setTabListener(new TabListener<ArtistFragment>(
                               this, "artist", ArtistFragment.class));
    actionBar.addTab(tab);

    tab = actionBar.newTab()
                   .setText(R.string.album)
                   .setTabListener(new TabListener<AlbumFragment>(
                           this, "album", AlbumFragment.class));
    actionBar.addTab(tab);
}

当你的activity停止,你需要维护当前选中的tab的状态,这样当用户返回时可以回到合适的tab。在保存状态时,你可要调用getSelectedNavigationIndex()来获取选中的tab,它返回选中的tab的index position。

警告:保存每个fragment的状态很重要,用户会随时切换tab到不同的fragment。有一些状态时默认保存,但是你需要手动保存一些自定义的view。

说明:上述的ActionBar.TabListener的实现是一种可行的技术之一。其他的实现方法是使用ViewPager来管理fragment,用户可以使用swipe gesture来切换tab。这种方式下,你可以简单的告诉ViewPager你的tab的位置(调用onTabSelected() callback)。

7.增加下拉导航

Activity导航的另一种模式,是action bar提供的一种下拉式清单(也称为spinner)。这种下拉式清单可以给activity里分类的内容提供不同的显示模式。
当你的内容需要切换,但切换频率并不频繁,则可以使用下拉式清单这种方式实现。如果内容切换频率频繁,则建议使用navigation tabs方式实现。

使下拉导航的基本程序是:
1. 创建一个SpinnerAdapter,提供选择项清单和清单中每项绘制的布局。
2. 实现ActionBar.OnNavigationListener,定义用户选中清单中某项时的操作行为。
3. 在你的activity的onCreate()方法里,通过调用setNavigationMode(NAVIGATION_MODE_LIST)使能action bar的下拉式清单功能。
4. 通过setListNavigationCallbacks()为下拉式清单设置回调函数。例如:
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

这个方法取得你的SpinnerAdapter和ActionBar.OnNavigationListener。
上述步骤相对简洁,但是最核心的工作是实现SpinnerAdapter和ActionBar.OnNavigationListener。还有很多方法可以实现这些定义的功能为您的下拉导航和执行各种类型的spinneradapter,但它们超出了本文的范围。
下面是一个让你开始的示例:
SinnerAdapter 是一个适配器能够为你的spinner widget 提供数据源,比如动作栏中的下拉列表。SpinnerAdapter是你需要实现的接口,但是Android已经包含了一些有用的实现,供你来扩展,例如ArrayAdapter和SimpleCursorAdapter。下面的方式就是通过扩展ArrayAdapter来实现SpinnerAdapter,使用string 数组作为数据源。

SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
        R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
createFromResource()方法获取3个参数:应用的Context,string array的resource ID,每个清单项的布局。
定义数组资源是这样的:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="action_list">
        <item>Mercury</item>
        <item>Venus</item>
        <item>Earth</item>
    </string-array>
</resources>
createFromResource() 返回ArrayAdapter,你可把它设置到setListNavigationCallbacks()中了。
当用户从下拉清单里选择某项时,在你的activity或fragment需要改变代码的地方实现ActionBar.OnNavigationListener。这个listener只有一个方法需要实现:onNavigationItemSelected()。onNavigationItemListener()方法接收清单项在list中的位置和由SpinnerAdapter提供的项ID。
如下示例显示了onNavigationListener的一个匿名实现,在R.id.fragment_container的布局容器里插入了一个Fragment:
mOnNavigationListener = new OnNavigationListener() {
  // Get the same strings provided for the drop-down's ArrayAdapter
  String[] strings = getResources().getStringArray(R.array.action_list);

  @Override
  public boolean onNavigationItemSelected(int position, long itemId) {
    // Create new fragment from our own Fragment class
    ListContentFragment newFragment = new ListContentFragment();
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

    // Replace whatever is in the fragment container with this fragment
    // and give the fragment a tag name equal to the string at the position
    // selected
    ft.replace(R.id.fragment_container, newFragment, strings[position]);

    // Apply changes
    ft.commit();
    return true;
  }
};

在这个例子里,当用户选择下拉式清单里的某项时,在当前布局里会添加一个Fragment。增加的Fragment会设置一个标识身份的tag。
下面的示例显示了ListContentFragment类定义的fragment:
public class ListContentFragment extends Fragment {
    private String mText;

    @Override
    public void onAttach(Activity activity) {
      // This is the first callback received; here we can set the text for
      // the fragment as defined by the tag specified during the fragment
      // transaction
      super.onAttach(activity);
      mText = getTag();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // This is called to define the layout for the fragment;
        // we just create a TextView and set its text to be the fragment tag
        TextView text = new TextView(getActivity());
        text.setText(mText);
        return text;
    }
}

8.定义ActionBar的样式

如果你想实现代表你自己的应用风格的视觉显示,action bar允许定制它的外观,包括action bar的颜色,文本颜色,按钮风格等。你需要遵循android的style and theme框架来设计你的风格。

8.1 常规外观

actionBarStyle
    为动作栏的各种样式属性指定一个样式资源。默认的style是Widget.AppCompat.ActionBar,你可以把它作为父style。
     包含的style属性有:

background ---为action bar背景,定义一个 drawable资源

backgroundStacked ---为stacked action bar(tabs)背景,定义一个drawable资源

backgroundSplit --- 为split action bar(拆分动作栏)背景,定义一个drawable资源。

actionButtonStyle --- 为action button定义一个style资源。它的默认值为Widget.AppCompat.ActionButton。用作继承父style。

actionOverflowStyle --- 为overflow action项定义一个style资源。默认值为Widget.AppComat.ActionButton.Overflow。用作继承父style。

displayOptions --- 定义action bar显示项,包含是否用app logo,显示title,或使能up action等。

divider --- 为action项之间的分割间隔,定义一个drawable资源。

titleTextStyle --- 为action bar title定义style资源。默认值为TextAppearance.AppCompat.Widget.ActionBar.Title,用作继承父stytle。

windowActionBarOverlay
     声明action bar是否使用overlay模式。默认值为false。
    通常,action bar有自己独立的空间,而activity layout使用剩余的空间。当action bar使用overlay模式,activity layout使用全部的空间,系统将action bar画在layout的顶部。当action bar隐藏或显示时,如果你想保持你的内容为固定的大小和位置,就可以使用overlay模式。或者你仅仅使用overlay模式作为一种显示效果,你可以在action bar上使用一个半透明的背景图,用户可以透过action bar看到下面的activity layout。
当overlay模式启用时,你的activity布局并没有意识到action bar的位置在它的顶部。因此,顶部这块背action bar覆盖的地方,你的layout布局应当小心谨慎,不要将重要的UI显示放在这一块。如果需要,你可以通过actionBarSize属性来确定action bar的高度。例如:
<SomeView
    ...
    android:layout_marginTop="?android:attr/actionBarSize" />

8.2 Action items

actionButtonStyle ---为action button定义style 资源。默认值为Widget.AppCompat.AcitonButton。用于父style。

actionBarItemBackground --- 为每个action项定义背景,定义drawable 资源。这应该是个state-list drawable类型,指示不同的选项状态。

itemBackground  ---为每个动作overflow 中的item的背景色,定义一个drawable  资源。这应该是个state-list drawable类型,指示不同的选项状态。

actionBarDivider ---为action项之间的分割间隔,  定义一个drawable 资源。

actionMenuTextColor --- 为每个action项的文本显示颜色。

actionMenuTextAppearance --- 为每个action项的文本显示外观定义一个style资源。

actionBarWidgetTheme ---为action view的定义一个theme资源。

8.3 Navigation tabs

actionBarTabStyle --- 为action bar中的tabs定义一个style资源。默认值为Widget .AppCompat.ActionBar.TabView。用于继承父style。 

actionBarTabBarStyle --定义显示在navigation tabs下的thin bar的style 资源。默认值为Widget.AppCompat.ActionBar.TabBar。用于继承父style。

actionBarTabTextStyle ---定义navigation tabs的文本 style 资源。默认值为Widget.AppCompat.ActionBar.TabText。用于继承父style。

8.4 Drop-down lists

actionDropDownStyle --- 定义drop-down navigation的sytle(例如,background和text style)。默认值为Widget.AppCompat.Spinner.DropDown.ActionBar。用于继承父style。

注意: 每个theme和style会声明一个parent theme或parent style。你只需要覆写你需要修改的style属性,其他的属性由parent提供实现。

你可能感兴趣的:(android,Actionbar)