最近公司新开发了一个新App,android程序员就我一个人,爱咋写咋写~~于是我尝试用Kotlin完成了这个App。开发过程中越来越感动……太好用了!!!解决了android开发中的好多痛点!
dp, sp, px间的转换 传统的java写法通常是写个类似Util.dpToPx(int value)
这样的方法。看看Kotlin是怎么通过Extension 来优雅的解决的:
1 2 3 4 5 6 fun Int .dpToPx () : Int { if (toInt() in intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) { return this } return (this * Global.density).toInt() }
然后就可以这样写了:params.topMargin = 16.dpTpPx()
是不是感动到哭😭
设置View的宽高 如果用java来写通常也是会有个Util方法ViewHelper.setSize(View view, int width, int height)
,我每次在ViewHolder中调用一堆这样的方法都觉得很蛋疼……我们的救世主来了:
1 2 3 4 5 6 fun View.setSize (width: Int , height: Int ) { val params = layoutParams params.width = width params.height = height layoutParams = params }
注意这里的layoutParams
是Kotlin将setLayoutParams()
和getLayoutParams()
自动转为了Property ,写过C#的应该不会陌生。然后我们就可以愉快的写代码了~yourView.setSize(100, 100)
View的动画 每次写Animation都是一个头两个大……这回我们不看java了,直接上Kotlin代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 fun View.animateTopMargin (valueFromInDP: Int , valueToInDP: Int , duration: Long = 300 ) { val animation = object : Animation() { override fun applyTransformation (interpolatedTime: Float , t: Transformation ?) { val params = layoutParams as ViewGroup.MarginLayoutParams val from = valueFromInDP.dpToPx() val to = valueToInDP.dpToPx() params.topMargin = from + ((to - from) * interpolatedTime).toInt() layoutParams = params } } animation.duration = duration startAnimation(animation) }
哇,duration还贴心的设了个默认值呢!然后就可以这样写了~yourView.animateTopMargin(16, 32)
或者你想把duration设长一点yourView.animateTopMargin(16, 32, 500)
让Fresco更易用 Fresco 是Facebook出品的网络图片加载库,实现了许多在android上非常有用的功能。比如Multi-URI 功能可以优先显示低分辨率的图片,等高分辨率的图片下载完成后再进行替换。这个功能在移动设备上太太太有用了,就是写起来有点蛋疼:
1 2 3 4 5 6 7 Uri lowResUri, highResUri; DraweeController controller = Fresco.newDraweeControllerBuilder() .setLowResImageRequest(ImageRequest.fromUri(lowResUri)) .setImageRequest(ImageRequest.fromUri(highResUri)) .setOldController(mSimpleDraweeView.getController()) .build(); mSimpleDraweeView.setController(controller);
于是我又祭出了神器Extension
1 2 3 4 5 6 7 8 fun SimpleDraweeView.setMultiUri (lowResUri: Uri , highResUri: Uri ) { val newController = Fresco.newDraweeControllerBuilder() .setLowResImageRequest(ImageRequest.fromUri(lowResUri)) .setImageRequest(ImageRequest.fromUri(highResUri)) .setOldController(controller) .build(); controller = newController }
然后怎么使用我就不写了,大家都知道了:smiley:
焕然一新的SharedPreference 你看到这里可能觉得不过是扩展方法嘛,Obj-C也可以做到啊,没什么嘛。哈哈哈哈图样图森破!有请Delegated Properties !下面这段代码来源于Omar Miatello 在今年米兰的GDG DevFest的演讲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class AppPreferences (private val context: Context) { val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) var userName by PreferenceDelegates.string(defaultValue = context.getString(R.string.user_name)) var password by PreferenceDelegates.string() } object PreferenceDelegates { public fun string (defaultValue: String ? = null ) : ReadWriteProperty<AppPreferences, String?> { return PrefString(defaultValue) } } private class PrefString (private val defaultValue: String?) : ReadWriteProperty<AppPreferences, String?> { override fun getValue (thisRef: AppPreferences , property: KProperty <*>) : String? { return thisRef.preferences.getString(property.name, defaultValue) } override fun setValue (thisRef: AppPreferences , property: KProperty <*>, value: String ?) { thisRef.preferences.edit().putString(property.name, value).apply() } }
Hmm,乍一看有点摸不着头脑呢……简单的讲,AppPreferences这个class里面有两个delegate property,分别是userName和password。他们的get和set方法分别对应了SharedPreference中的getString和setString方法。纳尼?这么神奇???接着往下看
1 2 3 4 5 6 7 8 class App : Application () { val pref by lazy { AppPreferences(this ) } override fun onCreate () { super .onCreate() } }
在Application中定义了pref
,注意它被by lazy
修饰了,这样它就只在第一次调用的时候初始化,是不是很方便?
1 2 3 4 open class BaseActivity : AppCompatActivity () { val app by lazy { application as App } val pref by lazy { app.pref } }
这里我们在所有Activity的父类中引入了pref,即AppPreferences的实例。然后……就可以爽了~~
1 2 3 4 5 6 7 8 9 10 class MyActivity : BaseActivity () { override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) pref.userName = "Who" pref.password = "You" Log.d("userName" , pref.userName) Log.d("password" , pref.password) } }
看到没有,用起来简直和field一毛一样!再也不用写pref.edit().putString("userName", "Who").apply()
了!!!老板再也不用担心我把SharedPreference的key写错了😂
Lambdas 这个话题可就大了,一本书都讲不完,举个例子好了。RxJava 现在很火,光头哥 写了一篇很好的文章 介绍了RxJava在android中的应用。抄一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 getUser(userId) .doOnNext(new Action1<User>() { @Override public void call (User user) { processUser(user); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onNext (User user) { userView.setUser(user); } @Override public void onCompleted () { } @Override public void onError (Throwable error) { ... } });
嗯,链式调用,很清晰,但用Kotlin还可以更简洁:
1 2 3 4 5 6 7 8 getUser(userId) .doOnNext{ user -> processUser(user) } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ user -> userView.setUser(user) },{ error -> })
Done!收拾东西下班回家~
风险 我知道你在担心什么。Kotlin目前还是beta版,用于正式项目要评估风险。事实上在第一个beta版中就出现了在android 4.3设备上无法安装的bug,好在官方很快就修复了。怎么讲呢,这是需要项目组全体成员一起评估的问题。我之所以敢在实际项目中使用,一是因为项目比较简单,万一遇到不可解决的问题可以改用java写,反正Kotlin和java可以无缝混用;二是因为程序员就我一个人,胆子粗啊哈哈哈哈~
感想 整个项目做下来让我感到很愉悦,代码的优雅足以说服我冒一点点风险。开发过程中我常常在想,要是有一个包含各种扩展方法的library就好了,我就不用自己造小轮子了。但转念一下,我真的需要一个完整的library吗?那必然会像Guava 一样成为一个巨无霸,但实际用到的功能可能只有10%。在Kotlin的官方Slack 里Jake Wharton (没错就是那个大神)提了一个有趣的思路:在一个git里放了很多不同类别的扩展方法文件(比如ViewExt.kt,IntExt.kt),需要哪个文件就把它pull下来,而不是compile整个jar包。如果能用gradle实现就太方便了😆
补充资料 Antonio Leiva 写了一本书Kotlin for Android Developers ,价格实惠干货满满,值得购买。配套的源码在https://github.com/antoniolg/Kotlin-for-Android-Developers