上篇说到了尝试自定义标题栏。
1.一些常识
这里先介绍一下关于页面的一些常识术语。和自定义导航栏无关,仅作扩展知识。
上面提到了,我这里要自定义标题栏,也就是上图中的额ActionBar
这个东西。然后在官方文档中找到了ActionBar
的API
但是却没有找到先关的配置说明指引,故而还是百度了一波。希望了解的可以读读这篇文章:【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解。介绍的很详细!
相信您看完了就可以简单的实现一些ActionBar
的内容的定制。下面我也会就这篇文章做一个简单的实验过程记录。但是,我似乎没有看见有办法去除ActionBar
左侧那个难看的图标,而且如果现实向上按钮的时候,这个返回按钮和图片的距离也太近了,难看。故而还是需要自定义ActionBar
。
所以这里我们又如下几点:
-
ActionBar
显示文本item
、响应事件 -
ActionBar
显示图片item
、响应事件 - 自定义
ActionBar
、响应事件
2.使用ActionBar
我们知道我们创建的Activity
是默认含有ActionBar
组件的,不过默认的黑色的不太好看,这里我们就选择一个亮色的。因为如果需要使用ActionBar
,就需要在AndroidManifest.xml
中指定Application
或Activity
的theme
是Theme.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
就可以了。运行结果:
在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
关联的menu
是menu
下面的main.xml
文件。故而定义Item
就是在这个配置文件中。不妨打开res/menu/main.xml
文件,然后定制一下。
2.1 使用文字按钮
如:
具体的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
属性,下面就来实践一下,效果图:
首先定义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>
当然所涉及的图标图片我这里也放置在这里:
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);
}
效果:
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>
效果:
这里也还是放置好left
和more
的图片在下面:
当然,还有点击事件,如果要添加点击事件(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
。
效果:
3.2.2 封装ActionBar
前面讲了只使用一次的bar
封装,其实看看代码,也可以用多次,也并不麻烦。不过这里还是来封装一下,练练手,毕竟编程越到后面可能就越需要自己定义一些封装。
这里的自定义bar
的布局文件还是使用上面的layout/my_actionbar.xml
文件。只是这里我们需要删除几行代码,就是在ImageView
下的android:src
和TextVeiw
下的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>
效果:
至于点击效果,我们就需要定义一个接口,然后在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();
}
});
效果:
参考文档:
[1] 【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解 [2] android 自定义ActionBar
[3] Android——自定义TopBar(ActionBar)
[4] 安卓ActionBar
作者:无涯明月