项目中都可以使用的 8 种简单扩展方法

1. 自定义 View 的属性操作

在设计自己的 View 时,通常从视图层次结构和标记中获取所需属性的方法是使用 TypedArray。然而,像所有资源一样,这样的容器对象必须正确地获取,然后在使用后释放。以下扩展旨在简化此过程:

fun View.withTypedArray(
    set: AttributeSet?,
    @StyleableRes attrs: IntArray,
    @AttrRes defStyleAttr: Int = 0,
    @StyleRes defStyleRes: Int = 0,
    action: TypedArray.() -> Unit
) {
    val typedArray = context.theme.obtainStyledAttributes(
        set, attrs, defStyleAttr, defStyleRes
    )
    action(typedArray)
    typedArray.recycle()
}

2. 遍历视图层次结构

有时需要遍历所有(或部分)视图树,并对每个视图执行特定操作。在这种情况下,最简单和最方便的方法就是递归地遍历树。但是,可以通过使用扩展方法来隐藏递归函数的常数实现:

fun View.forEach(action: View.(Int) -> Unit) = forEach(0, action)

private fun View.forEach(level: Int = 0, action: View.(Int) -> Unit) {
    this.action(level)
    if (this is ViewGroup) {
        this.children.forEach { it.forEach(level + 1, action) }
    }
}

3. 删除子视图

在 Android SDK 中,要从层次结构中删除视图,需要同时访问父元素和被删除的元素。为了简化这个过程,可以使用以下扩展来通过 ID 删除:

fun ViewGroup.removeView(id: Int) {
    val v = findViewById(id) ?: return
    this.removeView(v)
}

4. EditText

在大多数情况下,输入字段(EditText 或其子类)容纳的是 CharSequence 接口最常见的实现——一个简单的字符串。然而,为了在运行时设置文本,需要将其转换为 Editable,这就是以下简单扩展的作用:

fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this)

5. ImageView

尽管建议使用 ColorStateList 机制来处理颜色资源,但是通常情况下,应用程序会使用硬编码的颜色集(例如,以匹配品牌)。然后就需要不断创建包装器来转换为 ColorStateList,以下扩展被提出来消除这种模板代码:

fun ImageView.setTint(@ColorRes color: Int) {
    this.imageTintList = ColorStateList.valueOf(
        resolveColor(color)
    )
}

6. 在运行时修改 ConstraintLayout 约束

ConstraintLayout 在布局其子视图方面具有极大的灵活性。通常,只需在 XML 文件中配置布局即可,但是有时候需要在运行时修改约束集(改变单个约束,删除一些约束或添加新的约束)。这是使用 ConstraintSet 完成的,它应该绑定到 ConstraintLayout,但是始终很难从头开始创建一个完整的约束集,最好重用已经存在于布局中的所有约束。在下面的例子中,首先为此目的创建了一个新的空 ConstraintSet,然后使用布局中已经存在的所有约束填充它;然后进行额外的自定义(最常用的方法是 connect 和 clear),最后为 ConstraintLayout 设置新的完整约束集。

fun ConstraintLayout.updateConstraints(action: ConstraintSet.() -> Unit) {
    ConstraintSet().apply {
        clone(this@updateConstraints)
        action(this)
        applyTo(this@updateConstraints)
    }
}

7. 即时列表修改

几乎每个应用程序中都有列表(以某种形式)。这些列表通常是动态的,可能有多个数据源,然后需要合并这些数据,避免遗漏或重复,如果有后者,选择最合适的重复项。以下方法就是为此目的创建的:

fun <T, K> MutableList<T>.addAllDistinct(
    other: List<T>,
    distinctPredicate: (T) -> K = { it },
    onRepeat: (T, T) -> T = { f, s -> f },
    fromStart: Boolean = false
) {
    val added = this.map(distinctPredicate).toMutableSet()
    for (item in other) {
        val k = distinctPredicate(item)
        if (added.add(k)) {
            if (fromStart) {
                this.add(0, item)
            } else {
                this.add(item)
            }
        } else {
            val indexOfExisting = this.indexOfFirst { distinctPredicate(it) == k }
            if (indexOfExisting != -1) {
                this[indexOfExisting] = onRepeat(this[indexOfExisting], item)
            }
        }
    }
}

8. 字符串居中

padStart 和 padEnd 方法允许你通过在字符串的开始或结束处添加所选字符的所需数量来填充字符串到所需长度。下面的方法也提供了所需的长度,但是在左侧和右侧都添加了相同数量的字符,从而保持原始字符串相对于其中心的对称位置:

fun String.padSymmetric(length: Int, padChar: Char): String {
    require(length >= 0)
    if (length <= this.length) return this
    val countAtStart = (length - this.length) / 2
    return this.padStart(this.length + countAtStart, padChar).padEnd(length, padChar)
}

转自:Android 项目中都可以使用的 8 种简单扩展方法