作者:王俊
自谷歌2018年发布支持Android开发的KTX扩展库以来,KTX扩展库主要是对Android原始的一些api进行了扩展,旨在帮助开发者更为简洁、通顺和优雅地使用 Kotlin进行开发Android程序。
添加依赖
implementation 'androidx.core:core-ktx:1.0.1'
ktx 对哪些内容进行了扩展
简单使用
AnimatorKt
animations 相关扩展库
- Animation listener 事件的监听
一般情况下我们需要对一个属性动画进行监听的话,写法不管在美观上还是在代码量上都会有点繁琐。但是如果我们使用ktx的话会简洁清爽很多
val animator = ObjectAnimator.ofFloat(TextView(this), "translationX", 0f, 500f)
//kotlin 普通写法
animator.addListener(object :Animator.AnimatorListener{
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
//ktx 写法
animator.addListener {
handleAnimator(it)
}
另外我们还可以接收annimation动画的多个callbacks,对于想添加几个callback完全可以根据自己的使用场景来实现, 这样对于原本就不需要太多callback来说,我们的代码量就减少了很多,也很清爽。
//ktx 写法
animator.addListener(
onStart = {},
onEnd = {},
onCancel = {},
onRepeat = {}
)
- 对animation个别event的事件的监听
除了addListener 里面对 onStart 、onEnd 、 onCancel 、onRepeat 自由的添加监听外,ktx 还增加了对Pause 事件的监听:
// ktx 对pause的监听
animator.addPauseListener {
handleAnimator(it)
}
//或者
animator.addPauseListener(
onPause = {},
onResume = {}
)
ktx 监听单个event:
animator.doOnStart { handleAnimator(it) }
animator.doOnEnd { handleAnimator(it) }
animator.doOnPause { handleAnimator(it) }
animator.doOnCancel { handleAnimator(it) }
animator.doOnRepeat { handleAnimator(it) }
animator.doOnResume { handleAnimator(it) }
TransitionsKt 转场动画
Transitions转场动画也像animator 那样,我们可以通过使用addListener调用转场动画的回调监听。具体的思路实现可以参考AnimatorKt
val slide = Slide(Gravity.LEFT)
slide.duration = 1000
slide.interpolator = FastOutSlowInInterpolator()
//kotlin 普通写法
slide.addListener(object : Transition.TransitionListener {
override fun onTransitionEnd(transition: Transition?) {
}
override fun onTransitionResume(transition: Transition?) {
}
override fun onTransitionPause(transition: Transition?) {
}
override fun onTransitionCancel(transition: Transition?) {
}
override fun onTransitionStart(transition: Transition?) {
}
})
//ktx
slide.addListener {
handleTransition(it)
}
slide.addListener(
onEnd = {},
onStart = {},
onCancel = {},
onResume = {},
onPause = {}
)
slide.doOnCancel { }
slide.doOnEnd { }
slide.doOnPause { }
slide.doOnResume { }
slide.doOnStart { }
OS
ktx 为Android OS 包提供了一系列的扩展支持,以下仅列举我们常见的一些写法。
- 比如我们最常见的handler扩展:
//kotlin 普通写法
handler.postAtTime({
Log.d("kotlin 正常写法 ", "我被执行了")
},2000L)
//ktx
handler.postAtTime(uptimeMillis = 200L) {
Log.d("os extensions ", "我被执行了")
}
- 创建bundle实例传递参数:
//kotlin 普通写法
val bundle = Bundle()
bundle.putString("hello","大王叫我来巡山")
//ktx
val bundle = bundleOf("hello" to "大王叫我来巡山")
val persitableBundle = persistableBundleOf("hello" to "大王叫我来巡山")
Utils
Utils 包主要是针对files 、arrays 以及其他的数据类型的扩展支持。
- AtomicFiles
AtomicFile是Android API17中引入的对文件进行原子操作的帮助类(原子是指在对整个文件操作时,要么不操作,要么操作成功。如果操作失败,不会影响文件内容)
// AtomicFiles
val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val file = File("${directory.absolutePath}.png")
val atomicFile = AtomicFile(file)
val readBytes = atomicFile.readBytes()
val text = atomicFile.readText(charset = Charset.defaultCharset())
atomicFile.tryWrite {
//TODO 执行写入操作
}
atomicFile.writeBytes(readBytes)
atomicFile.writeText("大王叫我来巡山", charset = Charset.defaultCharset())
- Arrays
对于LongSparseArray,SparseArray,SparseBooleanArray,SparseIntArray,SparseLongArray类型(我们平时可能使用SparseBooleanArray 比较多),我们可以这样使用:
//SparseLongArray
val array = SparseBooleanArray()
for (i in 1..8) {
array[i] = i % 2 == 0
}
val size = array.size
array.contains(8)
array.containsKey(8)
array.containsValue(true)
array.forEach { key, value -> Log.d("SparseLongArray", "key=${key},value=${value}") }
array.getOrDefault(key = 8, defaultValue = false)
array.getOrElse(key = 8, defaultValue = {
false
})
array.isEmpty()
array.isNotEmpty()
val keyIterator = array.keyIterator()
val valueIterator = array.valueIterator()
val anotherArray = SparseBooleanArray()
anotherArray[99] = true
array.plus(anotherArray)
array.putAll(anotherArray)
array.remove(key = 8, value = false)
array.set(key = 6, value = true)
Resources
当我们自定义view的使用使用到自定义属性必须用到TypedArray 类,ktx 在简化了TypedArray使用方式的同时,如果对于某个属性必须强制使用的话可以考虑用这个扩展( Retrieve the xxx value for the attribute at [index] or throws [IllegalArgumentException])
class CustomerView : View {
constructor(context: Context) : this(context, null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyle: Int) : super(context, attributeSet, defStyle) {
val typedArray = context.theme.obtainStyledAttributes(attributeSet, R.styleable.CustomerView, defStyle, 0)
//kotlin 普通写法
val tempColorAttr = typedArray.getColor(R.styleable.CustomerView_testColor, Color.BLACK)
//ktx
val colorAttr = typedArray.getBooleanOrThrow(R.styleable.CustomerView_testColor)
val booleanAttr = typedArray.getColorOrThrow(R.styleable.CustomerView_testBoolean)
typedArray.recycle()
}
}
Text 富文本
我们在开发中使用富文本的场景很多,而ktx对于这部分也提供了一些很方便的扩展功能,比如我们常见的SpannableStringBuilder等类。
例如我们在SpannableStringBuilder中append加粗的文本:
val tempText = "大王叫我来巡山"
val builder = SpannableStringBuilder()
//kotlin 普通写法
builder.append(tempText)
.setSpan(StyleSpan(Typeface.BOLD), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
tvContent.text = builder
//ktx
builder.bold { append(tempText) }
tvContent.text = builder
//当然你也可以这样
builder.bold { italic { underline { append("我渴望世界和平") } } }
tvContent.text = builder
当然我们可以这样去设置文本的背景色和设置Span属性:
builder.backgroundColor(color = Color.RED) {
// builder action
append("大王叫我来巡山")
}
builder.inSpans(span = StyleSpan(Typeface.BOLD_ITALIC)) {
append("猴子派来的逗比")
}
最后还有一个buildSpannedString扩展方法,这样可以直接构建SpannableStringBuilder实例,写起来更加方便。
tvContent.text = buildSpannedString { bold { append("hello world!!!") } }
Net
ktx 对net包扩展主要是URI 这块的扩展
String to Uri
val myUriString = "ezbuy://android.home"
//kotlin 普通写法
val uri = Uri.parse(myUriString)
// ktx
val uri = myUriString.toUri()
Content
主要针对ContentValues、Context、SharedPreferences 等的扩展
- Context
比如我们需要获取SystemService服务:
//kotlin 普通写法
val manager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
//ktx
val alarmManager = getSystemService()
- Styled Attributes
比如我们自定义View里获取typedArray并设置attributes时:
//kotlin 普通写法
val typedArray = context.theme.obtainStyledAttributes(attributeSet, R.styleable.CustomerView, defStyle, 0)
val tempColorAttr = typedArray.getColor(R.styleable.CustomerView_testColor, Color.BLACK)
//ktx
context.withStyledAttributes(set = attributeSet,attrs = R.styleable.CustomerView,defStyleAttr = defStyle,defStyleRes = 0){
//TypedArray actions
val tempColorAttr = getColor(R.styleable.CustomerView_testColor, Color.BLACK)
}
- SharedPreferences
val sharedPreferences = getSharedPreferences("data", Context.MODE_PRIVATE)
//kotlin 普通写法
sharedPreferences.edit()
.putString("leon", "大王叫我来巡山")
.apply()
//ktx
sharedPreferences.edit {
putString("leon", "大王叫我来巡山")
putBoolean("male", true)
}
- ContentValues
//kotlin 普通写法
val contentValues = ContentValues()
contentValues.put("key","大王叫我来巡山")
//ktx
val contentValues2 = contentValuesOf("key1" to "大王叫我来巡山",
"key2" to "猴子派来的逗比")
Graphics
ktx 对Graphics包的相关类做了比较多的扩展,比如我们常见的Bitmap、Canvas、Color、Rect、Path、Shader等。
- 常见的位图转换:
val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
val adaptiveIcon = bitmap.toAdaptiveIcon()
val icon = bitmap.toIcon()
val resources = ImageView(this).resources
val bitmapDrawable = bitmap.toDrawable(resources)
val color = resources.getColor(R.color.colorAccent)
val colorDrawable = color.toDrawable()
val myUri = "ezbuy://android.home".toUri()
val icon2 = myUri.toIcon()
val drawable = resources.getDrawable(R.drawable.ic_launcher_background)
val bitmap2 = drawable.toBitmap(width = 100, height = 100, config = Bitmap.Config.ARGB_8888)
- bitmap 常见的扩展操作
val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
//set canvas
bitmap.applyCanvas {
val paint = Paint()
paint.color = Color.BLUE
paint.style = Paint.Style.STROKE
paint.strokeWidth = 10F
drawText("大王叫我来巡山", 0F, 0F, paint)
}
//get location color
val colorInt = bitmap.get(100, 100)
//create new bitmap
val bitmap1 = bitmap.scale(100, 100, filter = true)
// set location color
bitmap.set(100, 100, Color.RED)
- Canvas
val canvas = Canvas()
//旋转
canvas.withRotation(90F, 0F, 0F) {
// canvas actions
}
//保存Canvas的状态
canvas.withSave {
// canvas actions
}
//缩放
canvas.withScale(0F, 0F, 100F, 100F) {
// canvas actions
}
//位移
canvas.withTranslation(100F, 100F) {
// canvas actions
}
- Color
// 解构
val (r, g, b, a) = Color.RED
// 合并两个颜色
var color = Color.RED
color += Color.BLACK
//透明度
val alpha = color.alpha
- Rect
val rect = someRect and anotherRect
val (left, top, right, bottom) = someRect
someRect.contains(somePoint)
val region = someRect - anotherRect
val rect = someRect - someInt
val rect = someRect - somePoint
val rect = someRect or someRect
val rect = someRect + someRect
val rect = someRect + someInt
val rect = someRect + somePoint
val rectF = someRect.toRectF()
val region = someRect.toRegion()
val region = someRect xor someRect
- Path
val path = somePath and anotherPath
val path = somePath.flatten(error = 1f)
val path = somePath - anotherPath
val path = somePath or anotherPath
val path = somePath + anotherPath
val path = somePath xor anotherPath
Views
- layout 回调监听
//kotlin 普通写法
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
v: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
) {
//callback
view.removeOnLayoutChangeListener(this)
view.width// 获取宽度
view.height// 获取高度
}
})
//ktx
view.doOnLayout {
//callback
it.apply {
removeOnLayoutChangeListener(this)
width// 获取宽度
height// 获取高度
}
}
view.doOnNextLayout {
//callback
it.apply {
removeOnLayoutChangeListener(this)
width// 获取宽度
height// 获取高度
}
}
- postDelayed 写法
//kotlin 普通写法
view.postDelayed({
Log.d("View", "postDelayed")
}, 500)
//ktx
view.postDelayed(delayInMillis = 500) {
Log.d("View", "postDelayed")
}
- postOnAnimationDelayed 同上
//kotlin 普通写法
view.postOnAnimationDelayed({
Log.d("View", "postOnAnimationDelayed")
}, 500)
//ktx
view.postOnAnimationDelayed(delayInMillis = 500) {
Log.d("View", "postOnAnimationDelayed")
}
- padding
view 的padding 属性设置也变得清晰明了很多
//kotlin 普通写法
view.setPadding(10, 10, 10, 10)
view.setPaddingRelative(10, 10, 10, 10)
//ktx
view.setPadding(10) // view.setPadding(10, 10, 10, 10)
view.updatePadding(left = 16, right = 11, top = 19, bottom = 20) //view.setPadding(16, 11, 19, 20)
view.updatePaddingRelative(start = 10, end = 10, top = 10, bottom = 10)
ViewGroup
ViewGroup 常见的方法扩展
- 检查ViewGroup 是否包含某个View
val viewGroup = LinearLayout(this)
//kotlin 普通写法
viewGroup.indexOfChild(view)
//ktx
val doesContain = viewGroup.contains(view)
- 遍历子View
//kotlin 普通写法
for (index in 0 until viewGroup.size) {
handleChildView(view = viewGroup.getChildAt(index))
}
//ktx
viewGroup.forEach {
handleChildView(view = it)
}
viewGroup.forEachIndexed { index, view ->
handleChildView(index, view)
}
- MutableIterator 迭代器
val viewGroupIterator = viewGroup.iterator()
- 其他操作符
viewGroup.isEmpty()
viewGroup.isNotEmpty()
viewGroup.size
// 移除一个View
viewGroup -= view
// 添加一个View
viewGroup += view
总结
总体使用情况来说利用kotlin扩展函数简化一些api,体验还是很好的,代码看起来简洁、清爽。但是从前几个迭代版本中了解到废弃和新增了不少,该扩展库在不断的稳定和完善中;另外常见的扩展库还是比较少的,我们相信ktx在不断的迭代更新中提供更多简洁的api。
参考资料
https://android.github.io/android-ktx/core-ktx/index.html
https://developer.android.com/kotlin/ktx