View简介
在Android操作系统中,几乎所有的UI元素都是基于View
和ViewGroup
创建出来的。View
就是一块可以用来进行绘画,可以处理输入事件进行交互的矩形区域,而ViewGroup
就是一种可以容纳View
的矩形容器。
下图就是ViewGroup
和View
组成的UI布局结构。View
和ViewGroup
通过这种树形结构组合在一起,构成了我们在手机屏幕上看到的一个个的复杂的界面。
从设计模式的角度看,ViewGroup
和View
是组合模式的典型应用。View
是基本的控件元素,ViewManager
接口定义了添加、删除View的接口addView、removeView
,ViewGroup
实现了ViewParent
的接口,因此可以作为容器管理View
,同时ViewGroup
又继承自View
,可以被其他的ViewGroup
管理。这样ViewGroup
和View
就可以组成上面的树状结构了。
实际的代码实现过程中很少会直接使用ViewGroup
和View
,而是使用继承自它们的之类,如FrameLayout
、LinearLayout
、ListView
等是ViewGroup
的子类,TextView
、ImageView
、SurfaceView
等是View
的子类。
创建View树
上面介绍了ViewGroup
和View
是通过View
树的形式组合在一起的,一个View
树也就是一个Layout
布局,下面就介绍一下怎样创建、管理Layout
布局。
从Layout
资源创建View
树
最常见的创建Layout
的方式就是使用Layout
资源。Android 应用的代码目录结构里,在资源res文件夹下有一个layout文件夹,应用中使用的layout资源都会放在这个文件夹下。父View与子View是以XML的形式嵌套在一起的,下面就是一个layout的例子:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a TextView" />
</FrameLayout>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a Button" />
</LinearLayout>
同时layout之间也可以相互嵌套,如下面的形式:
<include layout="@layout/other_layout"/>
当在代码中需要使用XML资源里定义的layout时可以使用下面的代码:
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.main, null);
很多类中将通过LayoutInflater
加载资源的过程也封装好了,只需要提供资源就可以了,如设置Activity
的ContentView
的代码:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
通过Layout
资源创建View
树的优势是层次结构清晰,管理方便。同时View
树中各个View的属性也可以在layout资源里进行设置。缺点是无法在应用运行时对View树结构进行变化。
从代码创建View树
从Java代码中通过ViewGroup
的addView
、removeView
等接口同样可以管理View树,如下面的代码:
TextView text = new TextView(this);
Button button = new Button(this);
FrameLayout frame = new FrameLayout(this);
frame.addView(text);
LinearLayout linear = new LinearLayout(this);
linear.setOrientation(LinearLayout.VERTICAL);
linear.addView(frame);
linear.addView(button);
从代码中创建View树最多的应用场景是在使用AdapterView
的子类的时候,如ListView、GridView、Spinner
等。继承自AdapterView
的控件一般会通过一个适配器Adapter来添加、删除自己的子View
。
ListView list = new ListView(this);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);
list.setAdapter(adapter);
在代码中创建View
树的优点是对View
的管理比较灵活,可以在程序运行过程中动态的管理View树的状态,缺点就是代码实现比较复杂,程序的扩展性不好不容易维护。
通常在开发过程中这两种创建View
树的方式是结合在一起使用的。
管理View
View
树里的每一个View
都会有一个整型ID,用来方便对这个View进行查找管理,View
的ID可以在布局文件里定义(如前面布局资源文件里为TextView
指定的android:id="@+id/text"
)也可以在View实例创建后通过View.setId(int id)
方法设置。通过View树中根节点的findViewById(int id)
方法就可以查找到id对应的View。
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.main, null);
TextView text = (TextView) view.findViewById(R.id.text);
View
树中每个View的 ID 并不强制要求是唯一的,这样可以方便一些子View树重复利用(如ListView
中每个item的布局),但是通过findViewById()
查找时只会得到最先找到的View
,因此建议在资源文件中定义的View
使用唯一的ID。
创建了view树以后,还可能会根据实际场景对View
树里进行添加或者删除View
的操作,使用前面提到过的addView、removeView
接口就可以,如:
LayoutInflater inflater = getLayoutInflater();
FrameLayout container = (FrameLayout) inflater.inflate(R.layout.main, null);
TextView text = (TextView) container.findViewById(R.id.text);
container.removeView(text);
Button button = new Button(this);
button.setId(button.hashCode());
container.addView(button);