您的位置:首页 > 移动开发 > Android开发

Android 自定义View、ViewGroup(一)之工作原理

2014-09-13 17:49 281 查看
学习Android自定义View,通常需要用到LayoutInflater这个类,先来简单的介绍一下LayoutInflater这个类的简单的用法。

1.两种获取LayoutInflater的方法

LayoutInflater layoutInflater = LayoutInflater.from(context);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

2.LayoutInflater的工作原理

采用XmlPullParser 解析layout文件 创建view实例,最后调用viewGroup.addView(view, params);

3.activity中的setContentView方法内幕

如果我们在activity_main中定义根布局为LinearLayout,父布局是一个FrameLayout,而这个FrameLayout就是由系统自动帮我们添加上的,其id就是content。这就是为什么Activity中有setContentView(),而不是setView()了

举一个简单的例子:

1.在activity_main.xml中写一个布局文件如下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_view" >
</TextView>

</LinearLayout>
2.在MainActivity中设置布局

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//		LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//		View contentView = layoutInflater.inflate(R.layout.activity_main, null);
//		setContentView(contentView);

setContentView(R.layout.activity_main);
}
}
我们用sdk/tools下自带的hierarchyviewer.bat工具可以清楚的看到整个布局情况。



下面我们来分析一下上面的流程

1.我们找到Activity.java的源码,看一下setContentView这个方法

public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
}
2.可以看出其实调用的是getWindow这个方法返回的对象的setContentView方法,那么getWindow方法得到的是什么呢?

public Window getWindow() {
return mWindow;
}


3.那么mWindow是什么呢?我们在Activity.java中attach方法中找到一行代码

mWindow = PolicyManager.makeNewWindow(this);
4.我们去PolicyManager中看一下makeNewWindow方法

private static final IPolicy sPolicy;
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
5.最终我们发现,原来是这个IPolicy这个接口调用makeNewWindow方法产生出的一个PhonwWindow实例(Window的实现类),然后调用它的setContentView方法
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
6.PhonwWindow的setContentView方法也是通过LayoutInflater的inflate方法去解析xml文件,转换成View对象,然后通过VIewGroup的addView方法完成,具体代码如下

public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;

try {
// Look for the root node.
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}

if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
//此方法内部解析XmlPullParser解析资源文件,调用Viewgroup的addView方法添加View到父View中
rInflate(parser, root, attrs);
} else {
// Temp is the root view that was found in the xml
View temp = createViewFromTag(name, attrs);

ViewGroup.LayoutParams params = null;

if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}

// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
//如果root不为null,调用ViewGroup的addView方法将解析出的View添加到根view中
root.addView(temp, params);
}

}

} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}

return result;
}
}
我们看一下rInflate方法吧

private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
throws XmlPullParserException, IOException {

final int depth = parser.getDepth();
int type;

while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

if (type != XmlPullParser.START_TAG) {
continue;
}

final String name = parser.getName();

if (TAG_REQUEST_FOCUS.equals(name)) {
parseRequestFocus(parser, parent);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(name, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflate(parser, view, attrs);
viewGroup.addView(view, params);
}
}

parent.onFinishInflate();
}

至此,整个Activity加载View的原理就说完了。

下载Demo请猛戳
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: