AAChartCore icon indicating copy to clipboard operation
AAChartCore copied to clipboard

触摸数据条弹出的框里的数据格式要如何自定义显示?

Open InnocentYang opened this issue 1 year ago • 14 comments

sleep_chart_data

你好,我用AAChart画睡眠数据的图表,有两个serias分别表示深睡时长和浅睡时长, 数据本来是以秒为单位,在绘制图表时手动把它转换成以小时为单位,比如数据=31320秒,图表显示=8.7小时。

客户提出要显示成 8小时42分,我不知道该怎么做。(还要做国际化,小时分钟数字要根据手机的Locale显示不同的语言)

sleep_chart_code

我试了试,数据如果直接传秒,y轴坐标也会有问题,显示的单位也是10K--秒,不知道怎么将y坐标也显示成小时

请你帮我处理一下,非常感谢

InnocentYang avatar May 08 '24 06:05 InnocentYang

参考在线文档:

  • https://api.highcharts.com/highcharts/tooltip.formatter

AAChartModel avatar May 08 '24 08:05 AAChartModel

实在不好意思,JS代码我看不懂,能帮我写一下这个代码吗?我自己实在搞不定。 你可以给我的邮箱 [email protected] 发个收款码,我发20块钱红包给你。 谢谢。

image 我 本来想尝试取一个index,然后做一个数据数组,但thisPoint.index undefined。

InnocentYang avatar May 08 '24 10:05 InnocentYang

红包就免了吧. 你把你的图表配置 AAChartModel 或者 AAOptions 实例的代码粘贴一下, 我这边复现一下, 我看看怎么弄.

PS: 请直接粘贴代码文本, 不要截取代码图片.

AAChartModel avatar May 08 '24 10:05 AAChartModel

String[] categories = { "周一","周二","周三","周四","周五","周六","周日"};

Object[] deepSeconds = {31320, 31320, 31320, 31320, 31320, 31320, 31320};  // 原始数据,单位是秒
Object[] lightSeconds = {3600, 3600, 3600, 3600, 3600, 3600, 3600};

Object[] deepHours = {8.7, 8.7, 8.7, 8.7, 8.7, 8.7, 8.7}; // 除以3600得到  单位是小时
Object[] lightHours = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};

创建 AAChartModel


generateChartModel(categories , deepHours, lightHours)

private AAChartModel generateChartModel(String[] categories, Object[] deepTimes, Object[] lightTimes) {
        AASeriesElement element1 = new AASeriesElement()
                .name(getString(R.string.deep_sleep))
                .dataLabels(new AADataLabels()
                        .y(-10)
                        .format("{total} mm")
                        .color(AAColor.Red)
                        .shape("callout")
                        .backgroundColor(AAColor.White)
                        .borderColor(AAColor.Red)
                        .borderRadius(1)
                        .borderWidth(1)
                )
                .data(deepTimes);
        AASeriesElement element2 = new AASeriesElement()
                .name(getString(R.string.light_sleep))
                .data(lightTimes);
        AASeriesElement[] series = {element1, element2};

        return new AAChartModel()
                .chartType(AAChartType.Column)
                .stacking(AAChartStackingType.Normal)
                .legendEnabled(true)
                .xAxisTickInterval(xAxisInterval())
                .markerRadius(0)
                .categories(categories)
                .tooltipValueSuffix(getString(R.string.hour))
                .colorsTheme(colorsTheme)
                .markerSymbolStyle(AAChartSymbolStyleType.BorderBlank)
                .touchEventEnabled(true)
                .series(series);
    }

期望:

(1)点击图表弹出的Tooltip里面显示的是: 周三 深睡:8小时42分 浅睡:1小时0分
(还要做多语言适配,如果实在不行,8H 42M 也行) (2)y轴显示小时数字

非常感谢

InnocentYang avatar May 08 '24 11:05 InnocentYang

我在尝试用 AAOptions ,在拿这段代码改,在各种混乱的尝试

