不销毁activity实现白天黑夜主题切换

Android activity 加载布局文件流程


一.onCreate初始化
AppCompatActivity.onCreate 先调用getDelegate() 创建 AppCompatDelegateImplN(最终继承AppCompatDelegateImplV9->AppCompatDelegateImplBase)对象 delegate,然后调用 delegate.installViewFactory()
AppCompatDelegateImplV9 实现接口LayoutInflater.Factory2
->AppCompatDelegateImplV9.installViewFactory()  先调用 layoutInflater = LayoutInflater.from(mContext) 获取LAYOUT_INFLATER_SERVICE 服务,然后调用LayoutInflaterCompat.setFactory2(layoutInflater, this)。
ps:LAYOUT_INFLATER_SERVICE 服务 是ContextImpl类加载时调用 SystemServiceRegistry.createServiceCache() new PhoneLayoutInflater(ctx.getOuterContext())创建的,PhoneLayoutInflater继承 LayoutInflater。
->LayoutInflaterCompat.setFactory2  调用IMPL.setFactory2(inflater, factory), 这里的IMPL 为LayoutInflaterCompatApi21Impl.
->LayoutInflaterCompatApi21Impl.setFactory2(inflater, factory) 方法只是调用inflater.setFactory2(factory) 这里inflater为服务端的 PhoneLayoutInflater对象,factory为AppCompatDelegateImplV9对象.调用服务端接口:
->LayoutInflater.setFactory2(factory) 将客户端的 AppCompatDelegateImplV9对象 赋值给服务端变量 mFactory2
二.setContentView 加载布局文件
AppCompatActivity.setContentView(int layoutResID)  执行2步
步骤1 :调用ensureSubDecor() 创建SubDecor,并将SubDecor添加到了DecorView
步骤2: 调用getDelegate().setContentView(layoutResID) 这里getDelegate()  获取的是上面创建的 AppCompatDelegateImplN
->AppCompatDelegateImplV9.setContentView(int resId) 调用LayoutInflater.from(mContext).inflate(resId, contentParent) 执行服务端 inflate方法:
->LayoutInflater.inflate( int resource, ViewGroup root)  调用 inflate(parser, root, root != null)
->LayoutInflater.inflate( int resource, ViewGroup root, boolean attachToRoot)  基于布局资源id resource获取 parser
->LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)  执行2步
1. 调用createViewFromTag(root, name, inflaterContext, attrs,false) 实例化根节点view
2.调用rInflateChildren(parser, temp, attrs, true)该方法遍历xml布局控件 具体流程如下:
->LayoutInflater.rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate)
->LayoutInflater.rInflate(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate) 该方法遍历布局中 控件节点,调用createViewFromTag(parent, name, context, attrs) ,name为控件名称如:Button
->LayoutInflater.createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) 
 该方法调用view = mFactory2.onCreateView(parent, name, context, attrs)  mFactory2 为客户端 对象AppCompatDelegateImplV9,调用客户端mFactory2接口:
->AppCompatDelegateImplV9.onCreateView(View parent, String name, Context context, AttributeSet attrs) 调用createView(parent, name, context, attrs)
->AppCompatDelegateImplV9.createView(View parent, String name, Context context, AttributeSet attrs) 调用new AppCompatViewInflater() 创建mAppCompatViewInflater,
然后调用mAppCompatViewInflater.createView(parent, name, context, attrs...) 
->AppCompatViewInflater.createView(parent, name, context, attrs...) 
基于控件名称name 创建AppCompat控件 并作为方法返回值返回,例如:name 为 Button 调用new AppCompatButton(context, attrs)创建AppCompatButton ,

不销毁activity实现白天黑夜主题切换思路:


既然LAYOUT_INFLATER_SERVICE 服务端加载布局文件遍历控件时候会调用客户端传递的 Factory2接口对象的onCreateView方法,
那我们可以通过参考源码自定义Factory2 接口实现类,并将该实现类设置到 服务端,这样就可以获取到所有的布局文件中的控件对象了,我们可以先缓存下来这些控件。
当收到白天黑夜模式切换的回调时,重新加载所有控件的背景,字体颜色等就可以完成换肤操作。

具体操作步骤
一、创建自定义类:
1.参考AppCompatDelegateImplV9 创建 CustomSkinLayoutInflaterFactory 继承 LayoutInflater.Factory2, 实现 Factory2 接口方法,
2.参考LayoutInflaterCompat 创建 LayoutInflaterCompat ,  封装 LayoutInflater服务端接口调用 。
3.创建所有AppCompat控件子类,例如:创建类 CustomSkinAppCompatButton 继承 AppCompatButton 实现 自定义接口uiModeChangeListener.applyCustomSkin方法。
控件收到回调后,控件中调用 setBackground,setPadding,setTextColor,setHintTextColor等设置资源的颜色的操作,实现换肤,
4.参考 AppCompatViewInflater 创建自定义类 CustomSkinAppCompatViewInflater ,系统遍历布局中的所有控件会通过Factory2调用该类createView方法, 这里可以考虑扩展自定义View的Inflater 也是要有 createView方法
5.创建CustomSkinCompatResources 用来缓存所有的自定义的 AppCompatViewInflater

二、应用初始化时候操作:
1.注册所有 activity监听 
2.向 CustomSkinCompatResources 缓存所有CustomSkinLayoutInflater 例如: CustomSkinAppCompatViewInflater (转换常规控件android.widget) 等

三、Activity onCreate初始化
通过一注册的acitivity 监听,当onActivityCreated 时候 为当前activity 创建 CustomSkinLayoutInflaterFactory 并缓存到map中,调用inflater.setFactory2(CustomSkinLayoutInflaterFactory) 向服务端设置 自定义的 Factory2
布局加载时候回遍历所有的view控件 调用  Factory2.onCreateView
CustomSkinLayoutInflaterFactory.onCreateView
调用  getInflaters 获取缓存所有CustomSkinLayoutInflater 调用 inflater.createView,例如:
->CustomSkinAppCompatViewInflater.createView 最终 返回创建的CustomSkinCompat控件对象,例如:CustomSkinCompatButton
CustomSkinLayoutInflaterFactory.onCreateView 就可以缓存布局中所有的CustomSkinCompat控件。
四、白天黑夜切换
保证白天黑夜切换activity不销毁,需要在AndroidManifest.xml  android:configChanges="uiMode",同时在acvitivity 重载onConfigurationChanged 方法。白天黑夜切换时候会回调
CustomSkinActivity.onConfigurationChanged 调用CustomSkinManager.getInstance().uiModeChange(this)
CustomSkinLayoutInflaterFactory.applyCustomSkin()   遍历前面缓存布局中所有的CustomSkinCompat控件 调用applyCustomSkin。例如:
CustomSkinCompatButton.applyCustomSkin  控件中调用 setBackground,setPadding,setTextColor,setHintTextColor,setCompoundDrawablesWithIntrinsicBounds
这样就完成了不销毁activity实现 白天黑夜主题资源的切换。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值