Android ActionBar彻底解析(上)

  1. }  

这部分代码很简单,仅仅是调用了MenuInflater的inflate()方法来加载menu资源就能够了。如今从新运行一下程序,结果以下图所示:html

能够看到,action_compose和action_delete这两个按钮已经在ActionBar中显示出来了,而action_settings这个按钮因为showAsAction属性设置成了never,因此被隐藏到了overflow当中,只要点击一下overflow按钮就能够看到它了。java

这里咱们注意到,显示在ActionBar上的按钮都只有一个图标而已,咱们在title中指定的文字并无显示出来。没错,title中的内容一般状况下只会在overflow中显示出来,ActionBar中因为屏幕空间有限,默认是不会显示title内容的。可是出于如下几种因素考虑,即便title中的内容没法显示出来,咱们也应该给每一个item中都指定一个title属性:android

  • 当ActionBar中的剩余空间不足的时候,若是Action按钮指定的showAsAction属性是ifRoom的话,该Action按钮就会出如今overflow当中,此时就只有title可以显示了。
  • 若是Action按钮在ActionBar中显示,用户可能经过长按该Action按钮的方式来查看到title的内容。

下载Action按钮图标

Google为咱们绘制好了不少Action按钮,你能够在这里下载到:https://developer.android.com/design/downloads/index.html#action-bar-icon-pack

响应Action按钮的点击事件

当用户点击Action按钮的时候,系统会调用Activity的onOptionsItemSelected()方法,经过方法传入的MenuItem参数,咱们能够调用它的getItemId()方法和menu资源中的id进行比较,从而辨别出用户点击的是哪个Action按钮,好比:[java] view plaincopy在CODE上查看代码片派生到个人代码片ide

  1. @Override  
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3.     switch (item.getItemId()) {  
  4.     case R.id.action_compose:  
  5.         Toast.makeText(this, "Compose", Toast.LENGTH_SHORT).show();  
  6.         return true;  
  7.     case R.id.action_delete:  
  8.         Toast.makeText(this, "Delete", Toast.LENGTH_SHORT).show();  
  9.         return true;  
  10.     case R.id.action_settings:  
  11.         Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show();  
  12.         return true;  
  13.     default:  
  14.         return super.onOptionsItemSelected(item);  
  15.     }  
  16. }  

能够看到,咱们让每一个Action按钮被点击的时候都弹出一个Toast,如今从新运行一下代码,结果以下图所示:ui

 

经过Action Bar图标进行导航

启用ActionBar图标导航的功能,能够容许用户根据当前应用的位置来在不一样界面之间切换。好比,A界面展现了一个列表,点击某一项以后进入了B界面,这时B界面就应该启用ActionBar图标导航功能,这样就能够回到A界面。this

咱们能够经过调用setDisplayHomeAsUpEnabled()方法来启用ActionBar图标导航功能,好比:[java] view plaincopy在CODE上查看代码片派生到个人代码片spa

  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setTitle("天气");  
  5.     setContentView(R.layout.activity_main);  
  6.     ActionBar actionBar = getActionBar();  
  7.     actionBar.setDisplayHomeAsUpEnabled(true);  
  8. }  

如今从新运行一下程序,结果以下图所示:.net

能够看到,在ActionBar图标的左侧出现了一个向左的箭头,一般状况下这都表示返回的意思,所以最简单的实现就是在它的点击事件里面加入finish()方法就能够了,以下所示:[java] view plaincopy在CODE上查看代码片派生到个人代码片设计

  1. @Override  
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3.     switch (item.getItemId()) {  
  4.     case android.R.id.home:  
  5.         finish();  
  6.         return true;  
  7.     ……  
  8.     }  
  9. }  

当点击ActionBar图标的时候,系统一样会调用onOptionsItemSelected()方法,而且此时的itemId是android.R.id.home,因此finish()方法也就是加在这里的了。3d

如今看上去,ActionBar导航和Back键的功能貌似是同样的。没错,若是咱们只是简单地finish了一下,ActionBar导航和Back键的功能是彻底同样的,但ActionBar导航的设计初衷并非这样的,它和Back键的功能仍是有一些区别的,举个例子吧。