ArrayList<String[]> strList = new ArrayList<>();
        strList.add(deepStringArr);
        strList.add(lightStringArr);

        AAChartModel aaChartModel = generateChartModel(categories, deepTimes, lightTimes);

        String jsFormatterStr = String.format("function () {\n" +
                "        let wholeContentStr = this.points[0].x + '<br/>';\n" +
                "        let length = this.points.length;\n" +
                "        for (let i = 0; i < length; i++) {\n" +
                "            let thisPoint = this.points[i];\n" +
                "            let yValue = thisPoint.y;\n" +
                "            let indexValue = thisPoint.index;\n" +
                "            if (yValue != 0) {\n" +
                "                let prefixStr = '<span style=\\\"' + 'color:'+ thisPoint.color + '; font-size:13px\\\"' + '>◉ ';\n" +
                "                wholeContentStr += prefixStr + thisPoint.series.name + ' : ' + thisPoint.series.index + ' : ' + yValue + ' : ' + indexValue + '" + suffix + "' + '<br/>';\n" +
                "            }\n" +
                "        }\n" +
                "        return wholeContentStr;\n" +
                "    }");

        AATooltip aaTooltip = new AATooltip()
                .useHTML(true)
                .valueSuffix(suffix)
                .formatter(jsFormatterStr);
        AAOptions aaOptions = aaChartModel.aa_toAAOptions();
        aaOptions.tooltip(aaTooltip);
        return aaOptions;

InnocentYang avatar May 08 '24 11:05 InnocentYang

配置 AAOptions

    public static AAOptions customizeTooltipFormatter() {
        String[] categories = { "周一","周二","周三","周四","周五","周六","周日"};

        Object[] deepSeconds = {31320, 31320, 31320, 31320, 31320, 31320, 31320};  // 原始数据,单位是秒
        Object[] lightSeconds = {3600, 3600, 3600, 3600, 3600, 3600, 3600};

        Object[] deepHours = {8.7, 8.7, 8.7, 8.7, 8.7, 8.7, 8.7}; // 除以3600得到  单位是小时
        Object[] lightHours = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};

        AAChartModel aaChartModel = generateChartModel(categories , deepHours, lightHours);

        AAOptions aaOptions = aaChartModel.aa_toAAOptions();
        aaOptions.tooltip.shared(true)
                .formatter("function () {\n" +
                        "        function hoursToHoursMinutes (hours) {\n" +
                        "            const hoursInt = Math.floor(hours); \n" +
                        "            const minutes = Math.round((hours - hoursInt) * 60); \n" +
                        "            return hoursInt + \"小时 \" + minutes + \"分钟\";\n" +
                        "        }\n" +
                        "\n" +
                        "        let s = '<b>' + this.x + '</b>' + '<br/>';\n" +
                        "        const colorDot1 = '<span style=\\\"'+'color:#1e90ff; font-size:13px\\\"'+'>◉</span> ';\n" +
                        "        const colorDot2 = '<span style=\\\"'+'color:#ef476f; font-size:13px\\\"'+'>◉</span> ';\n" +
                        "\n" +
                        "        const originalPoint1 = this.points[0].y;\n" +
                        "        const originalPoint2 = this.points[1].y;\n" +
                        "\n" +
                        "        const finalPoint1 = hoursToHoursMinutes (originalPoint1);\n" +
                        "        const finalPoint2 = hoursToHoursMinutes (originalPoint2);\n" +
                        "\n" +
                        "        const s1 = colorDot1 +this.points[0].series.name + ': ' + finalPoint1 + '<br/>';\n" +
                        "        const s2 = colorDot2 +this.points[1].series.name + ': ' + finalPoint2;\n" +
                        "        s += s1 + s2;\n" +
                        "        return s;\n" +
                        "    }");


        //禁用图例点击事件
        aaOptions.plotOptions.series.events = new AASeriesEvents()
                .legendItemClick("function() { " +
                        "return false; " +
                        "}");

        return aaOptions;
    }



    private static AAChartModel generateChartModel(String[] categories, Object[] deepTimes, Object[] lightTimes) {
        AASeriesElement element1 = new AASeriesElement()
                .name("深度睡眠")
                .dataLabels(new AADataLabels()
                        .y(-10)
                        .format("{total} mm")
                        .color(AAColor.Red)
                        .shape("callout")
                        .backgroundColor(AAColor.White)
                        .borderColor(AAColor.Red)
                        .borderRadius(1)
                        .borderWidth(1)
                )
                .data(deepTimes);
        AASeriesElement element2 = new AASeriesElement()
                .name("浅睡眠")
                .data(lightTimes);
        AASeriesElement[] series = {element1, element2};

        return new AAChartModel()
                .chartType(AAChartType.Column)
                .stacking(AAChartStackingType.Normal)
                .legendEnabled(true)
//                .xAxisTickInterval(xAxisInterval())
                .markerRadius(0)
                .categories(categories)
//                .tooltipValueSuffix(getString(R.string.hour))
//                .colorsTheme(colorsTheme)
                .markerSymbolStyle(AAChartSymbolStyleType.BorderBlank)
                .touchEventEnabled(true)
                .series(series);
    }

