zoukankan      html  css  js  c++  java
  • 【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解

    一、ActionBar介绍

      在Android 3.0中除了我们重点讲解的Fragment外,Action Bar也是一个非常重要的交互元素,Action Bar取代了传统的tittle bar和menu,在程序运行中一直置于顶部,对于Android平板设备来说屏幕更大它的标题使用Action Bar来设计可以展示更多丰富的内容,方便操控。

     

    二、ActionBar的功能

      用图的方式来讲解它的功能

      

      <1> ActionBar的图标,可显示软件图标,也可用其他图标代替。当软件不在最高级页面时,图标左侧会显示一个左箭头,用户可以通过这个箭头向上导航;

      <2> 如果你的应用要在不同的View中显示数据,这部分允许用户来切换视图。一般的作法是用一个下拉菜单或者是Tab选项卡。如果只有一个界面,那这里可以显示应用程序的标题或者是更长一点的商标信息;

      <3> 两个action按钮,这里放重要的按钮功能,为用户进行某项操作提供直接的访问;

      <4> overflow按钮,放不下的按钮会被置于“更多...”菜单项中,“更多...”菜单项是以下拉形式实现的。

     

    三、ActionBar 奥义·详解

    1、添加ActionBar

        ActionBar的添加非常简单,只需要在AndroidManifest.xml中指定Application或Activity的theme是 Theme.Holo或其子类就可以了,在Android 3.0及更高的版本中,Activity中都默认包含有ActionBar组件。

     

    2、取消ActionBar

      如果需要隐藏Action Bar可以在你的Activity的属性中设置主题风格为NoTitleBar在你的manifest文件中

    <activity android:theme="@android:style/Theme.NoTitleBar">

      还有一种做法,在运行时调用hide()方法也可以隐藏ActionBar,调用show()方法来显示ActionBar()。

    ActionBar actionBar = getActionBar();  
    actionBar.hide();  

      当你隐藏ActionBar时,系统会将Activity的整个内容充满整个空间。

      注意:如果使用一个主题(theme)来移除Activity上得ActionBar,那么窗口将不再会有ActionBar,因此在运行时也就没有办法来添加ActionBar——调用getActionBar()方法会返回null值。

     

    3.修改Action Bar的图标和标题

    默认情况下,系统会使用<application>或者<activity>中icon属性指定的图片来作为ActionBar 的图标,但是我们也可以改变这一默认行为。如果我们想要使用另外一张图片来作为ActionBar的图标,可以在<application> 或者<activity>中通过logo属性来进行指定,而标题中的内容使用label属性来指定。比如项目的res/drawable目录 下有一张cnblog_icon.png图片,就可以在AndroidManifest.xml中这样指定:

     <activity
                android:name=".MainActivity"
                android:label="召唤ActionBar吧"
                android:logo="@drawable/cnblog_icon" >

    效果图如下:

     

    4.添加Action按钮

    ActionBar还可以根据应用程序当前的功能来提供与其相关的Action按钮,这些按钮都会以图标或文字的形式直接显示在ActionBar上。当 然,如果按钮过多,ActionBar上显示不完,多出的一些按钮可以隐藏在overflow里面(最右边的三个点就是overflow按钮),点击一下 overflow按钮就可以看到全部的Action按钮了。

    当Activity启动的时候,系统会调用Activity的onCreateOptionsMenu()方法来取出所有的Action按钮,我们只需要在这个方法中去加载一个menu资源,并把所有的Action按钮都定义在资源文件里面就可以了。
    那么我们先来看下menu资源文件该如何定义,代码如下所示:
    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item
            android:id="@+id/user_p"
            android:icon="@drawable/icon_user_p"
            android:showAsAction="always"
            android:title="用户"/>
        <item
            android:id="@+id/write_p"
            android:icon="@drawable/icon_write_p"
            android:showAsAction="always"
            android:title="发布"/>
        <item
            android:id="@+id/favo_p"
            android:icon="@drawable/icon_favo_p"
            android:showAsAction="never"
            android:title="收藏"/>
    
    </menu>
    复制代码

    可以看到,这里 我们通过两个<item>标签定义了三个Action按钮。<item>标签中又有一些属性,其中id是该Action按钮的唯 一标识符,icon用于指定该按钮的图标,title用于指定该按钮可能显示的文字(在图标能显示的情况下,通常不会显示文 字),actionViewClass用来指定一个构建视窗所使用的布局资源,showAsAction则指定了该按钮显示的位置,主要有以下几种值可 选:

    fRoom 会显示在Item中,但是如果已经有4个或者4个以上的Item时会隐藏在溢出列表中。当然个
    数并不仅仅局限于4个,依据屏幕的宽窄而定
    never 永远不会显示。只会在溢出列表中显示,而且只显示标题,所以在定义item的时候,最好
    把标题都带上。
    always 无论是否溢出,总会显示。
    withText withText值示意Action bar要显示文本标题。Action bar会尽可能的显示这个
    标题,但是,如果图标有效并且受到Action bar空间的限制,文本标题有可
    能显示不全。
       collapseActionView   声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开。否则,
    这个操作视窗在默认的情况下是可见的,并且即便在用于不适用的时候,也要占据操作栏的有效空间。
    一般要配合ifRoom一起使用才会有效果。

     

    接着,重写Activity的onCreateOptionsMenu()方法,代码如下所示:

    复制代码
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) {  
        MenuInflater inflater = getMenuInflater();  
        inflater.inflate(R.menu.menu_main, menu);  
    return super.onCreateOptionsMenu(menu);  
    }  
    复制代码

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

    可以看到,menu_search和menu_setting这两个按钮已经在ActionBar中显示出来了,而menu_delete这个按钮由于 showAsAction属性设置成了never,所以被隐藏到了overflow当中,只要点击一下overflow按钮就可以看到它了。

    这里我们注意到,显示在ActionBar上的按钮都只有一个图标而已,我们在title中指定的文字并没有显示出来。没错,title中的内容通常情况 下只会在overflow中显示出来,ActionBar中由于屏幕空间有限,默认是不会显示title内容的。但是出于以下几种因素考虑,即使 title中的内容无法显示出来,我们也应该给每个item中都指定一个title属性:
    • 当ActionBar中的剩余空间不足的时候,如果Action按钮指定的showAsAction属性是ifRoom的话,该Action按钮就会出现在overflow当中,此时就只有title能够显示了。
    • 如果Action按钮在ActionBar中显示,用户可能通过长按该Action按钮的方式来查看到title的内容。

     

    5.响应Action按钮的点击事件

    当用户点击 Action按钮的时候,系统会调用Activity的onOptionsItemSelected()方法,通过方法传入的MenuItem参数,我们 可以调用它的getItemId()方法和menu资源中的id进行比较,从而辨别出用户点击的是哪一个Action按钮,比如:

    复制代码
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
             switch (item.getItemId()) {  
             case R.id.user_p:  
                    Toast.makeText(this, "你点击了“用户”按键!", Toast.LENGTH_SHORT).show();  
                    return true;  
                case R.id.write_p:  
                    Toast.makeText(this, "你点击了“发布”按键!", Toast.LENGTH_SHORT).show();  
                    return true;  
                case R.id.favo_p:  
                    Toast.makeText(this, "你点击了“收藏”按键!", Toast.LENGTH_SHORT).show();  
                    return true;  
                default:  
                    return super.onOptionsItemSelected(item);  
                }  
        }
    复制代码

    可以看到,我们让每个Action按钮被点击的时候都弹出一个Toast,现在重新运行一下代码,结果如下图所示:

     

    5.通过Action Bar图标进行导航

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

    我们可以通过调用setDisplayHomeAsUpEnabled()方法来启用ActionBar图标导航功能,比如:
    setTitle("Yanis");  
    setContentView(R.layout.activity_main);
    ActionBar actionBar = getActionBar();  
    actionBar.setDisplayHomeAsUpEnabled(true); 

    现在重新运行一下程序,结果如下图所示:

    可以看到,在ActionBar图标的左侧出现了一个向左的箭头,通常情况下这都表示返回的意思,因此最简单的实现就是在它的点击事件里面加入finish()方法就可以了,如下所示:

    复制代码
    switch (item.getItemId()) {  
            case android.R.id.home:  
                finish();
                return true; 
            ...
            }  
    复制代码
    当点击ActionBar图标的时候,系统同样会调用onOptionsItemSelected()方法,并且此时的itemId是android.R.id.home,所以finish()方法也就是加在这里的了。
    现在看上去,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,如下所示:

     <activity android:name="com.yanis.actionbar.TabActivity">
                 <meta-data
                    android:name="android.support.PARENT_ACTIVITY"
                    android:value="com.yanis.actionbar.MainActivity" />
            </activity>

    可以看到,这里通过meta-data标签指定了MainActivity的父Activity是LaunchActivity,在Android 4.1版本之后,也可以直接使用android:parentActivityName这个属性来进行指定,如下所示:

    <activity 
        android:name="com.yanis.actionbar.TabActivity"
        android:parentActivityName="com.yanis.actionbar.MainActivity" > 
    </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。

    这样,就按照标准的规范成功实现ActionBar导航的功能了。
    效果图如下:

     

    6.添加Action View

    ActionView是一种可以在ActionBar中替换Action按钮的控件,它可以允许用户在不切换界面的情况下通过ActionBar完成一些较为丰富的操作。比如说,你需要完成一个搜索功能,就可以将SeachView这个控件添加到ActionBar中。

    为了声明一个ActionView,我们可以在menu资源中通过actionViewClass属性来指定一个控件,还记得前面写过的吗:
    <item
            android:id="@+id/action_search"
            android:actionViewClass="android.widget.SearchView"
            android:showAsAction="always"
            android:title="搜索"/>

    如果你还希望在代码中对SearchView的属性进行配置(比如添加监听事件等),完全没有问题,只需要在onCreateOptionsMenu()方法中获取该ActionView的实例就可以了,代码如下所示:

    复制代码
    @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

    除此之外,有些程序可能还希望在ActionView展开和合并的时候显示不同的界面,其实我们只需要去注册一个ActionView的监听器就能实现这样的功能了,代码如下所示:

    复制代码
    @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操作就可以了。

     

    7.Overflow按钮不显示的情况

    虽然现在我们已 经掌握了不少ActionBar的用法,但是当你真正去使用它的时候还是可能会遇到各种各样的问题,比如很多人都会碰到overflow按钮不显示的情 况。明明是同样的一份代码,overflow按钮在有些手机上会显示,而在有些手机上偏偏就不显示,如下图:

    可以看到,ActionBar最右边的overflow按钮不见,按一下Menu键,隐藏在overflow中的Action按钮就会从底部出来。

    有人总结了一 下,overflow按钮的显示情况和手机的硬件情况是有关系的,如果手机没有物理Menu键的话,overflow按钮就可以显示,如果有物理Menu 键的话,overflow按钮就不会显示出来。比如我们启动一个有Menu键的模拟器,然后将代码运行到该模拟器上

    实际上,在 ViewConfiguration这个类中有一个叫做sHasPermanentMenuKey的静态变量,系统就是根据这个变量的值来判断手机有没有 物理Menu键的。当然这是一个内部变量,我们无法直接访问它,但是可以通过反射的方式修改它的值,让它永远为false就可以了,代码如下所示:

    复制代码
    @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,现在重新运行一下代码,结果如下图所示:

    可以看到,即使是在有Menu键的手机上,也能让overflow按钮显示出来了,这样就可以大大增加我们软件界面和操作的统一性。

     

    8.让Overflow中的选项显示图标

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

    这是官方的默认效果,Google认为隐藏在overflow中的Action按钮都应该只显示文字。当然,如果你认为这样不够美观,希望在overflow中的Action按钮也可以显示图标,我们仍然可以想办法来改变这一默认行为。

    其实,overflow中的Action按钮应不应该显示图标,是由 MenuBuilder这个类的setOptionalIconsVisible变量来决定的,如果我们在overflow被展开的时候将这个变量赋值为 true,那么里面的每一个Action按钮对应的图标就都会显示出来了。赋值的方法当然仍然是用反射了,代码如下所示:
    复制代码
    @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就可以了。

    现在重新运行一下代码,结果如下图所示:  
     
    9.添加Action Provider

    和Action View有点类似,Action Provider也可以将一个Action按钮替换成一个自定义的布局。但不同的是,Action Provider能够完全控制事件的所有行为,并且还可以在点击的时候显示子菜单。

    为了添加一个 Action Provider,我们需要在<item>标签中指定一个actionViewClass属性,在里面填入Action Provider的完整类名。我们可以通过继承ActionProvider类的方式来创建一个自己的Action Provider,同时,Android也提供好了几个内置的Action Provider,比如说ShareActionProvider。

    由于每个Action Provider都可以自由地控制事件响应,所以它们不需要在onOptionsItemSelected()方法中再去监听点击事件,而是应该在onPerformDefaultAction()方法中去执行相应的逻辑。

    那么我们就先来看一下ShareActionProvider的简单用法吧,编辑menu资源文件,在里面加入ShareActionProvider的声明,如下所示:

    复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android" >
    
        <item 
            android:id="@+id/action_share" 
            android:actionProviderClass="android.widget.ShareActionProvider" 
            android:showAsAction="ifRoom" 
            android:title="分享" /> 
    ...
    </menu>
    复制代码

    注意,ShareActionProvider会自己处理它的显示和事件,但我们仍然要记得给它添加一个title,以防止它会在overflow当中出现。

     

    接着剩下的事情 就是通过Intent来定义出你想分享哪些东西了,我们只需要在onCreateOptionsMenu()中调用MenuItem的 getActionProvider()方法来得到该ShareActionProvider对象,再通过setShareIntent()方法去选择构 建出什么样的一个Intent就可以了。代码如下所示:

    复制代码
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.menu_main, menu);
            MenuItem shareItem = menu.findItem(R.id.action_share);
            ShareActionProvider provider = (ShareActionProvider) shareItem
                    .getActionProvider();
            provider.setShareIntent(getDefaultIntent());
            return super.onCreateOptionsMenu(menu);
        }
    
        private Intent getDefaultIntent() {
            Intent intent = new Intent(Intent.ACTION_SEND);
            intent.setType("image/*");
            return intent;
        }
    复制代码

    可以看到,这里我们通过getDefaultIntent()方法来构建了一个Intent,该Intent表示会将所有可以共享图片的程度都列出来。重新运行一下程序,效果如下图所示:

    细心的你一定观察到了,这个ShareActionProvider点击之后是可以展开的,有点类似于overflow的效果,这就是Action Provider的子菜单。

     

    10.添加导航Tabs

    Tabs的应用 可以算是非常广泛了,它可以使得用户非常轻松地在你的应用程序中切换不同的视图。而Android官方更加推荐使用ActionBar中提供的Tabs功 能,因为它更加的智能,可以自动适配各种屏幕的大小。比如说,在平板上屏幕的空间非常充足,Tabs会和Action按钮在同一行显示,如下图所示:

    而如果是在手机上,屏幕的空间不够大的话,Tabs和Action按钮则会分为两行显示,如下图所示:

    下面我们就来看一下如何使用ActionBar提供的Tab功能,大致可以分为以下几步:

    1. 实现ActionBar.TabListener接口,这个接口提供了Tab事件的各种回调,比如当用户点击了一个Tab时,你就可以进行切换Tab的操作。

    2.为每一个你想添加的Tab创建一个ActionBar.Tab的实例,并且调用setTabListener()方法来设置ActionBar.TabListener。除此之外,还需要调用setText()方法来给当前Tab设置标题。

    3.最后调用ActionBar的addTab()方法将创建好的Tab添加到ActionBar中。

    看起来并不复杂,总共就只有三步,那么我们现在就来尝试一下吧。首先第一步需要创建一个实现ActionBar.TabListener接口的类,代码如下所示:

    复制代码
    package com.yanis.yc_ui_actionbar_tab;
    
    import android.app.ActionBar;
    import android.app.ActionBar.Tab;  
    import android.app.Activity;  
    import android.app.Fragment;  
    import android.app.FragmentTransaction; 
    
    public 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.
        }
    }
    复制代码

     

    这段代码并不 长,我们简单分析一下。当Tab被选中的时候会调用onTabSelected()方法,在这里我们先判断mFragment是否为空,如果为空的话就创 建Fragment的实例并调用FragmentTransaction的add()方法,如果不会空的话就调用FragmentTransaction 的attach()方法。

     

    而当Tab没有被选中的时候,则调用FragmentTransaction的detach()方法,将UI资源释放掉。

    当Tab被重新选中的时候会调用onTabReselected()方法,如果没有特殊需求的话,通常是不需要进行处理的。

    接下来第二步要给每一个Tab创建一个ActionBar.Tab的实例,在此之前要先准备好每个Tab页对应的Fragment。比如说这里我们想创建三个Tab页,准备好这三个Tab页对应的Fragment和对应的布局文件。

    复制代码
    package com.yanis.yc_ui_actionbar_tab;
    
    import android.os.Bundle;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class Fragment1 extends android.app.Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
    
            return inflater.inflate(R.layout.fragment1, container, false);
        }
    }
    复制代码

     

    没有什么实质性的代码,只是显示了指定的布局文件。

    Fragment都准备好了之后,接下来就可以开始创建Tab实例了,创建好了之后则再调用addTab()方法添加到ActionBar当中,这两步通常都是在Activity的onCreate()方法中执行的,代码如下:

    复制代码
    package com.yanis.actionbar;
    
    import android.app.ActionBar;
    import android.app.ActionBar.Tab;
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.app.NavUtils;
    import android.support.v4.app.TaskStackBuilder;
    import android.view.MenuItem;
    
    public class TabActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_tab);
    
            initView();
        }
    
        private void initView() {
            // 提示getActionBar方法一定在setContentView后面
            ActionBar actionBar = getActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    
            // 添加Tab选项
            Tab tab = actionBar
                    .newTab()
                    .setText("澳门风云2")
                    .setTabListener(
                            new TabListener<Fragment1>(this, "film1",
                                    Fragment1.class));
            actionBar.addTab(tab);
    
            tab = actionBar
                    .newTab()
                    .setText("五十度灰")
                    .setTabListener(
                            new TabListener<Fragment2>(this, "film2",
                                    Fragment2.class));
            actionBar.addTab(tab);
            tab = actionBar
                    .newTab()
                    .setText("爸爸去哪儿2")
                    .setTabListener(
                            new TabListener<Fragment3>(this, "film3",
                                    Fragment3.class));
            actionBar.addTab(tab);
        }
    
        @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;
            default:
                return super.onOptionsItemSelected(item);
            }
        }
    
    }
    复制代码

    可以看到,这里 是使用连缀的写法来创建Tab的。首先调用ActionBar的newTab()方法来创建一个Tab实例,接着调用了setText()方法来设置标 题,然后再调用setTabListener()方法来设置事件监听器,最后再调用ActionBar的addTab()方法将Tab添加到 ActionBar中。

    好了,这样的话代码就编写完了,效果如下图所示:

     

    11.添加下拉列表导航

    1.1 简单介绍

    作为Activity内部的另一种导航(或过滤)模式,操作栏提供了内置的下拉列表。下拉列表能够提供Activity中内容

    的不同排序模式。

    启用下拉式导航的基本过程如下:

    <1> 创建一个给下拉提供可选项目的列表,以及描画列表项目时所使用的布局;

    <2> 实现ActionBar.OnNavigationListener回调,在这个回调中定义当用户选择列表中一个项目时所发生的行为;

    <3> 用setNavigationMode()方法该操作栏启用导航模式;

    <4> 用setListNavigationCallbacks()方法给下拉列表设置回调方法。

     

    1.2 效果图如下:

    1.3 代码实现

    ①准备列表数据( strings.xml)

     

        <string-array name="action_list">
            <item>Fragment1</item>
            <item>Fragment2</item>
            <item>Fragment3</item>
        </string-array>

    ②然后就是主界面代码了

    复制代码
    package com.yanis.actionbar;
    
    import android.app.ActionBar;
    import android.app.ActionBar.OnNavigationListener;
    import android.app.Activity;
    import android.app.Fragment;
    import android.app.TaskStackBuilder;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v4.app.NavUtils;
    import android.view.MenuItem;
    import android.widget.ArrayAdapter;
    import android.widget.SpinnerAdapter;
    
    public class ListActivity extends Activity {
        private OnNavigationListener mOnNavigationListener;
        private String[] arry_list;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_list);
    
            initView();
        }
    
        /**
         * 初始化组件
         */
        private void initView() {
            ActionBar actionBar = getActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
            // //导航模式必须设为NAVIGATION_MODE_LIST
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
    
            // 定义一个下拉列表数据适配器
            SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
                    R.array.action_list,
                    android.R.layout.simple_spinner_dropdown_item);
            arry_list = getResources().getStringArray(R.array.action_list);
            mOnNavigationListener = new OnNavigationListener() {
    
                @Override
                public boolean onNavigationItemSelected(int position, long itemId) {
                    Fragment newFragment = null;
                    switch (position) {
                    case 0:
                        newFragment = new Fragment1();
                        break;
                    case 1:
                        newFragment = new Fragment2();
                        break;
                    case 2:
                        newFragment = new Fragment3();
                        break;
                    default:
                        break;
                    }
                    getFragmentManager()
                            .beginTransaction()
                            .replace(R.id.container, newFragment,
                                    arry_list[position]).commit();
                    return true;
                }
            };
            actionBar.setListNavigationCallbacks(mSpinnerAdapter,
                    mOnNavigationListener);
        }
    
        @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;
            default:
                return super.onOptionsItemSelected(item);
            }
        }
    }
    复制代码

     

    12.自定义ActionBar样式

    虽说ActionBar给用户提供了一种全局统一的界面风格和操作方式,但这并不意味着所有应用程序的ActionBar都必须要长得一模一样。如果你需要修改ActionBar的样式来更加好地适配你的应用,可以非常简单地通过Android样式和主题来实现。

    其实Android内置的几个Activity主题中就已经包含了"dark"或"light"这样的ActionBar样式了,同时你也可以继承这些主题,然后进行更深一步的定制。

    1. 使用主题

    Android中有两个最基本的Activity主题可以用于指定ActionBar的颜色,分别是:
    深色系主题样式的效果如下图所示:
     
    浅色系主题样式的效果如下图所示:
     
    你可以将这些主题应用到你的整个应用程序,也可以只应用于某个 Activity。通过在AndroidManifest.xml文件中给<application>或<activity>标 签指定android:theme属性就可以实现了。比如:
    <application android:theme="@android:style/Theme.Holo.Light" ... />

    如果你只想让ActionBar使用深色系的主题,而Activity的内容部分仍然使用浅色系的主题,可以通过声明Theme.Holo.Light.DarkActionBar这个主题来实现,效果如下图所示:

    2. 自定义背景

    如果想要修改ActionBar的背景,我们可以通过创建一个自定义主题并 重写actionBarStyle属性来实现。这个属性可以指向另外一个样式,然后我们在这个样式中重写background这个属性就可以指定一个 drawable资源或颜色,从而实现自定义背景的功能。
    编辑styles.xml文件,在里面加入一个自定义的主题,如下所示:
    复制代码
    <resources>  
     
        <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light">  
            <item name="android:actionBarStyle">@style/MyActionBar</item>  
        </style>  
     
        <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar">  
            <item name="android:background">#f4842d</item>  
        </style>  
     
    </resources>  
    复制代码

    可以看到,这里 我们定义了一个CustomActionBarTheme主题,并让它继承自Theme.Holo.Light。然后在其内部重写了 actionBarStyle这个属性,然后将这个属性指向了MyActionBar这个样式,我们在这个样式中又重写了background属性,并给 它指定了一个背景色。

    现在重新运行一下程序,效果如下图所示:
    这样我们就成功修改ActionBar的背景色了。不过现在看上去还有点怪怪的,因为只是ActionBar的背景色改变了,Tabs的背景色还是原来的样子,这样就感觉不太协调。那么下面我们马上就来修改一下Tabs的背景色,编辑styles.xml文件,如下所示:
    复制代码
    <resources> 
     
        <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> 
            <item name="android:actionBarStyle">@style/MyActionBar</item> 
        </style> 
     
        <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> 
            <item name="android:background">#f4842d</item> 
            <item name="android:backgroundStacked">#d27026</item> 
        </style> 
     
    </resources> 
    复制代码

    可以看到,这里又重写了backgroundStacked属性,这个属性就是用于指定Tabs背景色的。那么再次重新运行程序,效果如下图所示:

    3. 自定义文字颜色

    现在整个ActionBar的颜色是属于偏暗系的,而ActionBar中文字的颜色又偏偏是黑色的,所以看起来并不舒服,那么接下来我们就学习一下如果自定义文字颜色,将文字颜色改成白色。

    修改styles.xml文件,如下所示:
    复制代码
    <resources> 
     
        ......  
     
        <style name="MyActionBar" parent="@android:style/Widget.Holo.Light.ActionBar"> 
            ......  
            <item name="android:titleTextStyle">@style/MyActionBarTitleText</item> 
        </style> 
     
        <style name="MyActionBarTitleText" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title"> 
            <item name="android:textColor">#fff</item> 
        </style> 
     
    </resources> 
    复制代码

    可以看到,这里在MyActionBar样式里面重写了titleTextStyle属性,并将它指向了另一个自定义样式MyActionBarTitleText,接着我们在这个样式中指定textColor的颜色是#fff,也就是白色。

    现在重新运行一下程序,结果如下图所示:
    OK,ActionBar标题文字的颜色已经成功改成白色了,那Tab标题的文字又该怎么修改呢?继续编辑styles.xml文件,如下所示:
    复制代码
    <resources> 
     
        <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> 
            <item name="android:actionBarStyle">@style/MyActionBar</item> 
            <item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item> 
        </style> 
          
        <style name="MyActionBarTabText" 
               parent="@android:style/Widget.Holo.ActionBar.TabText"> 
            <item name="android:textColor">#fff</item> 
        </style> 
     
    </resources> 
    复制代码

    这里我们在CustomActionBarTheme主题中重写actionBarTabTextStyle属性,并将它指向一个新建的MyActionBarTabText样式,然后在这个样式中重写textColor属性,将颜色指定为白色即可。

    重新运行一下程序,结果如下图所示:

    4. 自定义Tab Indicator

    为了可以明确分辨出我们当前选中的是哪一个Tab项,通常情况下都会在选中 Tab的下面加上一条横线作为标识,这被称作Tab Indicator。那么上图中的Tab Indicator是蓝色的,明显和整体风格不相符,所以我们接下来就学习一下如何自定义Tab Indicator。
    首先我们需要重写actionBarTabStyle这个属性,然后将它指 向一个新建的Tab样式,然后重写background这个属性即可。需要注意的是,background必须要指定一个state-list drawable文件,这样在各种不同状态下才能显示出不同的效果。
    那么在开始之前,首先我们需要准备四张图片,分别用于表示Tab的四种状态,如下所示:
          
    这四张图片分别表示Tab选中未按下,选中且按下,未选中未按下,未选中且按下这四种状态,那么接着新建res/drawable/actionbar_tab_indicator.xml文件,代码如下所示:
    复制代码
    <?xml version="1.0" encoding="utf-8"?> 
    <selector xmlns:android="http://schemas.android.com/apk/res/android"> 
     
        <item  android:state_selected="false" 
              android:state_pressed="false" 
              android:drawable="@drawable/tab_unselected" /> 
        <item android:state_selected="true" 
              android:state_pressed="false" 
              android:drawable="@drawable/tab_selected" /> 
        <item android:state_selected="false" 
              android:state_pressed="true" 
              android:drawable="@drawable/tab_unselected_pressed" /> 
        <item android:state_selected="true" 
              android:state_pressed="true" 
              android:drawable="@drawable/tab_selected_pressed" /> 
     
    </selector> 
    复制代码

    四种状态分别引用了四张图片,这样就把state-list drawable文件写好了。接着修改style.xml文件,代码如下所示:

    复制代码
    <resources> 
     
        <style name="CustomActionBarTheme" parent="@android:style/Theme.Holo.Light"> 
            ......  
            <item name="android:actionBarTabStyle">@style/MyActionBarTabs</item> 
        </style> 
          
        <style name="MyActionBarTabs" parent="@android:style/Widget.Holo.ActionBar.TabView"> 
            <item name="android:background">@drawable/actionbar_tab_indicator</item> 
        </style> 
     
    </resources> 
    复制代码

    这里先是重写了actionBarTabStyle这个属性,并将它指向了另一个自定义样式MyActionBarTabs,接着在这个样式中重写background属性,然后指向我们刚才创建的actionbar_tab_indicator即可。

    现在重新运行一下程序,效果如下所示:
    可以看到,Tab Indicator的颜色已经变成了白色,这样看上去就协调得多了。
    除此之外,Action Bar还有许许多多的属性可以进行自定义,这里我们无法一一涵盖到本篇文章中,更多的自定义属性请参考官方文档进行学习。
  • 相关阅读:
    Codeforces 1316B String Modification
    Codeforces 1305C Kuroni and Impossible Calculation
    Codeforces 1305B Kuroni and Simple Strings
    Codeforces 1321D Navigation System
    Codeforces 1321C Remove Adjacent
    Codeforces 1321B Journey Planning
    Operating systems Chapter 6
    Operating systems Chapter 5
    Abandoned country HDU
    Computer HDU
  • 原文地址:https://www.cnblogs.com/yaowen/p/5482331.html
Copyright © 2011-2022 走看看