天天看点

Android 动态式换肤框架1-setContentView源码分析1 AppCompatActivity.java2 AppCompatDelegateImpl.java3 LayoutInflater.java

参考:

https://blog.csdn.net/samlss/article/details/81332395

https://blog.csdn.net/csdn_aiyang/article/details/86626748

文章目录

  • 1 AppCompatActivity.java
  • 2 AppCompatDelegateImpl.java
  • 3 LayoutInflater.java

在了解动态换肤之前,有必要对setContentView源码进行分析。

这里分析AppCompatActivity的setContentView方法,Activity的setContentView方法会不同。

1 AppCompatActivity.java

/android.support.v7.app.AppCompatActivity.java

public void setContentView(@LayoutRes int layoutResID) {
        this.getDelegate().setContentView(layoutResID);
    }
           

2 AppCompatDelegateImpl.java

/android.support.v7.app.AppCompatDelegateImpl.java

public void setContentView(int resId) {
        this.ensureSubDecor();
        ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
        contentParent.removeAllViews();
        LayoutInflater.from(this.mContext).inflate(resId, contentParent);//1
        this.mOriginalWindowCallback.onContentChanged();
    }
           
注释1 使用LayoutInflater布局加载器将布局文件加载到contentParent当中。

3 LayoutInflater.java

/android.view.LayoutInflater.java

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        final Resources res = getContext().getResources();//1
        final XmlResourceParser parser = res.getLayout(resource);//2
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }
           

注释1获取到一个Resources对象。

注释2获取到xml的解析器XmlResourceParser,解析布局文件。

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
	...
	final String name = parser.getName();//1
	if (TAG_MERGE.equals(name)) {
    	...     
    } else {
    	final View temp = createViewFromTag(root, name, inflaterContext, attrs);//2
		...
	}


}
           

注释1通过解析器获取一个节点名,比如我们布局文件的的一个Image节点。

注释2创建这个节点对应的View。

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class");
        }

		...

        try {
            View view;
            if (mFactory2 != null) {
                view = mFactory2.onCreateView(parent, name, context, attrs);//1
            } else if (mFactory != null) {
                view = mFactory.onCreateView(name, context, attrs);
            } else {
                view = null;
            }

            if (view == null && mPrivateFactory != null) {
                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
            }

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {//2
                        view = onCreateView(parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);//3
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }

            return view;
        } 
		...
    }
           

注释1 通过工厂Factory2 创建View。

注释2 表示自定义View的创建(自定义View包含包名,包名中包含".")。

注释3 创建系统自带的View

createView方法:

public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Constructor<? extends View> constructor = sConstructorMap.get(name);
      	
      	...
      	
        Class<? extends View> clazz = null;
        try {
            if (constructor == null) {
                clazz = mContext.getClassLoader().loadClass(
                        prefix != null ? (prefix + name) : name).asSubclass(View.class);//1

             	...
             	 
                constructor = clazz.getConstructor(mConstructorSignature);//2
                constructor.setAccessible(true);
                sConstructorMap.put(name, constructor);//缓存,保存到一个map中
            }else{
				
				...
				
			} 
            
			...
			
            final View view = constructor.newInstance(args);//3
           
           	...
           
            return view;

        } 
		
		...
		
    }
           

注释1 获取到View的Class。

注释2 通过反射获取到View的构造方法。

注释3 通过构造函数创建View。

还有一点需要注意的是setFactory2只能设置一次,否则会抛出异常"A factory has already been set on this LayoutInflater"。所以在自定义Factory2的时候,需要重新设置mFactorySet的值。因为mFactorySet是私有的,可以通过反射,然后修改该参数值。

private boolean mFactorySet;
	
	...
	
    public void setFactory2(Factory2 factory) {
        if (mFactorySet) {
            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
        }
        if (factory == null) {
            throw new NullPointerException("Given factory can not be null");
        }
        mFactorySet = true;
        if (mFactory == null) {
            mFactory = mFactory2 = factory;
        } else {
            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
        }
    }