JetPack Compose极简优雅的让Navigation传递参数并支持数组

339 阅读2分钟

本文内容很简单,利用泛型简单的封装了JetPack Compose Navigation传递的参数类型,尽可能让Compose项目使用方便,维护简单。

最终效果

NavType.kt维护项目Parcelable类型参数

val TrainSettingType = SmartParcelableType(isNullableAllowed = true) {  TrainingSetting::class.java }
val TrainSettingsType = SmartParcelableArrayType{ Array<TrainingSetting>::class.java }

Paths.kt

object Params {
    const val TRAINING_SETTING = "trainingSetting"
}

object Paths {
    const val LAUNCHER = "/launcher"
    const val TRAINING = "/training/{${Params.TRAINING_SETTING}}"
 // 传递对象以及对象数组的路径为/training/${Json字符串},
 // 传递数组的路径为/training/[1,2,3,4,5]
 // 当然还有基础数据类型
}

Routes.kt

这里的SmartXXXType最终对应的是it.arguments?.getXXX()具体实现查看下文

fun NavGraphBuilder.trainingGraph() {
    composable(
        Paths.TRAINING,
        arguments = listOf(
            navArgument(Params.TRAINING_SETTING) { type = TrainSettingType },  //Parcelable
//            navArgument(Params.TRAINING_SETTING) { type = TrainSettingTypeArray }, //ParcelableArray
//            navArgument(Params.TRAINING_SETTING) { type = SmartIntArrayType },   // IntArray
//            navArgument(Params.TRAINING_SETTING) { type = SmartStringArrayType }, // StringArray
//            navArgument(Params.TRAINING_SETTING) { type = SmartFloatArrayType },  //FloatArray
//        …………
        )
    ) {
        // 对应arguments.getXXX方法
        val trainingSetting = it.arguments?.getParcelable<TrainingSetting>(Params.TRAINING_SETTING)
        TrainingSettingPage(trainingSetting)
    }
}

SmartNavType.kt复制粘帖即用

自定义NavTypePath字符串解析参数,存入bundle

a、ParcelableParcelableArray通过Gson进行序列化和反序列化

b、SmartPrimitiveArrayType基础数据数组类型,解析路径/training/[1,2,3,4]路径里中括号里的内容。

class SmartParcelableType<T : Parcelable>(
    isNullableAllowed: Boolean,
    private val clazz: () -> Class<T>
) : NavType<T>(isNullableAllowed) {

    override fun get(bundle: Bundle, key: String): T? {
        return bundle.getParcelable(key)
    }

    override fun parseValue(value: String): T {
        return Gson().fromJson(value, clazz.invoke())
    }

    override fun put(bundle: Bundle, key: String, value: T) {
        bundle.putParcelable(key, value)
    }
}

@Suppress("UNCHECKED_CAST")
class SmartParcelableArrayType<T : Parcelable>(private val clazz: () -> Class<Array<T>>) :
    NavType<Array<T>>(false) {

    override fun get(bundle: Bundle, key: String): Array<T>? {
        return bundle.getParcelableArray(key) as? Array<T>
    }

    override fun parseValue(value: String): Array<T> {
        return Gson().fromJson(value, clazz.invoke()) as Array<T>
    }

    override fun put(bundle: Bundle, key: String, value: Array<T>) {
        bundle.putParcelableArray(key, value)
    }
}

@Suppress("UNCHECKED_CAST")
class SmartPrimitiveArrayType<T>(private val clazz: () -> Class<T>) : NavType<T>(false) {

    private fun getArrayFromKey(bundle: Bundle, key: String): T? {
        return when (clazz.invoke()) {
            IntArray::class.java -> bundle.getIntArray(key) as? T
            FloatArray::class.java -> bundle.getFloatArray(key) as? T
            BooleanArray::class.java -> bundle.getBooleanArray(key) as? T
            LongArray::class.java -> bundle.getLongArray(key) as? T
            DoubleArray::class.java -> bundle.getDoubleArray(key) as? T
            CharArray::class.java -> bundle.getCharArray(key) as? T
            ByteArray::class.java -> bundle.getByteArray(key) as? T
            ShortArray::class.java -> bundle.getShortArray(key) as? T
            Array<String>::class.java -> bundle.getStringArray(key) as? T
            else -> null
        }
    }

