Android 动态式换肤框架1-setContentView源码分析

在了解动态换肤前,需对setContentView源码分析。本文分析AppCompatActivity的setContentView方法,涉及AppCompatActivity.java、AppCompatDelegateImpl.java、LayoutInflater.java等文件,介绍了布局加载、解析及View创建过程,还提到setFactory2只能设置一次及修改参数值的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:

https://blog.youkuaiyun.com/samlss/article/details/81332395

https://blog.youkuaiyun.com/csdn_aiyang/article/details/86626748

在了解动态换肤之前,有必要对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);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值