Pantrix UI

Chart

Bir dizi yer boyunca değerlerin grafiği: her serinin her yerdeki değerinden geçen çizgiler ve dolgulu alanlar, o değerlere çıkan çubuklar, altlarında yerlerin, yanlarında değerlerin ekseniyle.

import com.pantrixui.components.chart.cartesian.PantrixChart

Örnekler

İki seri

En yalın hâli: aynı satırlardan okunan iki seri. Her LineSeries anahtarını, adını, satırları ve o satırlardan yeri (x) ve değeri (y) okuyan iki fonksiyonu alır.

Gerisi kendiliğinden gelir: altta ayların, yanda yuvarlak değerlerin ekseni, değerlerde kılavuz çizgileri, iki seri olduğu için bir lejant ve gösterilen yerin tooltip'i. İmleci grafiğin üstünde gezdir ya da parmağını grafik boyunca sürükle; lejanttaki bir ada basıp seriyi çıkar.

val series: List<ChartSeries> = listOf(
    LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }),
    LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile })
)
PantrixChart(
    series = series,
    description = "Aylara göre masaüstü ve mobil ziyaretler",
    tagKey = "two_series"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Kendi satırlarından seriler

Seriler aynı listeden okunmak zorunda değildir. Burada ölçülen değerler bir listeden, tahmin başka bir listeden gelir ve ikisi yalnızca Eylül'de buluşur. Grafik, serilerinden herhangi birinin adını verdiği her yeri ilk verildiği sırayla dizer; bir serinin değer vermediği yerde çizgisi yoktur.

Tahmin kesik çizgiyle çizildi (dash) ve noktaları işaretlendi (marker): ölçülenden renginden fazlasıyla ayrılır.

PantrixChart(
    series = listOf(
        LineSeries("measured", "Ölçülen", MEASURED, x = { it.first }, y = { it.second }),
        LineSeries(
            key = "forecast",
            name = "Tahmin",
            data = FORECAST,
            x = { it.first },
            y = { it.second },
            marker = ChartMarker.Circle,
            dash = ChartDash.Dashed
        )
    ),
    description = "Yılın ölçülen ve tahmin edilen satışları",
    tagKey = "own_rows"
)

private val MEASURED = listOf(
    "Oca" to 42,
    "Şub" to 48,
    "Mar" to 45,
    "Nis" to 57,
    "May" to 61,
    "Haz" to 58,
    "Tem" to 66,
    "Ağu" to 71,
    "Eyl" to 69
)

private val FORECAST = listOf("Eyl" to 69, "Eki" to 74, "Kas" to 80, "Ara" to 88)

Çubukların üstünde bir çizgi

Türler aynı grafikte karışır: her ayın siparişi bir çubuk (BarSeries), üç aylık ortalaması bir çizgi (LineSeries). Çubuğu olan grafikte her yerin bir bandı vardır ve çizgi bantların ortasından geçer. Sonra verilen seri öncekinin üstüne çizilir: çizgiyi çubuklardan sonra ver.

Tooltip yerin iki değerini birlikte yazar; lejantta çubuk bir yama, çizgi işaretli kısa bir çizgidir.

PantrixChart(
    series = listOf(
        BarSeries("orders", "Sipariş", HALF_YEAR, x = { it.first }, y = { it.second }),
        LineSeries(
            key = "average",
            name = "Üç aylık ortalama",
            data = HALF_YEAR,
            x = { it.first },
            y = { it.third },
            marker = ChartMarker.Circle
        )
    ),
    description = "Aylara göre sipariş ve üç aylık ortalaması",
    tagKey = "bars_and_line"
)

private val HALF_YEAR = listOf(
    Triple("Oca", 310, 298),
    Triple("Şub", 285, 302),
    Triple("Mar", 342, 312),
    Triple("Nis", 398, 342),
    Triple("May", 371, 370),
    Triple("Haz", 426, 398)
)

İki eksen

İki türden değer tek grafikte: her ayın siparişi bir sayı, iade oranı bir pay. Aynı eksende çizilseler oran sıfıra yapışırdı. axis = ChartYAxis.Second diyen seri ikinci eksenle okunur; o eksen grafiğin öbür yanında yazılır (secondYAxis) ve işaretleri ilk eksenin kılavuz çizgilerinin üstünde durur.

Her değer kendi ekseninin yazımıyla yazılır: tooltip'te sipariş bir sayı, oran bir yüzdedir; ekran okuyucu da öyle söyler. secondYAxis = null ekseni çizmez, seri yine kendi ölçeğiyle durur.

val strings = PantrixTheme.strings
PantrixChart(
    series = listOf(
        BarSeries("orders", "Sipariş", RETURNS, x = { it.first }, y = { it.second }),
        LineSeries(
            key = "returned",
            name = "İade oranı",
            data = RETURNS,
            x = { it.first },
            y = { it.third },
            marker = ChartMarker.Circle,
            axis = ChartYAxis.Second
        )
    ),
    yAxis = ChartAxis(title = "Sipariş"),
    secondYAxis = ChartAxis(title = "İade oranı", format = { strings.percent(it, fractionDigits = 1) }),
    description = "Aylara göre sipariş sayısı ve iade oranı",
    tagKey = "two_axes"
)

private val RETURNS = listOf(
    Triple("Oca", 310, 0.042),
    Triple("Şub", 285, 0.051),
    Triple("Mar", 342, 0.038),
    Triple("Nis", 398, 0.036),
    Triple("May", 371, 0.047),
    Triple("Haz", 426, 0.031)
)

Hedef çizgisi ve bantlar

Seriler bir şeye göre okunur: bir hedefe, olağan bir aralığa, bir kampanyanın aylarına. marks, grafiğin serilerin yanında çizdiği işaretleri alır.

Çizgiler serilerin üstüne, bantlar arkasına çizilir; inFront bunu değiştirir. Bir değerdeki işaret eksenini kendisini gösterecek kadar uzatır: buradaki hedef, ziyaretlerin hiçbirinin ulaşmadığı 450'de. Etiketi olan işaret, ekran okuyucuya grafiğin açıklamasından sonra söylenir.

İşaretin yazısı serilerin üstünde, tooltip renginde bir zeminin üstüne yazılır: bir çizginin ya da çubuğun üstünde de okunur. labelPosition yazının işaret boyunca yerini söyler: çizginin yazısı kendiliğinden sonundadır, bandınki başında. Burada "Olağan" bandın sonunda, "Yeni site" çizginin dibinde.

Çizgilerin kalınlığı ve bantların tonu stilin: burada iki dp ve rengin beşte biri (markLineWidth, markBandAlpha).

val marks: List<ChartMark> = listOf(
    ChartReferenceBand(y1 = 200, y2 = 300, label = "Olağan", labelPosition = ChartMarkLabelPosition.End),
    ChartReferenceBand(x1 = "Haz", x2 = "Ağu", label = "Yaz kampanyası"),
    ChartReferenceLine(
        x = "Eki",
        label = "Yeni site",
        dash = ChartDash.Dotted,
        labelPosition = ChartMarkLabelPosition.Start
    ),
    ChartReferenceLine(y = 450, label = "Hedef", color = PantrixTheme.colors.success.color)
)
PantrixChart(
    series = listOf(LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop })),
    marks = marks,
    description = "Aylara göre masaüstü ziyaretleri",
    style = ChartStyle(markLineWidth = 2.dp, markBandAlpha = 0.2f),
    tagKey = "marks"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Eksenler

