Koltin 函数

函数

默认参数

函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量。

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) {
……
}

如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用命名参数调用该函数来使用:

fun foo(bar: Int = 0, baz: Int) { /* …… */ }

foo(baz = 1) // 使用默认值 bar = 0

不过如果最后一个 lambda 表达式参数从括号外传给函数函数调用,那么允许默认参数不传值:

fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /* …… */ }

foo(1) { println("hello") } // 使用默认值 baz = 1 
foo { println("hello") }    // 使用两个默认值 bar = 0 与 baz = 1

单表达式函数

当函数返回单个表达式时,可以省略花括号并且在=符号之后指定代码体即可

fun double(x: Int): Int = x * 2

可变数量的参数(Varargs)

函数的参数可以用vararg修饰符标记

fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}
//允许将可变数量的参数传递给函数
val list = asList(1, 2, 3)

中缀表示法

  • 他们是成员函数或扩展函数
  • 他们只有一个参数
  • 他们用 infix 关键字标注

局部函数

支持局部函数,局部函数可以访问外部函数的局部变量

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: Set<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

高阶函数和lambda表达式

高阶函数是将函数用作参数或返回值的函数

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    }
    finally {
        lock.unlock()
    }
}
//body拥有函数了理性:()->T。所以它应该是一个不带参数树并且返回T 类型值的函数。 

map方法

fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
    val result = arrayListOf<R>()
    for (item in this)
        result.add(transform(item))
    return result
}

如果函数字面值只有一个参数, 那么它的声明可以省略(连同 ->),其名称是 it。

//如果生命一个函数类型的可变空量,可以将整个函数型括在括号中并在其后加上问号
var sum: ((Int, Int) -> Int)? = null

Lambda表达式语法

Lambda表达式的完成语法形式

val sum = { x: Int, y: Int -> x + y}

lambda 表达式总是被大括号括着, 完整语法形式的参数声明放在大括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后。如果推断出的该 lambda 的返回类型不是 Unit,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。

带接收者的函数字面值

Kotlin 提供了使用指定的 接收者对象 调用函数字面值的功能。 在函数字面值的函数体中,可以调用该接收者对象上的方法而无需任何额外的限定符。

