本章内容主要介绍泛型,常见泛型函数,SharedPreferences,枚举,等知识点的使用
1.泛型
在java中,泛型主要是用来在代码中约束数据的类型的。常见的泛型一般在类的定义和函数的定义中.
举个例子,下面的代码中有个叫doSomething的函数,他需要传入某一类型的数据K,返回类型T的数据,那么泛型的定义如下:
class TypeClass {
fun <K,T>doSomething(k: K): T? {
return null
}
}
当然,在java中,我们的类型支持上限(extends)和下限(super),而在kotlin中简化了这一操作。比如,我觉得上面的函数可以传入任意类型,那么也可以这么写:
class TypeClass {
fun <K:Any,T:Any>doSomething(k: K): T? {
return null
}
}
这样,当我们传入的数据为任意类型的子类都可以通过,比如我传递一个String类型的参数,返回一个Int类型的数据,也是可以通过的编译的。
以上介绍的是泛型定义在函数中,如果出现在类型呢?上面的代码可以变为:
class TypeClass <K:Any,T:Any>{
fun doSomething(k: K): T? {
return null
}
}
上面的代码说明了该类有2个类型需要限定,这是Java的做法,但有时候我们要清楚哪个类型的传入参数的类型,哪个参数是返回的参数的类型,这里参考了C#语言,可以改变为:
class TypeClass <in K:Any,out T:Any>{
fun doSomething(k: K): T? {
return null
}
}
上面的代码中in 代表的是传入参数的类型,out代表使用该类型作为某个函数的返回值类型,不同的函数可以再类型多次声明只需要逗号隔开即可。接下来看看下面的代码如何操作:
//1.创建一个type1对象
var type1 = TypeClass<Any, String>()
//2.表达式右边函数的返回值根据上面的判断返回的类型应该是String(假如是"Hello Android !")
//3.那么表达式应该类似于 val doSomething:Any = "Hello Android !"
// 这样符合Java的逻辑 因为String是Any的子类
val doSomething: Any? = type1.doSomething(22.2)
Log.i("IT520", "doSomething $doSomething")
2.SharedPreferences的使用
对于一个APP而言,经常需要保存一些配置信息到手机内部,如果是简单的键值对我们可以使用SharedPreferences.假设现在想传递一个Long类型的数据到SharedPreferences中,我的目的是这样的,把该存储的数据操作设置给某个对象做为属性委托,当某个属性设值,则直接存储到sp中,当访问某个属性,则从sp中取出数据,代码如下:
class LongPreference(val ctx: Context, val name: String, val default: Long)
: ReadWriteProperty<Any, Long> {
val prefs: SharedPreferences by lazy {
//创建SharedPreferences对应保存文件
ctx.getSharedPreferences("defaultfile", Context.MODE_PRIVATE)
}
//当获取某个值的时候 调用该方法 内部将数据从SharedPreferences文件中取出
override fun getValue(thisRef: Any, property: KProperty<*>): Long {
return prefs.getLong(name, default)
}
//当设置某个值的时候 调用该方法 内部将数据存储到SharedPreferences文件中
override fun setValue(thisRef: Any, property: KProperty<*>, value: Long) {
prefs.edit().putLong(name,value).commit()
}
}
接下来,我们可以使用object关键字声明一个对象,并通过函数返回上面的委托属性对象:
object DelegatesExt {
fun longPreference(ctx: Context, name: String, default: Long)
= LongPreference2(ctx, name, default)
}
如果使用该委托呢?下面在MainActivity中我们该出使用代码:
class MainActivity : AppCompatActivity(){
var testValue: Long by DelegatesExt.longPreference(this, "zipCode", 0L)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//设置该值 就会间接调用该委托将数据存储进SP
testValue = 22L
Log.i("IT520", "$testValue")
}
3.结合泛型和sp让代码更加通用
前面我们提到了如何让属性委托实现数据的存储,但是该存储只能存储Long类型的数据。接下来我们将通过泛型改装代码:
class PreferenceDelegate<T>(val ctx: Context, val name: String, val default: T)
: ReadWriteProperty<Any, T> {
val prefs: SharedPreferences by lazy {
ctx.getSharedPreferences("defaultfile", Context.MODE_PRIVATE)
}
override fun getValue(thisRef: Any, property: KProperty<*>): T {
return findSharedPreference(name, default)
}
//根据传递数据类型决定取出数据
fun findSharedPreference(name: String, default: T): T {
with(prefs) {
val result: Any = when (default) {
is Long -> getLong(name, default)
is Int -> getInt(name, default)
is Float -> getFloat(name, default)
is String -> getString(name, default)
is Boolean -> getBoolean(name, default)
else -> throw IllegalArgumentException()
}
return result as T
}
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
putSharedPreference(name, value)
}
//根据传递数据类型决定存放数据
fun putSharedPreference(name: String, value: T) {
with(prefs.edit()) {
when (value) {
is Long -> putLong(name, value).apply()
is Int -> putInt(name, value).apply()
is Float -> putFloat(name, value).apply()
is String -> putString(name, value).apply()
is Boolean -> putBoolean(name, value).apply()
else -> throw IllegalArgumentException()
}
}
}
}
object DelegatesExt {
fun <T : Any> preference(ctx: Context, name: String, default: T)
= PreferenceDelegate(ctx, name, default)
}
4.枚举
枚举这方面的技术与java的差不多,只是做了一点点的改进,如何创建枚举类呢?
enum class Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FIRDAY, SATURDAY
}
枚举改进的地方是,除了可以提供一个枚举值,还可以将枚举的附属信息添加到枚举值中,下面的Int就是我模拟的附属信息,可以一个也可以多个:
enum class Direction(val value:Int){
EAST(1),
WEST(2),
SOUTH(3),
NORTH(4)
}
接下来我们就可以使用枚举变量了:
//定义一个枚举变量
var today = Day.FIRDAY
//获取枚举变量的名称
Log.i("IT520", "FIRDAY name ${today.name}")
//获取枚举变量的索引
Log.i("IT520", "FIRDAY ordinal ${today.ordinal}")
//获取该枚举概念中所有数据的个数
Log.i("IT520", "Day size ${Day.values().size}")
5.Anko提供一个启动新界面的新方式
之前我们在上几篇提到,anko是kotlin在安卓中的轻量级额外开发包。他的commons包提供了一个方便使用Intent的框架类Intents.kt。
5.1 添加anko依赖包
在项目的build.gradle文件中,添加如下代码:
buildscript {
...
ext.ANKO_VERSION = '0.10.0'
}
在app的build.gradle文件中,添加如下代码:
dependencies {
...
compile "org.jetbrains.anko:anko-commons:$ANKO_VERSION"
}
5.2 Intent启动新的界面
假设我们有一个界面叫MainActivity,想启动一个新的SecondActivity的,并携带2个参数,那么MainActivity的启动代码应该是这样的:
startActivity<SecondActivity>(
SecondActivity.USERNAME_KEY to "xiaoming",
SecondActivity.PWD_KET to "123")
5.3 源码分析
inline fun <reified T: Activity> Context.startActivity(vararg params: Pair<String, Any>) {
AnkoInternals.internalStartActivity(this, T::class.java, params)
}
object AnkoInternals {
fun internalStartActivity(
ctx: Context,
activity: Class<out Activity>,
params: Array<out Pair<String, Any>>) {
ctx.startActivity(createIntent(ctx, activity, params))
}
}
当然如果你查看源码 你会发现这真的是个很棒的类,除了可以启动新的界面,还可以启动服务,还能发送SMS,启动浏览器,发邮件,分享等等…