Android ActionBar总结二

本文详细讲解了Android中ActionBar的各种高级特性,包括ActionItem的配置、使用SplitActionBar改善用户体验、通过图标实现回退功能、添加ActionView和ActionProvider扩展功能、利用Tabs进行导航、创建下拉菜单以及自定义ActionBar样式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请注明 http://blog.youkuaiyun.com/sinat_30276961/article/details/48031057

上一篇Android ActionBar总结一介绍了Actionbar的基本用法,这一篇承接上篇,继续讲述ActionBar更多功能。

Action Item

对于每个action item,如果你既要显示图标又要显示文字,那就这样定义:

<item yourapp:showAsAction="ifRoom|withText" ... />

我们再看下每个item的定义方式:

<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>

这里,有一点必须注意的是,android:title这个属性必须要定义。原因有这些:
1.当如果action item放不下了,它会自动移动到右边的overflow里,那里显示方式是只显示title。
2.长按某个action item,会触发显示title。

还有一点需要注意的是:
如果是这种情况,你关联action item和menu是在fragment里,使用的是fragment的onCreateOptionsMenu(),然后点击某个action item时的回调顺序是:
activity.onOptionsItemSelected() –> fragment.onOptionsItemSelected()。
所以,如果你在activity里复写了onOptionsItemSelected,记得调用下return super.onOptionsItemSelected(item);这样才会进入fragment的onOptionsItemSelected。

使用split action bar

split action bar,顾名思义是分离的actionbar。它的作用就是在窄屏的情况下,分离部分actionbar到屏幕底端,从而可以显示全部的action item。

如下图:
这里写图片描述

使用方式也很简单,对于support v7使用者只要两步:
1.manifest里的activity定义uiOptions=”splitActionBarWhenNarrow”
2.调用v7库需要使用meta-data元素

<manifest ...>
    <activity uiOptions="splitActionBarWhenNarrow" ... >
        <meta-data android:name="android.support.UI_OPTIONS"
                   android:value="splitActionBarWhenNarrow" />
    </activity>
</manifest>

就像图中第三个手机显示的情况,对于action tab,可以通过设置setDisplayShowHomeEnabled(false)和setDisplayShowTitleEnabled(false),把title和icon显示去掉,然后再设置split属性,就OK了。

通过图标回退

这里写图片描述

如上图展示的,在图标的左边有个小箭头,那个就意味着图标回退功能已经打开。
我们可以通过setDisplayHomeAsUpEnabled()来打开这个功能。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_details);

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

打开这个功能之后,我们有两种方式可以设置其点击之后的反馈:

1.在manifest里指定parent activity。
这种情况一般针对该activity确定永远返回到某个上层的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>

2.通过复写getSupportParentActivityIntent()和onCreateSupportNavigateUpTaskStack()
这种情况一般针对该activity有可能返回到不同的上层activity,这样一来,就不要在manifest定义parent activity。只能通过复写上面的方法,自己控制了。

那上面的两个复写方法系统调用情况怎么安排呢?

getSupportParentActivityIntent(),当前的activity在自己app的task里的话,系统会调用该方法。然后你可以自己创建个intent,设置好flag,然后自己选择返回到的堆栈里的某个activity。

onCreateSupportNavigateUpTaskStack(),当前的activity不在该app的task里的话,系统会调用该方法。然后要使用TaskStackBuilder构造合适的back stack。

要注意的是:
如果你构建的界面层级关系是通过fragment构建的,那么上述方案就不可行了。在这种情况下,你应该复写onSupportNavigateUp()来执行合适的fragment事务。一般通过调用popBackStack()来回退。

增加Action view

Action View是用来扩展action item的,它可以在不替换actionbar的情况下,展示其他action item。
如下图所示:

这里写图片描述

要定义action view,你可以使用布局文件或者是一个自定义控件。上图第一个展示的布局形式的,第二个展示的是自定义view形式的。

怎么使用呢?
1.需要在menu xml文件里定义:

<?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_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属性,然后是定义actionLayout或者actionViewClass这两个属性。