Bir eksen ChartAxis ile tarif edilir. Burada değerlerin ekseninin bir başlığı var ve değerlerini temanın diliyle kısa yazıyor (format); yerlerin ekseninin de bir başlığı var.

Çizgilerden oluşan bir grafiğin ekseni sıfırdan başlamaz: okunan, çizginin inişi ve çıkışıdır. startFromZero ekseni sıfıra indirir; anahtarı çevir, aynı değerlerin iki eksende nasıl göründüğüne bak. Dolgulu bir serisi olan grafik kendiliğinden sıfırdan başlar: bir alanın yüksekliği miktar olarak okunur.

var fromZero by remember { mutableStateOf(false) }
val strings = PantrixTheme.strings
PantrixSwitchButton(
    checked = fromZero,
    onCheckedChange = { fromZero = it },
    label = "Değerlerin ekseni sıfırdan başlasın",
    tagKey = "chart_from_zero"
)
PantrixChart(
    series = listOf(LineSeries("revenue", "Gelir", REVENUE, x = { it.first }, y = { it.second })),
    xAxis = ChartAxis(title = "2026"),
    yAxis = ChartAxis(title = "Gelir (TL)", startFromZero = fromZero, format = { strings.compact(it) }),
    description = "2026 yılının aylık geliri",
    tagKey = "axes"
)

private val REVENUE = listOf(
    "Oca" to 1_240_000,
    "Şub" to 1_310_000,
    "Mar" to 1_285_000,
    "Nis" to 1_420_000,
    "May" to 1_390_000,
    "Haz" to 1_510_000,
    "Tem" to 1_640_000,
    "Ağu" to 1_585_000,
    "Eyl" to 1_720_000,
    "Eki" to 1_790_000,
    "Kas" to 1_865_000,
    "Ara" to 1_950_000
)

Lejantın yeri

Lejant grafiğin dört yanından birinde durur. Üstte ve altta kayıtlar bir satıra dizilir ve sığmazsa alt satıra geçer; yanlarda alt alta dururlar. alignment lejantın kendi yanındaki yerini söyler.

Her kayıt serisinin bir anahtarıdır: basınca seri grafikten çıkar ve eksen kalanlara göre yeniden kurulur, kayıt soluk çizilerek yerinde kalır. Çıkan seri rengini korur; ötekilerin rengi de değişmez.

var place by remember { mutableStateOf(ChartLegendPosition.Top) }
PantrixSegmentButton(
    options = LEGEND_PLACES,
    selected = place,
    onSelect = { place = it },
    fullWidth = false,
    tagKey = "legend_place"
)
PantrixChart(
    series = listOf(
        LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }),
        LineSeries("tablet", "Tablet", VISITS, x = { it.month }, y = { it.tablet })
    ),
    legend = ChartLegend(position = place),
    description = "Aylara göre üç cihazın ziyaretleri",
    tagKey = "legend"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

private val LEGEND_PLACES = listOf(
    SegmentOption(value = ChartLegendPosition.Top, label = "Üstte", tagKey = "top"),
    SegmentOption(value = ChartLegendPosition.Bottom, label = "Altta", tagKey = "bottom"),
    SegmentOption(value = ChartLegendPosition.Start, label = "Başta", tagKey = "start"),
    SegmentOption(value = ChartLegendPosition.End, label = "Sonda", tagKey = "end")
)

Tooltip: birlikte ya da tek tek

Tooltip iki türlü okur. shared açıkken gösterilen yerdeki bütün serilerin değerleri birlikte gelir: grafik bir yerde böyle okunur ve parmağın nişan alması gerekmez. Kapalıyken tooltip yalnızca imlece en yakın serinin değeridir; klavyede yukarı ve aşağı oklar bir serinin değerinden ötekine geçer.

crosshair gösterilen yerden geçen çizgidir. Tooltip'in yerine null verilirse kart hiç gösterilmez; yer yine işaretlenir ve grafiğin durumuna yazılır.

var shared by remember { mutableStateOf(true) }
var crosshair by remember { mutableStateOf(true) }
Row(horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.lg)) {
    PantrixSwitchButton(
        checked = shared,
        onCheckedChange = { shared = it },
        label = "Seriler birlikte",
        tagKey = "tooltip_shared"
    )
    PantrixSwitchButton(
        checked = crosshair,
        onCheckedChange = { crosshair = it },
        label = "Yerden geçen çizgi",
        tagKey = "tooltip_crosshair"
    )
}
PantrixChart(
    series = listOf(
        LineSeries(
            key = "desktop",
            name = "Masaüstü",
            data = VISITS,
            x = { it.month },
            y = { it.desktop },
            marker = ChartMarker.Circle
        ),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }, marker = ChartMarker.Square)
    ),
    tooltip = ChartTooltip(shared = shared, crosshair = crosshair),
    description = "Aylara göre masaüstü ve mobil ziyaretler",
    tagKey = "tooltip"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Kendi tooltip'in

Kartın içi ekranın olabilir: content, gösterilen yerdeki değerleri alır ve onlardan istediğini çizer. Burada iki serinin değeri toplanıyor ve mobilin payı yüzde olarak yazılıyor; ikisi de temanın sayı biçimiyle.

Kartın kendisi, dolgusu, kenarı ve yeri grafiğin kalır.

val strings = PantrixTheme.strings
PantrixChart(
    series = listOf(
        LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile })
    ),
    tooltip = ChartTooltip(
        content = { points ->
            val total = points.sumOf { it.value }
            val mobile = points.firstOrNull { it.series == "mobile" }?.value ?: 0.0
            PantrixText(text = points.first().label, style = PantrixTheme.typography.footnote)
            PantrixText(text = "${strings.decimal(total)} ziyaret", style = PantrixTheme.typography.label)
            PantrixText(
                text = "Mobilin payı ${strings.percent(mobile / total)}",
                style = PantrixTheme.typography.caption,
                color = PantrixTheme.colors.textSecondary
            )
        }
    ),
    description = "Aylara göre toplam ziyaret ve mobilin payı",
    tagKey = "own_tooltip"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Durumu paylaşan iki grafik

ChartState okuyucunun gizlediği serileri ve gösterdiği yeri tutar. Ekran onu rememberChartState ile kurup grafiğe verirse ikisini de okur: alttaki satır gösterilen ayı durumdan yazıyor.

Aynı durumu alan iki grafik birlikte davranır. Üsttekinde bir ayı göster: alttaki de aynı ayı işaretler. Birinin lejantında gizlenen seri ötekinde de gizlenir, çünkü iki grafiğin serileri aynı anahtarları taşır.