上图中的Conversation List是收件箱的主界面,如今咱们点击第一封邮件会进入到Conversation1 details界面,而后点击下一封邮件会进入到Conversation 2 details界面,再点击下一封邮箱会进入到Conversation3 details界面。好的,这个时候若是咱们按下Back键,应该会回到Conversation 2 details界面,再按一次Back键应该回到Conversation1 details界面,再按一次Back键才会回到Conversation List。而ActionBar导航则不该该表现出这种行为,不管咱们当前在哪个Conversation details界面,点击一下导航按钮都应该回到Conversation List界面才对。

这就是ActionBar导航和Back键在设计上的区别,那么该怎样才能实现这样的功能呢?其实并不复杂,实现标准的ActionBar导航功能只需三步走。

第一步咱们已经实现了,就是调用setDisplayHomeAsUpEnabled()方法,并传入true。

第二步须要在AndroidManifest.xml中配置父Activity,以下所示:[html] view plaincopy在CODE上查看代码片派生到个人代码片

  1. <activity  
  2.     android:name="com.example.actionbartest.MainActivity"  
  3.     android:logo="@drawable/weather" >  
  4.     <meta-data  
  5.         android:name="android.support.PARENT_ACTIVITY"  
  6.         android:value="com.example.actionbartest.LaunchActivity" />  
  7. </activity>  

能够看到,这里经过meta-data标签指定了MainActivity的父Activity是LaunchActivity,在Android 4.1版本以后,也能够直接使用android:parentActivityName这个属性来进行指定,以下所示:[html] view plaincopy在CODE上查看代码片派生到个人代码片

  1. <activity  
  2.     android:name="com.example.actionbartest.MainActivity"  
  3.     android:logo="@drawable/weather"  
  4.     android:parentActivityName="com.example.actionbartest.LaunchActivity" >  
  5. </activity>  

第三步则须要对android.R.id.home这个事件进行一些特殊处理,以下所示:[java] view plaincopy在CODE上查看代码片派生到个人代码片

  1. @Override  
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3.     switch (item.getItemId()) {  
  4.     case android.R.id.home:  
  5.         Intent upIntent = NavUtils.getParentActivityIntent(this);  
  6.         if (NavUtils.shouldUpRecreateTask(this, upIntent)) {  
  7.             TaskStackBuilder.create(this)  
  8.                     .addNextIntentWithParentStack(upIntent)  
  9.                     .startActivities();  
  10.         } else {  
  11.             upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
  12.             NavUtils.navigateUpTo(this, upIntent);  
  13.         }  
  14.         return true;  
  15.         ......  
  16.     }  
  17. }  

其中,调用NavUtils.getParentActivityIntent()方法能够获取到跳转至父Activity的Intent,而后若是父Activity和当前Activity是在同一个Task中的,则直接调用navigateUpTo()方法进行跳转,若是不是在同一个Task中的,则须要借助TaskStackBuilder来建立一个新的Task。

这样,就按照标准的规范成功实现ActionBar导航的功能了。

 

添加Action View

ActionView是一种能够在ActionBar中替换Action按钮的控件,它能够容许用户在不切换界面的状况下经过ActionBar完成一些较为丰富的操做。好比说,你须要完成一个搜索功能,就能够将SeachView这个控件添加到ActionBar中。

为了声明一个ActionView,咱们能够在menu资源中经过actionViewClass属性来指定一个控件,例如可使用以下方式添加SearchView:[html] view plaincopy在CODE上查看代码片派生到个人代码片

  1. <menu xmlns:android="http://schemas.android.com/apk/res/android" >  
  2.   
  3.     <item  
  4.         android:id="@+id/action_search"  
  5.         android:icon="@drawable/ic_action_search"  
  6.         android:actionViewClass="android.widget.SearchView"  
  7.         android:showAsAction="ifRoom|collapseActionView"  
  8.         android:title="@string/action_search" />  
  9.     ......  
  10.   
  11. </menu>  

