android ActionBar与Menu

android2.x到现在的4.0有很大的差别,而手机开发者又没有经历过3.x版本的过渡,所以对于手机开发者的压力是比较大的,需要学习和适应大量新的组件与api

 

本文主要总结对ActionBar和Menu的学习

注意:这里以4.0为base

 

作为开发者,应该阅读下面重要新闻

谷歌敦促Android开发者停止使用菜单按钮

先说几句题外话

我个人觉得,应该保留实体menu按键,如果没有实体按键,那应该如何与menu交互?android提供了方案,使用action bar,action bar是显示在屏幕上的

等等,似乎有点问题,这也就是说:如果开发者需要menu,那么就需要消耗一部分屏幕的空间,虽然现在的手机很少有3寸以下的屏幕了,但是也没大到增加一个action bar而不使用户与开发者心疼的地步,起码我是这么觉得的

之前我可以隐藏状态栏标题栏但是保留menu,以后估计不行了

 

开始学习,以api demos - App - Action Bar中内容为主

 

boolean android.app.Activity.onCreateOptionsMenu(Menu menu)还是保留下来了的

 

Java代码   收藏代码
  1. MenuItem actionItem = menu.add("Action Button");  
  2. actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);  

setShowAsAction是设置ActionBar中menu的显示方式的

显示方式一共有如下5种:

从不显示;如果有空间就显示;总是显示;显示时跟随文字;默认收缩其action view

Java代码   收藏代码
  1. /** Never show this item as a button in an Action Bar. */  
  2. public static final int SHOW_AS_ACTION_NEVER = 0;  
  3. /** Show this item as a button in an Action Bar if the system decides there is room for it. */  
  4. public static final int SHOW_AS_ACTION_IF_ROOM = 1;  
  5. /** 
  6. * Always show this item as a button in an Action Bar. 
  7. * Use sparingly! If too many items are set to always show in the Action Bar it can 
  8. * crowd the Action Bar and degrade the user experience on devices with smaller screens. 
  9. * A good rule of thumb is to have no more than 2 items set to always show at a time. 
  10. */  
  11. public static final int SHOW_AS_ACTION_ALWAYS = 2;  
  12.   
  13. /** 
  14. * When this item is in the action bar, always show it with a text label even if 
  15. * it also has an icon specified. 
  16. */  
  17. public static final int SHOW_AS_ACTION_WITH_TEXT = 4;  
  18.   
  19. /** 
  20. * This item's action view collapses to a normal menu item. 
  21. * When expanded, the action view temporarily takes over 
  22. * a larger segment of its container. 
  23. */  
  24. public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8;  

其中SHOW_AS_ACTION_WITH_TEXT需要注意一下

在手机中,竖屏状态下text是不会显示的,切换到横屏才会显示text

 

SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW也需要特别说明一下

menu的每个item都可以设置action view,如果设置了,那么这个view默认是显示的,设置了SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW后,则默认不显示

 

另外需要说明的是:

现在手机有两种,一种是有实体按键的手机,不在action bar显示的item,按menu则会看到剩余的item

另一种是没有实体menu键的手机,他们通过action bar等触发menu,所有的item都会在action bar显示,“显示不下”的item会在action bar最后一个item触发显示剩余item列表,类似于之前我们按menu键后出现的“more”

 

 

ActionBar还提供了Tab功能

4.0中大家发现:TabActivity已经称为了过时的class,那么新的标签页如何实现,ActionBar提供一种简单的方式

Java代码   收藏代码
  1. final ActionBar bar = getActionBar();  
  2. final int tabCount = bar.getTabCount();  
  3. final String text = "Tab " + tabCount;  
  4. bar.addTab(bar.newTab()  
  5.         .setText(text)  
  6.         .setTabListener(new TabListener(new TabContentFragment(text))));  

其中TabListener要实现ActionBar.TabListener,并实现如下几个函数

 

