diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 959b705..93ec858 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sun Aug 07 22:38:24 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/library/src/main/java/com/dzeio/charts/ChartView.kt b/library/src/main/java/com/dzeio/charts/ChartView.kt index 99ccb35..a059a51 100644 --- a/library/src/main/java/com/dzeio/charts/ChartView.kt +++ b/library/src/main/java/com/dzeio/charts/ChartView.kt @@ -9,6 +9,7 @@ import android.graphics.Paint import android.graphics.Rect import android.graphics.RectF import android.util.AttributeSet +import android.util.Log import android.view.MotionEvent import android.view.View import com.dzeio.charts.axis.XAxis @@ -82,16 +83,21 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet return@setOnChartMoved } if (xAxis.scrollEnabled) { - xAxis.x += (movementX - lastMovementX) * xAxis.getDataWidth() / width + val currentXMax = xAxis.getCurrentMax() + val currentXMin = xAxis.getCurrentMin() + val change = (movementX.toDouble() - lastMovementX) * (currentXMax - currentXMin) / width + xAxis.setCurrent( + currentXMin + change, + currentXMax + change + ) lastMovementX = movementX.toDouble() } if (yAxis.scrollEnabled) { - val currentYMax = yAxis.getYMax() - val currentYMin = yAxis.getYMin() + val currentYMax = yAxis.getCurrentMax() + val currentYMin = yAxis.getCurrentMin() val change = (movementY - lastMovementY) * (currentYMax - currentYMin) / height - yAxis.setYMax(currentYMax + change) - yAxis.setYMin(currentYMin + change) + yAxis.setCurrent(currentYMin + change, currentYMax + change) lastMovementY = movementY } @@ -99,7 +105,9 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet } setOnToggleScroll { // note: true == no scroll - parent?.requestDisallowInterceptTouchEvent(!it && (yAxis.scrollEnabled || xAxis.scrollEnabled)) + parent?.requestDisallowInterceptTouchEvent( + !it && (yAxis.scrollEnabled || xAxis.scrollEnabled || xAxis.zoomEnabled || yAxis.zoomEnabled) + ) } setOnChartClick { x, y -> if (getDataset().isEmpty()) { @@ -132,11 +140,53 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet } refresh() } -// setOnZoomChanged { -// Log.d(TAG, "New Zoom: $it") + setOnZoomChanged { scaleX, scaleY -> + val animationEnabled = animator.enabled + if (animationEnabled) animator.enabled = false + Log.d(TAG, "Zoom: Start") + val factor = 0.05f + if (scaleX != 0f && xAxis.zoomEnabled && xAxis.scrollEnabled) { + Log.d(TAG, "ScaleX: scaleX") + Log.d(TAG, "ScaleX: $scaleX") + val xDistance = xAxis.getCurrentMax() - xAxis.getCurrentMin() + Log.d(TAG, "ScaleX: xDistance * (scaleX * factor)") + Log.d(TAG, "ScaleX: $xDistance * ${(scaleX * factor)}") + val xTransformer = xDistance * (scaleX * factor) + Log.d(TAG, "ScaleX: xTransformer") + Log.d(TAG, "ScaleX: $xTransformer") + val xMin = xAxis.getCurrentMin() - xTransformer + val dataWidth = xAxis.getDataWidth() + xTransformer + Log.d(TAG, "ScaleX: previousXMin, previousDataWidth") + Log.d(TAG, "ScaleX: ${xAxis.getCurrentMin()}, ${xAxis.getDataWidth()}") + Log.d(TAG, "ScaleX: xMin, dataWidth") + Log.d(TAG, "ScaleX: $xMin, $dataWidth") + xAxis.setCurrentMin(xMin) + xAxis.dataWidth = dataWidth + } + if (scaleY != 0f && yAxis.zoomEnabled && yAxis.scrollEnabled) { + Log.d(TAG, "ScaleY: scaleY") + Log.d(TAG, "ScaleY: $scaleY") + val yDistance = yAxis.getCurrentMax() - yAxis.getCurrentMin() + Log.d(TAG, "ScaleY: yDistance * (scaleY * factor)") + Log.d(TAG, "ScaleY: $yDistance * ${(scaleY * factor)}") + val yTransformer = yDistance * (scaleY * factor) + Log.d(TAG, "ScaleY: yTransformer") + Log.d(TAG, "ScaleY: $yTransformer") + val yMin = yAxis.getCurrentMin() - yTransformer + val yMax = yAxis.getCurrentMax() + yTransformer + Log.d(TAG, "ScaleY: previousYMin, previousYMax") + Log.d(TAG, "ScaleY: ${yAxis.getCurrentMin()}, ${yAxis.getCurrentMax()}") + Log.d(TAG, "ScaleY: yMin, yMax") + Log.d(TAG, "ScaleY: $yMin, $yMax") + yAxis.setCurrentMin(yMin) + yAxis.setCurrentMax(yMax.coerceAtLeast(yMin + 1f)) + } +// Log.d(TAG, "Zoom: Done") +// Log.d(TAG, "[after] : tr: $scaleX, $scaleY, xMin: $xMin, xMax: $xMax, yMin: $yMin, yMax: $yMax") // zoom = (it * 1.2).toFloat() -// refresh() -// } + refresh() + if (animationEnabled) animator.enabled = true + } } // rect used for calculations diff --git a/library/src/main/java/com/dzeio/charts/axis/AxisInterface.kt b/library/src/main/java/com/dzeio/charts/axis/AxisInterface.kt new file mode 100644 index 0000000..853c962 --- /dev/null +++ b/library/src/main/java/com/dzeio/charts/axis/AxisInterface.kt @@ -0,0 +1,119 @@ +package com.dzeio.charts.axis + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.RectF +import com.dzeio.charts.Entry + +interface AxisInterface { + /** + * enable/disable the display of the xAxis + */ + var enabled: Boolean + + /** + * paint for the lines + */ + val linePaint: Paint + + /** + * text Paint + */ + val textPaint: Paint + + /** + * if global limits are sets to true [getCurrentMin] while never be `<` to [getMin] and same for max + */ + var keepGlobalLimits: Boolean + + /** + * indicate the number of labels displayed + */ + var labelCount: Int + + var onValueFormat: (value: T) -> String + + /** + * is Horizontal Scrolling enabled + */ + var scrollEnabled: Boolean + + /** + * is horizontal zooming enabled + */ + var zoomEnabled: Boolean + + /** + * get the entry position on the rect + * + * @param entry the entry to place + * @param drawableSpace the space it should go into + * + * @return the position of the entry on the defined axis + */ + fun getPositionOnRect(entry: Entry, drawableSpace: RectF): T + + /** + * get the entry position on the rect + * + * @param position the position on your point + * @param drawableSpace the space it should go into + * + * @return the position of the entry on the defined axis + */ + fun getPositionOnRect(position: T, drawableSpace: RectF): T + + /** + * get the current/displayed minimum (inclusive) + */ + fun getCurrentMin(): T + + /** + * set the new min/max of the element + * + * if one or both can't be set to the value both won't be set + */ + fun setCurrent(min: T?, max: T?): Boolean + + /** + * set the new minimum displayed value of the axis (inclusive) + */ + fun setCurrentMin(value: T?) + + /** + * set the new maximum displayed value of the axis (inclusive) + */ + fun setCurrentMax(value: T?) + + /** + * get the current/displayed maximum (inclusive) + */ + fun getCurrentMax(): T + + /** + * get the maximum the axis can get to + */ + fun getMax(): T + + /** + * get the minimum value the axis can get to + */ + fun getMin(): T + + /** + * run when manually refreshing the system + * + * this is where the pre-logic is handled to make [onDraw] quicker + */ + fun refresh() + + /** + * function that draw our legend + * + * @param canvas the canvas to draw on + * @param space the space where it is allowed to draw on + * + * @return the the width or height of the axis + */ + fun onDraw(canvas: Canvas, space: RectF): Float +} diff --git a/library/src/main/java/com/dzeio/charts/axis/Line.kt b/library/src/main/java/com/dzeio/charts/axis/Line.kt index f315dd9..b53e250 100644 --- a/library/src/main/java/com/dzeio/charts/axis/Line.kt +++ b/library/src/main/java/com/dzeio/charts/axis/Line.kt @@ -2,7 +2,7 @@ package com.dzeio.charts.axis import android.graphics.Paint -data class Line ( +data class Line( /** * is the bar dotted */ @@ -12,4 +12,4 @@ data class Line ( * Custom Paint */ var paint: Paint? = null -) \ No newline at end of file +) diff --git a/library/src/main/java/com/dzeio/charts/axis/XAxis.kt b/library/src/main/java/com/dzeio/charts/axis/XAxis.kt index 7cf7da5..131d405 100644 --- a/library/src/main/java/com/dzeio/charts/axis/XAxis.kt +++ b/library/src/main/java/com/dzeio/charts/axis/XAxis.kt @@ -15,24 +15,24 @@ class XAxis( ) : XAxisInterface { private companion object { - const val TAG = "Charts/XAxis" + const val TAG = "XAxis" } - override var x: Double = 0.0 - set(value) { - val max = getXMax() - val min = getXMin() - - field = value.coerceIn(min, max.coerceAtLeast(min)) - } + private var x: Double = 0.0 override var enabled = true + override val linePaint = Paint().apply { + isAntiAlias = true + color = Color.BLACK + } + override var dataWidth: Double? = null override var labelCount: Int = 2 override var scrollEnabled: Boolean = false + override var zoomEnabled: Boolean = false var spacing = 16.0 @@ -46,6 +46,7 @@ class XAxis( private val rect = Rect() private var height: Float? = null + override var keepGlobalLimits = false override fun getHeight(): Float? { return height @@ -65,22 +66,69 @@ class XAxis( return drawableSpace.left + drawableSpace.width() * (position - x) / getDataWidth() } - override fun getXMax(): Double { - return view.series.maxOf { serie -> - if (serie.entries.isEmpty()) { - return 0.0 - } - serie.entries.maxOf { entry -> entry.x } - } + override fun getCurrentMin(): Double { + return this.x } - override fun getXMin(): Double { - return view.series.minOf { serie -> - if (serie.entries.isEmpty()) { - return 0.0 - } - serie.entries.minOf { entry -> entry.x } + override fun setCurrent(min: Double?, max: Double?): Boolean { + val previousMin = getCurrentMin() + val previousMax = getCurrentMax() + setCurrentMin(min) + if (previousMin != getCurrentMin()) { + setCurrentMin(previousMin) + return false } + setCurrentMax(max) + if (previousMax != getCurrentMax()) { + setCurrentMax(previousMax) + setCurrentMin(previousMin) + return false + } + return true + } + + override fun setCurrentMin(value: Double?) { + if (keepGlobalLimits) { + this.x = (value ?: 0.0).coerceIn(getMin(), getMax()) + return + } + this.x = value ?: 0.0 + } + + override fun setCurrentMax(value: Double?) { + if (value == null) { + dataWidth = null + return + } + var real = value + if (keepGlobalLimits) { + real = value.coerceIn(getMin(), getMax()) + } + dataWidth = real.coerceAtLeast(this.x + 1) - this.x + } + + override fun getCurrentMax(): Double { + return this.x + getDataWidth() + } + + override fun getMax(): Double { + return view.series + .maxOf { serie -> + if (serie.entries.isEmpty()) { + return@maxOf 0.0 + } + return@maxOf serie.entries.maxOf { entry -> entry.x } + } + } + + override fun getMin(): Double { + return view.series + .minOf { serie -> + if (serie.entries.isEmpty()) { + return@minOf 0.0 + } + return@minOf serie.entries.minOf { entry -> entry.x } + } } override var onValueFormat: (value: Double) -> String = { it -> it.roundToInt().toString() } @@ -138,7 +186,7 @@ class XAxis( } override fun getDataWidth(): Double { - // TODO: handle the auto dataWidth better (still not sure it is good enough) - return dataWidth ?: (getXMax() - getXMin() + 1) + // TODO: handle the auto dataWidth better + return dataWidth ?: (getMax() - getMin() + 1) } } diff --git a/library/src/main/java/com/dzeio/charts/axis/XAxisInterface.kt b/library/src/main/java/com/dzeio/charts/axis/XAxisInterface.kt index dfb33d3..72748a3 100644 --- a/library/src/main/java/com/dzeio/charts/axis/XAxisInterface.kt +++ b/library/src/main/java/com/dzeio/charts/axis/XAxisInterface.kt @@ -1,21 +1,8 @@ package com.dzeio.charts.axis -import android.graphics.Canvas -import android.graphics.Paint import android.graphics.RectF -import com.dzeio.charts.Entry -sealed interface XAxisInterface { - - /** - * enable/disable the display of the xAxis - */ - var enabled: Boolean - - /** - * set X position - */ - var x: Double +sealed interface XAxisInterface : AxisInterface { /** * the "width" of the graph @@ -26,60 +13,6 @@ sealed interface XAxisInterface { */ var dataWidth: Double? - /** - * text Paint - */ - val textPaint: Paint - - /** - * indicate the number of labels displayed - */ - var labelCount: Int - - /** - * is Horizontal Scrolling enabled - */ - var scrollEnabled: Boolean - - var onValueFormat: (value: Double) -> String - - /** - * run when manually refreshing the system - * - * this is where the pre-logic is handled to make [onDraw] quicker - */ - fun refresh() - - /** - * get the entry position on the rect - * - * @param entry the entry to place - * @param drawableSpace the space it should go into - * - * @return the left side of the position of the entry - */ - fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double - - /** - * get the entry position on the rect - * - * @param position the position of your point - * @param drawableSpace the space it should go into - * - * @return the left side of the position of the entry - */ - fun getPositionOnRect(position: Double, drawableSpace: RectF): Double - - /** - * get the maximum the X can get to - */ - fun getXMax(): Double - - /** - * get the minimum the X can get to - */ - fun getXMin(): Double - /** * get the size of an entry in the graph * @@ -92,16 +25,6 @@ sealed interface XAxisInterface { */ fun getDataWidth(): Double - /** - * onDraw event that will draw the XAxis - * - * @param canvas the canvas to draw on - * @param space the space where it is allowed to draw - * - * @return the final height of the XAxis - */ - fun onDraw(canvas: Canvas, space: RectF): Float - /** * return the height of the XAxis (available after first draw) */ diff --git a/library/src/main/java/com/dzeio/charts/axis/YAxis.kt b/library/src/main/java/com/dzeio/charts/axis/YAxis.kt index d9bb25f..ceced1f 100644 --- a/library/src/main/java/com/dzeio/charts/axis/YAxis.kt +++ b/library/src/main/java/com/dzeio/charts/axis/YAxis.kt @@ -18,7 +18,7 @@ class YAxis( override var enabled = true - override val textLabel = Paint().apply { + override val textPaint = Paint().apply { isAntiAlias = true color = Color.BLACK textSize = 30f @@ -36,6 +36,8 @@ class YAxis( strokeWidth = 4f } + override var keepGlobalLimits = true + override var onValueFormat: (value: Float) -> String = { it -> it.roundToInt().toString() } override var labelCount = 5 @@ -51,20 +53,125 @@ class YAxis( } override var scrollEnabled: Boolean = false + override var zoomEnabled: Boolean = false private val rect = Rect() - override fun setYMin(yMin: Float?): YAxisInterface { - min = yMin - return this + override fun setCurrent(min: Float?, max: Float?): Boolean { + val previousMin = getCurrentMin() + val previousMax = getCurrentMax() + setCurrentMin(min) + if (previousMin != getCurrentMin()) { + setCurrentMin(previousMin) + return false + } + setCurrentMax(max) + if (previousMax != getCurrentMax()) { + setCurrentMax(previousMax) + setCurrentMin(previousMin) + return false + } + return true } - override fun setYMax(yMax: Float?): YAxisInterface { - max = yMax - return this + override fun setCurrentMin(value: Float?) { + if (keepGlobalLimits) { + min = (value ?: 0f).coerceIn(getMin(), getMax()) + return + } + min = value } - override fun getYMax(): Float { + override fun setCurrentMax(value: Float?) { + if (keepGlobalLimits) { + max = (value ?: 0f).coerceIn(getMin(), getMax()) + return + } + max = value + } + + override fun getMax(): Float { + val max = this.lines.keys.maxOrNull() ?: 0f + + if (view.series.isEmpty()) { + return max + } + + if (view.type == ChartType.STACKED) { + val nList: ArrayList = arrayListOf() + + for (serie in view.series) { + val size = serie.entries.size + while (nList.size < size) { + nList.add(0f) + } + for (index in 0 until serie.entries.size) { + val entry = serie.entries[index] + if (sign(entry.y) <= 0f && nList[index] > 0f) { + continue + } else if (nList[index] < 0f && entry.y > 0f) { + nList[index] = entry.y + continue + } + nList[index] += entry.y + } + } + + val localMax = nList.maxOf { it } + return if (localMax > max) localMax else max + } + val seriesMax = view.series + .maxOf { serie -> + if (serie.entries.isEmpty()) { + return@maxOf 0f + } + return@maxOf serie.entries.maxOf { entry -> entry.y } + } + return if (seriesMax > max) seriesMax else max + } + + override fun getMin(): Float { + val min = this.lines.keys.minOrNull() ?: 0f + + if (view.series.isEmpty()) { + return min + } + + if (view.type == ChartType.STACKED) { + val nList: ArrayList = arrayListOf() + + for (serie in view.series) { + val size = serie.entries.size + while (nList.size < size) { + nList.add(0f) + } + for (index in 0 until serie.entries.size) { + val entry = serie.entries[index] + if (sign(entry.y) >= 0f && nList[index] < 0f) { + continue + } else if (nList[index] > 0f && entry.y < 0f) { + nList[index] = entry.y + continue + } + nList[index] += entry.y + } + } + + val localMin = nList.minOf { it } + + return if (localMin < min) localMin else min + } + val localMin = view.series + .minOf { serie -> + if (serie.entries.isEmpty()) { + return@minOf 0f + } + return@minOf serie.entries.minOf { entry -> entry.y } + } + return if (localMin < min) localMin else min + } + + override fun getCurrentMax(): Float { if (max != null) { return max!! } @@ -110,7 +217,7 @@ class YAxis( return if (seriesMax > max) seriesMax else max } - override fun getYMin(): Float { + override fun getCurrentMin(): Float { if (min != null) { return min!! } @@ -162,8 +269,8 @@ class YAxis( return 0f } - val min = getYMin() - val max = getYMax() - min + val min = getCurrentMin() + val max = getCurrentMax() - min var maxWidth = 0f val valueIncrement = max / (labelCount - 1).coerceAtLeast(1) @@ -176,7 +283,7 @@ class YAxis( } val text = onValueFormat(min + (valueIncrement * index)) - textLabel.getTextBounds(text, 0, text.length, rect) + textPaint.getTextBounds(text, 0, text.length, rect) maxWidth = maxWidth.coerceAtLeast(rect.width().toFloat()) val posY = getPositionOnRect(value, space) @@ -185,7 +292,7 @@ class YAxis( text, space.width() - rect.width().toFloat(), (posY + rect.height() / 2).coerceAtLeast(rect.height().toFloat()), - textLabel + textPaint ) canvas.drawLine(space.left, posY, space.right - maxWidth - 32f, posY, linePaint) } @@ -193,7 +300,7 @@ class YAxis( for ((y, settings) in lines) { val pos = getPositionOnRect(y, space) val text = onValueFormat(y) - textLabel.getTextBounds(text, 0, text.length, rect) + textPaint.getTextBounds(text, 0, text.length, rect) if (settings.dotted) { canvas.drawDottedLine( 0f, @@ -216,7 +323,7 @@ class YAxis( text, space.width() - rect.width().toFloat(), (pos + rect.height() / 2).coerceAtLeast(rect.height().toFloat()), - textLabel + textPaint ) } @@ -274,11 +381,11 @@ class YAxis( return getPositionOnRect(entry.y, drawableSpace) } - override fun getPositionOnRect(point: Float, drawableSpace: RectF): Float { - val min = getYMin() - val max = getYMax() + override fun getPositionOnRect(position: Float, drawableSpace: RectF): Float { + val min = getCurrentMin() + val max = getCurrentMax() - return (1 - (point - min) / (max - min)) * + return (1 - (position - min) / (max - min)) * drawableSpace.height() + drawableSpace.top } diff --git a/library/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt b/library/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt index 2b08927..4d6e285 100644 --- a/library/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt +++ b/library/src/main/java/com/dzeio/charts/axis/YAxisInterface.kt @@ -1,97 +1,20 @@ package com.dzeio.charts.axis -import android.graphics.Canvas import android.graphics.Paint -import android.graphics.RectF -import com.dzeio.charts.Entry -sealed interface YAxisInterface { - - /** - * whether or not this axis is displayed - */ - var enabled: Boolean - - /** - * get/set the number of label of this Y axis - * - * the first/last labels are at the bottom/top of the chart - */ - var labelCount: Int - - /** - * text label paint - */ - val textLabel: Paint - - /** - * paint for the lines - */ - val linePaint: Paint +sealed interface YAxisInterface : AxisInterface { /** * Goal line paint */ val goalLinePaint: Paint - /** - * is vertical scrolling enabled - */ - var scrollEnabled: Boolean - - var onValueFormat: (value: Float) -> String - /** * do the Zero line gets drawn? */ @Deprecated("use the new global function", ReplaceWith("YAxisInterface.addLine")) var drawZeroLine: Boolean - /** - * run when manually refreshing the system - * - * this is where the pre-logic is handled to make [onDraw] quicker - */ - fun refresh() - - /** - * override Y minimum - * - * @param yMin is set the min will ba at the value, if null it is calculated - */ - fun setYMin(yMin: Float?): YAxisInterface - - /** - * override Y maximum - * - * @param yMax is set the max will ba at the value, if null it is calculated - */ - fun setYMax(yMax: Float?): YAxisInterface - - /** - * get Y maximum - * - * @return the maximum value Y can get (for displayed values) - */ - fun getYMax(): Float - - /** - * get Y minimum - * - * @return the minimum value Y can get (for displayed values) - */ - fun getYMin(): Float - - /** - * function that draw our legend - * - * @param canvas the canvas to draw on - * @param space the space where it is allowed to draw on - * - * @return the width of the sidebar - */ - fun onDraw(canvas: Canvas, space: RectF): Float - /** * Add a Goal line * @@ -125,24 +48,4 @@ sealed interface YAxisInterface { * Remove every lines */ fun clearLines() - - /** - * get the position of an [entry] Y position in the [drawableSpace] - * - * if the chart type is stacked it will automatically calculate the position depending on it - * - * @param entry the entry to search to position - * @param drawableSpace the space in which it should appear - * @return the float position (can be out of the [drawableSpace]) - */ - fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Float - - /** - * get the position of a [point] in the [drawableSpace] - * - * @param point the point to search to position - * @param drawableSpace the space in which it should appear - * @return the float position (can be out of the [drawableSpace]) - */ - fun getPositionOnRect(point: Float, drawableSpace: RectF): Float } diff --git a/library/src/main/java/com/dzeio/charts/components/ChartScroll.kt b/library/src/main/java/com/dzeio/charts/components/ChartScroll.kt index d86d7f6..d99470a 100644 --- a/library/src/main/java/com/dzeio/charts/components/ChartScroll.kt +++ b/library/src/main/java/com/dzeio/charts/components/ChartScroll.kt @@ -1,5 +1,6 @@ package com.dzeio.charts.components +import android.util.Log import android.view.MotionEvent import android.view.MotionEvent.INVALID_POINTER_ID import android.view.ScaleGestureDetector @@ -9,7 +10,7 @@ import kotlin.math.abs /** * Class handling the scroll/zoom for the library */ -class ChartScroll(view: View) { +class ChartScroll(private val view: View) { /** * Enabled the zoom/unzoom of datas @@ -43,7 +44,7 @@ class ChartScroll(view: View) { onChartMoved = fn } - private var onZoomChanged: ((scale: Float) -> Unit)? = null + private var onZoomChanged: ((scaleX: Float, scaleY: Float) -> Unit)? = null /** * @param fn.scale Float starting from 100% @@ -51,7 +52,7 @@ class ChartScroll(view: View) { * 99-% zoom out, * 101+% zoom in */ - fun setOnZoomChanged(fn: (scale: Float) -> Unit) { + fun setOnZoomChanged(fn: (scaleX: Float, scaleY: Float) -> Unit) { onZoomChanged = fn } @@ -59,29 +60,29 @@ class ChartScroll(view: View) { view.context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { - if (currentZoom != detector.scaleFactor) { - currentZoom = detector.scaleFactor - onZoomChanged?.invoke(lastZoom + -currentZoom + 1) - } + Log.d("ChartScroll", detector.scaleFactor.toString()) + val scaleX = processScaling(detector.currentSpanX, detector.previousSpanX) + val scaleY = processScaling(detector.currentSpanY, detector.previousSpanY) + onZoomChanged?.invoke( + 1 - scaleY, + 1 - scaleX, + ) return super.onScale(detector) } - - override fun onScaleEnd(detector: ScaleGestureDetector) { - super.onScaleEnd(detector) - - lastZoom += -currentZoom + 1 - } } ) + private fun processScaling(current: Float, previous: Float): Float { + return if (previous > 0f) current / previous else 1f + } + private var hasMoved = false /** * Code mostly stolen from https://developer.android.com/training/gestures/scale#drag */ fun onTouchEvent(ev: MotionEvent): Boolean { - if (zoomEnabled) { scaleGestureDetector.onTouchEvent(ev) } @@ -117,7 +118,6 @@ class ChartScroll(view: View) { posX += moveX posY += moveY - if (scrollEnabled) { onChartMoved?.invoke(-posX, posY) } diff --git a/library/src/main/java/com/dzeio/charts/series/BaseSerie.kt b/library/src/main/java/com/dzeio/charts/series/BaseSerie.kt index 4f28587..a0c3d43 100644 --- a/library/src/main/java/com/dzeio/charts/series/BaseSerie.kt +++ b/library/src/main/java/com/dzeio/charts/series/BaseSerie.kt @@ -33,8 +33,8 @@ sealed class BaseSerie( } override fun getDisplayedEntries(): ArrayList { - val minX = view.xAxis.x - val maxX = minX + view.xAxis.getDataWidth() + val minX = view.xAxis.getCurrentMin() + val maxX = view.xAxis.getCurrentMax() val result: ArrayList = arrayListOf() diff --git a/sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt b/sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt index 0631afa..b5fd732 100644 --- a/sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt +++ b/sample/src/main/java/com/dzeio/chartsapp/ui/ChartFragment.kt @@ -125,7 +125,7 @@ class ChartFragment : Fragment() { binding.sliderXAxisScroll.visibility = View.VISIBLE } else { chart.xAxis.dataWidth = null - chart.xAxis.x = 0.0 + chart.xAxis.setCurrentMin(0.0) binding.sliderXAxisScroll.visibility = View.GONE } chart.refresh() diff --git a/sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt b/sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt index 3590d22..12391de 100644 --- a/sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt +++ b/sample/src/main/java/com/dzeio/chartsapp/ui/MainFragment.kt @@ -72,6 +72,11 @@ class MainFragment : Fragment() { LineSerie(this).apply { entries = Utils.generateRandomDataset(5) } + this.xAxis.keepGlobalLimits = true + this.xAxis.zoomEnabled = true + this.xAxis.scrollEnabled = true + this.yAxis.zoomEnabled = true + this.yAxis.scrollEnabled = true MaterialUtils.materielTheme(this, requireView()) } diff --git a/sample/src/main/java/com/dzeio/chartsapp/utils/MaterialUtils.kt b/sample/src/main/java/com/dzeio/chartsapp/utils/MaterialUtils.kt index 346b9c1..2402132 100644 --- a/sample/src/main/java/com/dzeio/chartsapp/utils/MaterialUtils.kt +++ b/sample/src/main/java/com/dzeio/chartsapp/utils/MaterialUtils.kt @@ -9,7 +9,7 @@ import com.google.android.material.color.MaterialColors object MaterialUtils { fun materielTheme(chart: ChartViewInterface, view: View) { chart.yAxis.apply { - textLabel.color = + textPaint.color = MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer) linePaint.color = MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer) @@ -17,8 +17,13 @@ object MaterialUtils { MaterialColors.getColor(view, com.google.android.material.R.attr.colorError) } - chart.xAxis.textPaint.color = - MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer) + chart.xAxis.apply { + textPaint.color = + MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer) + linePaint.color = + MaterialColors.getColor(view, com.google.android.material.R.attr.colorOnPrimaryContainer) + } + chart.annotator.apply { backgroundPaint.color = MaterialColors.getColor( view, diff --git a/sample/src/main/res/layout/fragment_main.xml b/sample/src/main/res/layout/fragment_main.xml index ea565e9..a4b0574 100644 --- a/sample/src/main/res/layout/fragment_main.xml +++ b/sample/src/main/res/layout/fragment_main.xml @@ -32,7 +32,7 @@ + android:layout_height="500dp" />