Android Chart 坐标Y轴 刻度美化

7 阅读1分钟

遵循 4 步算法

    1. 计算数据范围
    1. 理想步长 = 范围 / (刻度数 - 1)
    1. 归一化到 10 的幂 → 取 1/2/2.5/5 系数 → 得到规整步长
    1. 轴起点向下取整、终点向上取整到步长的整数倍
object NiceNumberUtil {

   /**
    * 计算规整后的坐标轴范围
    */
   fun calculateNiceRange(
       yAxis: YAxis,
       minValue: Double,
       maxValue: Double,
       tickCount: Int = 5
   ) {
       // 最大值 == 最小值,特殊处理
//        if (maxValue == minValue) {
//            return NiceRangeResult(
//                niceMin = maxValue - 1.0,
//                niceMax = maxValue + 1.0,
//                tickSpacing = 1.0
//            )
//        }
       // 计算粗略步长
       val range = maxValue - minValue
       val roughStep = range / (tickCount - 1)
       // 规整步长(round = YES)
       val niceStep = niceNumberNew(roughStep, round = true)
       // 上下取整
       val niceMin = floor(minValue / niceStep) * niceStep
       val niceMax = ceil(maxValue / niceStep) * niceStep
       // 防止极值相等
       val finalMax = if (niceMax == niceMin) {
           niceMin + niceStep
       } else {
           niceMax
       }
       yAxis.axisMaximum = finalMax.toFloat()
       yAxis.axisMinimum = niceMin.toFloat()
       yAxis.labelCount = tickCount
       yAxis.setLabelCount(tickCount, true)
   }

   /**
    * NiceNumber 核心算法
    * 归一化到 10 的幂 → 取 1/2/2.5/5 规整系数
    */

   private fun niceNumberNew(value: Double, round: Boolean): Double {
       val exponent = floor(log10(value))
       val fraction = value / 10.0.pow(exponent)
       val niceFraction: Double

       if (round) {
           // 向上取整,选择第一个大于等于 fraction 的优美系数
           niceFraction = when {
               fraction <= 1.0 -> 1.0
               fraction <= 2.0 -> 2.0
               fraction <= 2.5 -> 2.5
               fraction <= 5.0 -> 5.0
               else -> 10.0
           }
       } else {
           // 向下取整,选择第一个小于等于 fraction 的优美系数
           niceFraction = when {
               fraction < 1.0 -> 1.0
               fraction < 2.0 -> 2.0
               fraction < 2.5 -> 2.5
               fraction < 5.0 -> 5.0
               else -> 10.0
           }
       }

       return niceFraction * 10.0.pow(exponent)
   }

   /**
    * 核心:Nice Number 计算
    */
   private fun niceNumber(value: Double, round: Boolean): Double {
       val exponent = floor(log10(value))
       val fraction = value / 10.0.pow(exponent)
       val niceFraction: Double

       if (round) {
           niceFraction = when {
               fraction < 1.5 -> 1.0
               fraction < 3.0 -> 2.0
               fraction < 7.0 -> 5.0
               else -> 10.0
           }
       } else {
           niceFraction = when {
               fraction <= 1.0 -> 1.0
               fraction <= 2.0 -> 2.0
               fraction <= 5.0 -> 5.0
               else -> 10.0
           }
       }
       return niceFraction * 10.0.pow(exponent)
   }

   /**
    * 结果数据类
    */
   data class NiceAxisResult(
       val min: Double,      // 规整后最小值
       val max: Double,      // 规整后最大值
       val tickSpacing: Double // 刻度步长
   ) {
       /**
        * 获取所有刻度值列表
        */
       fun getTickValues(): List<Double> {
           val ticks = mutableListOf<Double>()
           var current = min
           while (current <= max + 1e-10) { // 浮点精度容错
               ticks.add(current)
               current += tickSpacing
           }
           return ticks
       }
   }
}