每次在Github上看到炫酷效果的demo时,都会想,什么时候自己也能实现这样的效果。要想有所收获,就必须得付出。于是,我决定花时间来学习自定义View,虽说自定义View水很深,但凡事得一步步来,终会有所收获的。要实现自定义View,必须得先了解View的绘制流程,但在学习View的绘制流程之前,先来了解下setContentView这个方法时如何将XML布局加载到当前Activity?
从Activity源码可以看出,setContentView方法有三个重载方法,分别是:
setContentView(int layoutResId):将布局文件加载到当前Activity,并且在Activity充当父布局角色。
setContentView(View view):将View加载到当前Activity。当调用这个方法设置View时,自己对View设置布局参数宽高会被忽略,默认的宽高是android.view.ViewGroup.LayoutParams.MATCH_PARENT。如果想使用自己定义的布局参数,可以使用setContentView(View view, ViewGroup.LayoutParams params)这个方法代替。
setContentView(View view, ViewGroup.LayoutParams params):将View加载到当前Activity。
从源码来看,这个三个重载方法的实现逻辑都差不多,所以我只分析setContentView(int layoutResId).
Step 1
进入到Activity源码,找到setContentView(int layoutResId)方法如下:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
只有两行代码,有木有一种挺简单的感觉。其实不然,往下看就知道有多复杂了。从第2行看起,进入getWindow()这个方法,代码如下
public Window getWindow() {
return mWindow;
}
获取Activity当前Window对象,Window是抽象类,不能实例化对象,那么是怎么获取到Window对象?在Activity源码中attach方法有这样的一行代码
mWindow = PolicyManager.makeNewWindow(this);
很明显,这是获取Window对象的,继续往下看。进入PolicyManager类makeNewWindow()方法
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
那么sPolicy又是什么呢,代码如下:
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
// Pull in the actual implementation of the policy at run-time
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be loaded", ex);
} catch (InstantiationException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(
POLICY_IMPL_CLASS_NAME + " could not be instantiated", ex);
}
}
从以上代码可以看出,IPolicy是接口,通过反射机制获取到sPolicy,该接口的实现类是Policy,所以定位到该类makeNewWindow()方法
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
终于获取到Window对象,该对象的类型是