上篇说到了尝试自定义标题栏。

1.一些常识

这里先介绍一下关于页面的一些常识术语。和自定义导航栏无关,仅作扩展知识。

android修改导航标题 安卓导航栏自定义_ActionBar自定义


上面提到了,我这里要自定义标题栏,也就是上图中的额ActionBar这个东西。然后在官方文档中找到了ActionBarAPI但是却没有找到先关的配置说明指引,故而还是百度了一波。希望了解的可以读读这篇文章:【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解。介绍的很详细!

相信您看完了就可以简单的实现一些ActionBar的内容的定制。下面我也会就这篇文章做一个简单的实验过程记录。但是,我似乎没有看见有办法去除ActionBar左侧那个难看的图标,而且如果现实向上按钮的时候,这个返回按钮和图片的距离也太近了,难看。故而还是需要自定义ActionBar

所以这里我们又如下几点:

  • ActionBar显示文本item、响应事件
  • ActionBar显示图片item、响应事件
  • 自定义ActionBar、响应事件

2.使用ActionBar

我们知道我们创建的Activity是默认含有ActionBar组件的,不过默认的黑色的不太好看,这里我们就选择一个亮色的。因为如果需要使用ActionBar,就需要在AndroidManifest.xml中指定ApplicationActivitythemeTheme.Holo或其子类。
在配置文件的application中配置:

android:theme="@android:style/Theme.Holo.Light"

这里调试程序,由于模拟器运行太慢,就用了MUMU模拟器来看结果。Eclipse连接MUMU模拟器,帖子也很多,这里简单记录一下:

打开目录:E:\software\mumu\emulator\nemu\vmonitor\bin 打开命令行窗口,运行:adb_server connect 127.0.0.1:7555

就可以了。运行结果:

android修改导航标题 安卓导航栏自定义_ActionBar自定义_02


MainActivity.java文件中,有下面的代码:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

我们很容易知道这个Activity关联的menumenu下面的main.xml文件。故而定义Item就是在这个配置文件中。不妨打开res/menu/main.xml文件,然后定制一下。

2.1 使用文字按钮

如:

android修改导航标题 安卓导航栏自定义_ActionBar自定义_03


具体的xml代码如下:

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

	<item
        android:id="@+id/item1"
        android:orderInCategory="6"
        android:showAsAction="collapseActionView"
        android:title="设置"
        />
	<item
        android:id="@+id/item1"
        android:orderInCategory="6"
        android:showAsAction="collapseActionView"
        android:title="设置"
        />
	<item
        android:id="@+id/item1"
        android:orderInCategory="6"
        android:showAsAction="always"
        android:title="设置"
        />
	<item
        android:id="@+id/item1"
        android:orderInCategory="6"
        android:showAsAction="always"
        android:title="设置"
        />
</menu>

这个showAsAction有几个值可以用来设置显示在ActionBar上,还是显示在省略号中,具体如下:

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

当然,上面的也是我抄的。n(≧▽≦)n

2.2 图片按钮

就是使用icon属性,下面就来实践一下,效果图:

android修改导航标题 安卓导航栏自定义_android标题栏自定义_04


首先定义Application的图标:

<application
        android:allowBackup="true"
        android:icon="@drawable/logo"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.Holo.Light"
         >

然后定义这个Activity的名字:

<activity
            android:name=".MainActivity"
            android:label="@string/home"
            >

然后在定义这个Activity加载的menu,在res/menu/main.xml文件:

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

	<item
        android:id="@+id/item1"
        android:orderInCategory="1"
        android:actionViewClass="android.widget.SearchView"
        android:showAsAction="always"
        android:title="搜索"
        />
	<item
        android:id="@+id/item2"
        android:orderInCategory="2"
        android:icon="@drawable/setting"
        android:showAsAction="always"
        android:title="设置"
        />
	<item
        android:id="@+id/item3"
        android:orderInCategory="3"
        android:showAsAction="collapseActionView|withText"
        android:title="签到"
        />
	<item
        android:id="@+id/item4"
        android:orderInCategory="4"
        android:showAsAction="collapseActionView|withText"
        android:title="扫一扫"
        />
</menu>

当然所涉及的图标图片我这里也放置在这里:

android修改导航标题 安卓导航栏自定义_android修改导航标题_05


android修改导航标题 安卓导航栏自定义_ActionBar自定义_06

2.3 点击处理

处理ActionBar中的Item的点击事件,我们就需要在Activity中复写onOptionsItemSelected方法,在该方法中使用item.getItemId()方法和menu资源中的id进行比较,从而辨别出用户点击的是哪一个Action按钮。如:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
	// TODO Auto-generated method stub
	String info = "其他";
	if(item.getItemId() == R.id.item1){
		info = "搜索";
	}else if(item.getItemId() == R.id.item2){
		info = "设置";
	}
	Toast.makeText(getApplicationContext(), info, 0).show();
	return super.onOptionsItemSelected(item);
}

效果:

android修改导航标题 安卓导航栏自定义_android修改导航标题_07

3. 自定义ActionBar

上面提到了,我不想要应用左边的图标。但是不知道如何做,所以还是自定义图标。对于如何自定义图标,我这里也还是百度了一下,有兴趣的可以看原文:android 自定义ActionBar

3.1 取消ActionBar

需要自定义ActionBar就需要将原本的主题给No掉。也就是不要ActionBar。有两种方式取消:

  • 配置文件,<activity android:theme="@android:style/Theme.NoTitleBar">
  • 在Activity中使用代码动态隐藏,getActionBar().hide()

3.2 自定义ActionBar

在自定义ActionBar前,我们需要明白一件事,就是为什么自定义ActionBar
如果只用这一次,不妨直接在页面去掉原有ActionBar,然后在页面布局文件中做一个类似的就可以了,完全可以用,也没必要自定义ActionBar
我们自定义ActionBar肯定是出于下面的需求:

  • 自定义ActionBar不止使用一次,为了方便日后使用,就将自定义ActionBar的功能抽离出来,做成组件;
  • 锻炼自己代码封装的逻辑能力;

3.2.1 只使用一次的ActionBar

其实也可以不叫ActionBar,就是直接写xml文件。当然,这里为了直观,我这里就直接在布局文件中用include这个标签。
首先,定义my_actionbar.xml文件在layout下,代码内容就是你想要展示的内容,我这里是这样的(my_actionbar.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="45dp"
    android:background="#FFFFFF"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:orientation="horizontal" >
    <ImageView 
        android:layout_gravity="center"
        android:id="@+id/left"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/left"
        />
	<TextView 
	    android:id="@+id/center"
	    android:gravity="center"
	    android:layout_width="wrap_content"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:text="首页"
        android:textSize="24sp"
	    />
    <ImageView 
        android:layout_gravity="center"
        android:id="@+id/right"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/more"
        />
</LinearLayout>

然后在MainActivity的布局文件中,引入(activity_main.xml):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    
    <include layout="@layout/my_actionbar" android:id="@+id/myBar"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/myBar"
        android:text="@string/hello_world" />

</RelativeLayout>

效果:

android修改导航标题 安卓导航栏自定义_ActionBar的_08


这里也还是放置好leftmore的图片在下面:

android修改导航标题 安卓导航栏自定义_android标题栏自定义_09


android修改导航标题 安卓导航栏自定义_android修改导航标题_10


当然,还有点击事件,如果要添加点击事件(MainActivity.java):

ImageView imageView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 使用include的控件,一定放在setContentView的后面,不要在前面
    imageView = (ImageView)findViewById(R.id.right);
    imageView.setOnClickListener(new View.OnClickListener() {
		
		@Override
		public void onClick(View v) {
			Toast.makeText(getApplicationContext(), "Click", 0).show();
		}
	});
}

需要注意的是,不能为了省事,在定义的时候初始化,会报空指针。初始化要放置在setContentView的后面。
当然,还有一种写法:

imageView = (ImageView)findViewById(R.id.myBar).findViewById(R.id.right);

就是先找到这个 LinearLayout,然后在在这个LinearLayout下找right

效果:

android修改导航标题 安卓导航栏自定义_android修改导航标题_11

3.2.2 封装ActionBar