    private fun putArrayFromKeyValue(bundle: Bundle, key: String, value: T) {
        when (clazz.invoke()) {
            IntArray::class.java -> bundle.putIntArray(key, value as IntArray)
            FloatArray::class.java -> bundle.putFloatArray(key, value as FloatArray)
            BooleanArray::class.java -> bundle.putBooleanArray(key, value as BooleanArray)
            LongArray::class.java -> bundle.putLongArray(key, value as LongArray)
            DoubleArray::class.java -> bundle.putDoubleArray(key, value as DoubleArray)
            CharArray::class.java -> bundle.putCharArray(key, value as CharArray)
            ByteArray::class.java -> bundle.putByteArray(key, value as ByteArray)
            ShortArray::class.java -> bundle.putShortArray(key, value as ShortArray)
            Array<String>::class.java -> bundle.putStringArray(
                key,
                value as Array<out String>
            ) as? T
            else -> {
                // ignore
            }
        }
    }

    private fun parseValueFromString(paramsString: String): T {
        val params = paramsString.replace("[", "").replace("]", "").split(",")
        when (clazz.invoke()) {
            IntArray::class.java -> {
                return IntArray(params.size) {
                    params[it].toInt()
                } as T
            }
            FloatArray::class.java -> {
                return FloatArray(params.size) {
                    params[it].toFloat()
                } as T
            }
            BooleanArray::class.java -> {
                return BooleanArray(params.size) {
                    params[it].toBoolean()
                } as T
            }
            LongArray::class.java -> {
                return LongArray(params.size) {
                    params[it].toLong()
                } as T
            }
            DoubleArray::class.java -> {
                return DoubleArray(params.size) {
                    params[it].toDouble()
                } as T
            }
            CharArray::class.java -> {
                return CharArray(params.size) {
                    params[it][0]
                } as T
            }
            ByteArray::class.java -> {
                return ByteArray(params.size) {
                    params[it].toByte()
                } as T
            }
            ShortArray::class.java -> {
                return ShortArray(params.size) {
                    params[it].toShort()
                } as T
            }
            Array<String>::class.java -> {
                return params.toTypedArray() as T
            }
            else -> {
                throw IllegalArgumentException("SmartArrayType can not support this type !!!")
            }
        }
    }

    override fun get(bundle: Bundle, key: String): T? {
        return getArrayFromKey(bundle, key)
    }

    override fun parseValue(value: String): T {
        return parseValueFromString(value)
    }

    override fun put(bundle: Bundle, key: String, value: T) {
        putArrayFromKeyValue(bundle, key, value)
    }
}

val SmartIntArrayType = SmartPrimitiveArrayType { IntArray::class.java }
val SmartFloatArrayType = SmartPrimitiveArrayType { FloatArray::class.java }
val SmartBooleanArrayType = SmartPrimitiveArrayType { BooleanArray::class.java }
val SmartLongArrayType = SmartPrimitiveArrayType { LongArray::class.java }
val SmartDoubleArrayType = SmartPrimitiveArrayType { DoubleArray::class.java }
val SmartCharArrayType = SmartPrimitiveArrayType { CharArray::class.java }
val SmartByteArrayType = SmartPrimitiveArrayType { ByteArray::class.java }
val SmartShortArrayType = SmartPrimitiveArrayType { ShortArray::class.java }
val SmartStringArrayType = SmartPrimitiveArrayType { Array<String>::class.java }
val SmartFloatType = NavType.FloatType
val SmartIntType = NavType.IntType
val SmartBooleanType = NavType.BoolType
val SmartStringType = NavType.StringType

处理Path数组参数拼接,对象使用gson转json串和基础数据类型直接转String

private val gson = Gson()
fun arrayToPath(value: Array<*>): String {
    return value.joinToString(",", prefix = "[", postfix = "]", transform = {
        if (it is String) return@joinToString it
        if (it is Parcelable) return@joinToString gson.toJson(it)
        return@joinToString it.toString()
    })
}

Over!