最终图表:

Screenshot_20240508_215314

AAChartModel avatar May 08 '24 13:05 AAChartModel

以上 Java 代码最终转化而成的 JS 代码对应的内容如下:

Highcharts.chart('container', {
    "chart": {
        "backgroundColor": "#ffffff",
        "inverted": false,
        "panning": true,
        "pinchType": "none",
        "polar": false,
        "type": "column"
    },
    "colors": [
        "#fe117c",
        "#ffc069",
        "#06caf4",
        "#7dffc0"
    ],
    "credits": {
        "enabled": false
    },
    "legend": {
        "enabled": true,
        "itemStyle": {

        }
    },
    "plotOptions": {
        "column": {
            "borderRadius": 0,
            "borderWidth": 0
        },
        "series": {
            "dataLabels": {
                "enabled": false
            },
            "stacking": "normal"
        }
    },
    "series": [
        {
            "data": [
                8.7,
                8.7,
                8.7,
                8.7,
                8.7,
                8.7,
                8.7
            ],
            "dataLabels": {
                "backgroundColor": "white",
                "borderColor": "red",
                "borderRadius": 1,
                "borderWidth": 1,
                "color": "red",
                "format": "{total} mm",
                "shape": "callout",
                "y": -10
            },
            "name": "深睡"
        },
        {
              "data": [
                1,
                1,
                1,
                1,
                1,
                1,
                1
            ],
            "name": "浅睡"
        }
    ],
    "subtitle": {

    },
    "title": {
        "text": ""
    },
    "tooltip": {
       "formatter": function () {
        function hoursToHoursMinutes (hours) {
            const hoursInt = Math.floor(hours); 
            const minutes = Math.round((hours - hoursInt) * 60); 
            return hoursInt + "小时 " + minutes + "分钟";
        }

        let s = '<b>' + this.x + '</b>' + '<br/>';
        const colorDot1 = '<span style=\"'+'color:#1e90ff; font-size:13px\"'+'>◉</span> ';
        const colorDot2 = '<span style=\"'+'color:#ef476f; font-size:13px\"'+'>◉</span> ';

        const originalPoint1 = this.points[0].y;
        const originalPoint2 = this.points[1].y;

        const finalPoint1 = hoursToHoursMinutes (originalPoint1);
        const finalPoint2 = hoursToHoursMinutes (originalPoint2);

        const s1 = colorDot1 +this.points[0].series.name + ': ' + finalPoint1 + '<br/>';
        const s2 = colorDot2 +this.points[1].series.name + ': ' + finalPoint2;
        s += s1 + s2;
        return s;
    },
        "shared": true
    },
    "touchEventEnabled": true,
    "xAxis": {
        "categories": [
            "周一",
            "周二",
            "周三",
            "周四",
            "周五",
            "周六",
            "周日"
        ],
        "gridLineWidth": 0,
        "labels": {
            "enabled": true,
            "style": {

            }
        },
        "reversed": false
    },
    "yAxis": {
        "gridLineWidth": 1,
        "labels": {
            "enabled": true,
            "style": {

            }
        },
        "reversed": false,
        "title": {
            "style": {

            },
            "text": ""
        }
    }
});

你可以将这段内容粘贴到在线 Highcharts 代码编辑器中, 自行测试运行查看效果。

AAChartModel avatar May 08 '24 14:05 AAChartModel

Highcharts 在线代码编辑器:

  • https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/tooltip/formatter-simple/

AAChartModel avatar May 08 '24 14:05 AAChartModel

好的,非常感谢,我明天去公司试试。 另外,我看代码,是传入 deepHours/lightHours 然后手动计算出 hour 和 minute, 有没有办法 直接传入 秒数组 deepSeconds,并实现需求:tooltip显示 x小时y分钟,Y轴显示小时数? 因为我有些担心分钟数的计算会有四舍五入的问题,导致UI显示时数值差1。

