原生实现

调用了切换方法以后,当前界面会重建,其他界面也一样,这样就会改变颜色。使用原生的方法实现比较简单,缺点就是切换效果不好(因为界面会重建,设置了动画效果也不好),界面会重建,而且就两种模式可选。

  1. activity

    1. /**
    2. * @Author: 13009
    3. * @CreateDate: 2021/8/3 13:57
    4. *@Email:kiwilss@163.com
    5. *@Description: 系统原生的方式实现夜间模式
    6. * 优点:方法简单,实现简单
    7. * 缺点:切换效果不好,只能实现两种模式,界面会重新绘制
    8. */
    9. class SkinColorActivity: AppCompatActivity(R.layout.activity_skin_color) {
    10. override fun onCreate(savedInstanceState: Bundle?) {
    11. super.onCreate(savedInstanceState)
    12. Log.e(TAG, "onCreate: ")
    13. //获取当前模式
    14. btnSkinColorMode.setOnClickListener {
    15. val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
    16. Log.e(TAG, "onCreate: $mode")
    17. }
    18. btnSkinColorDay.setOnClickListener {
    19. val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
    20. if (mode == Configuration.UI_MODE_NIGHT_YES){//是夜间模式时修改
    21. // showAnimation()
    22. AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
    23. window.setWindowAnimations(R.style.WindowAnimationFadeInOut)
    24. recreate()
    25. //设置后整个app都会生效,动画效果不好,使用recreate后界面数据丢失
    26. }
    27. }
    28. btnSkinColorNight.setOnClickListener {
    29. val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
    30. if (mode == Configuration.UI_MODE_NIGHT_NO){//是日间模式时修改
    31. // showAnimation()
    32. AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
    33. /* window.setWindowAnimations(R.style.WindowAnimationFadeInOut)
    34. recreate()*/
    35. }
    36. }
    37. }
    38. override fun onResume() {
    39. super.onResume()
    40. Log.e(TAG, "onResume: ")
    41. }
    42. override fun onConfigurationChanged(newConfig: Configuration) {
    43. super.onConfigurationChanged(newConfig)
    44. Log.e(TAG, "onConfigurationChanged: ")
    45. }
    46. /**
    47. * 展示一个切换动画
    48. */
    49. private fun showAnimation() {
    50. val decorView = window.decorView
    51. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
    52. if (decorView is ViewGroup && cacheBitmap != null) {
    53. val view = View(this)
    54. view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap))
    55. val layoutParam = ViewGroup.LayoutParams(
    56. ViewGroup.LayoutParams.MATCH_PARENT,
    57. ViewGroup.LayoutParams.MATCH_PARENT
    58. )
    59. decorView.addView(view, layoutParam)
    60. val objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
    61. objectAnimator.duration = 300
    62. objectAnimator.addListener(object : AnimatorListenerAdapter() {
    63. override fun onAnimationEnd(animation: Animator) {
    64. super.onAnimationEnd(animation)
    65. decorView.removeView(view)
    66. }
    67. })
    68. objectAnimator.start()
    69. }
    70. }
    71. /**
    72. * 获取一个 View 的缓存视图
    73. *
    74. * @param view
    75. * @return
    76. */
    77. private fun getCacheBitmapFromView(view: View): Bitmap? {
    78. val drawingCacheEnabled = true
    79. view.isDrawingCacheEnabled = drawingCacheEnabled
    80. view.buildDrawingCache(drawingCacheEnabled)
    81. val drawingCache = view.drawingCache
    82. val bitmap: Bitmap?
    83. if (drawingCache != null) {
    84. bitmap = Bitmap.createBitmap(drawingCache)
    85. view.isDrawingCacheEnabled = false
    86. } else {
    87. bitmap = null
    88. }
    89. return bitmap
    90. }
    91. }
  2. 布局

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:orientation="vertical"
    6. android:background="@color/skin_white">
    7. <TextView
    8. android:layout_width="wrap_content"
    9. android:layout_height="wrap_content"
    10. android:text="测试颜色变化相关"
    11. android:textColor="@color/skin_black"
    12. android:layout_gravity="center"
    13. android:layout_marginTop="@dimen/dp_10"/>
    14. <Button
    15. android:id="@+id/btnSkinColorMode"
    16. android:layout_width="wrap_content"
    17. android:layout_height="wrap_content"
    18. android:text="获取当前模式"
    19. android:textColor="@color/skin_red"
    20. android:background="@color/skin_yellow"
    21. android:layout_marginTop="@dimen/dp_10"
    22. android:layout_gravity="center"
    23. />
    24. <Button
    25. android:id="@+id/btnSkinColorNight"
    26. android:layout_width="wrap_content"
    27. android:layout_height="wrap_content"
    28. android:text="夜间模式"
    29. android:textColor="@color/skin_red"
    30. android:background="@color/skin_yellow"
    31. android:layout_marginTop="@dimen/dp_10"
    32. android:layout_gravity="center"
    33. />
    34. <Button
    35. android:id="@+id/btnSkinColorDay"
    36. android:layout_width="wrap_content"
    37. android:layout_height="wrap_content"
    38. android:text="日间模式"
    39. android:textColor="@color/skin_red"
    40. android:background="@color/skin_yellow"
    41. android:layout_marginTop="@dimen/dp_10"
    42. android:layout_gravity="center"
    43. />
    44. </LinearLayout>
  3. 颜色
    values里颜色:

    1. <!-- 换肤相关颜色-->
    2. <color name="skin_white">#FFFFFF</color>
    3. <color name="skin_black">#000000</color>
    4. <color name="skin_green">#00ff00</color>
    5. <color name="skin_yellow">#ffff00</color>
    6. <color name="skin_red">#ff0000</color>
  4. values-night里的颜色:

    1. <!-- 换肤相关颜色-->
    2. <color name="skin_white">#000000</color>
    3. <color name="skin_black">#FFFFFF</color>
    4. <color name="skin_green">#00ff00</color>
    5. <color name="skin_yellow">#ffff00</color>
    6. <color name="skin_red">#ff0000</color>

记录模式,通过控件设置颜色实现