Java代码   收藏代码
  1. private class TabListener implements ActionBar.TabListener {  
  2.     public void onTabSelected(Tab tab, FragmentTransaction ft) {}  
  3.     public void onTabUnselected(Tab tab, FragmentTransaction ft) {}  
  4.     public void onTabReselected(Tab tab, FragmentTransaction ft) {}  
  5. }  

 函数从名称就能看出是做什么的了,不再赘述

 

上面是ApiDemos的示例,只需addTab即可

Java代码   收藏代码
  1. void android.app.ActionBar.addTab(Tab tab)  
  2. void android.app.ActionBar.addTab(Tab tab, boolean setSelected)  
  3. void android.app.ActionBar.addTab(Tab tab, int position)  
  4. void android.app.ActionBar.addTab(Tab tab, int position, boolean setSelected  

除此之外,android也提供了remove和removeAll的api,不再列举

api demos中给出了Action Bar中使用menu的简单演示,效果如下


  

上面三个menu,下面两个,点击Sort之后弹出二级菜单

只需在xml中进行配置即可

 

Xml代码   收藏代码
  1. <menu xmlns:android="http://schemas.android.com/apk/res/android">  
  2.     <item android:id="@+id/action_search"  
  3.           android:icon="@android:drawable/ic_menu_search"  
  4.           android:title="@string/action_bar_search"  
  5.           android:showAsAction="ifRoom"  
  6.           android:actionViewClass="android.widget.SearchView" />  
  7.     <item android:id="@+id/action_add"  
  8.           android:icon="@android:drawable/ic_menu_add"  
  9.           android:title="@string/action_bar_add" />  
  10.     <item android:id="@+id/action_edit"  
  11.           android:icon="@android:drawable/ic_menu_edit"  
  12.           android:showAsAction="always"  
  13.           android:title="@string/action_bar_edit" />  
  14.     <item android:id="@+id/action_share"  
  15.           android:icon="@android:drawable/ic_menu_share"  
  16.           android:title="@string/action_bar_share"  
  17.           android:showAsAction="ifRoom" />  
  18.     <item android:id="@+id/action_sort"  
  19.           android:icon="@android:drawable/ic_menu_sort_by_size"  
  20.           android:title="@string/action_bar_sort"  
  21.           android:showAsAction="ifRoom">  
  22.         <menu>  
  23.             <item android:id="@+id/action_sort_size"  
  24.                   android:icon="@android:drawable/ic_menu_sort_by_size"  
  25.                   android:title="@string/action_bar_sort_size"  
  26.                   android:onClick="onSort" />  
  27.             <item android:id="@+id/action_sort_alpha"  
  28.                   android:icon="@android:drawable/ic_menu_sort_alphabetically"  
  29.                   android:title="@string/action_bar_sort_alpha"  
  30.                   android:onClick="onSort" />  
  31.         </menu>  
  32.     </item>  
  33. </menu>  

 

当把手机变成横屏的时候,由于空间充足,Sort就会显示在Action Bar中,下图是点击Sort后的效果


 

我们点击By size和Alphabetically会发现,Sort的图标会跟着改变

 

Java代码   收藏代码
  1. public void onSort(MenuItem item) {  
  2.     mSortMode = item.getItemId();  
  3.     // Request a call to onPrepareOptionsMenu so we can change the sort icon  
  4.     invalidateOptionsMenu();  
  5. }  

 

invalidateOptionsMenu会通知系统menu发生了改变,触发onPrepareOptionsMenu和onCreateOptionsMenu,然后再看看onPrepareOptionsMenu做了些什么

 

Java代码   收藏代码
  1. public boolean onPrepareOptionsMenu(Menu menu) {  
  2.     if (mSortMode != -1) {  
  3.         Drawable icon = menu.findItem(mSortMode).getIcon();  
  4.         menu.findItem(R.id.action_sort).setIcon(icon);  
  5.     }  
  6.     return super.onPrepareOptionsMenu(menu);  
  7. }  

 

调用的时候会判断状态,根据状态设置Sort图标

 

当我们点击Search按钮的时候,它的Action View会显示出来

 

Xml代码   收藏代码
  1. <item android:id="@+id/action_search"  
  2.       android:icon="@android:drawable/ic_menu_search"  
  3.       android:title="@string/action_bar_search"  
  4.       android:showAsAction="ifRoom"  
  5.       android:actionViewClass="android.widget.SearchView" />  

 

xml中显示了Action View调用的是android.widget.SearchView这个class

 

Java代码   收藏代码
  1. public boolean onCreateOptionsMenu(Menu menu) {  
  2.     MenuInflater inflater = getMenuInflater();  
  3.     inflater.inflate(R.menu.actions, menu);  
  4.     SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();  
  5.     searchView.setOnQueryTextListener(this);  
  6.     return true;  
  7. }  

在这里为SearchView设置监听,onQueryTextChange在输入框文字修改时触发,onQueryTextSubmit在提交搜索时触发

 

在xml也可以设置ActionProvider,例如

 

Java代码   收藏代码
  1. <item android:id="@+id/menu_item_action_provider_action_bar"  
  2.     android:showAsAction="ifRoom"  
  3.     android:title="@string/action_bar_settings"  
  4.     android:actionProviderClass="com.example.android.apis.app.ActionBarSettingsActionProviderActivity$SettingsActionProvider"/>  

 

ActionProvider的使用暂时不做过多讨论

 


上面的图片是一个Action Bar,可以分为几个部分,他们都可以设置为显示或者不显示,从左至右依次为:

ActionBar.DISPLAY_HOME_AS_UP:类似于提示作用,上图为一个脱字符(Up caret),不能单独只显示这个。。

ActionBar.DISPLAY_SHOW_HOME:大大的android图片为home,当然这个默认为apk的图标,可以通过ActionBar.DISPLAY_USE_LOGO来设置使用什么图片

ActionBar.DISPLAY_SHOW_TITLE:接下来的是title

ActionBar.DISPLAY_SHOW_CUSTOM:然后是定制的View,可以通过void android.app.ActionBar.setCustomView(View view, LayoutParams layoutParams)设置


这里以Email为例,简单分析一下ActionBar在实际中的应用

上面是在模拟器上登录原生Email的截图

 

这里说一下所谓的原生:

原生不是指大家买来手机之后没有修改过任何内容,软件本身的样子。也不是指google的各种亲儿子本身的样子,因为无论是前者还是后者,最起码都是要经过优化和修复bug的,不是亲儿子那就很有可能进行了整容,让你看不出原来的样子

这里的原生是用google发布的源码编译出来的apk,安装到手机或模拟器上面大家就可以看到

 

我们主要看看Email如何使用ActionBar和menu的

页面可以发现,Email既有AcionBar又有menu,menu被放在了底部,先看ActionBar

Email的主Activity是EmailActivity,我就不画类图了,因为总是画不明白,怕误导大家,所以就用类似堆栈log的形式来介绍

Java代码   收藏代码
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     ……  
  4.     initUIController();  
  5.     ……  
  6. }  
  7.   
  8. private void initUIController() {  
  9.     mUIController = UiUtilities.useTwoPane(this)  
  10.             ? new UIControllerTwoPane(this) : new UIControllerOnePane(this);  
  11. }  