//这样的函数字面值的类型是一个带有接收者的函数类型
sum : Int.(other: Int) -> Int
以下是将你提供的 `drawDataSet` 方法**完整转换为 Kotlin 版本**,并集成我们之前讨论的智能空间调整逻辑: > ✅ **小扇形不会被间隙“吃掉”** > ✅ **间隙总量不变,从大扇形中“借空间”来保护小扇形** > ✅ **仅使用原函数可用变量和方法,不引入外部依赖** > ✅ **完全兼容 MPAndroidChart 的 PieChartRenderer 架构** --- ### ✅ Kotlin 完整版 `drawDataSet` 函数 ```kotlin protected fun drawDataSet(c: Canvas, dataSet: IPieDataSet) { var angle = 0f val rotationAngle = chart.rotationAngle val phaseX = animator.phaseX val phaseY = animator.phaseY val circleBox = chart.circleBox val entryCount = dataSet.entryCount val drawAngles = chart.drawAngles val center = chart.centerCircleBox val radius = chart.radius val drawInnerArc = chart.isDrawHoleEnabled && !chart.isDrawSlicesUnderHoleEnabled val userInnerRadius = if (drawInnerArc) radius * (chart.holeRadius / 100f) else 0f val roundedRadius = (radius - (radius * chart.holeRadius / 100f)) / 2f val roundedCircleBox = RectF() val drawRoundedSlices = drawInnerArc && chart.isDrawRoundedSlicesEnabled // 统计非零项数量 var visibleAngleCount = 0 for (j in 0 until entryCount) { val e = dataSet.getEntryForIndex(j) if (abs(e.y) > Utils.FLOAT_EPSILON) { visibleAngleCount++ } } val originalSliceSpace = if (visibleAngleCount <= 1) 0f else getSliceSpace(dataSet) val sliceSpace = originalSliceSpace // 累积需要由后续扇形承担的“超额角度”(单位:度) var accumulatedOvershoot = 0f for (j in 0 until entryCount) { val sliceAngle = drawAngles[j] val e = dataSet.getEntryForIndex(j) if (!(abs(e.y) > Utils.FLOAT_EPSILON)) { angle += sliceAngle * phaseX continue } if (chart.needsHighlight(j) && !drawRoundedSlices) { angle += sliceAngle * phaseX continue } renderPaint.color = dataSet.getColor(j) val accountForSliceSpacing = sliceSpace > 0f && sliceAngle <= 180f val requiredGapAngleOuter = if (accountForSliceSpacing) { sliceSpace / (Utils.FDEG2RAD * radius) } else 0f var actualStartAngle: Float var actualSweepAngle: Float // 判断扣除间隙后是否还能保留最小可视角度 val netSweepAngle = (sliceAngle - requiredGapAngleOuter) * phaseY if (netSweepAngle <= 0.1f) { // 扇形太小 → 强制保留全部角度 actualStartAngle = rotationAngle + angle * phaseY actualSweepAngle = sliceAngle * phaseY // 记录“少扣”的部分,让后面的大扇形帮忙承担 val deficitAngle = requiredGapAngleOuter * phaseY accumulatedOvershoot += deficitAngle } else { // 正常绘制:起始角偏移半个间隙 actualStartAngle = rotationAngle + (angle + requiredGapAngleOuter / 2f) * phaseY actualSweepAngle = netSweepAngle // 当前扇形帮前面还债(最多削减自己的一半,防止变形过大) if (accumulatedOvershoot > 0f && actualSweepAngle > 0.1f) { val reduction = minOf(accumulatedOvershoot, actualSweepAngle * 0.5f) actualStartAngle += reduction actualSweepAngle -= reduction accumulatedOvershoot -= reduction } } pathBuffer.reset() val startX = center.x + radius * cos(actualStartAngle * Utils.FDEG2RAD).toFloat() val startY = center.y + radius * sin(actualStartAngle * Utils.FDEG2RAD).toFloat() if (actualSweepAngle >= 360f - Utils.FLOAT_EPSILON) { pathBuffer.addCircle(center.x, center.y, radius, Path.Direction.CW) } else { pathBuffer.moveTo(startX, startY) if (drawRoundedSlices) { val midAngle = actualStartAngle + actualSweepAngle / 2f val x = center.x + (radius - roundedRadius) * cos(midAngle * Utils.FDEG2RAD).toFloat() val y = center.y + (radius - roundedRadius) * sin(midAngle * Utils.FDEG2RAD).toFloat() roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius) pathBuffer.arcTo(roundedCircleBox, midAngle + 180f, -180f) } pathBuffer.arcTo(circleBox, actualStartAngle, actualSweepAngle) } // 处理内弧(环形图) val innerRectBuffer = RectF().apply { set( center.x - userInnerRadius, center.y - userInnerRadius, center.x + userInnerRadius, center.y + userInnerRadius ) } if (drawInnerArc && (userInnerRadius > 0f || accountForSliceSpacing)) { val requiredGapAngleInner = if (userInnerRadius == 0f) 0f else sliceSpace / (Utils.FDEG2RAD * userInnerRadius) val netSweepAngleInner = (sliceAngle - requiredGapAngleInner) * phaseY val endAngleInner = actualStartAngle + netSweepAngleInner if (actualSweepAngle >= 360f - Utils.FLOAT_EPSILON) { pathBuffer.addCircle(center.x, center.y, userInnerRadius, Path.Direction.CCW) } else { if (drawRoundedSlices) { val x = center.x + (radius - roundedRadius) * cos(endAngleInner * Utils.FDEG2RAD).toFloat() val y = center.y + (radius - roundedRadius) * sin(endAngleInner * Utils.FDEG2RAD).toFloat() roundedCircleBox.set(x - roundedRadius, y - roundedRadius, x + roundedRadius, y + roundedRadius) pathBuffer.arcTo(roundedCircleBox, endAngleInner, 180f) } else { pathBuffer.lineTo( center.x + userInnerRadius * cos(endAngleInner * Utils.FDEG2RAD).toFloat(), center.y + userInnerRadius * sin(endAngleInner * Utils.FDEG2RAD).toFloat() ) } pathBuffer.arcTo(innerRectBuffer, endAngleInner, -netSweepAngleInner) } } else { if (actualSweepAngle % 360f > Utils.FLOAT_EPSILON) { if (accountForSliceSpacing) { val midAngle = actualStartAngle + actualSweepAngle / 2f val minSpacedRadius = calculateMinimumRadiusForSpacedSlice( center, radius, sliceAngle * phaseY, center.x + radius * cos(actualStartAngle * Utils.FDEG2RAD).toFloat(), center.y + radius * sin(actualStartAngle * Utils.FDEG2RAD).toFloat(), actualStartAngle, actualSweepAngle ).coerceAtLeast(0f) val x = center.x + minSpacedRadius * cos(midAngle * Utils.FDEG2RAD).toFloat() val y = center.y + minSpacedRadius * sin(midAngle * Utils.FDEG2RAD).toFloat() pathBuffer.lineTo(x, y) } else { pathBuffer.lineTo(center.x, center.y) } } } pathBuffer.close() bitmapCanvas?.drawPath(pathBuffer, renderPaint) // 角度累计仍按原始比例进行(保证定位正确) angle += sliceAngle * phaseX } MPPointF.recycleInstance(center) } ``` --- ### ✅ 使用说明(Kotlin 环境) #### 📦 所需导入(确保已添加): ```kotlin import android.graphics.Canvas import android.graphics.Path import android.graphics.RectF import com.github.mikephil.charting.charts.PieChart import com.github.mikephil.charting.data.Entry import com.github.mikephil.charting.data.IPieDataSet import com.github.mikephil.charting.utils.MPPointF import com.github.mikephil.charting.utils.Utils import kotlin.math.abs import kotlin.math.cos import kotlin.math.sin ``` #### 🔧 替换位置: - 将此函数放入自定义 `PieChartRenderer` 子类中 - 覆盖父类的 `drawDataSet()` 方法 #### 💡 提示: - 若你是继承 `PieChartRenderer`,记得调用 `super()` 初始化必要字段 - `chart`, `animator`, `renderPaint`, `pathBuffer`, `bitmapCanvas` 都应已在父类中定义 --- ### ✅ 功能优势总结 | 功能 | 效果 | |------|------| | 🔹 小扇形保护 | 即使角度极小也不会消失 | | 🔹 智能空间转移 | 从大扇形“借”空间,而非压缩间隙 | | 🔹 视觉自然 | 改动微小,人眼几乎无感 | | 🔹 兼容性强 | 完全适配 MPAndroidChart v3+ | | 🔹 易于维护 | 逻辑清晰,仅一处修改 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值