Hi Guys,
I am implementing the column chart, but the xAxis value is duplicate when I scroll the chart. Please see the images attachment.
Here are the piece of code, and wandering what cause this, thx!
package com.refinitiv.android.presentation.view.chart.stack
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.widget.FrameLayout
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.scichart.charting.ClipMode
import com.scichart.charting.Direction2D
import com.scichart.charting.model.ChartModifierCollection
import com.scichart.charting.model.dataSeries.IXyDataSeries
import com.scichart.charting.modifiers.AxisDragModifierBase
import com.scichart.charting.modifiers.XAxisDragModifier
import com.scichart.charting.modifiers.ZoomPanModifier
import com.scichart.charting.numerics.labelProviders.NumericLabelFormatter
import com.scichart.charting.numerics.labelProviders.NumericLabelProvider
import com.scichart.charting.numerics.tickProviders.NumericTickProvider
import com.scichart.charting.visuals.SciChartSurface
import com.scichart.charting.visuals.axes.AutoRange
import com.scichart.charting.visuals.axes.AxisTickLabelStyle
import com.scichart.charting.visuals.axes.IAxis
import com.scichart.charting.visuals.renderableSeries.IRenderableSeries
import com.scichart.charting.visuals.renderableSeries.StackedColumnRenderableSeries
import com.scichart.charting.visuals.renderableSeries.VerticallyStackedColumnsCollection
import com.scichart.core.framework.UpdateSuspender
import com.scichart.core.model.DoubleValues
import com.scichart.core.model.IntegerValues
import com.scichart.data.model.DoubleRange
import com.scichart.drawing.canvas.RenderSurface
import com.scichart.drawing.common.FontStyle
import com.scichart.drawing.common.PenStyle
import com.scichart.drawing.common.SolidPenStyle
import com.scichart.extensions.builders.SciChartBuilder
import timber.log.Timber
import java.util.*
import kotlin.math.roundToInt
private const val GROW_BY: Double = 0.0
private const val MAX_VISIBLE_COLUMNS = 11
private const val MIN_VISIBLE = -0.5
class StackColumnChartView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int = 0,
defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
private val chart = SciChartSurface(context)
private val typefaceSemibold =
ResourcesCompat.getFont(context, R.font.proxima_nova_fin_semibold)
private val tickFontStyle = FontStyle(
typefaceSemibold,
resources.getDimension(R.dimen.chart_axis_text_size),
getColorFromAttrOrDefault(R.attr.chartAxisTextColor, R.color.dove_grey),
true
)
private val majorGridLineAndTickStyle: PenStyle = SolidPenStyle(
getColorFromAttrOrDefault(R.attr.cardViewItemDividerBackground, R.color.desert_storm_50),
true,
resources.getDimension(R.dimen.chart_grid_line_thickness),
null
)
var xAxisLabelList = emptyList<String>()
var yAxisLabelList = mutableListOf("0%", "20%", "40%", "60%", "80%", "100%")
var dataList: List<List<Double>> = emptyList()
private lateinit var xAxisData: List<Double>
init {
chart.renderSurface = RenderSurface(context)
val params = LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT
)
chart.layoutParams = params
addView(chart)
chart.theme = R.style.SciChart
}
fun buildChart() {
Timber.tag("CHART").d("CURVE CHART View build chart")
SciChartBuilder.init(context)
val sciChartBuilder: SciChartBuilder = SciChartBuilder.instance()
xAxisData = xAxisLabelList.mapIndexed { index, _ ->
index.toDouble()
}
val xAxis = initXAxis(sciChartBuilder)
val yAxis = initYAxis(sciChartBuilder)
val dataSeries = initDataSeries(context, sciChartBuilder)
val surfaceChartModifiers: ChartModifierCollection = chart.chartModifiers
val zoomPanModifier = ZoomPanModifier()
zoomPanModifier.direction = Direction2D.XDirection
zoomPanModifier.clipModeX = ClipMode.ClipAtExtents
zoomPanModifier.clipModeY = ClipMode.None
zoomPanModifier.zoomExtentsY = false
val dragModifier = XAxisDragModifier()
dragModifier.dragMode = AxisDragModifierBase.AxisDragMode.Pan
surfaceChartModifiers.add(dragModifier)
UpdateSuspender.using(chart) {
chart.xAxes.clear()
chart.yAxes.clear()
chart.annotations.clear()
chart.renderableSeries.clear()
Collections.addAll(chart.xAxes, xAxis)
Collections.addAll(chart.yAxes, yAxis)
Collections.addAll(chart.renderableSeries, dataSeries)
Collections.addAll(chart.chartModifiers, zoomPanModifier)
Collections.addAll(chart.chartModifiers, dragModifier)
}
}
fun clearChart() {
UpdateSuspender.using(chart) {
chart.xAxes.clear()
chart.yAxes.clear()
chart.annotations.clear()
chart.renderableSeries.clear()
}
}
private fun initDataSeries(
context: Context, sciChartBuilder: SciChartBuilder
): IRenderableSeries {
val verticalCollection = VerticallyStackedColumnsCollection()
val seriesList = dataList.mapIndexed { _, xValue ->
val series: IXyDataSeries<Double, Double> = sciChartBuilder.newXyDataSeries(
Double::class.javaObjectType,
Double::class.javaObjectType
).build()
for (i in xAxisData.indices) {
series.append(xAxisData[i], xValue[i])
}
series
}
val result = seriesList.mapIndexed { index, series ->
val color: Int = if (index < colorList.size) {
ContextCompat.getColor(context, colorList[index])
} else {
ContextCompat.getColor(context, colorList[index % colorList.size])
}
val stack: StackedColumnRenderableSeries =
sciChartBuilder.newStackedColumn().withDataSeries(series).withFillColor(color)
.withStrokeStyle(
ContextCompat.getColor(
context, R.color.chatline_white
), 0.2F
)
.build()
stack
}
verticalCollection.addAll(result)
verticalCollection.dataPointWidth = 0.4
verticalCollection.isOneHundredPercent = true
return verticalCollection
}
private fun initXAxis(
sciChartBuilder: SciChartBuilder
): IAxis {
val horizontalAxisTickLabelStyle = AxisTickLabelStyle(
Gravity.CENTER_VERTICAL,
0,
context.resources.getDimensionPixelSize(R.dimen.pe_firm_investment_profile_chat_view_label_margin_vertical),
0,
0,
)
val visibleMin = if (xAxisLabelList.size > MAX_VISIBLE_COLUMNS) {
(xAxisLabelList.size - MAX_VISIBLE_COLUMNS).toDouble()
} else {
MIN_VISIBLE
}
val xVisibleRange = DoubleRange(visibleMin, (xAxisLabelList.size + MIN_VISIBLE))
return sciChartBuilder
.newNumericAxis()
.build()
.apply {
axisTickLabelStyle = horizontalAxisTickLabelStyle
tickLabelStyle = tickFontStyle
drawMinorGridLines = false
drawMinorTicks = false
drawMajorTicks = false
drawMajorBands = false
drawMajorGridLines = false
autoFitMarginalLabels = true
visibleRange = xVisibleRange
// tickProvider = XTickProvider(xAxisData)
labelProvider =
NumericLabelProvider(FirmProfileDateAxisLabelFormatter(xAxisLabelList))
growBy = DoubleRange(GROW_BY, GROW_BY)
maxAutoTicks = xAxisLabelList.size
}
}
private fun initYAxis(
sciChartBuilder: SciChartBuilder
): IAxis {
val verticalAxisTickLabelStyle = AxisTickLabelStyle(
Gravity.CENTER_HORIZONTAL,
0,
0,
0,
0
)
return sciChartBuilder
.newNumericAxis()
.build()
.apply {
axisTickLabelStyle = verticalAxisTickLabelStyle
drawMajorGridLines = true
minimalZoomConstrain = 0.0
tickLabelStyle = tickFontStyle
majorTickLineStyle = majorGridLineAndTickStyle
majorGridLineStyle = majorGridLineAndTickStyle
labelProvider = FirmProfileYAxisLabelProvider(yAxisLabelList)
autoRange = AutoRange.Always
maxAutoTicks = yAxisLabelList.size
growBy = DoubleRange(GROW_BY, GROW_BY)
}
}
fun dispose() {
SciChartBuilder.dispose()
}
class XTickProvider(private val xAxisData: List<Double>) : NumericTickProvider() {
override fun updateCullingPriorities(
cullingPriorities: IntegerValues?,
majorTicks: DoubleValues?
) {
super.updateCullingPriorities(cullingPriorities, majorTicks)
}
override fun updateTicks(minorTicks: DoubleValues?, majorTicks: DoubleValues?) {
// super.updateTicks(minorTicks, majorTicks)
xAxisData.forEach {
majorTicks?.add(it)
}
}
override fun getMajorTickIndex(tick: Double): Int {
Timber.tag("StackColumn").v("getMajorTickIndex-:${tick}")
return super.getMajorTickIndex(tick)
}
override fun shouldUpdateTicks(): Boolean {
val should = super.shouldUpdateTicks()
Timber.tag("StackColumn").v("shouldUpdateTicks-:${should}")
return should
}
override fun isFirstMajorTickEven(majorTicks: DoubleValues?): Boolean {
return super.isFirstMajorTickEven(majorTicks)
}
}
class FirmProfileDateAxisLabelFormatter(private val labelTitles: List<String>) :
NumericLabelFormatter() {
private var lastFormatLabel = ""
override fun formatLabel(p0: Double): CharSequence {
Timber.tag("StackColumn").v("formatLabel-:${p0}")
if (labelTitles[p0.toInt()] == lastFormatLabel) {
return ""
}
return labelTitles[p0.toInt()]
}
override fun formatCursorLabel(p0: Double): CharSequence {
return formatLabel(p0)
}
}
class FirmProfileYAxisLabelProvider(private val labelList: List<String>) :
NumericLabelProvider() {
var index = 0
override fun formatLabel(p0: Double): CharSequence {
if (labelList.isEmpty()) {
return ""
}
val title = labelList[index]
index += 1
index = if (index < labelList.size) {
index
} else {
0
}
return title
}
override fun formatCursorLabel(p0: Double): CharSequence {
return formatLabel(p0)
}
}
companion object {
val colorList = mutableListOf(
R.color.chatline_blue_ribbon,
R.color.chatline_black,
R.color.chatline_teal,
R.color.chatline_butterfly,
R.color.chatline_olive,
R.color.chatline_grenadier,
R.color.chatline_rain_forest,
R.color.chatline_cerise_red,
R.color.chatline_scorpion,
R.color.chatline_jelly_bean,
R.color.chatline_genoa,
R.color.chatline_brown_rust,
R.color.chatline_torea_bay,
R.color.chatline_saddle_brown,
R.color.chatline_victoria,
R.color.chatline_sherpa_blue,
R.color.chatline_oregon,
R.color.chatline_kaitoke_green,
R.color.chatline_maroon_flush,
R.color.chatline_tundora,
R.color.chatline_chathams_blue,
R.color.chatline_eden,
R.color.chatline_mule_fawn,
)
}
}
- Steve Shan asked 2 years ago
- last edited 2 years ago
- You must login to post comments
Hi Steve,
Thanks for providing code sample. I took a look on it and the problem is that your LabelFormatter implementation (FirmProfileDateAxisLabelFormatter) doesn’t support fraction values and when you cast double to int you’re rounding it.
override fun formatLabel(p0: Double): CharSequence {
if (labelTitles[p0.toInt()] == lastFormatLabel) {
return ""
}
return labelTitles[p0.toInt()]
}
So, for example, when you’re getting sequence of values to format like [ 0.0, 0.5. 1.0, 1.5, 2.0 ] you’ll get label titles with next indices on screen [0, 0, 1, 1, 2]. That’s why you have repetition of labels for xAxis.
As workaround I would suggest you to try to set MajorDelta to be equal to 1.0, so default TickProvider implementation will produce ticks that have no fraction part which will be lost during casting to int. As alternative you can create custom TickProvider and provide custom calculations to ensure that majorTicks will have correct values.
Best regards,
Yura
- Yura Khariton answered 2 years ago
-
Thank you for your help, yes, we fix it after set MajorDelta. Your comments are really helpful, thx!
- You must login to post comments
Please login first to submit.