如果你要添加一些监听事件,对于布局形式的,在onCreateOptionsMenu()进行操作。
你要先获得对应的MenuItem,然后通过MenuItemCompat.getActionView()获得ActionView。

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_next, menu);
        final MenuItem editItem = menu.findItem(R.id.action_edit);
        View editView = MenuItemCompat.getActionView(editItem);
        TextView title = (TextView) editView.findViewById(R.id.title);
        title.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(NextActivity.this, "Click Title", Toast.LENGTH_SHORT).show();
            }
        });
        ImageView imgEdit = (ImageView) editView.findViewById(R.id.img_edit);
        imgEdit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(NextActivity.this, "Click Edit Icon", Toast.LENGTH_SHORT).show();
                // 销毁actionview
                MenuItemCompat.collapseActionView(editItem);
            }
        });
        MenuItemCompat.setOnActionExpandListener(editItem, new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                Toast.makeText(NextActivity.this, "Edit ActionView expand", Toast.LENGTH_SHORT).show();
                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                Toast.makeText(NextActivity.this, "Edit ActionView collapse", Toast.LENGTH_SHORT).show();
                return true;
            }
        });

        final MenuItem searchItem = menu.findItem(R.id.action_refresh);
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
        return true;
    }

如果是3.0以上的直接使用actionbar的,通过如下方式获得ActionView:

menu.findItem(R.id.action_search).getActionView()

可以通过MenuItemCompat.setOnActionExpandListener监听actionview的展开和销毁。注意,那里复写的onMenuItemActionCollapse和onMenuItemActionExpand两个方法里必须返回true,否则不会有效果。

增加Action provider

Action Provider和Action View其实很类似,唯一的区别应该是Action Provider不能用布局去定义,但是可以展示子菜单。

在menu xml里定义的方式很简单,通过actionProviderClass定义actionprovider,不需要在onOptionsItemSelected做任何操作,因为它的交互都定义在其内部。如果你非要有其他交互,最后要返回false,这样,actionprovider才能工作

使用ShareActionProvider

对于常规的应用,传递数据给其他应用是很常见的。ShareActionProvider给我们提供了这样的功能。如下是其定义:

<?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>

这样一来,ShareActionProvider已经定义好了,现在需要做的是,给它一个intent,让它去展示符合这个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;
}

如上代码,通过MenuItemCompat.getActionProvider获取到ActionProvider。然后通过ShareActionProvider.setShareIntent(),给它一个intent。这部分在onCreateOptionsMenu里定义。

ShareActionProvider自己有管理一个记录,记录各个应用的使用情况,然后按使用频繁程度,从上到下排列出来。这部分做个了解就行。

自定义Action Provider

自定义action provider的好处是,封装了事件监听和反馈,这样可以不用每次在各个activity或者fragment里写相同的事件响应代码,复用性就体现出来了。

我们通过继承ActionProvider,然后实现它的回调方法就行。

public class MyActionProvider extends ActionProvider{
    Context mContext;
    /**
     * Creates a new instance.
     *
     * @param context Context for accessing resources.
     */
    public MyActionProvider(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public View onCreateActionView() {
        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
        View view = layoutInflater.inflate(R.layout.action_provider_layout, null);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent1 = new Intent(mContext, MyActivity.class);
                mContext.startActivity(intent1);
            }
        });
        return view;
    }
}

从上面可以看到,你必须实现两个方法。
1.构造函数,可以传context进去
2.onCreateActionView,用于返回给actionbar来显示。

增加导航tabs

这里写图片描述

这里写图片描述

Actionbar整合tab引导键的显示,横屏时可以显示在actionbar,竖屏时,如果显示不下,就分开显示。

它的定义方式很简单。
你首先需要准备一个或多个fragment,用来展示内容。你可以使用自己的布局文件,比方说activity_main,在里面定义一个fragment的容器。如果你要展示的内容充满整个activity,其实可以不用布局文件,也就是说可以不用调用setContentView()。直接使用系统给的framelayout。

这里插一句好了,我们定义的布局文件其实是放到系统设置的布局文件的framelayout里。

通过android.R.id.content获取到那个framelayout。