Durum ekrandan da yazılır: düğme state.selection'a ChartSelection(x = "Mar") verir ve iki grafik de Mart'ı gösterir.

val state = rememberChartState()
PantrixChart(
    series = listOf(
        LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile })
    ),
    modifier = Modifier.height(200.dp),
    state = state,
    description = "Aylara göre ziyaretler",
    tagKey = "shared_visits"
)
PantrixChart(
    series = listOf(
        LineSeries(
            key = "desktop",
            name = "Masaüstü",
            data = VISITS,
            x = { it.month },
            y = { it.desktop * 3 / 100 },
            fill = ChartFill.Solid
        ),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile * 2 / 100 }, fill = ChartFill.Solid)
    ),
    modifier = Modifier.height(160.dp),
    legend = null,
    state = state,
    description = "Aylara göre siparişler",
    tagKey = "shared_orders"
)
PantrixText(
    text = state.selection?.let { "Gösterilen ay: ${it.x}" } ?: "Bir ay gösterilmiyor",
    style = PantrixTheme.typography.footnote,
    color = PantrixTheme.colors.textSecondary
)
PantrixButton(
    text = "Mart'ı göster",
    onClick = { state.selection = ChartSelection(x = "Mar") },
    variant = ButtonVariant.Outline,
    size = ButtonSize.Small,
    tagKey = "show_march"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Bir değere basmak

onSelect, basılan değeri verir: serisini, yerini, değerini ve grafiğin onu nasıl yazdığını. Bir değere bas ya da klavyeyle bir yere gidip Enter'a bas: kartın altındaki satır basılanı yazar.

Tooltip burada serileri ayırıyor (shared = false), böylece basılan, gösterilen serinin değeridir. Seriler birlikte gösterilirken basılan, basışa en yakın serinin değeridir.

val report = LocalExampleEvents.current
PantrixChart(
    series = listOf(
        LineSeries(
            key = "desktop",
            name = "Masaüstü",
            data = VISITS,
            x = { it.month },
            y = { it.desktop },
            marker = ChartMarker.Circle
        ),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }, marker = ChartMarker.Circle)
    ),
    tooltip = ChartTooltip(shared = false),
    description = "Aylara göre masaüstü ve mobil ziyaretler",
    onSelect = { point: ChartPoint -> report("${point.label}, ${point.name}: ${point.text}") },
    tagKey = "pressed"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Eksik değerler

Değeri olmayan bir satırda çizginin ne yapacağını missing söyler. Bu sensör Nisan'da ve Ağustos'ta ölçüm vermedi. Gap çizgiyi orada keser: eksik olanın eksik olduğu görülür. Connect bir önceki noktadan bir sonrakine sürer. Zero satırı sıfır sayar.

Öncesinde ve sonrasında nokta olmayan bir değer, işareti verilmemiş olsa da bir noktayla çizilir: tek bir noktadan geçen çizginin gösterecek bir şeyi yoktur.

var missing by remember { mutableStateOf(ChartMissing.Gap) }
PantrixSegmentButton(
    options = MISSING_KINDS,
    selected = missing,
    onSelect = { missing = it },
    fullWidth = false,
    tagKey = "missing"
)
PantrixChart(
    series = listOf(
        LineSeries(
            key = "sensor",
            name = "Sıcaklık",
            data = SENSOR,
            x = { it.first },
            y = { it.second },
            marker = ChartMarker.Circle,
            missing = missing
        )
    ),
    description = "Sensörün aylık ortalama sıcaklığı",
    tagKey = "missing"
)

private val SENSOR: List<Pair<String, Double?>> = listOf(
    "Oca" to 4.5,
    "Şub" to 6.1,
    "Mar" to 9.8,
    "Nis" to null,
    "May" to 18.2,
    "Haz" to 23.4,
    "Tem" to 26.9,
    "Ağu" to null,
    "Eyl" to 21.3,
    "Eki" to 15.6,
    "Kas" to 10.2,
    "Ara" to 5.9
)

private val MISSING_KINDS = listOf(
    SegmentOption(value = ChartMissing.Gap, label = "Boşluk", tagKey = "gap"),
    SegmentOption(value = ChartMissing.Connect, label = "Birleştir", tagKey = "connect"),
    SegmentOption(value = ChartMissing.Zero, label = "Sıfır say", tagKey = "zero")
)

Boş grafik

Değeri olmayan grafik, çizim alanının ortasına temanın sözünü ("Veri yok") yazar. empty o sözün yerine kendi içeriğini koyar: burada ne olduğunu söyleyen bir satır ve verileri getiren bir düğme. Ekran okuyucuya da "Veri yok" söylenmez; içerik kendini söyler. Değerler gelince içerik gider.

var loaded by remember { mutableStateOf(false) }
PantrixChart(
    series = listOf(
        BarSeries(
            key = "desktop",
            name = "Masaüstü",
            data = if (loaded) VISITS.take(6) else emptyList(),
            x = { it.month },
            y = { it.desktop }
        )
    ),
    empty = {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            PantrixText(
                text = "Bu dönemde ziyaret yok",
                style = PantrixTheme.typography.footnote,
                color = PantrixTheme.colors.textSecondary
            )
            PantrixButton(
                text = "Örnek verileri yükle",
                onClick = { loaded = true },
                variant = ButtonVariant.Outline,
                size = ButtonSize.Small,
                tagKey = "no_data_load"
            )
        }
    },
    description = "Masaüstü ziyaretleri",
    tagKey = "no_data"
)
if (loaded) {
    PantrixButton(
        text = "Verileri kaldır",
        onClick = { loaded = false },
        variant = ButtonVariant.Soft,
        size = ButtonSize.Small,
        tagKey = "no_data_clear"
    )
}

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Çok yer

Doksan gün. Günlerin adları yan yana sığmaz; grafik onları bir kuralla atlar, her altıncı ya da her onuncu, ve pencere daralıp genişledikçe aynı kuralı yeniden kurar. Değerlerin hepsi yerinde durur: imleçle ya da parmakla her günün değeri gösterilir.

Çizgi yumuşak geçiyor (ChartCurve.Smooth): noktalarının arasında kalır, değerlerin sahip olmadığı bir tepe ya da çukur göstermez.

PantrixChart(
    series = listOf(
        LineSeries(
            key = "sessions",
            name = "Oturum",
            data = DAILY,
            x = { it.first },
            y = { it.second },
            curve = ChartCurve.Smooth,
            fill = ChartFill.Gradient
        )
    ),
    xAxis = ChartAxis(label = { "$it. gün" }),
    description = "Son doksan günün günlük oturum sayısı",
    tagKey = "many"
)

private val DAILY: List<Pair<Int, Int>> = List(DAYS) { day ->
    (day + 1) to BASE + day * GROWTH + WEEK[day % WEEK.size]
}

private const val DAYS = 90

private const val BASE = 320

private const val GROWTH = 3

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

Zaman ekseni

Yerleri an olan bir grafik: bir sunucunun bir gün içinde düzensiz aralıklarla ölçülen yanıt süresi. Satırların x'i bir kotlin.time.Instant verdiği için grafik onları zamana göre dizer: sık ölçülen saatler sıkışık, seyrek ölçülenler aralıklı durur, ve eksen ölçümlere değil yuvarlak saatlere işaretlenir.