这种方式实现原理很简单,切换模式时,获取控件设置对应的背景或图片文字颜色,这种方式不用重启界面,切换效果就会好很多,缺点就是要对每个控件进行设置。

  1. activity ``` /**
    • @Author: 13009
    • @CreateDate: 2021/8/4 18:28 @Email:kiwilss@163.com @Description: 通过代码对控件修改颜色实现
    • 优点:不用重启,切换效果很好
    • 缺点:需要单独对每个控件设置颜色 */ class SkinColorActivity2 : AppCompatActivity(R.layout.activity_skin_color2) { private var width = 0 private var height = 0 private var statusBarHeight = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) llSkinRoot.post { getInitData() }
  1. btnSkinToggle.setOnClickListener {
  2. //切换夜间
  3. toggleMode(true)
  4. }
  5. btnSkinToggle2.setOnClickListener {
  6. toggleMode(false)
  7. }
  8. }
  9. private fun toggleMode(isNight: Boolean) {
  10. /* if (isNight) {
  11. setNightTheme()
  12. } else {
  13. setDayTheme()
  14. }*/
  15. toggleDayNight(isNight)
  16. }
  17. private fun toggleDayNight(night: Boolean) {
  18. showAnimation()
  19. if (night){
  20. setNightThemeInfo()
  21. }else{
  22. setDayThemeInfo()
  23. }
  24. }
  25. /**
  26. * 展示一个切换动画
  27. */
  28. private fun showAnimation() {
  29. val decorView = window.decorView
  30. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
  31. if (decorView is ViewGroup && cacheBitmap != null) {
  32. val view = View(this)
  33. view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap))
  34. val layoutParam = ViewGroup.LayoutParams(
  35. ViewGroup.LayoutParams.MATCH_PARENT,
  36. ViewGroup.LayoutParams.MATCH_PARENT
  37. )
  38. decorView.addView(view, layoutParam)
  39. val objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
  40. objectAnimator.duration = 500
  41. objectAnimator.addListener(object : AnimatorListenerAdapter() {
  42. override fun onAnimationEnd(animation: Animator) {
  43. super.onAnimationEnd(animation)
  44. decorView.removeView(view)
  45. }
  46. })
  47. objectAnimator.start()
  48. }
  49. }
  50. /**
  51. * 获取一个 View 的缓存视图
  52. *
  53. * @param view
  54. * @return
  55. */
  56. private fun getCacheBitmapFromView(view: View): Bitmap? {
  57. val drawingCacheEnabled = true
  58. view.isDrawingCacheEnabled = drawingCacheEnabled
  59. view.buildDrawingCache(drawingCacheEnabled)
  60. val drawingCache = view.drawingCache
  61. val bitmap: Bitmap?
  62. if (drawingCache != null) {
  63. bitmap = Bitmap.createBitmap(drawingCache)
  64. view.isDrawingCacheEnabled = false
  65. } else {
  66. bitmap = null
  67. }
  68. return bitmap
  69. }
  70. /**
  71. * 设置夜间模式
  72. */
  73. private fun setNightTheme() {
  74. val imageView = ImageView(this)
  75. imageView.layoutParams = ViewGroup.LayoutParams(width, height - statusBarHeight)
  76. val bitmap = loadBitmapFromView(llSkinRoot)
  77. imageView.setImageBitmap(bitmap)
  78. llSkinRoot.addView(imageView)
  79. //设置新主题
  80. setNightThemeInfo()
  81. val colorA = Color.parseColor("#ffffff")
  82. val colorB = Color.parseColor("#333444")
  83. val objectAnimator = ObjectAnimator.ofInt(imageView, "backgroundColor", colorA, colorB)
  84. objectAnimator.duration = 800
  85. objectAnimator.setEvaluator(ArgbEvaluator())
  86. objectAnimator.addListener(object : Animator.AnimatorListener {
  87. override fun onAnimationStart(animation: Animator) {}
  88. override fun onAnimationEnd(animation: Animator) {
  89. llSkinRoot.removeView(imageView)
  90. }
  91. override fun onAnimationCancel(animation: Animator) {}
  92. override fun onAnimationRepeat(animation: Animator) {}
  93. })
  94. objectAnimator.start()
  95. }
  96. /**
  97. * 设置日间模式
  98. */
  99. private fun setDayTheme() {
  100. val imageView = ImageView(this)
  101. imageView.layoutParams = ViewGroup.LayoutParams(width, height - statusBarHeight)
  102. imageView.scaleType = ImageView.ScaleType.CENTER_CROP
  103. val bitmap: Bitmap? = loadBitmapFromView(llSkinRoot)
  104. imageView.setImageBitmap(bitmap)
  105. llSkinRoot.addView(imageView)
  106. //设置新主题
  107. setDayThemeInfo()
  108. val colorA = Color.parseColor("#333444")
  109. val colorB = Color.parseColor("#ffffff")
  110. val objectAnimator = ObjectAnimator.ofInt(imageView, "backgroundColor", colorA, colorB)
  111. objectAnimator.duration = 800
  112. objectAnimator.setEvaluator(ArgbEvaluator())
  113. objectAnimator.addListener(object : Animator.AnimatorListener {
  114. override fun onAnimationStart(animation: Animator) {}
  115. override fun onAnimationEnd(animation: Animator) {
  116. llSkinRoot.removeView(imageView)
  117. }
  118. override fun onAnimationCancel(animation: Animator) {}
  119. override fun onAnimationRepeat(animation: Animator) {}
  120. })
  121. objectAnimator.start()
  122. }
  123. /**
  124. * 设置夜间模式具体代码
  125. */
  126. private fun setNightThemeInfo() {
  127. llSkinRoot.setBackgroundColor(Color.parseColor("#333444"))
  128. tvSkinText.setTextColor(Color.parseColor("#666666"))

// imageView.setImageResource(R.mipmap.night_icon) btnSkinToggle.setTextColor(ContextCompat.getColor(this,R.color.skin_black)) btnSkinToggle.setBackgroundColor(ContextCompat.getColor(this,R.color.skin_white)) btnSkinToggle2.setTextColor(ContextCompat.getColor(this,R.color.skin_black)) btnSkinToggle2.setBackgroundColor(ContextCompat.getColor(this,R.color.skin_white)) }

  1. /**
  2. * 设置日渐模式具体代码
  3. */
  4. private fun setDayThemeInfo() {
  5. llSkinRoot.setBackgroundColor(Color.parseColor("#FFFFFF"))
  6. tvSkinText.setTextColor(Color.parseColor("#222222"))

// imageView.setImageResource(R.mipmap.day_icom) btnSkinToggle.setTextColor(ContextCompat.getColor(this,R.color.skin_white)) btnSkinToggle.setBackgroundColor(ContextCompat.getColor(this,R.color.skin_black)) btnSkinToggle2.setTextColor(ContextCompat.getColor(this,R.color.skin_white)) btnSkinToggle2.setBackgroundColor(ContextCompat.getColor(this,R.color.skin_black)) }

  1. /**
  2. * 获取view截图对应的bitmap
  3. * @param v
  4. * @return
  5. */
  6. private fun loadBitmapFromView(v: View): Bitmap? {
  7. val b = Bitmap.createBitmap(width, height - statusBarHeight, Bitmap.Config.ARGB_8888)
  8. val c = Canvas(b)
  9. v.layout(0, 0, v.layoutParams.width, v.layoutParams.height)
  10. v.draw(c)
  11. return b
  12. }
  13. /**
  14. * 获取屏幕宽高和状态栏高度
  15. */
  16. private fun getInitData() {
  17. val wm = this.windowManager
  18. width = wm.defaultDisplay.width
  19. height = wm.defaultDisplay.height
  20. //获取status_bar_height资源的ID
  21. val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
  22. if (resourceId > 0) {
  23. //根据资源ID获取响应的尺寸值
  24. statusBarHeight = resources.getDimensionPixelSize(resourceId)
  25. }
  26. }

}

  1. 2. 界面

<?xml version=”1.0” encoding=”utf-8”?>

  1. <LinearLayout
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5. <TextView
  6. android:id="@+id/tvSkinText"
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:text="动画效果改变测试"
  10. android:textColor="@color/skin_black"
  11. android:layout_gravity="center"/>
  12. <Button
  13. android:id="@+id/btnSkinToggle"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:text="夜间模式"
  17. android:background="@color/skin_black"
  18. android:textColor="@color/skin_white"
  19. android:layout_gravity="center"/>
  20. <Button
  21. android:id="@+id/btnSkinToggle2"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:text="日间模式"
  25. android:background="@color/skin_black"
  26. android:textColor="@color/skin_white"
  27. android:layout_gravity="center"/>
  28. </LinearLayout>

  1. <a name="74a3bb78"></a>
  2. ### 通过主题获取颜色,再用代码设置
  3. 这种方法和上面实现类似,只是设置的颜色是由选中的主题获取,这样就可以设置很多个主题,实现多种颜色换肤。
  4. 1. activity

/**

  • @Author: 13009
  • @CreateDate: 2021/8/4 19:39 @Email:kiwilss@163.com @Description: 通过修改主题,再修改代码颜色实现
  • 优点:实现效果好,不需要重启,遍历当前界面控件更方便设置颜色,一种主题可以代表一种颜色
  • 缺点:需要记录当前主题,每次进入设置对应的颜色 */ class SkinColorThemeActivity : AppCompatActivity(R.layout.activity_skin_color_theme) { override fun onCreate(savedInstanceState: Bundle?) {

    1. super.onCreate(savedInstanceState)
    2. //这里可以获取保存的主题,再设置对应的颜色
    3. btnColorThemeDay.setOnClickListener {
    4. changeThemeTest(true)
    5. }
    6. btnColorThemeNight.setOnClickListener {
    7. changeThemeTest(false)
    8. }
    9. initRecycler()
    10. btnColorThemeJump.setOnClickListener {

    // startActivityK()

    1. startActivityForResult(Intent(this,SkinColorThemeActivity::class.java),1)
    2. }
    3. btnColorThemeJump2.setOnClickListener {
    4. setResult(RESULT_OK)
    5. finish()
    6. }

    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    1. super.onActivityResult(requestCode, resultCode, data)
    2. Log.e(TAG, "onActivityResult: ")
    3. setTheme(R.style.Theme_MyApplication2)
    4. SkinHelp.instance.refreshUI(this)

    }

  1. private fun initRecycler() {
  2. val madapter = SkinColorThemeAdapter()
  3. val list = mutableListOf<String>("1", "", "", "")
  4. rvColorThemeList.run {
  5. layoutManager = LinearLayoutManager(this@SkinColorThemeActivity)
  6. adapter = madapter
  7. }
  8. madapter.setList(list)
  9. }
  10. private fun changeThemeTest(isDay: Boolean) {
  11. if (isDay) {
  12. setTheme(R.style.Theme_MyApplication)
  13. } else {
  14. setTheme(R.style.Theme_MyApplication2)
  15. }
  16. SkinHelp.instance.showAnimationAndRefreshUI(this)

// showAnimation() // refreshUI() //这里也可以修改状态栏颜色

  1. }
  2. private fun refreshUI() {
  3. //获取主题对应的颜色
  4. val background = TypedValue() //背景色
  5. val textColor = TypedValue() //字体颜色
  6. val theme = theme
  7. theme.resolveAttribute(R.attr.main_bg, background, true)
  8. theme.resolveAttribute(R.attr.main_textcolor, textColor, true)
  9. //遍历控件设置颜色
  10. //注意这个vGroup并不是activity.xml中定义的根布局, mRootView才是。
  11. //注意这个vGroup并不是activity.xml中定义的根布局, mRootView才是。
  12. val vGroup = window.decorView.findViewById<View>(android.R.id.content) as ViewGroup
  13. val rootView = vGroup.getChildAt(0) as ViewGroup
  14. traversalView(vGroup, background, textColor)
  15. }
  16. private fun traversalView(rootView: ViewGroup, background: TypedValue, textColor: TypedValue) {
  17. for (i in 0 until rootView.childCount) {
  18. val childVg = rootView.getChildAt(i)
  19. val tag = childVg.tag ?: ""
  20. Log.e(TAG, "traversalView: $tag")
  21. when (childVg) {
  22. is ViewGroup -> {
  23. if (TextUtils.equals(tag.toString(), "bg")) {
  24. childVg.setBackgroundColor(
  25. ContextCompat.getColor(
  26. this@SkinColorThemeActivity,
  27. background.resourceId
  28. )
  29. )
  30. }
  31. traversalView(childVg, background, textColor)
  32. }
  33. is TextView -> {
  34. setTextViewSkin(tag, childVg, background, textColor)
  35. }
  36. is Button -> {
  37. setTextViewSkin(tag, childVg, background, textColor)
  38. }
  39. }
  40. }
  41. }
  42. private fun setTextViewSkin(
  43. tag: Any,
  44. childVg: TextView,
  45. background: TypedValue,
  46. textColor: TypedValue
  47. ) {
  48. val tags = tag.toString().split(",")
  49. Log.e(TAG, "setTextViewSkin: $tags")
  50. when (tags.size) {
  51. 1 -> {
  52. if (TextUtils.equals(tags[0], "textColor")) {
  53. childVg.setTextColor(
  54. ContextCompat.getColor(
  55. this@SkinColorThemeActivity,
  56. textColor.resourceId
  57. )
  58. )
  59. } else if (TextUtils.equals(tags[0], "bg")) {
  60. childVg.setBackgroundColor(
  61. ContextCompat.getColor(
  62. this@SkinColorThemeActivity,
  63. background.resourceId
  64. )
  65. )
  66. }
  67. }
  68. 2 -> {
  69. if (TextUtils.equals(tags[0], "textColor")) {
  70. childVg.setTextColor(
  71. ContextCompat.getColor(
  72. this@SkinColorThemeActivity,
  73. textColor.resourceId
  74. )
  75. )
  76. }
  77. if (TextUtils.equals(tags[1], "bg")) {
  78. childVg.setBackgroundColor(
  79. ContextCompat.getColor(
  80. this@SkinColorThemeActivity,
  81. background.resourceId
  82. )
  83. )
  84. }
  85. }
  86. }
  87. }
  88. /**
  89. *展示一个切换动画
  90. */
  91. fun showAnimation2(activity: Activity) {
  92. val decorView = activity.window.decorView as ViewGroup
  93. val imageView = ImageView(activity)
  94. val width = activity.resources.displayMetrics.widthPixels
  95. val height = activity.resources.displayMetrics.heightPixels
  96. val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
  97. //生成截图,设置
  98. decorView.draw(Canvas(bitmap))
  99. imageView.background = BitmapDrawable(activity.resources, bitmap)
  100. imageView.layoutParams = ViewGroup.LayoutParams(
  101. ViewGroup.LayoutParams.MATCH_PARENT,
  102. ViewGroup.LayoutParams.MATCH_PARENT
  103. )
  104. decorView.addView(imageView)
  105. //设置一个渐变动画
  106. val animator = ValueAnimator.ofFloat(1f,0f)
  107. animator.addUpdateListener {
  108. imageView.alpha = it.animatedValue as Float
  109. }
  110. animator.addListener {
  111. decorView.removeView(imageView)
  112. }
  113. animator.duration = 1000
  114. animator.start()
  115. }
  116. /**
  117. * 展示一个切换动画
  118. */
  119. private fun showAnimation() {
  120. val decorView = window.decorView
  121. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
  122. if (decorView is ViewGroup && cacheBitmap != null) {
  123. val view = View(this)

// view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap)) view.background = BitmapDrawable(resources, cacheBitmap) val layoutParam = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) decorView.addView(view, layoutParam) val objectAnimator = ObjectAnimator.ofFloat(view, “alpha”, 1f, 0f) objectAnimator.duration = 1000 objectAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) decorView.removeView(view) } }) objectAnimator.start() } }

  1. /**
  2. * 获取一个 View 的缓存视图
  3. *
  4. * @param view
  5. * @return
  6. */
  7. private fun getCacheBitmapFromView(view: View): Bitmap? {
  8. val drawingCacheEnabled = true
  9. view.isDrawingCacheEnabled = drawingCacheEnabled
  10. view.buildDrawingCache(drawingCacheEnabled)
  11. val drawingCache = view.drawingCache
  12. val bitmap: Bitmap?
  13. if (drawingCache != null) {
  14. bitmap = Bitmap.createBitmap(drawingCache)
  15. view.isDrawingCacheEnabled = false
  16. } else {
  17. bitmap = null
  18. }
  19. return bitmap
  20. }

}

  1. 2. 界面

<?xml version=”1.0” encoding=”utf-8”?>

  1. <TextView
  2. android:id="@+id/tvColorThemeText"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content"
  5. android:text="遍历所有控件,再通过切换主题实现"
  6. android:tag="textColor"
  7. android:textColor="?attr/main_textcolor"
  8. android:layout_gravity="center"
  9. android:layout_marginTop="@dimen/dp_10"/>
  10. <Button
  11. android:id="@+id/btnColorThemeNight"
  12. android:layout_width="wrap_content"
  13. android:layout_height="wrap_content"
  14. android:text="夜间"
  15. android:textColor="?attr/main_textcolor"
  16. android:background="?attr/main_bg"
  17. android:layout_gravity="center"
  18. android:layout_marginTop="@dimen/dp_40"/>
  19. <Button
  20. android:id="@+id/btnColorThemeDay"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="日间"
  24. android:textColor="?attr/main_textcolor"
  25. android:background="?attr/main_bg"
  26. android:layout_gravity="center"
  27. android:layout_marginTop="@dimen/dp_40"/>
  28. <Button
  29. android:id="@+id/btnColorThemeJump"
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:text="跳转"
  33. android:textColor="?attr/main_textcolor"
  34. android:background="?attr/main_bg"
  35. android:layout_gravity="center"
  36. android:layout_marginTop="@dimen/dp_40"/>
  37. <Button
  38. android:id="@+id/btnColorThemeJump2"
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:text="切换"
  42. android:textColor="?attr/main_textcolor"
  43. android:background="?attr/main_bg"
  44. android:layout_gravity="center"
  45. android:layout_marginTop="@dimen/dp_40"/>
  1. <RelativeLayout
  2. android:id="@+id/rlColorThemeTest"
  3. android:layout_width="match_parent"
  4. android:layout_height="100dp"
  5. android:background="?attr/main_bg"/>
  6. <androidx.recyclerview.widget.RecyclerView
  7. android:id="@+id/rvColorThemeList"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. />

  1. 3. adapter

class SkinColorThemeAdapter: BaseQuickAdapter(R.layout.item_skin_color_theme) { override fun convert(holder: BaseViewHolder, item: String) {

  1. }

}

  1. 4. item

<?xml version=”1.0” encoding=”utf-8”?>

  1. <TextView
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:text="测试列表中是否有效"
  5. android:layout_centerInParent="true"
  6. android:tag="textColor"
  7. android:textColor="?attr/main_textcolor"/>

  1. 5. attrs

<?xml version=”1.0” encoding=”utf-8”?>

  1. 6. theme

  1. <style name="TransparentTheme" parent="Theme.MyApplication">
  2. <item name="android:windowNoTitle">true</item>
  3. <item name="android:windowIsTranslucent">true</item>
  4. <item name="android:windowBackground">@color/translate</item>
  5. </style>
  6. <style name="WindowAnimationFadeInOut">
  7. <item name="android:windowEnterAnimation">@anim/fade_in</item>
  8. <item name="android:windowExitAnimation">@anim/fade_out</item>
  9. </style>

  1. 7. skinhelp

class SkinHelp private constructor() {

  1. companion object {
  2. val instance = SkinHelpSingle.holder
  3. }
  4. private object SkinHelpSingle {
  5. val holder = SkinHelp()
  6. }
  7. /**
  8. * 展示一个切换动画,并刷新界面
  9. */
  10. fun showAnimationAndRefreshUI(activity: Activity?) {
  11. if (activity == null) {
  12. return
  13. }
  14. val decorView = activity.window.decorView
  15. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
  16. if (decorView is ViewGroup && cacheBitmap != null) {
  17. val view = View(activity)

// view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap)) view.background = BitmapDrawable(activity.resources, cacheBitmap) val layoutParam = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) decorView.addView(view, layoutParam) val objectAnimator = ObjectAnimator.ofFloat(view, “alpha”, 1f, 0f) objectAnimator.duration = 1000 objectAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) decorView.removeView(view) } }) objectAnimator.start() } //刷新界面 refreshUI(activity) }

  1. fun refreshUI(activity: Activity) {
  2. //获取主题对应的颜色并设置
  3. val background = TypedValue() //背景色
  4. val textColor = TypedValue() //字体颜色
  5. val theme = activity.theme
  6. theme.resolveAttribute(R.attr.main_bg, background, true)
  7. theme.resolveAttribute(R.attr.main_textcolor, textColor, true)
  8. val vGroup = activity.window.decorView.findViewById<View>(android.R.id.content) as ViewGroup

// val rootView = vGroup.getChildAt(0) as ViewGroup traversalView(vGroup, background, textColor) }

  1. /**
  2. * 展示一个切换动画
  3. */
  4. fun showAnimation(activity: Activity?) {
  5. if (activity == null) {
  6. return
  7. }
  8. val decorView = activity.window.decorView
  9. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
  10. if (decorView is ViewGroup && cacheBitmap != null) {
  11. val view = View(activity)

// view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap)) view.background = BitmapDrawable(activity.resources, cacheBitmap) val layoutParam = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) decorView.addView(view, layoutParam) val objectAnimator = ObjectAnimator.ofFloat(view, “alpha”, 1f, 0f) objectAnimator.duration = 1000 objectAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { super.onAnimationEnd(animation) decorView.removeView(view) } }) objectAnimator.start() } } /* 遍历控件设置对应颜色

  1. * @param vGroup
  2. * @param background
  3. * @param textColor
  4. */
  5. private fun traversalView(vGroup: ViewGroup, background: TypedValue, textColor: TypedValue) {
  6. for (i in 0 until vGroup.childCount) {
  7. val childVg = vGroup.getChildAt(i)
  8. val tag = childVg.tag
  9. when (childVg) {
  10. is ViewGroup -> {
  11. if (tag != null &&TextUtils.equals("bg", tag.toString())) {
  12. childVg.setBackgroundColor(
  13. ContextCompat.getColor(
  14. MyApp.myApp,
  15. background.resourceId
  16. )
  17. )
  18. }
  19. traversalView(childVg, background, textColor)
  20. }
  21. is TextView -> {
  22. tag ?: continue
  23. setTextSkin(tag.toString(), childVg, background, textColor)
  24. }
  25. is Button -> {
  26. tag ?: continue
  27. setTextSkin(tag.toString(), childVg, background, textColor)
  28. }
  29. }
  30. }
  31. }
  32. private fun setTextSkin(
  33. tagss: String,
  34. childVg: TextView,
  35. background: TypedValue,
  36. textColor: TypedValue
  37. ) {
  38. val tags = tagss.split(",")
  39. if (tags.isNullOrEmpty()) return
  40. if (tags.contains("bg")) {
  41. childVg.setBackgroundColor(
  42. ContextCompat.getColor(
  43. MyApp.myApp,
  44. background.resourceId
  45. )
  46. )
  47. }
  48. if (tags.contains("textColor")) {
  49. childVg.setTextColor(
  50. ContextCompat.getColor(
  51. MyApp.myApp,
  52. textColor.resourceId
  53. )
  54. )
  55. }
  56. }
  57. /**
  58. * 获取一个 View 的缓存视图
  59. *
  60. * @param view
  61. * @return
  62. */
  63. private fun getCacheBitmapFromView(view: View): Bitmap? {
  64. val drawingCacheEnabled = true
  65. view.isDrawingCacheEnabled = drawingCacheEnabled
  66. view.buildDrawingCache(drawingCacheEnabled)
  67. val drawingCache = view.drawingCache
  68. val bitmap: Bitmap?
  69. if (drawingCache != null) {
  70. bitmap = Bitmap.createBitmap(drawingCache)
  71. view.isDrawingCacheEnabled = false
  72. } else {
  73. bitmap = null
  74. }
  75. return bitmap
  76. }

}

  1. <a name="f1fb8fd6"></a>
  2. ### 主题 + 自定义View
  3. 通过修改主题改变颜色,不过每个控件都要自定义,有点麻烦。
  4. 1. activity

/**

  • @Author: 13009
  • @CreateDate: 2021/8/3 15:47 @Email:kiwilss@163.com @Description: 通过切换主题实现夜间模式,一套主题就是一种颜色,可以实现换肤
  • 优点,不会重启当前activity,切换时效果非常好
  • 缺点,需要在记录当前主题,在baseActivity里面设置,需要自定义所有需要用到的控件 */ class SkinThemeActivity: AppCompatActivity(R.layout.activity_skin_theme) { override fun onCreate(savedInstanceState: Bundle?) {

    1. super.onCreate(savedInstanceState)
    2. //当前主题保存在数据库,可以随时获取
    3. btnSkinColorNight.setOnClickListener {
    4. /*setTheme(R.style.Theme_MyApplication2)
    5. change()*/

    // showAnimation() // toggleThemeSetting()

    1. setTheme(R.style.Theme_MyApplication2)
    2. change()
    3. }
    4. btnSkinColorDay.setOnClickListener {
    5. setTheme(R.style.Theme_MyApplication)
    6. change()
    7. }

    }

  1. fun change() {
  2. val rootView = window.decorView
  3. rootView.isDrawingCacheEnabled = true
  4. rootView.buildDrawingCache(true)
  5. val localBitmap = Bitmap.createBitmap(rootView.drawingCache)
  6. rootView.isDrawingCacheEnabled = false
  7. if (null != localBitmap && rootView is ViewGroup) {
  8. val tmpView = View(applicationContext)
  9. tmpView.setBackgroundDrawable(BitmapDrawable(resources, localBitmap))
  10. val params = ViewGroup.LayoutParams(
  11. ViewGroup.LayoutParams.MATCH_PARENT,
  12. ViewGroup.LayoutParams.MATCH_PARENT
  13. )
  14. rootView.addView(tmpView, params)
  15. tmpView.animate().alpha(0f).setDuration(400)
  16. .setListener(object : Animator.AnimatorListener {
  17. override fun onAnimationStart(animation: Animator) {
  18. ColorUiUtil.changeTheme(rootView, theme)
  19. System.gc()
  20. }
  21. override fun onAnimationEnd(animation: Animator) {
  22. rootView.removeView(tmpView)
  23. localBitmap.recycle()
  24. }
  25. override fun onAnimationCancel(animation: Animator) {
  26. }
  27. override fun onAnimationRepeat(animation: Animator) {
  28. }
  29. }).start()
  30. }
  31. //改变一下状态栏颜色
  32. //发送广播让所有界面全部改变
  33. }
  34. /**
  35. * 展示一个切换动画
  36. */
  37. private fun showAnimation() {
  38. val decorView = window.decorView
  39. val cacheBitmap: Bitmap? = getCacheBitmapFromView(decorView)
  40. if (decorView is ViewGroup && cacheBitmap != null) {
  41. val view = View(this)
  42. view.setBackgroundDrawable(BitmapDrawable(resources, cacheBitmap))
  43. val layoutParam = ViewGroup.LayoutParams(
  44. ViewGroup.LayoutParams.MATCH_PARENT,
  45. ViewGroup.LayoutParams.MATCH_PARENT
  46. )
  47. decorView.addView(view, layoutParam)
  48. val objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)
  49. objectAnimator.duration = 300
  50. objectAnimator.addListener(object : AnimatorListenerAdapter() {
  51. override fun onAnimationEnd(animation: Animator) {
  52. super.onAnimationEnd(animation)
  53. decorView.removeView(view)
  54. }
  55. })
  56. objectAnimator.start()
  57. }
  58. }
  59. /**
  60. * 获取一个 View 的缓存视图
  61. *
  62. * @param view
  63. * @return
  64. */
  65. private fun getCacheBitmapFromView(view: View): Bitmap? {
  66. val drawingCacheEnabled = true
  67. view.isDrawingCacheEnabled = drawingCacheEnabled
  68. view.buildDrawingCache(drawingCacheEnabled)
  69. val drawingCache = view.drawingCache
  70. val bitmap: Bitmap?
  71. if (drawingCache != null) {
  72. bitmap = Bitmap.createBitmap(drawingCache)
  73. view.isDrawingCacheEnabled = false
  74. } else {
  75. bitmap = null
  76. }
  77. return bitmap
  78. }

}

  1. 2. xml

<?xml version=”1.0” encoding=”utf-8”?>

  1. <com.leapmotor.explame.ui.skin.theme.ColorTextView
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:text="测试颜色变化相关"
  5. android:textColor="?attr/main_textcolor"
  6. android:layout_gravity="center"
  7. android:layout_marginTop="@dimen/dp_10"/>
  8. <com.leapmotor.explame.ui.skin.theme.ColorButton
  9. android:id="@+id/btnSkinColorMode"
  10. android:layout_width="wrap_content"
  11. android:layout_height="wrap_content"
  12. android:text="获取当前模式"
  13. android:textColor="?attr/main_textcolor"
  14. android:background="@color/skin_yellow"
  15. android:layout_marginTop="@dimen/dp_10"
  16. android:layout_gravity="center"
  17. />
  18. <com.leapmotor.explame.ui.skin.theme.ColorButton
  19. android:id="@+id/btnSkinColorNight"
  20. android:layout_width="wrap_content"
  21. android:layout_height="wrap_content"
  22. android:text="夜间模式"
  23. android:textColor="?attr/main_textcolor"
  24. android:background="@color/skin_yellow"
  25. android:layout_marginTop="@dimen/dp_10"
  26. android:layout_gravity="center"
  27. />
  28. <com.leapmotor.explame.ui.skin.theme.ColorButton
  29. android:id="@+id/btnSkinColorDay"
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:text="日间模式"
  33. android:textColor="?attr/main_textcolor"
  34. android:background="@color/skin_yellow"
  35. android:layout_marginTop="@dimen/dp_10"
  36. android:layout_gravity="center"
  37. />

  1. 3. ColorButton

@SuppressLint(“AppCompatCustomView”) public class ColorButton extends Button implements ColorUiInterface {

  1. private int attr_textColor = -1;
  2. private int attr_background = -1;
  3. public ColorButton(Context context) {
  4. this(context,null);
  5. }
  6. public ColorButton(Context context, AttributeSet attrs) {
  7. this(context, attrs,0);
  8. }
  9. public ColorButton(Context context, AttributeSet attrs, int defStyleAttr) {
  10. super(context, attrs, defStyleAttr);
  11. this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs);
  12. this.attr_background = ViewAttributeUtil.getBackgroundAttibute(attrs);
  13. }
  14. @Override
  15. public View getView() {
  16. return this;
  17. }
  18. @Override
  19. public void setTheme(Resources.Theme themeId) {
  20. if (attr_textColor != -1) {
  21. ViewAttributeUtil.applyTextColor(this, themeId, attr_textColor);
  22. }
  23. if (attr_background != -1) {
  24. ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_background);
  25. }
  26. }

}

  1. 4. COlorLinearLayout

public class ColorLinerLayout extends LinearLayout implements ColorUiInterface{

  1. private int attr_backgound = -1;
  2. public ColorLinerLayout(Context context) {
  3. super(context);
  4. }
  5. public ColorLinerLayout(Context context, AttributeSet attrs) {
  6. super(context, attrs);
  7. this.attr_backgound = ViewAttributeUtil.getBackgroundAttibute(attrs);
  8. }
  9. public ColorLinerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
  10. super(context, attrs, defStyleAttr);
  11. this.attr_backgound = ViewAttributeUtil.getBackgroundAttibute(attrs);
  12. }
  13. @Override
  14. public View getView() {
  15. return this;
  16. }
  17. @Override
  18. public void setTheme(Resources.Theme themeId) {
  19. if(attr_backgound != -1) {
  20. ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_backgound);
  21. }
  22. }

}

  1. 5. ColorTextView

@SuppressLint(“AppCompatCustomView”) public class ColorTextView extends TextView implements ColorUiInterface {

  1. private int attr_drawable = -1;
  2. private int attr_textAppearance = -1;
  3. private int attr_textColor = -1;
  4. private int attr_textLinkColor = -1;
  5. private int attr_background = -1;
  6. public ColorTextView(Context context) {
  7. this(context,null);
  8. }
  9. public ColorTextView(Context context, AttributeSet attrs) {
  10. this(context, attrs,0);

// this.attr_textAppearance = ViewAttributeUtil.getTextApperanceAttribute(attrs); / this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs); this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs); this.attr_textLinkColor = ViewAttributeUtil.getTextLinkColorAttribute(attrs);/ }

  1. public ColorTextView(Context context, AttributeSet attrs, int defStyleAttr) {
  2. super(context, attrs, defStyleAttr);

// this.attr_textAppearance = ViewAttributeUtil.getTextApperanceAttribute(attrs); this.attr_drawable = ViewAttributeUtil.getBackgroundAttibute(attrs); this.attr_textColor = ViewAttributeUtil.getTextColorAttribute(attrs); this.attr_textLinkColor = ViewAttributeUtil.getTextLinkColorAttribute(attrs); this.attr_background = ViewAttributeUtil.getBackgroundAttibute(attrs); }

  1. @Override
  2. public View getView() {
  3. return this;
  4. }
  5. @Override
  6. public void setTheme(Resources.Theme themeId) {
  7. Log.d("COLOR", "id = " + getId());
  8. if (attr_drawable != -1) {
  9. ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_drawable);
  10. }

// if(attr_textAppearance != -1) { // ViewAttributeUtil.applyTextAppearance(this, themeId, attr_textAppearance); // } if (attr_textColor != -1) { ViewAttributeUtil.applyTextColor(this, themeId, attr_textColor); } if (attr_textLinkColor != -1) { ViewAttributeUtil.applyTextLinkColor(this, themeId, attr_textLinkColor); } if (attr_background != -1) { ViewAttributeUtil.applyBackgroundDrawable(this, themeId, attr_background); } } }

  1. 6. ColorUiInterface

public interface ColorUiInterface {

  1. View getView();
  2. void setTheme(Resources.Theme themeId);

}

  1. 7. ColorUiUtil

public class ColorUiUtil { /**

  1. * 切换应用主题
  2. *
  3. * @param rootView
  4. */
  5. public static void changeTheme(View rootView, Resources.Theme theme) {
  6. if (rootView instanceof ColorUiInterface) {
  7. ((ColorUiInterface) rootView).setTheme(theme);
  8. if (rootView instanceof ViewGroup) {
  9. int count = ((ViewGroup) rootView).getChildCount();
  10. for (int i = 0; i < count; i++) {
  11. changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
  12. }
  13. }
  14. if (rootView instanceof AbsListView) {
  15. try {
  16. Field localField = AbsListView.class.getDeclaredField("mRecycler");
  17. localField.setAccessible(true);
  18. @SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
  19. Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear");
  20. localMethod.setAccessible(true);
  21. localMethod.invoke(localField.get(rootView));
  22. } catch (NoSuchFieldException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e1) {
  23. e1.printStackTrace();
  24. }
  25. }
  26. } else {
  27. if (rootView instanceof ViewGroup) {
  28. int count = ((ViewGroup) rootView).getChildCount();
  29. for (int i = 0; i < count; i++) {
  30. changeTheme(((ViewGroup) rootView).getChildAt(i), theme);
  31. }
  32. }
  33. if (rootView instanceof AbsListView) {
  34. try {
  35. Field localField = AbsListView.class.getDeclaredField("mRecycler");
  36. localField.setAccessible(true);
  37. @SuppressLint({"DiscouragedPrivateApi", "PrivateApi"})
  38. Method localMethod = Class.forName("android.widget.AbsListView$RecycleBin").getDeclaredMethod("clear");
  39. localMethod.setAccessible(true);
  40. localMethod.invoke(localField.get(rootView));
  41. } catch (NoSuchFieldException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e1) {
  42. e1.printStackTrace();
  43. }
  44. }
  45. }
  46. }

}

  1. 9. ViewAttributeUtil

public class ViewAttributeUtil {

  1. public static int getAttributeValue(AttributeSet attr, int paramInt) {
  2. int value = -1;
  3. int count = attr.getAttributeCount();
  4. for (int i = 0; i < count; i++) {
  5. if (attr.getAttributeNameResource(i) == paramInt) {
  6. String str = attr.getAttributeValue(i);
  7. if (null != str && str.startsWith("?")) {
  8. value = Integer.valueOf(str.substring(1, str.length())).intValue();
  9. return value;
  10. }
  11. }
  12. }
  13. return value;
  14. }
  15. public static int getBackgroundAttibute(AttributeSet attr) {
  16. return getAttributeValue(attr, android.R.attr.background);
  17. }
  18. public static int getCheckMarkAttribute(AttributeSet attr) {
  19. return getAttributeValue(attr, android.R.attr.checkMark);
  20. }
  21. public static int getSrcAttribute(AttributeSet attr) {
  22. return getAttributeValue(attr, android.R.attr.src);
  23. }
  24. public static int getTextApperanceAttribute(AttributeSet attr) {
  25. return getAttributeValue(attr, android.R.attr.textAppearance);
  26. }
  27. public static int getDividerAttribute(AttributeSet attr) {
  28. return getAttributeValue(attr, android.R.attr.divider);
  29. }
  30. public static int getTextColorAttribute(AttributeSet attr) {
  31. return getAttributeValue(attr, android.R.attr.textColor);
  32. }
  33. public static int getTextLinkColorAttribute(AttributeSet attr) {
  34. return getAttributeValue(attr, android.R.attr.textColorLink);
  35. }
  36. public static void applyBackgroundDrawable(ColorUiInterface ci, Resources.Theme theme, int paramInt) {
  37. TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
  38. Drawable drawable = ta.getDrawable(0);
  39. if (null != ci) {
  40. (ci.getView()).setBackgroundDrawable(drawable);
  41. }
  42. ta.recycle();
  43. }
  44. public static void applyImageDrawable(ColorUiInterface ci, Resources.Theme theme, int paramInt) {
  45. TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
  46. Drawable drawable = ta.getDrawable(0);
  47. if (null != ci && ci instanceof ImageView) {
  48. ((ImageView) ci.getView()).setImageDrawable(drawable);
  49. }
  50. ta.recycle();
  51. }
  52. public static void applyTextAppearance(ColorUiInterface ci, Resources.Theme theme, int paramInt) {
  53. TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
  54. int resourceId = ta.getResourceId(0, 0);
  55. if (null != ci && ci instanceof TextView) {
  56. ((TextView) ci.getView()).setTextAppearance(ci.getView().getContext(), resourceId);
  57. }
  58. ta.recycle();
  59. }
  60. public static void applyTextColor(ColorUiInterface ci, Resources.Theme theme, int paramInt) {
  61. TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
  62. int resourceId = ta.getColor(0, 0);
  63. if (null != ci && ci instanceof TextView) {
  64. ((TextView) ci.getView()).setTextColor(resourceId);
  65. }
  66. ta.recycle();
  67. }
  68. public static void applyTextLinkColor(ColorUiInterface ci, Resources.Theme theme, int paramInt) {
  69. TypedArray ta = theme.obtainStyledAttributes(new int[]{paramInt});
  70. int resourceId = ta.getColor(0, 0);
  71. if (null != ci && ci instanceof TextView) {
  72. ((TextView) ci.getView()).setLinkTextColor(resourceId);
  73. }
  74. ta.recycle();
  75. }

}

  1. <a name="bb8d6a84"></a>
  2. ### skin库
  3. github:[https://github.com/hongyangAndroid/AndroidChangeSkin](https://github.com/hongyangAndroid/AndroidChangeSkin)
  4. activity:

class SkinMActivity: AppCompatActivity(R.layout.activity_skinm) { var mIsDay = true override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) SkinManager.getInstance().register(this)

  1. //day
  2. btnSkinMToggle.setOnClickListener {
  3. SkinHelp.instance.showAnimation(this)
  4. SkinManager.getInstance().changeSkin("black")
  5. }
  6. //night
  7. btnSkinMToggle2.setOnClickListener {
  8. SkinHelp.instance.showAnimation(this)
  9. SkinManager.getInstance().changeSkin("white")
  10. }
  11. }
  12. override fun onDestroy() {
  13. super.onDestroy()
  14. SkinManager.getInstance().unregister(this)
  15. }

}

<?xml version=”1.0” encoding=”utf-8”?>

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="50dp"
  4. android:background="@color/text_color"
  5. android:tag="skin:text_color:background" />
  6. <TextView
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:layout_gravity="center"
  10. android:tag="skin:text_color:textColor"
  11. android:text="测试skin库应用内换肤"
  12. android:textColor="@color/text_color" />
  13. <Button
  14. android:id="@+id/btnSkinMToggle"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:layout_gravity="center"
  18. android:text="日间"
  19. android:textColor="@color/text_color_black" />
  20. <Button
  21. android:id="@+id/btnSkinMToggle2"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:layout_gravity="center"
  25. android:text="夜间"
  26. android:textColor="@color/text_color_black" />

  1. <color name="text_color">#FFff0000</color>
  2. <color name="text_color_black">#FF000000</color>
  3. <color name="text_color_white">#FFFFFFFF</color>
  4. <color name="bg">#FFBB86FC</color>
  5. <color name="bg_white">#000000</color>
  6. <color name="bg_black">#FFFFFF</color>

```

参考

  • 博客

探究APP换肤机制的设计与实现
日夜间及换肤(二)-原理分析
Android换肤方案总结
Android 主题换肤技术方案分析

  • 第三方库

Android-Skin-Loader
Android-skin-support
AndroidChangeSkin