这里初始化了一个UIController,其中的关系是这样的

UIControllerBase为父类,是一个抽象类,下面有两个实现类分别为UIControllerOnePane和UIControllerTwoPane(实在理解不了为什么这么命名。。。)

UIControllerOnePane为手机UI实现,UIControllerTwoPane为平板UI实现,我们这里只考虑UIControllerOnePane

Java代码   收藏代码
  1. public UIControllerBase(EmailActivity activity) {  
  2.     mActivity = activity;  
  3.     mFragmentManager = activity.getFragmentManager();  
  4.     mRefreshManager = RefreshManager.getInstance(mActivity);  
  5.     mActionBarController = createActionBarController(activity);  
  6.     if (DEBUG_FRAGMENTS) {  
  7.         FragmentManager.enableDebugLogging(true);  
  8.     }  
  9. }  

这里包含了一个ActionBarController,createActionBarController是抽象方法,下面是UIControllerOnePane的实现

Java代码   收藏代码
  1. @Override  
  2. protected ActionBarController createActionBarController(Activity activity) {  
  3.     // For now, we just reuse the same action bar controller used for 2-pane.  
  4.     // We may change it later.  
  5.     return new ActionBarController(activity, activity.getLoaderManager(),  
  6.             activity.getActionBar(), new ActionBarControllerCallback());  
  7. }  