Çizgi ve bant da anlarda durur, bir satırın anı olsun olmasın: dağıtımın yapıldığı an ve bakım penceresi. Ölçümlerden sonraki bir işaret, ertesi sabahki bakım, ekseni kendisine kadar uzatır; bir değer eksenini uzatan hedef çizgisi gibi. Saatler UTC'ye göre okunuyor (ChartTime'ın offset'i); verilmezse cihazın saatiyle okunur.

Anlar da uygulamanın kendi yazdığı gibi yazılıyor (ChartTime'ın format'ı): eksenin saat başı işaretleri "saat 14" diye, bir ölçümün anı ipucunda dakikasıyla. format'a verilen ChartTimeUnit neyin yazıldığını söyler: eksenin işaretleri aralarındaki birimle, bir yer ise yerlerin ayrıldığı en ince birimle sorulur.

PantrixChart(
    series = listOf(
        LineSeries(
            key = "response",
            name = "Yanıt süresi (ms)",
            data = RESPONSE_TIMES,
            x = { momentAt(it.first) },
            y = { it.second },
            marker = ChartMarker.Circle
        )
    ),
    xAxis = ChartAxis(time = ChartTime(format = ::clockOfUtc, offset = { Duration.ZERO })),
    marks = listOf(
        ChartReferenceBand(x1 = momentAt("02:00"), x2 = momentAt("04:00"), label = "Bakım"),
        ChartReferenceLine(x = momentAt("13:20"), label = "Dağıtım"),
        ChartReferenceLine(x = Instant.parse("2026-10-14T03:00:00Z"), label = "Sonraki bakım")
    ),
    description = "Bir sunucunun bir gün içindeki yanıt süresi, ölçüldüğü anlarda",
    tagKey = "time"
)

private val RESPONSE_TIMES = listOf(
    "00:10" to 182,
    "01:40" to 176,
    "02:30" to 240,
    "03:30" to 262,
    "05:00" to 190,
    "08:00" to 205,
    "09:00" to 231,
    "09:30" to 248,
    "10:00" to 236,
    "10:30" to 259,
    "11:00" to 244,
    "12:00" to 228,
    "13:00" to 252,
    "13:30" to 171,
    "14:00" to 158,
    "15:00" to 149,
    "17:30" to 144,
    "21:00" to 139,
    "23:30" to 133
)

private fun momentAt(time: String): Instant = Instant.parse("2026-10-13T$time:00Z")

private fun clockOfUtc(moment: Instant, unit: ChartTimeUnit): String {
    val minute = moment.epochSeconds.floorDiv(SECONDS_IN_A_MINUTE).mod(MINUTES_IN_A_DAY)
    val hour = (minute / MINUTES_IN_AN_HOUR).toString().padStart(2, '0')
    val ofTheHour = (minute % MINUTES_IN_AN_HOUR).toString().padStart(2, '0')
    return if (unit >= ChartTimeUnit.Hour) "saat $hour" else "$hour:$ofTheHour"
}

private const val SECONDS_IN_A_MINUTE = 60

private const val MINUTES_IN_AN_HOUR = 60

private const val MINUTES_IN_A_DAY = 24 * 60

Yakınlaştırma

zoom = ChartZoom() grafiğe yakından bakmayı açar: doksan günün bir kısmı çizim alanının tamamını doldurur ve geri kalanı boyunca kaydırılır. Çizim alanının üstündeki düğmeler sırayla geri ve ileri kaydırır, uzaklaştırır, yakınlaştırır ve bütün günleri geri getirir; yapacak işi olmayan düğme soluk durur.