注意在showAsAction属性中咱们还声明了一个collapseActionView,这个值表示该控件能够被合并成一个Action按钮。

如今从新运行一下程序,效果以下图所示:

OK,果真有一个搜索样式的Action按钮出现了,如今点击一下这个搜索按钮,效果以下图所示:

能够看到,这时SearchView就会展开占满整个ActionBar,而其它的Action按钮因为将showAsAction属性设置成了ifRoom,此时都会隐藏到overflow当中。

若是你还但愿在代码中对SearchView的属性进行配置(好比添加监听事件等),彻底没有问题,只须要在onCreateOptionsMenu()方法中获取该ActionView的实例就能够了,代码以下所示:

 
  1. @Override

  2. public boolean onCreateOptionsMenu(Menu menu) {

  3. // Inflate the menu; this adds items to the action bar if it is present.

  4. getMenuInflater().inflate(R.menu.menu_main, menu);

  5. final MenuItem searchItem = menu.findItem(R.id.action_search);

  6. final SearchView searchView = (SearchView) searchItem.getActionView();

  7. // Configure the search info and add any event listeners

  8. if (searchView != null) {

  9. searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {

  10. @Override

  11. public boolean onQueryTextSubmit(String query) {

  12. Toast.makeText(MainActivity.this, query, Toast.LENGTH_SHORT).show();

  13. searchItem.collapseActionView();

  14. return true;

  15. }

  16.  
  17. @Override

  18. public boolean onQueryTextChange(String newText) {

  19. return false;

  20. }

  21. });

  22. }

  23. return true;

  24. }


 

在获得了SearchView的实例以后,就能够任意地配置它的各类属性了。关于SearchView的更多详细用法,能够参考官方文档 http://developer.android.com/guide/topics/search/search-dialog.html 。

除此以外,有些程序可能还但愿在ActionView展开和合并的时候显示不一样的界面,其实咱们只须要去注册一个ActionView的监听器就能实现这样的功能了,代码以下所示:[java] view plaincopy在CODE上查看代码片派生到个人代码片

  1. @Override  
  2. public boolean onCreateOptionsMenu(Menu menu) {  
  3.     MenuInflater inflater = getMenuInflater();  
  4.     inflater.inflate(R.menu.main, menu);  
  5.     MenuItem searchItem = menu.findItem(R.id.action_search);  
  6.     searchItem.setOnActionExpandListener(new OnActionExpandListener() {  
  7.         @Override  
  8.         public boolean onMenuItemActionExpand(MenuItem item) {  
  9.             Log.d("TAG", "on expand");  
  10.             return true;  
  11.         }  
  12.           
  13.         @Override  
  14.         public boolean onMenuItemActionCollapse(MenuItem item) {  
  15.             Log.d("TAG", "on collapse");  
  16.             return true;  
  17.         }  
  18.     });  
  19.     return super.onCreateOptionsMenu(menu);  
  20. }  

能够看到,调用MenuItem的setOnActionExpandListener()方法就能够注册一个监听器了,当SearchView展开的时候就会回调onMenuItemActionExpand()方法,当SearchView合并的时候就会调用onMenuItemActionCollapse()方法,咱们在这两个方法中进行相应的UI操做就能够了。

 

Overflow按钮不显示的状况

虽然如今咱们已经掌握了很多ActionBar的用法,可是当你真正去使用它的时候仍是可能会遇到各类各样的问题,好比不少人都会碰到overflow按钮不显示的状况。明明是一样的一份代码,overflow按钮在有些手机上会显示,而在有些手机上恰恰就不显示,这是为何呢?后来我总结了一下,overflow按钮的显示状况和手机的硬件状况是有关系的,若是手机没有物理Menu键的话,overflow按钮就能够显示,若是有物理Menu键的话,overflow按钮就不会显示出来。好比咱们启动一个有Menu键的模拟器,而后将代码运行到该模拟器上,结果以下图所示:

