[译]Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)
转载请注明出处: http://blog.csdn.net/guolin_blog/article/details/18234477
本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文。
http://developer.android.com/guide/topics/ui/actionbar.html
Action Bar是一种新増的导航栏功能,在Android 3.0之后加入到系统的API当中,它标识了用户当前操作界面的位置,并提供了额外的用户动作、界面导航等功能。使用ActionBar的好处是,它可以给提供一种全局统一的UI界面,使得用户在使用任何一款软件时都懂得该如何操作,并且ActionBar还可以自动适应各种不同大小的屏幕。下面是一张使用ActionBar的界面截图:
其中,[1]是ActionBar的图标,[2]是两个action按钮,[3]是overflow按钮。
由于Action Bar是在3.0以后的版本中加入的,如果想在2.x的版本里使用ActionBar的话则需要引入 Support Library,不过3.0之前版本的市场占有率已经非常小了,这里简单起见我们就不再考虑去做向下兼容,而是只考虑4.0以上版本的用法。
添加和移除Action Bar
ActionBar的添加非常简单,只需要在AndroidManifest.xml中指定Application或Activity的theme是Theme.Holo或其子类就可以了,而使用Eclipse创建的项目自动就会将Application的theme指定成Theme.Holo,所以ActionBar默认都是显示出来的。新建一个空项目并运行,效果如下图所示:
而如果想要移除ActionBar的话通常有两种方式,一是将theme指定成Theme.Holo.NoActionBar,表示使用一个不包含ActionBar的主题,二是在Activity中调用以下方法:
ActionBar actionBar = getActionBar(); actionBar.hide();现在重新运行一下程序,就可以看到ActionBar不再显示了,如下图所示:
修改Action Bar的图标和标题
<activity android:name="com.example.actionbartest.MainActivity" android:logo="@drawable/weather" > </activity>现在重新运行一下程序,效果如下图所示:
<activity android:name="com.example.actionbartest.MainActivity" android:label="天气" android:logo="@drawable/weather" > </activity>
添加Action按钮
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context="com.example.actionbartest.MainActivity" > <item android:id="@+id/action_compose" android:icon="@drawable/ic_action_compose" android:showAsAction="always" android:title="@string/action_compose"/> <item android:id="@+id/action_delete" android:icon="@drawable/ic_action_delete" android:showAsAction="always" android:title="@string/action_delete"/> <item android:id="@+id/action_settings" android:icon="@drawable/ic_launcher" android:showAsAction="never" android:title="@string/action_settings"/> </menu>可以看到,这里我们通过三个<item>标签定义了三个Action按钮。<item>标签中又有一些属性,其中id是该Action按钮的唯一标识符,icon用于指定该按钮的图标,title用于指定该按钮可能显示的文字(在图标能显示的情况下,通常不会显示文字),showAsAction则指定了该按钮显示的位置,主要有以下几种值可选:always表示永远显示在ActionBar中,如果屏幕空间不够则无法显示,ifRoom表示屏幕空间够的情况下显示在ActionBar中,不够的话就显示在overflow中,never则表示永远显示在overflow中。
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); return super.onCreateOptionsMenu(menu); }这部分代码很简单,仅仅是调用了MenuInflater的inflate()方法来加载menu资源就可以了。现在重新运行一下程序,结果如下图所示:
- 当ActionBar中的剩余空间不足的时候,如果Action按钮指定的showAsAction属性是ifRoom的话,该Action按钮就会出现在overflow当中,此时就只有title能够显示了。
- 如果Action按钮在ActionBar中显示,用户可能通过长按该Action按钮的方式来查看到title的内容。
响应Action按钮的点击事件
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_compose: Toast.makeText(this, "Compose", Toast.LENGTH_SHORT).show(); return true; case R.id.action_delete: Toast.makeText(this, "Delete", Toast.LENGTH_SHORT).show(); return true; case R.id.action_settings: Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show(); return true; default: return super.onOptionsItemSelected(item); } }可以看到,我们让每个Action按钮被点击的时候都弹出一个Toast,现在重新运行一下代码,结果如下图所示:
通过Action Bar图标进行导航
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setTitle("天气"); setContentView(R.layout.activity_main); ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); }现在重新运行一下程序,结果如下图所示:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; …… } }当点击ActionBar图标的时候,系统同样会调用onOptionsItemSelected()方法,并且此时的itemId是android.R.id.home,所以finish()方法也就是加在这里的了。
<activity android:name="com.example.actionbartest.MainActivity" android:logo="@drawable/weather" > <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.actionbartest.LaunchActivity" /> </activity>可以看到,这里通过meta-data标签指定了MainActivity的父Activity是LaunchActivity,在Android 4.1版本之后,也可以直接使用android:parentActivityName这个属性来进行指定,如下所示:
<activity android:name="com.example.actionbartest.MainActivity" android:logo="@drawable/weather" android:parentActivityName="com.example.actionbartest.LaunchActivity" > </activity>第三步则需要对android.R.id.home这个事件进行一些特殊处理,如下所示:
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); if (NavUtils.shouldUpRecreateTask(this, upIntent)) { TaskStackBuilder.create(this) .addNextIntentWithParentStack(upIntent) .startActivities(); } else { upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); NavUtils.navigateUpTo(this, upIntent); } return true; ...... } }其中,调用NavUtils.getParentActivityIntent()方法可以获取到跳转至父Activity的Intent,然后如果父Activity和当前Activity是在同一个Task中的,则直接调用navigateUpTo()方法进行跳转,如果不是在同一个Task中的,则需要借助TaskStackBuilder来创建一个新的Task。
添加Action View
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_search" android:icon="@drawable/ic_action_search" android:actionViewClass="android.widget.SearchView" android:showAsAction="ifRoom|collapseActionView" android:title="@string/action_search" /> ...... </menu>注意在showAsAction属性中我们还声明了一个collapseActionView,这个值表示该控件可以被合并成一个Action按钮。
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); MenuItem searchItem = menu.findItem(R.id.action_search); SearchView searchView = (SearchView) searchItem.getActionView(); // 配置SearchView的属性 ...... return super.onCreateOptionsMenu(menu); }在得到了SearchView的实例之后,就可以任意地配置它的各种属性了。关于SearchView的更多详细用法,可以参考官方文档 http://developer.android.com/guide/topics/search/search-dialog.html 。
@Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); MenuItem searchItem = menu.findItem(R.id.action_search); searchItem.setOnActionExpandListener(new OnActionExpandListener() { @Override public boolean onMenuItemActionExpand(MenuItem item) { Log.d("TAG", "on expand"); return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { Log.d("TAG", "on collapse"); return true; } }); return super.onCreateOptionsMenu(menu); }可以看到,调用MenuItem的setOnActionExpandListener()方法就可以注册一个监听器了,当SearchView展开的时候就会回调onMenuItemActionExpand()方法,当SearchView合并的时候就会调用onMenuItemActionCollapse()方法,我们在这两个方法中进行相应的UI操作就可以了。
Overflow按钮不显示的情况
@Override protected void onCreate(Bundle savedInstanceState) { ...... setOverflowShowingAlways(); } private void setOverflowShowingAlways() { try { ViewConfiguration config = ViewConfiguration.get(this); Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); menuKeyField.setAccessible(true); menuKeyField.setBoolean(config, false); } catch (Exception e) { e.printStackTrace(); } }这里我们在onCreate()方法的最后调用了setOverflowShowingAlways()方法,而这个方法的内部就是使用反射的方式将sHasPermanentMenuKey的值设置成false,现在重新运行一下代码,结果如下图所示:
让Overflow中的选项显示图标
@Override public boolean onMenuOpened(int featureId, Menu menu) { if (featureId == Window.FEATURE_ACTION_BAR && menu != null) { if (menu.getClass().getSimpleName().equals("MenuBuilder")) { try { Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); m.setAccessible(true); m.invoke(menu, true); } catch (Exception e) { } } } return super.onMenuOpened(featureId, menu); }可以看到,这里我们重写了一个onMenuOpened()方法,当overflow被展开的时候就会回调这个方法,接着在这个方法的内部通过返回反射的方法将MenuBuilder的setOptionalIconsVisible变量设置为true就可以了。