Fareyle Ctrl (Mac'te Command) basılıyken tekerleği çevir: imlecin altındaki gün yerinde kalır. Yakınlaşınca grafiği sürükle. Dokunmatik ekranda iki parmağını aç, sonra birlikte kaydır; tek parmak yine bir günü gösterir. Klavyede grafiğe gelip artı, eksi ve sıfıra bas: yakınlaşınca daha çok günün adı yazılacak yer bulur.

PantrixChart(
    series = listOf(
        LineSeries("sessions", "Oturum", DAILY, x = { it.first }, y = { it.second }, marker = ChartMarker.Circle)
    ),
    xAxis = ChartAxis(label = { "$it. gün" }),
    zoom = ChartZoom(),
    description = "Son doksan günün günlük oturum sayısı, yakından bakılabilir",
    tagKey = "zoomed"
)

private val DAILY: List<Pair<Int, Int>> = List(DAYS) { day ->
    (day + 1) to BASE + day * GROWTH + WEEK[day % WEEK.size]
}

private const val DAYS = 90

private const val BASE = 320

private const val GROWTH = 3

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

Gezgin şeridi

Gezgin şeridi (navigator = true) çizim alanının altında durur ve bütün yerleri taşır: seriler üstünde küçük çizilir, görünen kısım işaretlidir. İşaretli kısmı ortasından tutup sürükle; iki ucundaki tutamaklarla genişlet ya da daralt; şeridin başka bir yerine bas, kısım oraya gelsin. Klavyede şeride gelince oklar kısmı kaydırır, Home ve End başa ve sona götürür.

Yerler burada an: altmış günün saatlik ölçümleri. Yakınlaştıkça eksenin işaretleri aylardan günlere, günlerden saatlere iner. Ekran okuyucu şeridi "görünen aralık" olarak, görünen ilk ve son anla söyler.

PantrixChart(
    series = listOf(
        LineSeries(
            key = "load",
            name = "Yük (%)",
            data = HOURLY,
            x = { hourAt(it.first) },
            y = { it.second },
            fill = ChartFill.Gradient
        )
    ),
    xAxis = ChartAxis(time = ChartTime(offset = { Duration.ZERO })),
    yAxis = ChartAxis(min = 0.0, max = 100.0),
    zoom = ChartZoom(navigator = true),
    description = "Bir sunucunun altmış gün boyunca saatlik yükü",
    tagKey = "navigated"
)

private val HOURLY: List<Pair<Int, Int>> = List(HOURS) { hour ->
    val weekend = (hour / A_DAY) % WEEK.size >= WEEK.size - 2
    hour to (if (weekend) 36 else 52) + (wave(hour, A_DAY) * 26).toInt() + hour * 7 % 9
}

private const val HOURS = 24 * 60

private const val A_DAY = 24

private fun hourAt(hour: Int): Instant = Instant.parse("2026-08-15T00:00:00Z") + hour.hours

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

private fun wave(
    step: Int,
    period: Int
): Double = sin(step * 2 * PI / period)

Birlikte yakınlaşan iki grafik

Aynı ChartState'i alan iki grafik birlikte yakınlaşır: görünen kısım durumda tutulur ve ikisi de onu gösterir. Üstteki grafiği yakınlaştır ya da alttakinin gezgin şeridini sürükle: öteki de aynı günleri gösterir. Bir günü göstermek de ortaktır.

Düğmeler üstteki grafikte, şerit alttakinde: ChartZoom(buttons = false, navigator = true). İki grafiğin yerleri aynı olduğu için aynı kısım aynı günlerdir.

val state = rememberChartState()
PantrixChart(
    series = listOf(LineSeries("sessions", "Oturum", DAILY, x = { it.first }, y = { it.second })),
    modifier = Modifier.height(220.dp),
    xAxis = ChartAxis(label = { "$it. gün" }),
    zoom = ChartZoom(),
    state = state,
    description = "Son doksan günün günlük oturum sayısı",
    tagKey = "together_sessions"
)
PantrixChart(
    series = listOf(
        LineSeries(
            key = "conversion",
            name = "Dönüşüm (%)",
            data = CONVERSION,
            x = { it.first },
            y = { it.second },
            fill = ChartFill.Solid
        )
    ),
    modifier = Modifier.height(220.dp),
    xAxis = ChartAxis(label = { "$it. gün" }),
    zoom = ChartZoom(buttons = false, navigator = true),
    state = state,
    description = "Son doksan günün günlük dönüşüm oranı",
    tagKey = "together_conversion"
)

private val DAILY: List<Pair<Int, Int>> = List(DAYS) { day ->
    (day + 1) to BASE + day * GROWTH + WEEK[day % WEEK.size]
}

private val CONVERSION: List<Pair<Int, Double>> = List(DAYS) { day ->
    (day + 1) to 3.2 - WEEK[day % WEEK.size] / 100.0 + day / 120.0
}

private const val DAYS = 90

private const val BASE = 320

private const val GROWTH = 3

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

Ekranın kendi düğmeleri

Görünen kısmı ekran da söyler: ChartState.zoomTo, yerlerin başından sonuna sıfırdan bire iki pay alır, resetZoom hepsini geri getirir. Buradaki üç seçenek grafiğin kendi düğmelerinin yerine geçiyor (ChartZoom(buttons = false)); fare, parmak ve klavye yine çalışır.

Durum okunur da: alttaki satır state.window'u yazıyor. Görünen kısım, gizlenen serilerle birlikte ekran döndürülünce de korunur (rememberChartState).

val state = rememberChartState()
var span by remember { mutableStateOf(DAYS) }
PantrixSegmentButton(
    options = SPANS,
    selected = span,
    onSelect = { days ->
        span = days
        // Günler grafiğin başından sonuna eşit aralıklarla durur: son `days` gün, sondan o kadar geride başlar.
        if (days == DAYS) state.resetZoom() else state.zoomTo(1f - (days - 1f) / (DAYS - 1f), 1f)
    },
    fullWidth = false,
    tagKey = "zoom_span"
)
PantrixChart(
    series = listOf(
        LineSeries("sessions", "Oturum", DAILY, x = { it.first }, y = { it.second }, marker = ChartMarker.Circle)
    ),
    xAxis = ChartAxis(label = { "$it. gün" }),
    zoom = ChartZoom(buttons = false),
    state = state,
    description = "Son doksan günün günlük oturum sayısı",
    tagKey = "zoom_from_screen"
)
val from = (state.window.start * PERCENT).toInt()
val to = (state.window.endInclusive * PERCENT).toInt()
PantrixText(
    text = if (state.zoomed) "Görünen kısım: %$from – %$to" else "Bütün günler görünüyor",
    style = PantrixTheme.typography.footnote,
    color = PantrixTheme.colors.textSecondary
)

private val DAILY: List<Pair<Int, Int>> = List(DAYS) { day ->
    (day + 1) to BASE + day * GROWTH + WEEK[day % WEEK.size]
}

private const val DAYS = 90

private val SPANS = listOf(
    SegmentOption(value = DAYS, label = "Tümü", tagKey = "all"),
    SegmentOption(value = 30, label = "Son 30 gün", tagKey = "month"),
    SegmentOption(value = 7, label = "Son 7 gün", tagKey = "week")
)

private const val BASE = 320

private const val GROWTH = 3

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

private const val PERCENT = 100

On binlerce nokta

Elli bin nokta: bir sensörün otuz beş gün boyunca dakikada bir ölçtüğü değer. Çizim alanı birkaç yüz piksel genişliğinde olduğu için çizgi her piksel kolonunun ilk, en yüksek, en düşük ve son noktasından geçer: ekrandaki çizgi bütün noktalardan geçenle aynı pikselleri boyar, tepeler ve çukurlar kaybolmaz.

Yakınlaştır: yalnızca görünen dakikalar çizilir, ve piksel başına iki noktadan aza inince her nokta kendisi çizilir. closest grafiğe en çok ne kadar yakından bakılacağını söyler: burada bin kat, yani elli dakika.

"LTTB ile seyrelt" (ChartDecimation(algorithm = DecimationAlgorithm.Lttb)) çizgiyi piksel başına bir noktadan geçirir ve şeklini korur; tutulan iki nokta arasında kalan bir dakikalık titreme çizilmez, çizgi daha yumuşak görünür. "Eksen görünen kısma otursun" (ChartZoom(fitValues = true)) yakınlaşınca değer eksenini yalnızca görünen dakikalara göre kurar: birkaç saatin bir iki derecelik değişimi çizim alanını doldurur. Kapalıyken eksen bütün günlere göre kalır ve kaydırırken oynamaz.

var lttb by remember { mutableStateOf(false) }
var fit by remember { mutableStateOf(false) }
PantrixSwitchButton(
    checked = lttb,
    onCheckedChange = { lttb = it },
    label = "LTTB ile seyrelt",
    tagKey = "many_points_lttb"
)
PantrixSwitchButton(
    checked = fit,
    onCheckedChange = { fit = it },
    label = "Eksen görünen kısma otursun",
    tagKey = "many_points_fit"
)
PantrixChart(
    series = listOf(
        LineSeries("temperature", "Sıcaklık (°C)", MINUTES, x = { minuteAt(it.first) }, y = { it.second })
    ),
    xAxis = ChartAxis(time = ChartTime(offset = { Duration.ZERO })),
    zoom = ChartZoom(closest = 1000f, navigator = true, fitValues = fit),
    decimation = ChartDecimation(algorithm = if (lttb) DecimationAlgorithm.Lttb else DecimationAlgorithm.MinMax),
    description = "Bir sensörün otuz beş gün boyunca dakikada bir ölçtüğü sıcaklık",
    tagKey = "many_points"
)

private val MINUTES: List<Pair<Int, Double>> by lazy {
    List(MANY_MINUTES) { minute ->
        minute to 18.0 + wave(minute, A_DAY * 60) * 6.0 + minute / 20_000.0 + (minute * 7919 % 41 - 20) / 25.0
    }
}

private const val MANY_MINUTES = 50_000

private const val A_DAY = 24

private fun minuteAt(minute: Int): Instant = Instant.parse("2026-09-08T00:00:00Z") + minute.minutes

private fun wave(
    step: Int,
    period: Int
): Double = sin(step * 2 * PI / period)

Dışa aktarma

export = ChartExport(...) grafiğin düğmelerinin arasına bir dışa aktarma düğmesi koyar. Bas ve türü seç: grafik dosyayı üretir ve onExport'a verir. Dosyanın nereye gideceği uygulamanındır: bu örnek adını, türünü ve boyunu söyler, PNG ise resmi altında geri gösterir, ve "Dosyayı kaydet" onu platformun kaydetme penceresiyle verir (rememberFileSaver): Android'de yeni belge penceresi, iOS'ta Dosyalar, masaüstünde kaydetme penceresi, web'de indirme.

Dosya grafiği ekrandaki hâliyle gösterir: lejanttan bir seriyi çıkar ve yeniden dışa aktar. Gösterilen yer ve tooltip'i dosyada yoktur; lejant çizimin altında durur. SVG ve PDF her boyutta keskindir ve birkaç kilobayttır.

var file by remember { mutableStateOf<ChartExportFile?>(null) }
val saver = rememberFileSaver()
PantrixChart(
    series = listOf(
        LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }, fill = ChartFill.Gradient),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }, dash = ChartDash.Dashed)
    ),
    export = ChartExport(fileName = "ziyaretler") { file = it },
    description = "Aylara göre masaüstü ve mobil ziyaretler",
    tagKey = "exported"
)
val made = file
PantrixText(
    text = made?.let { "${it.name} · ${it.mimeType} · ${it.bytes.size / KILOBYTE + 1} KB" }
        ?: "Henüz bir dosya üretilmedi",
    style = PantrixTheme.typography.footnote,
    color = PantrixTheme.colors.textSecondary
)
if (made != null) {
    PantrixButton(
        text = "Dosyayı kaydet",
        onClick = { saver.save(PantrixFile(made.name, made.bytes, made.mimeType)) {} },
        variant = ButtonVariant.Outline,
        size = ButtonSize.Small,
        tagKey = "exported_save"
    )
}
if (made != null && made.format == ChartExportFormat.Png) {
    val picture = remember(made) { made.bytes.decodeToImageBitmap() }
    PantrixImage(
        bitmap = picture,
        modifier = Modifier.fillMaxWidth().aspectRatio(picture.width.toFloat() / picture.height),
        contentDescription = "Üretilen resim"
    )
}

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

