V7下的ActionBar

转载请注明:http://blog.csdn.net/ganklun/article/details/44171039

微信、手机淘宝、优酷等等各种各样的APP主页面的台头部分都会有这么一个引导组件,几乎"满大街"的APP都充斥着这玩意。它就是ActionBar,这货为什么那么流行,有什么优势,事实上,它的功能完全可以由我们自己的ViewGroup结合PopWindow或者PopMenu实现,从用户体验的角度来讲甚至看不出任何差别。那么我们为什么要学它?确实,从用户体验的角度来说,确实是可以用其它手段实现的,但是别忘了还有我们开发者,它的好处就是可重用,可配置,方便管理和维护。试想一个场景,你有一个BaseActivity,你所有需要使用ActionBar的Activity都继承于它,那么它们都会有同样的ActionBar,并且可以修改配置,就像Web网站上的Banner条。

 


一、当下流行的ActionBar

目前我们使用的ActionBar主要有三种,第一种是我们Android 3.0以后的ActionBar,只要是继承Activity就可以通过getActionBar()方法取到ActionBar对象。但是如果要兼容2.1以上的版本怎么办呢。目前市面上有流行的ActionBar组件SherlockActionBar,也是十分好用。谷歌后来又推出了2.1兼容包V7,它下面也有一个ActionBar,也可以兼容2.1版本,我们暂且叫它SupportActionBar,取这个名字也不是随便取的,因为它需要我们的Activity继承一个叫ActionBarActivity的父类,通过getSupportActionBar()方法也可以获得ActionBar对象。因此本篇内容主要就是讲这个SupportActionBar.


二、配置SupportActionBar

首先我们需要在AndroidManifast里配置我们Activity的主题为AppCompat主题,比如

 <activity android:name="com.czl.struct.activity.ActionBarShowActivity"
           android:theme="@style/Theme.AppCompat.Light">        
 </activity>
当然我们首先需要将android-support-v7-appcompat作为library引入我们的项目。才可以获得到这个主题。接下来我们看我们的ActionBar布局,其实ActionBar的使用和我们的Menu菜单组件是密切相关的。我们之所以能看见我们Actionbar的视图,就是通过Menu的XML布局来实现的。看下面代码:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" >

    <item
        android:id="@+id/share"
        android:icon="@drawable/ic_action_share"
        android:orderInCategory="1"
        android:title="@string/txt_share"
        app:actionProviderClass="android.support.v7.widget.ShareActionProvider"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/search"
        android:icon="@drawable/ic_action_search"
        android:orderInCategory="10"
        android:title="@string/txt_search"
        app:showAsAction="ifRoom|withText"/>
    <item
        android:id="@+id/add"
        android:icon="@drawable/ic_action_new"
        android:orderInCategory="20"
        android:title="@string/txt_add"
        app:actionProviderClass="com.czl.struct.widget.MyActionProvider"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/edit"
        android:icon="@drawable/ic_action_edit"
        android:orderInCategory="30"
        android:title="@string/txt_edit"
        app:showAsAction="always|withText">
        <menu>
            <item
                android:id="@+id/abort"
                android:icon="@drawable/ic_action_discard"     
                android:orderInCategory="50"
                android:title="@string/txt_abort"
                app:showAsAction="ifRoom"/>
            <item
                android:id="@+id/action_settings"
                android:icon="@drawable/ic_action_settings"
                android:orderInCategory="100"
                android:showAsAction="never"
                android:title="@string/action_settings"/>
        </menu>
    </item>

</menu>