最关键的就是ActionBarController了,它包含ActionBar所有内容

Java代码   收藏代码
  1. public ActionBarController(Context context, LoaderManager loaderManager,  
  2.         ActionBar actionBar, Callback callback) {  
  3.     mContext = context;  
  4.     mLoaderManager = loaderManager;  
  5.     mActionBar = actionBar;  
  6.     mCallback = callback;  
  7.     mDelayedOperations = new DelayedOperations(Utility.getMainThreadHandler());  
  8.     mAllFoldersLabel = mContext.getResources().getString(  
  9.             R.string.action_bar_mailbox_list_title);  
  10.     mAccountsSelectorAdapter = new AccountSelectorAdapter(mContext);  
  11.   
  12.     // Configure action bar.  
  13.     mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM);  
  14.   
  15.     // Prepare the custom view  
  16.     mActionBar.setCustomView(R.layout.action_bar_custom_view);  
  17.     mActionBarCustomView = (ViewGroup) mActionBar.getCustomView();  
  18.   
  19.     // Account spinner  
  20.     mAccountSpinnerContainer =  
  21.             UiUtilities.getView(mActionBarCustomView, R.id.account_spinner_container);  
  22.     mAccountSpinner = UiUtilities.getView(mActionBarCustomView, R.id.account_spinner);  
  23.     mAccountSpinnerDefaultBackground = mAccountSpinner.getBackground();  
  24.   
  25.     mAccountSpinnerLine1View = UiUtilities.getView(mActionBarCustomView, R.id.spinner_line_1);  
  26.     mAccountSpinnerLine2View = UiUtilities.getView(mActionBarCustomView, R.id.spinner_line_2);  
  27.     mAccountSpinnerCountView = UiUtilities.getView(mActionBarCustomView, R.id.spinner_count);  
  28.   
  29.     // Account dropdown  
  30.     mAccountDropdown = new AccountDropdownPopup(mContext);  
  31.     mAccountDropdown.setAdapter(mAccountsSelectorAdapter);  
  32.   
  33.     mAccountSpinner.setOnClickListener(new View.OnClickListener() {  
  34.         @Override public void onClick(View v) {  
  35.             if (mAccountsSelectorAdapter.getCount() > 0) {  
  36.                 mAccountDropdown.show();  
  37.             }  
  38.         }  
  39.     });  
  40. }  

 从上面可以看出,ActionBar实际上也不复杂,只是设置了显示选项和一个CustomView,复杂的内容几乎全部集中在这个CustomView上了

Java代码   收藏代码
  1. // Configure action bar.  
  2. mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_CUSTOM);  
  3.   
  4. // Prepare the custom view  
  5. mActionBar.setCustomView(R.layout.action_bar_custom_view);  
  6. mActionBarCustomView = (ViewGroup) mActionBar.getCustomView();  

当点击CustomView时候会有弹出下拉框,mAccountDropdown就发挥作用了

Java代码   收藏代码
  1. private class AccountDropdownPopup extends ListPopupWindow  


设置了mAccountSpinner.setOnClickListener,调用到mAccountDropdownshow()

mAccountDropdownshow显示由mAccountsSelectorAdapter提供

Java代码   收藏代码
  1. public class AccountSelectorAdapter extends CursorAdapter  