private const val KILOBYTE = 1024

Görünen kısım, tek tür

Tek bir tür verilince düğme seçim açmaz: basınca o dosyayı üretir. Burada yalnızca PNG, dp başına üç pikselle (scale = 3f) ve beyaz bir zeminde (background); zemin verilmezse temanın zeminidir, koyu temada koyu.

Grafik yakınlaştırılabiliyor: yakınlaş ve dışa aktar. Dosyaya görünen kısım çıkar, gezgin şeridi ve düğmeler çıkmaz. Üretilen resim altta gösteriliyor; grafiğin çevresindeki boşluk dosyanın kendi payıdır (padding).

var picture by remember { mutableStateOf<ImageBitmap?>(null) }
val state = rememberChartState()
// Grafik son otuz altı günü göstererek açılır.
LaunchedEffect(state) { state.zoomTo(start = 0.6f, end = 1f) }
PantrixChart(
    series = listOf(BarSeries("sessions", "Oturum", DAILY, x = { it.first }, y = { it.second })),
    xAxis = ChartAxis(label = { "$it. gün" }),
    zoom = ChartZoom(navigator = true),
    export = ChartExport(
        formats = listOf(ChartExportFormat.Png),
        fileName = "oturumlar",
        scale = 3f,
        background = Color.White
    ) { picture = it.bytes.decodeToImageBitmap() },
    state = state,
    description = "Son doksan günün günlük oturum sayısı, dışa aktarılabilir",
    tagKey = "exported_part"
)
picture?.let { made ->
    PantrixImage(
        bitmap = made,
        modifier = Modifier.fillMaxWidth().aspectRatio(made.width.toFloat() / made.height),
        contentDescription = "Üretilen resim"
    )
}

private val DAILY: List<Pair<Int, Int>> = List(DAYS) { day ->
    (day + 1) to BASE + day * GROWTH + WEEK[day % WEEK.size]
}

private const val DAYS = 90

private const val BASE = 320

private const val GROWTH = 3

private val WEEK = listOf(40, 62, 55, 70, 48, -90, -120)

Renk körlüğü için desenler

Renk körlüğü için desenler (ChartStyle(patterns = true)): ilk seri düz kalır, ötekilerin çubuklarına sırayla bir desen oyulur ve çizgiler sırayla kesik ve noktalı çizilir; lejant ve tooltip aynı deseni gösterir. Anahtarı kapat: desenler gider, seriler yalnızca renkleriyle ayrılır.

Bir seri kendi desenini pattern ile söyleyebilir (ChartPattern); söylenen desen anahtar kapalıyken de çizilir: burada tabletin çubuklarındaki noktalar.

var patterns by remember { mutableStateOf(true) }
val half = VISITS.take(6)
PantrixSwitchButton(
    checked = patterns,
    onCheckedChange = { patterns = it },
    label = "Desenler",
    tagKey = "chart_patterns"
)
PantrixChart(
    series = listOf(
        BarSeries("desktop", "Masaüstü", half, x = { it.month }, y = { it.desktop }),
        BarSeries("mobile", "Mobil", half, x = { it.month }, y = { it.mobile }),
        BarSeries("tablet", "Tablet", half, x = { it.month }, y = { it.tablet }, pattern = ChartPattern.Dots)
    ),
    description = "İlk altı ayın cihaza göre ziyaretleri, desenli çubuklarla",
    style = ChartStyle(patterns = patterns),
    tagKey = "patterns"
)
PantrixChart(
    series = listOf(
        LineSeries("desktop", "Masaüstü", VISITS, x = { it.month }, y = { it.desktop }),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }),
        LineSeries("tablet", "Tablet", VISITS, x = { it.month }, y = { it.tablet })
    ),
    description = "Aylara göre cihazların ziyaretleri, sırayla kesik çizgilerle",
    style = ChartStyle(patterns = patterns),
    tagKey = "patterns_lines"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

Kendi renklerin ve ölçülerin

Bir grafiğin kendi renkleri ve ölçüleri. ChartColors.series serilere sırayla dağıtılan renklerdir; burada temanın grafik renkleri yerine paletten iki mor. Kılavuz çizgileri de bu grafikte daha soluk.

ChartStyle ölçüleri verir: çizgi daha kalın, işaretler daha büyük. Verilmeyen alan temanınki kalır. Bütün grafikler için aynı şey temaya verilir: renkler PantrixColors.chart ile, ölçüler temanın styles'ı ile.

val dark = LocalDemoDarkTheme.current
val violet = PantrixPalette.Violet
val pink = PantrixPalette.Pink
PantrixChart(
    series = listOf(
        LineSeries(
            key = "desktop",
            name = "Masaüstü",
            data = VISITS,
            x = { it.month },
            y = { it.desktop },
            marker = ChartMarker.Diamond
        ),
        LineSeries("mobile", "Mobil", VISITS, x = { it.month }, y = { it.mobile }, marker = ChartMarker.Triangle)
    ),
    description = "Aylara göre masaüstü ve mobil ziyaretler",
    style = ChartStyle(lineWidth = 3.dp, markerSize = 10.dp),
    colors = ChartColors(
        series = if (dark) listOf(violet.shade400, pink.shade300) else listOf(violet.shade600, pink.shade500),
        grid = PantrixTheme.colors.divider.copy(alpha = 0.5f)
    ),
    tagKey = "own_look"
)