首先我们关注第二行,我们配置了命名空间为app,当然您也可以根据自己的喜好去别的名字,这里在有些和ActionBar有关的属性上不能使用android命名空间,必须使用我们自己的命名空间,否则会失效。好了,我们看到这里一共初始化了四个ActionBar菜单项,其中最后一个还嵌套了一个menu。"orderInCategory"规定了我们的放置次序,但是这里请注意关注我们的"showAsAction"属性,它一共有这么几个值,”never“,"ifRoom","withText","always","collapseActionView"以及它们的组合使用。ActionBar可以分为活动区和OverFlow区,活动区就是我们看到的一排可供使用的导航项。overflow区就是所谓的"三点式",O(∩_∩)O哈哈~,调皮了一下,就是我们放置不下的导航项会放置到这个区域,类似于menu菜单里的"更多"."never"表示决不放置到活动区,"ifRoom"表示空间足够的话,会放置到活动区,这个空间足不足够,不是由你观察所决定的,而是由系统决定,可能明明放的下,系统也会认为空间不足。“withText”就是会显示title属性值。“always”表示必须放置到活动区,,"collapseActionView"这个值是可选的,并且声明了这个操作视窗应该被折叠到一个按钮中,当用户选择这个按钮时,这个操作视窗展开。否则,这个操作视窗在默认的情况下是不可见的,并且即便在用于不适用的时候,也要占据操作栏的有效空间。如果其他项都是ifRoom,而其中一项是always,则"orderInCategory"属性优先级会降低,以该属性为准,例如上面的edit项,虽然orderInCategory值大,但是仍然会排在前面。“icon”属性自然就是我们按钮的图标。title自然就是描述文字了。这两个属性尽量写上,icon就不多解释了,不写用户看什么呀,title虽然可以不写,但是当进入overflow区显示的就是它,或者说你用"withText"属性的时候也会显示它。这里我们还看到“actionProviderClass”这个属性,这个属性具体做什么用,后面我会介绍到。

三、ActionBar模式

ActionBar一共有三种模式,标准模式、列表模式、Tab模式。分别对应ActionBar.NAVIGATION_MODE_STANDARD,ActionBar.NAVIGATION_MODE_LIST,

ActionBar.NAVIGATION_MODE_TABS三个常量。默认为标准模式。列表模式就是类似Spinner的列表,Tab模式代表Tab页,会根据空间大小决定Tab页是

放置在一行,还是放置在title栏下面。下面看具体代码:

public class ActionBarShowActivity extends ActionBarActivity {

	private ActionBar actionBar;
	