其中的数据由CursorWithExtras com.android.email.activity.ActionBarController.mCursor提供

mCursor只在一个地方进行刷新,那就是加载账户邮箱信息的时候

Java代码   收藏代码
  1. /** 
  2.  * Load account/mailbox info, and account/recent mailbox list. 
  3.  */  
  4. private void loadAccountMailboxInfo(final long accountId, final long mailboxId) {  
  5.     mLoaderManager.restartLoader(LOADER_ID_ACCOUNT_LIST, null,  
  6.             new LoaderCallbacks<Cursor>() {  
  7.         @Override  
  8.         public Loader<Cursor> onCreateLoader(int id, Bundle args) {  
  9.             return AccountSelectorAdapter.createLoader(mContext, accountId, mailboxId);  
  10.         }  
  11.   
  12.         @Override  
  13.         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {  
  14.             mCursor = (AccountSelectorAdapter.CursorWithExtras) data;  
  15.             updateTitle();  
  16.         }  
  17.   
  18.         @Override  
  19.         public void onLoaderReset(Loader<Cursor> loader) {  
  20.             mCursor = null;  
  21.             updateTitle();  
  22.         }  
  23.     });  
  24. }  

我们可以发现,加载完毕之后Cursor更新了,然后updateTitle,不用去看函数也能猜出这里是更新ActionBar

为了刷新列表,调用了下面语句

Java代码   收藏代码
  1. mAccountsSelectorAdapter.swapCursor(mCursor);  

updateTitle就不再进行分析了,里面几乎都是刷新那个CustomView的内容

 

下面看看底部的菜单,之前的学习中,api demos里并没有演示这种情况,我们用hierarchyviewer查看,发现页面上下都是一个ActionBarContainer的容器

上面的ActionBar我们已经知道它是怎么来的了,那下面的呢?原因在于manifest的配置

 

Java代码   收藏代码
  1. <activity  
  2.     android:name=".activity.EmailActivity"  
  3.     android:uiOptions="splitActionBarWhenNarrow"  
  4.     >  
  5. </activity>  

splitActionBarWhenNarrow使得ActionBar被拆分成上下两个部分

splitActionBarWhenNarrow 用于显示Activity在窄屏设备(如竖屏手机)上运行时的所有menu项。当然,如果手机有menu键的时候,当menu项过多而显示不下的时候按Menu键即可

这时迷题已解,没有什么神秘的地方了,看看menu配置,一目了然

 

Xml代码   收藏代码
  1. <menu xmlns:android="http://schemas.android.com/apk/res/android">  
  2.     <item  
  3.         android:id="@+id/compose"  
  4.         android:orderInCategory="100"  
  5.         android:alphabeticShortcut="c"  
  6.         android:title="@string/compose_action"  
  7.         android:icon="@drawable/ic_menu_compose_normal_holo_light"  
  8.         android:showAsAction="ifRoom"  
  9.     />  
  10.     <item  
  11.         android:id="@+id/search"  
  12.         android:orderInCategory="200"  
  13.         android:alphabeticShortcut="s"  
  14.         android:title="@string/search_action"  
  15.         android:icon="@drawable/ic_menu_search_holo_light"  
  16.         android:showAsAction="ifRoom"  
  17.     />  
  18.     <item  
  19.         android:id="@+id/show_all_mailboxes"  
  20.         android:orderInCategory="300"  
  21.         android:alphabeticShortcut="c"  
  22.         android:title="@string/mailbox_list_account_selector_show_all_folders"  
  23.         android:icon="@drawable/ic_menu_move_to_holo_light"  
  24.         android:showAsAction="ifRoom"  
  25.     />  
  26.     <item  
  27.         android:id="@+id/refresh"  
  28.         android:orderInCategory="400"  
  29.         android:alphabeticShortcut="r"  
  30.         android:title="@string/refresh_action"  
  31.         android:icon="@drawable/ic_menu_refresh_holo_light"  
  32.         android:showAsAction="ifRoom"  
  33.     />  
  34.     <!-- Note the order; we want to show them to the right of delete/move -->  
  35.     <item  
  36.         android:id="@+id/newer"  
  37.         android:orderInCategory="1500"  
  38.         android:icon="@drawable/menu_item_newer"  
  39.         android:showAsAction="always"  
  40.         android:visible="false"  
  41.     />  
  42.     <item  
  43.         android:id="@+id/older"  
  44.         android:orderInCategory="1600"  
  45.         android:icon="@drawable/menu_item_older"  
  46.         android:showAsAction="always"  
  47.         android:visible="false"  
  48.     />  
  49.     <item  
  50.         android:id="@+id/mailbox_settings"  
  51.         android:orderInCategory="2000"  
  52.         android:title="@string/mailbox_settings_action"  
  53.         android:icon="@android:drawable/ic_menu_preferences"  
  54.     />  
  55.     <item  
  56.         android:id="@+id/account_settings"  
  57.         android:orderInCategory="3000"  
  58.         android:title="@string/settings_action"  
  59.         android:icon="@android:drawable/ic_menu_preferences"  
  60.     />  
  61. </menu>  

