Magic Mistletoe Android多主题(换肤)切换框架

背景

时隔四年,在网易换肤之前的思路下,做了几点改进,现在完全通过反射创建View,并且在SkinLoadManager中提供一个configCustomAttrs以支持自定义View的属性插队替换

摈弃了之前的AsyncTask,使用kotlin 协程进行主题包的资源转换

使用kotlin重构所有Java代码实现

效果展示

默认主题

点击切换主题

最佳使用方式

STEP 1 宿主项目(示例Demo为本项目app模块)依赖多主题框架AAR

//in root build.gradle
allprojects {
 	repositories {
 		maven { url 'https://jitpack.io' }
 	}
 }
// in app/module build.gradle
dependencies {
	        implementation 'com.github.mistletoe5215:MagicMistletoe:1.0.0'
	}

STEP 2 制作多主题包

找个壳工程(本Demo为theme-pkg模块),在res下放置资源文件,注意 资源文件名称需要与宿主app内的资源文件名称保持一致,这样主题切换的时候才可以成功替换

执行打包命令

找到生成的资源apk

重命名为你喜欢的名字.zip(如果有强迫症)

这里是做演示,(实际商用过程中,可将zip包给服务端,进行主题包签名处理,后通过下载的形式down到本地,解签,应用)拷贝主题文件至宿主app的assets目录下

释放assets目录主题文件至私有路径

10) {
return true
}
}
val `is`: InputStream = assets.open(fileName)
val fos = FileOutputStream(outFile)
val buffer = ByteArray(1024)
var byteCount: Int
while (`is`.read(buffer).also { byteCount = it } != -1) {
fos.write(buffer, 0, byteCount)
}
fos.flush()
`is`.close()
fos.close()
return true
} catch (e: IOException) {
e.printStackTrace()
}
return false
}
“>

   private fun copyAssetAndWrite(fileName: String): Boolean {
          try {
              val cacheDir = cacheDir
              if (!cacheDir.exists()) {
                  cacheDir.mkdirs()
              }
              val outFile = File(cacheDir, fileName)
              if (!outFile.exists()) {
                  val res = outFile.createNewFile()
                  if (!res) {
                      return false
                  }
              } else {
                  if (outFile.length() > 10) { 
                      return true
                  }
              }
              val `is`: InputStream = assets.open(fileName)
              val fos = FileOutputStream(outFile)
              val buffer = ByteArray(1024)
              var byteCount: Int
              while (`is`.read(buffer).also { byteCount = it } != -1) {
                  fos.write(buffer, 0, byteCount)
              }
              fos.flush()
              `is`.close()
              fos.close()
              return true
          } catch (e: IOException) {
              e.printStackTrace()
          }
          return false
      }

STEP 3 XML中需要更换主题的控件设置好多主题属性 multiTheme:enable="true"

<div class="highlight highlight-text-xml position-relative" data-snippet-clipboard-copy-content="

“>

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:multiTheme="http://schemas.android.com/apk/multi.theme"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:background="@color/main_bg"
   multiTheme:enable="true"
   tools:context=".MainActivity">
   <com.google.android.material.imageview.ShapeableImageView
       android:src="@drawable/ic_avatar"
       android:layout_marginTop="20dp"
       android:layout_width="220dp"
       android:layout_height="220dp"
       android:scaleType="centerCrop"
       android:id="@+id/avatar"
       android:background="@color/main_text"
       multiTheme:enable="true"
       app:shapeAppearance="@style/CircleStyle"
       app:layout_constraintBottom_toTopOf="@+id/copy"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />


   <TextView
       android:background="@color/teal_200"
       android:layout_marginTop="20dp"
       android:layout_width="match_parent"
       android:layout_height="100dp"
       android:gravity="center"
       android:id="@+id/copy"
       android:text="释放主题包到应用内存(切换前必点)"
       android:textColor="@color/main_text"
       multiTheme:enable="true"
       app:layout_constraintBottom_toTopOf="@+id/change_skin"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/avatar" />

   <TextView
       android:background="@color/teal_200"
       android:layout_marginTop="20dp"
       android:layout_width="match_parent"
       android:layout_height="100dp"
       android:gravity="center"
       android:id="@+id/change_skin"
       android:text="切换主题"
       android:textColor="@color/main_text"
       multiTheme:enable="true"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/copy" />

</androidx.constraintlayout.widget.ConstraintLayout>