Android ActionBar总结二

来源:互联网 发布:淘宝搜索流量下降 编辑:程序博客网 时间:2024/06/18 13:45

转载请注明 http://blog.csdn.net/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()来打开这个功能。

@Overrideprotected 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;@Overridepublic 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):

@Overrideprotected 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源码入口

0 0
原创粉丝点击