view和布局文件、控件的关系如下:
可以看到,我们所用的所有控件都是直接或间接继承自View 的,所用的所有布局都是直接或间接继承自ViewGroup 的。View 是Android 中一种最基本的UI 组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件,因此,我们使用的各种控件其实就是在View 的基础之上又添加了各自特有的功能。而ViewGroup 则是一种特殊的View,它可以包含很多的子View 和子ViewGroup,是一个用于放置控件和布局的容器。
如果系统自带的控件并不能满足我们的需求时,可以利用上面的继承结构来创建自定义控件。以下是详细步骤:
1 引入布局:
如果你用过iPhone 应该会知道,几乎每一个iPhone 应用的界面顶部都会有一个标题栏,标题栏上会有一到两个按钮可用于返回或其他操作(iPhone 没有实体返回键)。现在很多的Android 程序也都喜欢模仿iPhone 的风格,在界面的顶部放置一个标题栏。虽然Android 系统已经给每个活动提供了标题栏功能,但这里我们仍然决定不使用它,而是创建一个自定义的标题栏。经过前面两节的学习,我想创建一个标题栏布局对你来说已经不是什么困难的事情了,只需要加入两个Button 和一个TextView,然后在布局中摆放好就可以了。可是这样做却存在着一个问题,一般我们的程序中可能有很多个活动都需要这样的标题栏,如果在每个活动的布局中都编写一遍同样的标题栏代码,明显就会导致代码的大量重复。这个时候我们可以把我们想要的控件写成一个布局(比如:两个Button 和一个TextView,左边的Button可用于返回,右边的Button 可用于编辑,中间的TextView 则可以显示一段标题文本,保存成title.xml)。现在标题栏布局已经编写完成了,剩下的就是如何在程序中使用这个标题栏了,修改activity_main.xml 中的代码,如下所示:
public class TitleLayout extends LinearLayout {
public TitleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.title, this);
Button titleBack = (Button) findViewById(R.id.title_back);//事件注册和在activity中一样
Button titleEdit = (Button) findViewById(R.id.title_edit);
titleBack.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((Activity) getContext()).finish();
}
});
}
}
首先我们重写了LinearLayout 中的带有两个参数的构造函数,在布局中引入TitleLayout控件就会调用这个构造函数。然后在构造函数中需要对标题栏布局进行动态加载,这就要借助LayoutInflater 来实现了。通过LayoutInflater 的from()方法可以构建出一个LayoutInflater对象,然后调用inflate()方法就可以动态加载一个布局文件,inflate()方法接收两个参数,第一个参数是要加载的布局文件的id,这里我们传入R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout,于是直接传入this。
现在自定义控件已经创建好了,然后我们需要在布局文件中添加这个自定义控件,修改activity_main.xml 中的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.uicustomviews.TitleLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
></com.example.uicustomviews.TitleLayout>
</LinearLayout>
添加自定义控件和添加普通控件的方式基本是一样的,只不过在添加自定义控件的时候
我们需要指明控件的完整类名,包名在这里是不可以省略的。