能够看到,ActionBar最右边的overflow按钮不见了!那么此时咱们如何查看隐藏在overflow中的Action按钮呢?其实很是简单,按一下Menu键,隐藏的内容就会从底部出来了,以下图所示:

看到这里相信很多朋友都想吐槽一下了,这显然是一种很是蛋疼的设计,在不一样手机上居然显示了不一样的界面,并且操做方法也彻底不同,这样会给用户一种很是不习惯的感受。话说Google为何要把ActionBar的overflow设计成这样我也不太理解,可是咱们仍是有办法改变这一默认行为的。

实际上,在ViewConfiguration这个类中有一个叫作sHasPermanentMenuKey的静态变量,系统就是根据这个变量的值来判断手机有没有物理Menu键的。固然这是一个内部变量,咱们没法直接访问它,可是能够经过反射的方式修改它的值,让它永远为false就能够了,代码以下所示:[java] view plaincopy在CODE上查看代码片派生到个人代码片

  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     ......  
  4.     setOverflowShowingAlways();  
  5. }  
  6.   
  7. private void setOverflowShowingAlways() {  
  8.     try {  
  9.         ViewConfiguration config = ViewConfiguration.get(this);  
  10.         Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");  
  11.         menuKeyField.setAccessible(true);  
  12.         menuKeyField.setBoolean(config, false);  
  13.     } catch (Exception e) {  
  14.         e.printStackTrace();  
  15.     }  
  16. }  

这里咱们在onCreate()方法的最后调用了setOverflowShowingAlways()方法,而这个方法的内部就是使用反射的方式将sHasPermanentMenuKey的值设置成false,如今从新运行一下代码,结果以下图所示:

能够看到,即便是在有Menu键的手机上,也能让overflow按钮显示出来了,这样就能够大大增长咱们软件界面和操做的统一性。

 

让Overflow中的选项显示图标

若是你点击一下overflow按钮去查看隐藏的Action按钮,你会发现这部分Action按钮都是只显示文字不显示图标的,以下图所示:

这是官方的默认效果,Google认为隐藏在overflow中的Action按钮都应该只显示文字。固然,若是你认为这样不够美观,但愿在overflow中的Action按钮也能够显示图标,咱们仍然能够想办法来改变这一默认行为。

其实,overflow中的Action按钮应不该该显示图标,是由MenuBuilder这个类的setOptionalIconsVisible方法来决定的,若是咱们在overflow被展开的时候给这个方法传入true,那么里面的每个Action按钮对应的图标就都会显示出来了。调用的方法固然仍然是用反射了,代码以下所示:[java] view plaincopy在CODE上查看代码片派生到个人代码片

  1. @Override  
  2. public boolean onMenuOpened(int featureId, Menu menu) {  
  3.     if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {  
  4.         if (menu.getClass().getSimpleName().equals("MenuBuilder")) {  
  5.             try {  
  6.                 Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);  
  7.                 m.setAccessible(true);  
  8.                 m.invoke(menu, true);  
  9.             } catch (Exception e) {  
  10.             }  
  11.         }  
  12.     }  
  13.     return super.onMenuOpened(featureId, menu);  
  14. }  

能够看到,这里咱们重写了一个onMenuOpened()方法,当overflow被展开的时候就会回调这个方法,接着在这个方法的内部经过返回反射的方法将MenuBuilder的setOptionalIconsVisible变量设置为true就能够了。

如今从新运行一下代码,结果以下图所示:

使用split action bar
即将除Up Button、图标和标题以外的全部item移到底栏上

 
  1. <manifest ...>

  2. <activity uiOptions="splitActionBarWhenNarrow" ... >

  3. <meta-data

  4. android:name="android.support.UI_OPTIONS"

  5. android:value="splitActionBarWhenNarrow" />

  6. </activity>

  7. </manifest>


 

好了,目前为止咱们已经把ActionBar的基础知识介绍完了,那么今天的讲解就到这里,下篇文章中我会带领你们一块儿更深刻地了解ActionBar,感兴趣的朋友请继续阅读 Android ActionBar彻底解析(下)

相关文章
相关标签/搜索