YCharts

repository·main·Indexed 20 days ago

https://github.com/codeandtheory/ycharts

A Jetpack Compose library for Android applications used to visualize statistical data through Cartesian (XY) and Radial (Polar) charts. Supported chart types include Line, Bar, Wave, Bubble, Combined, Pie, and Donut charts. The library includes built-in accessibility support for canvas-drawn views via descriptive popups for screen readers like TalkBack.

Tokens
5.2K
Snippets
11
Records
17
Agent score
22%

What's inside YCharts

  1. Overview of YCharts

    main

    YCharts is a Jetpack Compose-based library designed for integrating various chart and graph types into Android UIs to represent statistical data. The library supports two primary categories of charts:

    1. Cartesian Charts (XY-charts):

    • Line chart
    • Bar chart
    • Wave chart
    • Bubble chart
    • Combined chart

    2. Radial Charts (Polar charts):

    • Pie chart
    • Donut chart
  2. Accessibility support in YCharts

    main

    YCharts provides built-in accessibility support for views drawn using canvas. Since canvas-drawn views do not natively support accessibility services as easily as standard Compose views, all graph components include an accessibility popup that appears when the container is tapped.

    This popup displays all values (points, bars, slices, or combined values) in a descriptive, scrollable list. This allows users to interact with the data using screen readers like TalkBack, which will read the values aloud when tapped.

    Customization: All descriptions visible in the accessibility popup can be customized to your required string format.

  3. Use the YCharts sample app for implementation guidance

    main
    The repository includes a sample app that demonstrates various chart implementations with different styling. You can study the source code of the sample app to learn how to implement, customize, and style Line, Bar, GroupedBar, Pie, and Donut charts using YCharts.
  4. Implement a Line Chart

    main

    To render a line chart, follow these steps:

    1. Create a list of Point data objects containing x and y coordinates.
    2. Build X and Y axes using AxisData.Builder(). You can configure axisStepSize, steps, backgroundColor, and labelData (a lambda providing labels for each step).
    3. Initialize LineChartData. This requires a LinePlotData object containing a list of Line objects. Each Line can be styled with components like LineStyle(), IntersectionPoint(), SelectionHighlightPoint(), ShadowUnderLine(), and SelectionHighlightPopUp().
    4. Use the LineChart Composable to render the chart.
    // 1. Data points
    val pointsData: List<Point> =
        listOf(Point(0f, 40f), Point(1f, 90f), Point(2f, 0f), Point(3f, 60f), Point(4f, 10f))
    
    // 2. Axis configuration
    val xAxisData = AxisData.Builder()
      .axisStepSize(100.dp)
      .backgroundColor(Color.Blue)
      .steps(pointsData.size - 1)
      .labelData { i -> i.toString() }
      .labelAndAxisLinePadding(15.dp)
      .build()
    
    val yAxisData = AxisData.Builder()
      .steps(steps)
      .backgroundColor(Color.Red)
      .labelAndAxisLinePadding(20.dp)
      .labelData { i -> 
          val yScale = 100 / steps
          (i * yScale).formatToSinglePrecision() 
      }.build()
    
    // 3. Chart data
    val lineChartData = LineChartData(
      linePlotData = LinePlotData(
        lines = listOf(
          Line(
            dataPoints = pointsData,
            LineStyle(),
            IntersectionPoint(),
            SelectionHighlightPoint(),
            ShadowUnderLine(),
            SelectionHighlightPopUp()
          )
        ),
      ),
      xAxisData = xAxisData,
      yAxisData = yAxisData,
      gridLines = GridLines(),
      backgroundColor = Color.White
    )
    
    // 4. Render
    LineChart(
      modifier = Modifier.fillMaxWidth().height(300.dp),
      lineChartData = lineChartData
    )
  5. Implement a Bar Chart

    main

    To render a bar chart:

    1. Prepare BarData (often using DataUtils.getBarChartData).
    2. Configure axes using AxisData.Builder(). For the X-axis, you can use axisLabelAngle and bottomPadding.
    3. Initialize BarChartData with the chart data, axes, paddingBetweenBars, and barWidth.
    4. Use the BarChart Composable.
    // 1. Data
    val barChartDataList = DataUtils.getBarChartData(barChartListSize, maxRange)
    
    // 2. Axes
    val xAxisData = AxisData.Builder()
      .axisStepSize(30.dp)
      .steps(barChartDataList.size - 1)
      .bottomPadding(40.dp)
      .axisLabelAngle(20f)
      .labelData { index -> barChartDataList[index].label }
      .build()
    
    val yAxisData = AxisData.Builder()
      .steps(yStepSize)
      .labelAndAxisLinePadding(20.dp)
      .axisOffset(20.dp)
      .labelData { index -> (index * (maxRange / yStepSize)).toString() }
      .build()
    
    // 3. Chart Data
    val barChartData = BarChartData(
      chartData = barChartDataList,
      xAxisData = xAxisData,
      yAxisData = yAxisData,
      paddingBetweenBars = 20.dp,
      barWidth = 25.dp
    )
    
    // 4. Render
    BarChart(modifier = Modifier.height(350.dp), barChartData = barChartData)
  6. Implement a Bubble Chart

    main

    Bubble charts represent three dimensions of data: X-axis, Y-axis, and bubble size.

    1. Prepare BubbleChartData. You can use DataUtils.getBubbleChartDataWithGradientStyle(pointsData) to generate data with gradient styling.
    2. Configure X and Y axes using AxisData.Builder().
    3. Initialize BubbleChartData with the plot data, axes, and gridLines.
    4. Render with the BubbleChart Composable.
    val bubbleChartData = BubbleChartData(
        DataUtils.getBubbleChartDataWithGradientStyle(pointsData),
        xAxisData = xAxisData,
        yAxisData = yAxisData,
        gridLines = GridLines()
    )
    
    BubbleChart(
        modifier = Modifier.fillMaxWidth().height(500.dp),
        bubbleChartData = bubbleChartData
    )
  7. Implement a Combined Chart

    main

    A CombinedChart allows overlaying line and bar plots in a single visualization.

    1. Prepare LinePlotData (a list of Line objects) and BarPlotData (containing groupBarList and barStyle).
    2. Configure shared X and Y axes using AxisData.Builder(). Ensure the X-axis steps accounts for the maximum size of both datasets.
    3. Initialize CombinedChartData by passing a list containing both barPlotData and linePlotData to the combinedPlotDataList parameter.
    4. Render with the CombinedChart Composable. Use the Legends component if you need to display legend information for the bars.
    // 1. Plot Data
    val linePlotData = LinePlotData(
      lines = listOf(
        Line(DataUtils.getLineChartData(listSize, 100), lineStyle = LineStyle(color = Color.Blue), ...),
        Line(DataUtils.getLineChartData(listSize, 100), lineStyle = LineStyle(color = Color.Black), ...)
      )
    )
    val barPlotData = BarPlotData(
      groupBarList = DataUtils.getGroupBarChartData(listSize, maxValueRange, barSize),
      barStyle = BarStyle(barWidth = 35.dp),
      barColorPaletteList = colorPaletteList
    )
    
    // 2. Axes
    val xAxisData = AxisData.Builder()
      .axisStepSize(30.dp)
      .steps(maxOf(barChartData.size - 1, lineChartData.size - 1))
      .bottomPadding(40.dp)
      .labelData { index -> index.toString() }
      .build()
    
    val yAxisData = AxisData.Builder()
      .steps(yStepSize)
      .labelAndAxisLinePadding(20.dp)
      .axisOffset(20.dp)
      .labelData { index -> (index * (maxRange / yStepSize)).toString() }
      .build()
    
    // 3. Chart Data
    val combinedChartData = CombinedChartData(
      combinedPlotDataList = listOf(barPlotData, linePlotData),
      xAxisData = xAxisData,
      yAxisData = yAxisData
    )
    
    // 4. Render
    CombinedChart(modifier = Modifier.height(400.dp), combinedCharthData = combinedChartData)
  8. Implement a Pie or Donut Chart

    main

    Both Pie and Donut charts use PieChartData.Slice to define segments (label, value, and color).

    For Pie Charts:

    • Use PieChartConfig to control percentVisible, isAnimationEnable, showSliceLabels, and animationDuration.
    • Render with the PieChart Composable.

    For Donut Charts:

    • Use PieChartConfig with additional properties like strokeWidth (to create the hole), percentageFontSize, and percentColor.
    • Render with the DonutPieChart Composable.
    // Pie Chart Example
    val pieChartData = PieChartData(
      slices = listOf(
        PieChartData.Slice("SciFi", 65f, Color(0xFF333333)),
        PieChartData.Slice("Comedy", 35f, Color(0xFF666a86))
      )
    )
    val pieChartConfig = PieChartConfig(
      percentVisible = true,
      isAnimationEnable = true,
      showSliceLabels = false,
      animationDuration = 1500
    )
    PieChart(modifier = Modifier.size(400.dp), pieChartData, pieChartConfig)
    
    // Donut Chart Example
    val donutChartData = PieChartData(
      slices = listOf(
        PieChartData.Slice("HP", 15f, Color(0xFF5F0A87)),
        PieChartData.Slice("Dell", 30f, Color(0xFF20BF55))
      )
    )
    val donutChartConfig = PieChartConfig(
      percentVisible = true,
      percentageFontSize = 42.sp,
      strokeWidth = 120f,
      percentColor = Color.Black,
      activeSliceAlpha = .9f,
      isAnimationEnable = true
    )
    DonutPieChart(modifier = Modifier.fillMaxWidth().height(500.dp), donutChartData, donutChartConfig)
  9. Implement a Grouped or Stacked Bar Chart

    main

    To render grouped or stacked bar charts:

    1. Create BarPlotData using BarData combinations (often via DataUtils.getGroupBarChartData) and a barColorPaletteList.
    2. Configure X and Y axes using AxisData.Builder().
    3. Initialize GroupBarChartData with the barPlotData and axes.
    4. Use the GroupBarChart Composable.
    // 1. Plot Data
    val groupBarPlotData = BarPlotData(
      groupBarList = DataUtils.getGroupBarChartData(
        barChartListSize,
        maxRange,
        eachGroupBarSize
      ),
      barColorPaletteList = getColorPaletteList(barSize)
    )
    
    // 2. Axes
    val xAxisData = AxisData.Builder()
      .axisStepSize(30.dp)
      .steps(groupBarData.size - 1)
      .bottomPadding(40.dp)
      .labelData { index -> groupBarData[index].label }
      .build()
    
    val yAxisData = AxisData.Builder()
      .steps(yStepSize)
      .labelAndAxisLinePadding(20.dp)
      .axisOffset(20.dp)
      .labelData { index -> (index * (maxRange / yStepSize)).toString() }
      .build()
    
    // 3. Chart Data
    val groupBarChartData = GroupBarChartData(
      barPlotData = groupBarPlotData,
      xAxisData = xAxisData,
      yAxisData = yAxisData
    )
    
    // 4. Render
    GroupBarChart(modifier = Modifier.height(300.dp), groupBarChartData = groupBarChartData)
  10. Implement a Wave Chart

    main

    Wave charts (waveform charts) visualize amplitude over time.

    1. Create WavePlotData containing a list of Wave objects. Each Wave requires dataPoints, waveStyle, and a waveFillColor (using WaveFillColor with topColor and bottomColor) to create the shaded area under the line.
    2. Configure X and Y axes using AxisData.Builder().
    3. Initialize WaveChartData with the wavePlotData and axes.
    4. Render with the WaveChart Composable.
    val waveChartData = WaveChartData(
        wavePlotData = WavePlotData(
            lines = listOf(
                Wave(
                    dataPoints = pointsData,
                    waveStyle = LineStyle(color = Color.Black),
                    selectionHighlightPoint = SelectionHighlightPoint(),
                    shadowUnderLine = ShadowUnderLine(),
                    selectionHighlightPopUp = SelectionHighlightPopUp(),
                    waveFillColor = WaveFillColor(topColor = Color.Green, bottomColor = Color.Red),
                )
            )
        ),
        xAxisData = xAxisData,
        yAxisData = yAxisData,
        gridLines = GridLines()
    )
    
    WaveChart(
        modifier = Modifier.fillMaxWidth().height(300.dp),
        waveChartData = waveChartData
    )