	private SpinnerAdapter  adapter;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_action_bar);
		setOverflowShowingAlways();
		actionBar = getSupportActionBar();
		actionBar.setTitle("天气");//设置图标title描述
		actionBar.setLogo(R.drawable.icon_share_twitter);//设置导航头图标
		actionBar.setHomeButtonEnabled(true);//设置导航头可点击
		actionBar.setDisplayHomeAsUpEnabled(true);//设置左边箭头显示可点击
		actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);//设置导航模式为列表模式
		adapter=ArrayAdapter.createFromResource(this, R.array.ball, android.R.layout.simple_spinner_dropdown_item);
		actionBar.setListNavigationCallbacks(adapter, new OnNavigationListener() {
			
			@Override
			public boolean onNavigationItemSelected(int position, long itemId) {
				Toast.makeText(ActionBarShowActivity.this, adapter.getItem(position).toString(), Toast.LENGTH_SHORT).show();
				return false;
			}
		});//设置导航列表

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {//初始化actionBar布局和导航项
		Log.e("onCreateOptionsMenu", "onCreateOptionsMenu");
		getMenuInflater().inflate(R.menu.main, menu);
		MenuItem menuItem=menu.findItem(R.id.share);
		ShareActionProvider shareActionProvider = new ShareActionProvider(this);
		MenuItemCompat.setActionProvider(menuItem, shareActionProvider);		
		Intent share = new Intent(Intent.ACTION_SEND);
		    share.setAction(Intent.ACTION_SEND).putExtra(Intent.EXTRA_TEXT, "TEST")
		            .setType("text/plain");
		shareActionProvider.setShareIntent(share);
		MenuItem menuItem2=menu.findItem(R.id.add);
		MyActionProvider myActionProvider=new MyActionProvider(this,3);
		MenuItemCompat.setActionProvider(menuItem2, myActionProvider);		
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {//在这里可以动态改变导航项的状态
		Log.e("onPrepareOptionsMenu", "onPrepareOptionsMenu");
		return super.onPrepareOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {//选中导航项后的事件处理
		Log.e("item", item.getItemId()+"");
		switch (item.getItemId()) {
		case android.R.id.home://导航头图标和返回箭头ID
			finish();
			break;

		default:
			break;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public boolean onMenuOpened(int featureId, Menu menu) {//默认OverFlow区只显示导航项的title,这里通过反射方式也可以显示图标啦。
		Log.e("onMenuOpened", "onMenuOpened");
		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);
	}

	private void setOverflowShowingAlways() {
         //这里通过反射方式处理Menu键,让其不显示,从而保证我们的ActionBar的OverFlow区一定显示。手机自带物理Menu键可能会出现OverFlow键不显示的情况。
         try {
			ViewConfiguration config = ViewConfiguration.get(this);
			Field menuKeyField = ViewConfiguration.class
					.getDeclaredField("sHasPermanentMenuKey");
			menuKeyField.setAccessible(true);
			menuKeyField.setBoolean(config, false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

四、添加ActionProvider

Action Provider可以将一个Action按钮替换成一个自定义的布局。Action Provider能够完全控制事件的所有行为,并且还可以在点击的时候显示子菜单。为了添加一个Action Provider,我们需要在<item>标签中指定一个actionViewClass属性,在里面填入Action Provider的完整类名。我们可以通过继承ActionProvider类的方式来创建一个自己的Action Provider,同时,Android也提供好了几个内置的Action Provider,比如说ShareActionProvider。由于每个Action Provider都可以自由地控制事件响应,所以它们不需要在onOptionsItemSelected()方法中再去监听点击事件,而是应该在onPerformDefaultAction()方法中去执行相应的逻辑。当然,当你有子菜单的时候,不会执行该方法。下面以我们自定义的ActionProvider为例:

public class MyActionProvider extends ActionProvider {

	private Context context;
	public MyActionProvider(Context context) {
		super(context);
		this.context=context;
	}
	
	public MyActionProvider(Context context,int a) {
		super(context);
		this.context=context;
		Log.e("a", "a");
	}

	@Override
	public View onCreateActionView() {//也可以在这里写自定义布局
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onPrepareSubMenu(SubMenu subMenu) {//添加子菜单
		subMenu.clear();
		subMenu.add("脸书").setIcon(R.drawable.icon_fb_blue)
				.setOnMenuItemClickListener(new OnMenuItemClickListener() {

					@Override
					public boolean onMenuItemClick(MenuItem item) {
						Toast.makeText(context, "脸书", Toast.LENGTH_SHORT).show();
						return true;
					}
				});
		subMenu.add("推特").setIcon(R.drawable.icon_tw_blue)
				.setOnMenuItemClickListener(new OnMenuItemClickListener() {

					@Override
					public boolean onMenuItemClick(MenuItem item) {
						Toast.makeText(context, "推特", Toast.LENGTH_SHORT).show();
						return false;
					}
				});
		super.onPrepareSubMenu(subMenu);
	}

	@Override
	public boolean onPerformDefaultAction() {
		Log.e("onPerformDefaultAction", "onPerformDefaultAction");
		return super.onPerformDefaultAction();
	}

	@Override
	public boolean hasSubMenu() {//表示有子菜单
		return true;
	}

}
最后不要忘了申明app:actionProviderClass属性为你自定义的Provider类全名。

五、常见问题解决

1、导包的时候记得要导入appcompat下的v4包,如果之前项目里已经有了,请统一使用appcompat下的v4包。否则会报错。

2、MenuItem与Action Provider的绑定,请使用我上面代码里的方法进行绑定,网上很多写法会报空指针异常。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值