InnocentYang avatar May 08 '24 14:05 InnocentYang

以上 tooltipformatter 函数配置过程比较详细,稍显啰嗦, 可以改的再简洁一些。 更简洁直观 JavaScript 代码示例如下:

  1. 使用原始数组内容去拼接字符串(低版本兼容性更强):
"tooltip": {
    "formatter": function () {
        function formatTime(hours) {
            return [Math.floor(hours), '小时', Math.round((hours - Math.floor(hours)) * 60), '分钟'].join(' ');
        }
        
        // 颜色点样式定义
        var colorStyles = {
            blue: '<span style="color:#1e90ff;font-size:13px;">◉</span> ',
            red: '<span style="color:#ef476f;font-size:13px;">◉</span> '
        };
        
        // 使用数组.join()方法代替模板字符串进行字符串拼接
        var output = [
            '<b>', this.x, '</b><br/>',
            colorStyles.blue, this.points[0].series.name, ': ', formatTime(this.points[0].y), '<br/>',
            colorStyles.red, this.points[1].series.name, ': ', formatTime(this.points[1].y)
        ].join('');
                      
        return output;
    },
    "shared": true
}
  1. 直接使用字符串模板(ES 6 语法):
"tooltip": {
    "formatter": function () {
        // 小时分钟格式化函数
        const formatTime = hours => `${Math.floor(hours)}小时 ${Math.round((hours - Math.floor(hours)) * 60)}分钟`;
        
        // 颜色点样式定义
        const colorStyles = {
            blue: '<span style="color: #1e90ff; font-size: 13px;">◉</span> ',
            red: '<span style="color: #ef476f; font-size: 13px;">◉</span> '
        };
        
        // 格式化悬浮提示信息
        return `
        <b>${this.x}</b><br/>
        ${colorStyles.blue}${this.points[0].series.name}: ${formatTime(this.points[0].y)}<br/>
        ${colorStyles.red}${this.points[1].series.name}: ${formatTime(this.points[1].y)}
    `;
    },
    "shared": true
}
  1. 直接使用字符串模板和更多箭头函数简化代码(ES 6 语法):
"tooltip": {
    "formatter": function () {
        const formatTime = hours => `${Math.floor(hours)}小时 ${Math.round((hours - Math.floor(hours)) * 60)}分钟`;

        // 颜色点样式作为函数以减少重复
        const pointStyle = (color, seriesName, yValue) =>
            `<span style="color:${color};font-size:13px;">◉</span> ${seriesName}: ${formatTime(yValue)}`;

        // 直接构建最终字符串
        return `
            <b>${this.x}</b><br/>
            ${pointStyle('#1e90ff', this.points[0].series.name, this.points[0].y)}<br/>
            ${pointStyle('#ef476f', this.points[1].series.name, this.points[1].y)}
        `;
    },
    "shared": true
}

AAChartModel avatar May 08 '24 14:05 AAChartModel

有没有办法 直接传入 秒数组 deepSeconds,并实现需求

这样的话, 那Y 轴的 labelformatter 应该也要自定义了, 稍嫌麻烦。

AAChartModel avatar May 08 '24 14:05 AAChartModel

睡眠时长估计不可能超过12小时,y轴就固定从0到12,也可以 。

val yAxis = AAYAxis() yAxis.title(AATitle().text("")) yAxis.min(0) yAxis.max(12) yAxis.tickInterval(1) aaOptions.yAxisArray = arrayOf(yAxis)

InnocentYang avatar May 08 '24 14:05 InnocentYang

image

我对代码做了一些小调整, 以前是把秒数/3600得到的数值,现在换成小数部分是分钟,这样可以直接把小数部分*100转换成分数,避免计算精度问题。

非常感谢。 我让iOS同事也依样修改。

InnocentYang avatar May 09 '24 01:05 InnocentYang

你好,如上问题; 请问iOS OC 的 .formatterSet(@AAJSFunc(function () { 这里面的JS应该如何写 }

liqinfu avatar May 09 '24 03:05 liqinfu

5061542c383deaa5d21aed7116becaf

InnocentYang avatar May 10 '24 01:05 InnocentYang