kotlin compose 实现应用内多语言切换(不重新打开App)

965 阅读3分钟

1. 示例图

​编辑

2.具体实现

如何实现上述示例,且不需要重新打开App

①自定义 MainApplication 实现 Application ,定义两个变量:

class MainApplication : Application() {

     object GlobalDpData {
        var language: String = ""
        var defaultLanguage: String = "en"
    }
    override fun onCreate() {
        defaultLanguage = Locale.getDefault().language 
        val cacheLanguage = "保存在手机App本地的切换后的语言,可用sp或文件实现" 
        language = if (cacheLanguage.isNullOrEmpty()) { 
            defaultLanguage 
        } else  { 
            cacheLanguage 
        }
     }
}

②在baseActivity中添加语言实现

override fun attachBaseContext(baseContext: Context) {
    var language = MainApplication.GlobalDpData.language
    if (language.isEmpty()) {
        val default = MainApplication.GlobalDpData.defaultLanguage
        language = "保存在手机App本地的切换后的语言,可用sp或文件实现" 
    }
    // 创建一个ContextWrapper对象
    val context = newWrap(baseContext, language)
    // 将新的的Context设置给Activity
    super.attachBaseContext(context)
}

/**
 *  创建ContextWrapper对象,
 */
private fun newWrap(context: Context, language: String): ContextWrapper {
    val configuration = context.resources.configuration
    configuration.fontScale = 1f
    val locale = Locale(language)
    val localeList = LocaleList(locale)
    LocaleList.setDefault(localeList)
    configuration.setLocales(localeList)
    return ContextWrapper(context.createConfigurationContext(configuration))
}

③在自定义语言实现Activty

设置语言实现的构造体

data class Language(val id: String, 
val name: String, 
var isSelected: Boolean = false)

id:即语言的id,比如英语"English",中文"简体中文",如 Language("English", "English", true)

数据变化的mutableStat List

private var languageList by mutableStateOf(listOf(Language("English", "English", true)))

companion object {
    const val AUTO_TAG = "auto_tag"
}

// 选中的语言
private var checkLanguageID = ""
// 要保存到sp的语言
private var spLanguageID = ""
// 语言是否被选中
private var isCheck by mutableStateOf(true)
private var defaultID = "en"

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    addLanguageList()
    setContent {
        SetShowView()
    }
}

/**
 * 初始化数据
 */
private fun addLanguageList() {
    val autoKey = getString(R.string.xxx)
    val mutableMap = mutableMapOf(
         AUTO_TAG to false,
        "English" to false,
        "Deutsch" to false,
        "简体中文" to false,
    )

    val stringKey = "保存在手机App本地的切换后的语言,可用sp或文件实现" 
    if (stringKey.isNullOrEmpty()) {
        mutableMap[AUTO_TAG] = true
        spLanguageID = AUTO_TAG
    } else {
        mutableMap[stringKey] = true
        spLanguageID = stringKey
    }

    val mutableList: MutableList<Language> = mutableListOf()
    for ((key, value) in mutableMap) {
        if (key == AUTO_TAG) {
            mutableList.add(Language(id = AUTO_TAG, name = autoKey, isSelected = value))
        } else {
            mutableList.add(Language(id = key, name = key, isSelected = value))
        }
    }
    languageList = mutableList
}


/**
 * 展示view
 */
@Composable
fun SetShowView() {
    Column (
        Modifier
            .fillMaxSize()
            .background(colorResource(id = R.color.xxx))
            .padding(top = xx.dp)
    ) {
        //标题
        SetTitleView(
            middleTitle = stringResource(id = R.string.xxx),
            rightTitle = stringResource(id = R.string.xxx),
            isClick = isCheck,
            onClick = {
                finishActivity()
            }, saveClick = {
                saveLanguage()
            }
        )
        Spacer(modifier = Modifier.height(xx.dp))
        LazyColumn (
            Modifier
                .fillMaxWidth()
                .weight(1f)
                .background(
                    color = colorResource(id = R.color.xx),
                    shape = RoundedCornerShape(
                        topStart = (xx).dp,
                        topEnd = (xx).dp
                    )
                )
        ) {
            items(languageList) { language ->
                ShowLanguageItem(language = language, onLanguageSelected = onLanguageSelected)
                Spacer(modifier = Modifier
                    .fillMaxWidth()
                    .height((xx).dp)
                    .padding(start = (xx).dp, end = (xx).dp)
                    .background(colorResource(id = R.color.xx)))
            }
        }
    }
}


/**
 * 展示单个Item view
 */
@Composable
fun ShowLanguageItem(language: Language, onLanguageSelected: (Language) -> Unit) {
    Row (
        Modifier
            .fillMaxWidth()
            .height((xx).dp)
            .padding(start = (xx).dp, end = (xx).dp)
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null
            ) {
                onLanguageSelected(language)
            },
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Start
    ) {
        // 文本
        Box(modifier = Modifier
            .fillMaxHeight()
            .weight(1f),
            contentAlignment = Alignment.CenterStart
        ) {
         Text(
            modifier = Modifier.padding(start = xx.dp, end = xx.dp),
            text = show,
            style = TextStyle(color = xxx , fontSize = xxx),
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
            textAlign = TextAlign.Center
        )
     }
       if (language.isSelected) { 
          Image ( painter = painterResource(id = xxx),
          contentDescription = null,
          modifier = Modifier.width(xxx).height(xxx).clip(RoundedCornerShape(size = xxx)),
          contentScale = ContentScale.Crop)
      }
   }
}


/**
 * 语言选中时,及时去
 * 更新语言列表选中状态
 */
private val onLanguageSelected : (Language) -> Unit  = { selected ->
    checkLanguageID = selected.id
    isCheck = checkLanguageID == spLanguageID
    languageList = languageList.map { language ->
        language.copy(isSelected = language.id == selected.id)
    }
}

/**
 * 保存语言逻辑
 */
private fun saveLanguage() {
    if (isCheck) {
        return
    }
    if (checkLanguageID != AUTO_TAG) {
        //保存到Sp中
        xxx.saveKeyString(this, "sp存储的key值", checkLanguageID)
    }

    //切换保存语言
    if (checkLanguageID.isNotEmpty()) {
        setLocale(languageID = checkLanguageID)
    }
    //关闭所有的activity,打开MainActivity
    备注:此处自己实现
    关闭所有的activity 自己实现
    打开 MainActivity如下参考,具体自己实现
    openActivity(context = this, MainActivity::class.java)
}


/**
 * 切换语言函数
 */
private fun setLocale(languageID: String) {
    val mutableMap = mutableMapOf(
        AUTO_TAG to MainApplication.GlobalDpData.defaultLanguage,
        "English" to "en",
        "Deutsch" to "de",
        "简体中文" to "zh",
    )

    val languageSet = mutableMap[languageID] ?: MainApplication.GlobalDpData.defaultLanguage
    MainApplication.GlobalDpData.language = languageSet
    if (languageID == AUTO_TAG) {
        // 更新跟随系统语言设置,置为空字符
        备注:此处自己实现保存到sp中
    } else {
        // 不跟随系统设置,更新语言设置
         备注:此处自己实现保存到sp中
    }
}

总结

以上是语言切换的具体大部分实现,小部分未提供的compose 实现,需要开发者自己补充;主要参考点在语言切换的逻辑。