前面讲了只使用一次的bar封装,其实看看代码,也可以用多次,也并不麻烦。不过这里还是来封装一下,练练手,毕竟编程越到后面可能就越需要自己定义一些封装。
这里的自定义bar的布局文件还是使用上面的layout/my_actionbar.xml文件。只是这里我们需要删除几行代码,就是在ImageView下的android:srcTextVeiw下的android:text不再需要了,因为自定义控件,这些东西应该是作为属性输入的。
然后我们就需要编写自定义控件所需要的属性(包括标题 左右资源等),这些东西写在values下面,这里我写在values/attrs.xml文件中,该文件内容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="myactionbar">
        <attr name="center_text" format="string" />
        <attr name="left_icon" format="reference"/>
        <attr name="right_icon" format="reference"/>
    </declare-styleable>
</resources>

因为我只需要左右图标加标题,所以没有定义其余的。
定义好了后,我们需要自定义一个类,我这里继承LinearLayout,你可以选择其他的。我这里定义为MyActionBar.java:

package com.weizu.utils;

import com.weizu.first.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MyActionBar extends LinearLayout{
	
	private ImageView left;
	private ImageView right;
	private TextView center;
	
	private String center_text;
	private Drawable left_icon;
	private Drawable right_icon;
	
	public MyActionBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		// 根据布局id把这个布局加载成一个View并返回的。
		View view = LayoutInflater.from(context).inflate(R.layout.my_actionbar,this);
		TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.myactionbar);
        center_text = ta.getString(R.styleable.myactionbar_center_text);
        left_icon = ta.getDrawable(R.styleable.myactionbar_left_icon);
        right_icon = ta.getDrawable(R.styleable.myactionbar_right_icon);
        
        //TypedArray回收一下。
        ta.recycle();
        // 找到控件
        left = (ImageView) view.findViewById(R.id.left);
        right = (ImageView)view.findViewById(R.id.right);
        center = (TextView)view.findViewById(R.id.center);
        // 为控件设置资源(图片, 文字)
        left.setImageDrawable(left_icon);
        center.setText(center_text);
        right.setImageDrawable(right_icon);
        
	}
}

然后在activity_main.xml布局文件中就不再是使用include了,而是使用下面的:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    
    <com.weizu.utils.MyActionBar 
		android:id="@+id/myBar"
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:background="#ff1"
		app:left_icon="@drawable/left"
		app:center_text="首页"
		app:right_icon="@drawable/more"
        >
    </com.weizu.utils.MyActionBar>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/myBar"
        android:text="@string/hello_world" />

</RelativeLayout>

效果:

android修改导航标题 安卓导航栏自定义_android标题栏自定义_12


至于点击效果,我们就需要定义一个接口,然后在MyActionBar 中响应。

首先定义一个接口,我这里定义为:myactionbarclicklistener.java

package com.weizu.utils;

public interface myactionbarclicklistener {
	void leftImageClick();
	void rightImageClick();
}

然后在MainActivity.java中实现这个接口,具体实现方法,我这里就用一个Toast

public class MainActivity extends Activity implements myactionbarclicklistener{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 设置监听器为当前类
        MyActionBar.setActionBarListener(this);
    }
    
	@Override
	public void leftImageClick() {
		Toast.makeText(getApplicationContext(), "left click", 0).show();
	}


	@Override
	public void rightImageClick() {
		Toast.makeText(getApplicationContext(), "right click", 0).show();
	}

上面是主要代码,其他没拷贝。然后是在MyActionBar中定义这个接口,并定义一个设置监听器的静态方法setActionBarListener:

// 定义监听器接口
private static myactionbarclicklistener listener;

// 初始化监听器
public static void setActionBarListener(myactionbarclicklistener lr){
	listener = lr;
}

// 为left,right添加监听
left.setOnClickListener(new OnClickListener() {
			
	@Override
	public void onClick(View v) {
		listener.leftImageClick();
	}
});
        
right.setOnClickListener(new OnClickListener() {
			
	@Override
	public void onClick(View v) {
		listener.rightImageClick();
	}
});

效果:

android修改导航标题 安卓导航栏自定义_标题栏的自定义_13


参考文档:
[1] 【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解 [2] android 自定义ActionBar
[3] Android——自定义TopBar(ActionBar)
[4] 安卓ActionBar


作者:无涯明月