private data class Visits(
    val month: String,
    val desktop: Int,
    val mobile: Int,
    val tablet: Int
)

private val VISITS = listOf(
    Visits("Oca", desktop = 186, mobile = 80, tablet = 40),
    Visits("Şub", desktop = 305, mobile = 200, tablet = 62),
    Visits("Mar", desktop = 237, mobile = 120, tablet = 58),
    Visits("Nis", desktop = 173, mobile = 190, tablet = 51),
    Visits("May", desktop = 209, mobile = 130, tablet = 66),
    Visits("Haz", desktop = 214, mobile = 140, tablet = 70),
    Visits("Tem", desktop = 280, mobile = 170, tablet = 76),
    Visits("Ağu", desktop = 310, mobile = 210, tablet = 83),
    Visits("Eyl", desktop = 268, mobile = 232, tablet = 90),
    Visits("Eki", desktop = 342, mobile = 250, tablet = 96),
    Visits("Kas", desktop = 390, mobile = 286, tablet = 91),
    Visits("Ara", desktop = 420, mobile = 301, tablet = 120)
)

API

series

Bir dizi yer boyunca değerlerin grafiği: her serinin her yerdeki değerinden geçen çizgiler ve dolgulu alanlar, o değerlere çıkan çubuklar, altlarında yerlerin, yanlarında değerlerin ekseniyle.

ParametreTürVarsayılanNe olduğu
seriesList<ChartSeries>seriler, çizildikleri ve listelendikleri sırayla: sonraki, öncekinin üstüne çizilir. Anahtarları onları ayırır ve aynı anahtarı taşıyan iki seri geri çevrilir.
modifierModifierModifiergrafiğin modifier'ı. Buradaki bir yükseklik grafiğindir, lejantınki de içinde.
xAxisChartAxis?ChartAxis()yerlerin ekseni, grafiğin altında, yatay grafikte yanında. null ondan hiçbir şey çizmez.
yAxisChartAxis?ChartAxis()değerlerin ekseni, grafiğin yanında, yatay grafikte altında. null ondan hiçbir şey çizmez.
secondYAxisChartAxis?ChartAxis()değerlerin ikinci ekseni, grafiğin öbür yanında, yatay grafikte üstünde: bir seri, ya da bir değerdeki bir işaret, onunla okunuyorsa çizilir, yoksa çizilmez. null ondan hiçbir şey çizmez; onunla okunan yine ona göre dizilir.
marksList<ChartMark>emptyList()serilerin yanında çizim alanına çizilen çizgiler ve bantlar.
stackingChartStackingChartStacking.Normalbir yığının serilerinin birbirinin üstünde nasıl durduğu: değerleri olarak ya da yığından aldıkları paylar olarak.
orientationChartOrientationChartOrientation.Verticalyerlerin hangi yöne gittiği: grafiğin altı boyunca, sütunlar olarak, ya da yanından aşağı, yatay çubuklar olarak; her ad bir satırlık yerde yatay yazılır. Yakından bakılan yatay grafik yerlerinde yukarı ve aşağı gider, gezgin şeridi çizim alanının yanında durur.
legendChartLegend?ChartLegend()serilerin adlarının durduğu yer. null hiç göstermez.
tooltipChartTooltip?ChartTooltip()gösterilen yer için gösterilen şey. null hiç göstermez.
zoomChartZoom?nullokuyucunun yerlerin bir kısmına yakından bakmasını ve geri kalanı boyunca kaydırmasını sağlayan şey. Grafiğe verilmedikçe null'dır; null bütün yerleri gösterir, başka bir şey yapmaz.
decimationChartDecimation?ChartDecimation()görünen noktaları çizim alanının yer bulduğundan çok olan bir çizginin, onların daha azından nasıl çizileceği. null her noktayı, kaç tane olursa olsun, çizer.
exportChartExport?nullokuyucunun grafiği uygulamadan bir dosya olarak çıkarmasını sağlayan şey: bir resim, ya da her boyutta keskin bir çizim. Grafiğe verilmedikçe null'dır, ve grafiğin böyle bir düğmesi olmaz.
empty(@Composable () -> Unit)?nullgrafiğin çizecek değeri yokken çizim alanında gösterdiği şey: temanın bunun için yazdığı sözün ("Veri yok") yerine; o zaman ekran okuyucuya da o söz söylenmez. null sözü yazar. Grafiğin dışa aktarıldığı dosya sözü yazar.
stateChartStaterememberChartState()okuyucunun gizledikleri ve gösterdiği yer: verilmezse grafiğin kendisininki.
descriptionString?nullgrafiğin ne gösterdiği, birkaç sözcükle, ekran okuyucu için: grafik hakkında söylediği ilk şey. Verilmezse grafik serilerinin adlarıyla duyurulur.
onSelect((ChartPoint) -> Unit)?nullbasılan değerle çağrılır: gösterilen değerle, ya da yerin değerleri birlikte gösteriliyorsa basışa en yakın serinin değeriyle.
styleChartStyle?nullbu grafiğin kendi geometrisi: burada verilen alanlar, gerisi için temanınkiler.
colorsChartColors?nullbu grafiğin kendi renkleri, temanınkiler olmadıkları yerde.
tagKeyString?nullgrafiğin otomasyon anahtarı: grafik Chart_<tagKey>, çizim alanı Chart_<tagKey>.Plot, bir yeri, birden sayılarak, Chart_<tagKey>.Point_<number>, bir serinin lejant kaydı Chart_<tagKey>.Legend_<series' key> ve tooltip Chart_<tagKey>.Tooltip olur. Bir değere basış, serinin anahtarıyla bir Chart select olayı olarak, bir lejant kaydına basış da anahtarla ve serinin gösterilip gösterilmediğiyle bir toggle olayı olarak bildirilir. Değerlerden hiçbir şey bildirilmez.

ChartStyle

Bir grafiğin geometrisi: hiçbir şey söylemediğinde ne kadar yüksek olduğu, çizgilerinin ne kadar kalın, işaretlerinin ne kadar büyük olduğu ve parçalarının arasındaki boşluk; ve dolgularının renklerinin yanında desen alıp almadığı.

