首页 > 其他分享 >不销毁activity实现白天黑夜主题切换

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

时间:2024-08-05 09:39:30浏览次数:5  
标签:控件 销毁 LayoutInflater parent 调用 attrs activity 白天黑夜 name

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实现 白天黑夜主题资源的切换。

标签:控件,销毁,LayoutInflater,parent,调用,attrs,activity,白天黑夜,name
From: https://www.cnblogs.com/adamli/p/18342628

相关文章

  • Android activity主题设置
    主题配置<stylename="MainThemeCamera"parent="Theme.AppCompat.DayNight.NoActionBar"><itemname="android:windowBackground">@color/black</item><itemname="android:windowTranslucentStatu......
  • java对象的销毁
    对象的销毁对象使用完之后需要对其进行清除。对象的清除是指释放对象占用的内存。在创建对象时,用户必须使用new操作符为对象分配内存。不过,在清除对象时,由系统自动进行内存回收,不需要用户额外处理。GC垃圾回收Java语言的内存自动回收称为垃圾回收(GarbageCollection)机制,简称......
  • 对象的销毁
    对象的销毁在编程中,对象的销毁是一个重要的概念,它涉及到了资源管理和内存释放。不同编程语言对对象销毁的处理方式有所不同,但大多数现代编程语言都提供了自动的内存管理机制(如垃圾回收机制),来帮助开发者管理内存和对象生命周期。以下是一些关于对象销毁的基本概念和不同编程语言中......
  • 为什么代码会引发错误而不是销毁 ursina 中的实体?
    我正在使用ursina制作一个无尽的跑步者,我在这3行代码中遇到了错误。defupdate(self):ifself.y>=8:destroy(self)我试图问我的朋友,但没有任何进展。我预期发生的是敌人最终会被消灭,而不是提出错误。在Ursina中的update函数内销毁一个......
  • [vue3] Vue3源码阅读笔记 reactivity - collectionHandlers
    源码位置:https://github.com/vuejs/core/blob/main/packages/reactivity/src/collectionHandlers.ts这个文件主要用于处理Set、Map、WeakSet、WeakMap类型的拦截。拦截是为了什么?为什么要处理这些方法?Vue3实现响应式的思路是使用ProxyAPI在getter中收集依赖,在setter触发更新......
  • [vue3] Vue3源码阅读笔记 reactivity - collectionHandlers
    源码位置:https://github.com/vuejs/core/blob/main/packages/reactivity/src/collectionHandlers.ts这个文件主要用于处理Set、Map、WeakSet、WeakMap类型的拦截。拦截是为了什么?为什么要处理这些方法?Vue3实现响应式的思路是使用ProxyAPI在getter中收集依赖,在setter触发更新......
  • 横竖屏切换,按home键,按返回键,锁屏与解锁屏幕,跳转透明Activity界面,启动一个 Theme
    A->B横竖屏切换:A走完ondestory才会走B的onCreate--会走pause按home键:本质上就是普通开B按返回键:也是开B但是会走关A锁屏与解锁屏幕:普通跳转透明Activity界面:会走pause但是不会走onstop启动一个Theme为Dialog的Activity:会onPause不会stop弹出Dialog时A......
  • Android9.0 Activity启动流程分析(三)
    文章目录   1、Android屏幕层级       1.1、Window和PhoneWindow的概念       1.2、View和ViewRootImpl的概念   2、ClientLiftCycleManager   3、handleLaunchActivity       3.1、CreatActivity       3.2、setContentView   4......
  • Android 8.0 源码分析 (四) Activity 启动
    链接:https://juejin.cn/post/6844903983442558989前言我们熟知一般Android工程师都是在应用层上���发,不会涉及系统源码,但是如果你想往底层发展,或者深入插件化、Framework系统层等开发工作,如果不了解Android源码可是不行的,那么接下来我基于自己的理解跟学习来记录跟Android......
  • 深入理解Activity启动流程和AMS框架(三)
    链接https://cloud.tencent.com/developer/article/1601480续:深入理解Activity启动流程和AMS框架(一)深入理解Activity启动流程和AMS框架(二)5、Task和LauncherMode(1)、如何才能开始一个新的Task?Intent中定义了一个标志FLAGACTIVITYNEW_TASK,在startActivity的Intent参数中加入该......