决定好你内容的展示,接着分两步定义:
1.实现ActionBar.TabListener这个接口,这个接口主要是回调tab选中没选中情况。
2.每个tab通过实例化ActionBar.Tab,然后给它设置ActionBar.TabListener,最后actionbar.addTab

要注意的是ActionBar.TabListener的回调函数里,返回的不是哪个fragment被选中或没选中,而是返回ActionBar.Tab。你必须自己关联各个ActionBar.Tab和fragment。
比方说如下代码:

public static 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(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(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

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

这里需要注意的是FragmentTransaction执行完之后,我们不需要添加commit(),系统会帮我们调用。同时,你也不能自己去添加fragment到back stack。

接下去,就只剩下创建每个ActionBar.Tab,然后添加到actionbar。同时,你必须设置一下模式setNavigationMode(NAVIGATION_MODE_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);
}

如果你要支持横竖屏切换,又要保存当前选择tab的情况,那就复写onSaveInstanceState,可以复写activity的那个,然后在里面通过getSelectedNavigationIndex()获取到当前选择的position。

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        int selectItem = getSupportActionBar().getSelectedNavigationIndex();
        outState.putInt("select_item", selectItem);
    }

然后在onCreate()再读取一下。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int selectItem = 0;
        if (savedInstanceState != null) {
            selectItem = savedInstanceState.getInt("select_item");
        }
        ......
        actionBar.setSelectedNavigationItem(selectItem);
    }

对于fragment的操作,保存state是很重要的,这样会给用户更好接受。

上述例子是用ActionBar.TabListener来控制fragment,还有一种,可以通过viewpager来控制。这里就点一下,等总结fragment再去讲。

添加drop-down菜单

Actionbar还提供了下拉式的导航菜单,它是由spinner来实现的,并维持在Actionbar内部,我们不需要去管理细节,只需要给它值和布局就可以。

一般在这种场合可以使用下拉式菜单,当年需要偶尔使用切换功能,但不经常使用。如果是经常使用的情况,那最好选择tab形式的导航菜单。

那怎么实现该功能呢?大体上,只要四步,就可以实现drop-down list。
1.创建一个SpinnerAdapter,给它绑定数据和布局文件。
2.实现ActionBar.OnNavigationListener接口,用来监听选择事件。
3.在Activity的onCreate()阶段,设置Actionbar的模式为list模式。

setNavigationMode(NAVIGATION_MODE_LIST).

4.关联spinnerAdapter和listener。如下所示:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

怎么样,很简单吧。

如果要查看代码,可以滚动到最后,那里会提供源码。

让Actionbar多样式

如果你不想让你的Actionbar总是那么千篇一律,毫无性格,毫无特点,那该怎么办?别着急,系统给我们提供了改变它外貌的办法,那就是通过自定它的主题和样式,然后把想要改变的属性增加改变就行。

常规外观

actionBarStyle

Actionbar的样式资源,里面定义了一堆关于actionbar样式的属性。
它的默认样式是Widget.AppCompat.ActionBar,也就是说,如果我们要复写actionbar的样式,就要继承它,这样你就不需要重新去定义大量的属性。

我们来看看它的常规属性:

1.background
这个大家最熟悉了。

2.backgroundStacked
actionbar上的tab背景。

3.backgroundSplit
actionbar分离到底部的背景。

4.actionButtonStyle
actionbar上button的样式资源,默认是Widget.AppCompat.ActionButton,你如果要复写,就要继承这个。

5.actionOverflowButtonStyle
actionbar上超出部分的按键样式资源(overflow),就是右边那三个点。
默认是Widget.AppCompat.ActionButton.Overflow,同样,要复写的话,继承该默认样式。

6.displayOptions
定义actionbar显示的属性,比方说是否使用logo,是否显示左边的返回键等等。

7.divider
定义每个action item之间的分隔图型

8.titleTextStyle
actionbar的title样式资源,默认是TextAppearance.AppCompat.Widget.ActionBar.Title。

windowActionBarOverlay

这个在上一篇有讲过,这里再带过一下。它是定义actionbar是否覆盖在activity上。如果涉及到需要频繁显示和隐藏actionbar,可以选择开启overlay模式。

Demo源码入口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值