ParametreTürVarsayılanNe olduğu
heightDpDp.UnspecifiedGrafiğin modifier'ı ona bir yükseklik vermediğinde çizim alanının, eksenleriyle birlikte, ne kadar yüksek olduğu. Çizim alanının üstündeki ya da altındaki bir lejant kendi yüksekliğini ekler.
lineWidthDpDp.UnspecifiedBir serinin çizgisinin kalınlığı.
markerSizeDpDp.UnspecifiedBir noktadaki işaretin genişliği ve yüksekliği.
selectedMarkerSizeDpDp.UnspecifiedAynısı, gösterilen noktadaki işaret için.
gridWidthDpDp.UnspecifiedKılavuz çizgilerinin, eksenler boyunca çizilen çizgilerin ve gösterilen yeri işaretleyen çizginin kalınlığı. Sıfır hiçbirini çizmez.
labelGapDpDp.UnspecifiedÇizim alanıyla yanına ve altına yazılanın arası, ve onunla bir eksenin başlığının arası.
tickSpacingDpDp.UnspecifiedDeğerlerin ekseni boyunca işaretlenen iki değerin arasındaki en az boşluk: eksen söylemediğinde kaç tanesinin işaretleneceğine karar veren şey.
legendGapDpDp.UnspecifiedLejantla yanında durduğu grafiğin arası.
legendItemGapDpDp.UnspecifiedLejantın bir kaydıyla sonrakinin arası.
swatchSizeDpDp.UnspecifiedBir serinin lejanttaki ve tooltip'teki örneğinin ne kadar geniş olduğu: çizgisinin kısa bir parçası ya da dolgusunun bir yaması.
swatchGapDpDp.UnspecifiedO örnekle ardındaki adın arası.
tooltipShapeShapeSpecShapeSpec.UnspecifiedTooltip'in köşeleri: markanın şekil ölçeğinin bir basamağı ya da sabit bir şekil.
tooltipHorizontalPaddingDpDp.UnspecifiedTooltip'in yanlarıyla içindekinin arası.
tooltipVerticalPaddingDpDp.UnspecifiedÜstü ve altıyla içindekinin arası, ve başlığıyla satırlarının arası.
tooltipGapDpDp.UnspecifiedGösterilen yerle yanındaki tooltip'in arası.
tooltipElevationDpDp.UnspecifiedTooltip'in gölgesi.
barGapDpDp.UnspecifiedBir yerde yan yana duran iki çubuğun arası. Bunun için fazla ince olan çubuklar daha yakın durur.
barMaxWidthDpDp.UnspecifiedBir çubuğun en fazla ne kadar geniş olduğu, yerinin ne kadar yeri olursa olsun: üç yerli bir grafik üç kalas değildir.
barRadiusDpDp.UnspecifiedBir çubuğun ucunun, sıfırdan uzak ucunun, köşelerinin ne kadar yuvarlak olduğu.
sliceGapDpDp.UnspecifiedBir pastanın yan yana duran iki dilimi arasında; pastanın ortasından kenarına kadar aynı genişlikte.
selectedSliceOffsetDpDp.UnspecifiedBir pastanın gösterilen diliminin, pastanın kenarından ne kadar dışarı taştığı. Pasta çevresinde bu kadar yer bırakır: dilimin duracağı yer olsun.
gaugeWidthDpDp.UnspecifiedBir göstergenin yayının kalınlığı. Bir yay, yarıçapının yarısından kalın olmaz.
meterHeightDpDp.UnspecifiedBir ölçüm grubunun çubuğunun yüksekliği.
trackerHeightDpDp.UnspecifiedBir durum izleyicinin bloklarının yüksekliği.
bubbleMinSizeDpDp.UnspecifiedBir saçılım grafiğinin en küçük balonunun genişliği: büyüklüğü hiç olmayan ya da bir alan olarak görülemeyecek kadar az olan nokta da bir noktadır.
bubbleMaxSizeDpDp.UnspecifiedEn büyük balonunun genişliği: büyüklüğü grafiğin en büyüğü olanın.
navigatorHeightDpDp.UnspecifiedZoom'unda gezgin şeridi olan bir grafikte, çizim alanının altındaki şeridin yüksekliği: grafiğin görünen kısmı onun üstünde işaretlenir ve kaydırılır. Yerleri yanından aşağı dizilen grafikte şerit çizim alanının yanında durur ve bu onun genişliğidir.
barBandShareFloatFloat.NaNBir yerin çubuklarının bandından aldığı pay, sıfırdan çok ve en fazla bir: kalanı bir yerin çubuklarıyla sonrakininkiler arasındaki boşluktur. Çubuklar yine de barMaxWidth'ten geniş olmaz.
markLineWidthDpDp.UnspecifiedBir işaretin çizgisinin kalınlığı: bir hedefin, bir sınırın, bir günün. Sıfır çizgi çizmez; işaretin yazısı yine yazılır.
markBandAlphaFloatFloat.NaNBir işaretin bandının renginin ne kadarıyla çizildiği, sıfırdan bire: bant serilerin arkasında durur ve bir tondur.
patternsBoolean?nullBir grafiğin serilerinin, dilimlerinin ve karolarının dolgularının renklerinin yanında desenlerle, çizgilerinin de sırayla kesik çizilip çizilmediği (ChartPattern): renkleri ayırt edemeyen bir okuyucu için onları ayıran şey. Verilmezse sistemin ayarına uyulur: sistem şeyleri renksiz ayırt etmeye ayarlıysa açık (iOS'ta "Differentiate Without Color", Android'de renk düzeltme), değilse kapalı.
nodeWidthDpDp.UnspecifiedBir sankey düğümünün ne kadar geniş olduğu.
nodeGapDpDp.UnspecifiedBir sankey kolonunun iki düğümü arası.

ChartColors

Temanın renklerini almayan bir grafik için, onun neyle boyandığı: verilen renk kullanılır, verilmeyen temanınki kalır.

ParametreTürVarsayılanNe olduğu
seriesList<Color>emptyList()Serilerin çizildiği renkler, dağıtıldıkları sırayla, kendi rengini söylemeyen seriler için. Boşsa temanın grafik renkleridir.
gridColorColor.UnspecifiedBir eksenin işaretlerinde grafik boyunca çizilen çizgiler. Temanın divider'ı.
axisColorColor.UnspecifiedBir eksen boyunca çizilen çizgi. Temanın border'ı.
labelColorColor.UnspecifiedEksenlerin yanına yazılanlar: değerler, yerler ve eksenlerin başlıkları, bir de lejanttaki adlar. Temanın textSecondary'si.
crosshairColorColor.UnspecifiedGösterilen yeri işaretleyen çizgi. Temanın borderHover'ı: içinden geçtiği kılavuz çizgilerinden güçlü, arkasında durduğu serilerden sessiz.
tooltipContainerColorColor.UnspecifiedTooltip'in dolgusu. Temanın surface'i.
tooltipBorderColorColor.UnspecifiedTooltip'in kenarı. Temanın border'ı.
tooltipTextColorColor.UnspecifiedTooltip'in başlığı ve değerleri. Temanın textPrimary'si.
tooltipLabelColorColor.UnspecifiedTooltip'teki seri adları. Temanın textSecondary'si.
risingColorColor.UnspecifiedYukarı giden bir değerin çizildiği renk: bir şelalenin ekleyen adımı. Temanın success'i.
fallingColorColor.UnspecifiedAşağı giden bir değerin çizildiği renk: bir şelalenin götüren adımı. Temanın danger'ı.

Erişilebilirlik