当我们横屏的时候,menu就会跑到上面去了

 

 

下面是带回退导航的


他能使用户回到上一个页面(并不是上一个activity,回到上一个activity可以通过按back键实现)

 

Java代码   收藏代码
  1. mActionBar.setDisplayOptions(showUp ? ActionBar.DISPLAY_HOME_AS_UP : 0, ActionBar.DISPLAY_HOME_AS_UP);  

在之前的refreshInernal中已经设置过了

 

当我们打开或新建一个邮件的时候,页面会跳转到新的activity

Java代码   收藏代码
  1. public class MessageCompose extends Activity implements OnClickListener, OnFocusChangeListener, DeleteMessageConfirmationDialog.Callback, InsertQuickResponseDialog.Callback  

这里我们看到了,页面依然有回退导航,点击之后能回退到上一个页面,当然,不再是当前的activity了

那么它是如何做到的,代码如下

 

Java代码   收藏代码
  1. @Override  
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3.     if (handleCommand(item.getItemId())) {  
  4.         return true;  
  5.     }  
  6.     return super.onOptionsItemSelected(item);  
  7. }  
  8.   
  9. private boolean handleCommand(int viewId) {  
  10.     switch (viewId) {  
  11.     case android.R.id.home:  
  12.         onBack(false /* systemKey */);  
  13.         return true;  
  14.     ……  
  15. }  
  16.   
  17. /** 
  18.  * Handle a tap to the system back key, or the "app up" button in the action bar. 
  19.  * @param systemKey whether or not the system key was pressed 
  20.  */  
  21. private void onBack(boolean systemKey) {  
  22.     finish();  
  23.     if (isOpenedFromWithinApp()) {  
  24.         // If opened from within the app, we just close it.  
  25.         return;  
  26.     }  
  27.   
  28.     if (isOpenedFromWidget() || !systemKey) {  
  29.         // Otherwise, need to open the main screen for the appropriate account.  
  30.         // Note that mAccount should always be set by the time the action bar is set up.  
  31.         startActivity(Welcome.createOpenAccountInboxIntent(this, mAccount.mId));  
  32.     }  
  33. }  

点击回退导航时,触发的menu是android.R.id.home 

剩下的事情就好办了,这里交给了onBack来处理

 

 

至此Email应用的ActionBar和menu就基本分析完了,如果有不准确的地方还希望大家指正

 

 

最后附上别人的几篇翻译,内容很基础,也比api demos多,值得一看

Android 用户界面---操作栏(Action Bar 一) :基本操作

Android 用户界面---操作栏(Action Bar 二) :分离式ActionBar与回退导航

Android 用户界面---操作栏(Action Bar 三) :ActionView

Android 用户界面---操作栏(Action Bar 四) :ActionProvider与Tab

Android 用户界面---操作栏(Action Bar 五) :主要是关于样式


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值