diff --git a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt index fea4f3e..2d3e59a 100644 --- a/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt +++ b/app/src/main/java/com/dzeio/openhealth/ui/steps/StepsHomeFragment.kt @@ -1,11 +1,15 @@ package com.dzeio.openhealth.ui.steps import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.widget.NestedScrollView import androidx.recyclerview.widget.LinearLayoutManager import com.dzeio.charts.Entry +import com.dzeio.openhealth.Application +import com.dzeio.openhealth.R import com.dzeio.openhealth.adapters.StepsAdapter import com.dzeio.openhealth.core.BaseFragment import com.dzeio.openhealth.databinding.FragmentStepsHomeBinding @@ -19,6 +23,10 @@ import java.util.Locale class StepsHomeFragment : BaseFragment(StepsHomeViewModel::class.java) { + companion object { + const val TAG = "${Application.TAG}/SHFragment" + } + override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentStepsHomeBinding = FragmentStepsHomeBinding::inflate @@ -46,7 +54,7 @@ class StepsHomeFragment : viewModel.items.observe(viewLifecycleOwner) { list -> adapter.set(list) -// chart.numberOfEntries = list.size / 2 + chart.xAxis.entriesDisplayed = 30 chart.numberOfLabels = 2 // chart.animation.enabled = false @@ -80,5 +88,21 @@ class StepsHomeFragment : chart.refresh() } + + val scrollView = requireActivity().findViewById(R.id.scrollView) + var scrollEnabled = false + scrollView.setOnTouchListener { view, _ -> + view.performClick() + if (scrollEnabled) { + } else { + return@setOnTouchListener !scrollEnabled + } + return@setOnTouchListener true + } + + binding.chart.setOnToggleScroll { + Log.d(TAG, it.toString()) + scrollEnabled = it + } } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index eedec14..7892df8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,6 +35,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" + android:id="@+id/scrollView" app:layout_behavior="@string/appbar_scrolling_view_behavior"> diff --git a/charts/src/main/java/com/dzeio/charts/Animation.kt b/charts/src/main/java/com/dzeio/charts/Animation.kt index 57694d6..08c19b9 100644 --- a/charts/src/main/java/com/dzeio/charts/Animation.kt +++ b/charts/src/main/java/com/dzeio/charts/Animation.kt @@ -1,6 +1,7 @@ package com.dzeio.charts import kotlin.math.abs +import kotlin.math.max data class Animation( /** @@ -27,25 +28,39 @@ data class Animation( * * @return the new updated value */ - fun updateValue(maxValue: Float, targetValue: Float, currentValue: Float): Float { + fun updateValue( + maxValue: Float, + targetValue: Float, + currentValue: Float, + minValue: Float, + minStep: Float + ): Float { if (!enabled) { return targetValue } - val moveValue = (maxValue - targetValue) / refreshRate + if (currentValue < minValue) { + return minValue + } + + val moveValue = max(minStep, (maxValue - targetValue) / refreshRate) var result = targetValue if (currentValue < targetValue) { - result = currentValue + moveValue + result = currentValue + moveValue } else if (currentValue > targetValue) { result = currentValue - moveValue } - if (abs(targetValue - currentValue) < moveValue) { + if ( + abs(targetValue - currentValue) <= moveValue || + result < minValue || + result > maxValue + ) { return targetValue } return result } fun getDelay() = this.duration / this.refreshRate -} \ No newline at end of file +} diff --git a/charts/src/main/java/com/dzeio/charts/Entry.kt b/charts/src/main/java/com/dzeio/charts/Entry.kt index 0cac6b8..1a46750 100644 --- a/charts/src/main/java/com/dzeio/charts/Entry.kt +++ b/charts/src/main/java/com/dzeio/charts/Entry.kt @@ -3,4 +3,4 @@ package com.dzeio.charts data class Entry( val x: Double, val y: Float -) \ No newline at end of file +) diff --git a/charts/src/main/java/com/dzeio/charts/Labels.kt b/charts/src/main/java/com/dzeio/charts/XAxisLabels.kt similarity index 99% rename from charts/src/main/java/com/dzeio/charts/Labels.kt rename to charts/src/main/java/com/dzeio/charts/XAxisLabels.kt index 59d45e2..5131caa 100644 --- a/charts/src/main/java/com/dzeio/charts/Labels.kt +++ b/charts/src/main/java/com/dzeio/charts/XAxisLabels.kt @@ -19,4 +19,4 @@ class XAxisLabels { it.textAlign = Paint.Align.CENTER } } -} \ No newline at end of file +} diff --git a/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt b/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt index 1931321..d44c5d8 100644 --- a/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt +++ b/charts/src/main/java/com/dzeio/charts/axis/XAxis.kt @@ -16,13 +16,10 @@ class XAxis { /** * Offset in the list - * - * WILL CHANGE AS SOON AS SCROLLING IS AVAILABLE */ var baseOffset = 0 var onValueFormat: (it: T) -> String = onValueFormat@{ return@onValueFormat it.toString() } - -} \ No newline at end of file +} diff --git a/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt b/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt index 1e67a66..1d7ec06 100644 --- a/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt +++ b/charts/src/main/java/com/dzeio/charts/axis/YAxis.kt @@ -9,4 +9,4 @@ class YAxis { @ColorInt var color = Color.parseColor("#FC496D") -} \ No newline at end of file +} diff --git a/charts/src/main/java/com/dzeio/charts/builders/ChartBuilder.kt b/charts/src/main/java/com/dzeio/charts/builders/ChartBuilder.kt deleted file mode 100644 index 99be12f..0000000 --- a/charts/src/main/java/com/dzeio/charts/builders/ChartBuilder.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.dzeio.charts.builders - -class ChartBuilder { -} \ No newline at end of file diff --git a/charts/src/main/java/com/dzeio/charts/views/BarChartView.kt b/charts/src/main/java/com/dzeio/charts/views/BarChartView.kt index ae30a86..c9a9bc5 100644 --- a/charts/src/main/java/com/dzeio/charts/views/BarChartView.kt +++ b/charts/src/main/java/com/dzeio/charts/views/BarChartView.kt @@ -22,6 +22,8 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute const val TAG = "DzeioCharts/BarView" } + var debug: Boolean = false + /** * Number of entries displayed at the same time */ @@ -47,7 +49,7 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute private var barWidth: Int = 0 - private val percentList: ArrayList = ArrayList() + private var percentList: ArrayList = ArrayList() /** * value goes from 1 to 0 (1 at bottom, 0 at top) @@ -68,10 +70,24 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute private val animator: Runnable = object : Runnable { override fun run() { var needNewFrame = false +// var txt = "" +// for (tpl in targetPercentList) { +// txt += "$tpl, " +// } +// Log.d(TAG, txt) for (i in targetPercentList.indices) { - val value = animation.updateValue(1f, targetPercentList[i], percentList[i]) + val value = animation.updateValue( + 1f, + targetPercentList[i], + percentList[i], + 0f, + 0.01f + ) if (value != percentList[i]) { +// if (!needNewFrame) { +// Log.d(TAG, "$i, $value, ${percentList[i]}") +// } needNewFrame = true percentList[i] = value } @@ -87,27 +103,17 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute private var bottomTexts: ArrayList = arrayListOf() -// init { -// val mockList = ArrayList() -// for (i in 0 until 25) { -// mockList.add(Entry(i.toDouble(), i.toFloat())) -// } -// -// list = mockList -// -// this.refresh() -// } + var previousRefresh = movementOffset fun refresh() { - val r = Rect() - //// prepare bottom texts + // // prepare bottom texts bottomTextDescent = 0 bottomTextHeight = 0 bottomTexts = arrayListOf() - //// prepare values + // // prepare values // set the bar Width (also handle div by 0) barWidth = measuredWidth / max(min(list.size, getDisplayedEntries()), 1) - spacing @@ -115,19 +121,21 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute // calculate max depending on the maximum value displayed or set in the yAxis params val max: Float = if (yAxis.max != null) yAxis.max!! else { var calculatedMax = 0f - for (entry in list.subList(this.getXOffset(), getDisplayedEntries() + this.getXOffset())) { + for (entry in list.subList( + this.getXOffset(), + getDisplayedEntries() + this.getXOffset() + )) { if (entry.y > calculatedMax) calculatedMax = entry.y } - calculatedMax + if (calculatedMax < 0) 0f else calculatedMax } // make sure the target list // Log.d(TAG, list.size.toString()) targetPercentList = arrayListOf() - - for ((i, item) in list.withIndex()) { - //// Process bottom texts + for (item in list.subList(getXOffset(), getXOffset() + getDisplayedEntries())) { + // // Process bottom texts val text = xAxis.onValueFormat(item.x) bottomTexts.add(text) @@ -145,13 +153,23 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute bottomTextDescent = descent } - //// process values + // // process values // add to animations the values - targetPercentList.add(min(1 - item.y / max, 1f)) + targetPercentList.add(1 - item.y / max) } // post list + val movement = movementOffset - previousRefresh + previousRefresh = movementOffset +// Log.d(TAG, movement.toString()) + if (movement >= 1) { + percentList = percentList.subList(1, percentList.size).toCollection(ArrayList()) + percentList.add(1f) + } else if (movement <= -1) { + percentList = percentList.subList(0, percentList.size - 1).toCollection(ArrayList()) + percentList.add(0, 1f) + } if (percentList.isEmpty() || percentList.size < targetPercentList.size) { val temp = targetPercentList.size - percentList.size for (i in 0 until temp) { @@ -165,7 +183,6 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute } // Misc operations - fgPaint = Paint().apply { isAntiAlias = true color = yAxis.color @@ -178,18 +195,26 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute override fun onDraw(canvas: Canvas) { if (percentList.isNotEmpty()) { // draw each rectangles - for (i in 1..getDisplayedEntries()) { + for (i in 1..percentList.size) { // Log.d(TAG, percentList[i - 1].toString()) val left = spacing * i + barWidth * (i - 1).toFloat() // Log.d(TAG, "$spacing, $i, $barWidth = $left") val right = (spacing + barWidth) * i.toFloat() val bottom = height - bottomTextHeight - textTopMargin.toFloat() - val top = bottom * percentList[this.getXOffset() + i - 1] + val top = bottom * percentList[i - 1] // create rounded rect canvas.drawRoundRect(left, top, right, bottom, 8f, 8f, fgPaint) // remove the bottom corners DUH canvas.drawRect(left, max(top, bottom - 8f), right, bottom, fgPaint) + if (debug) { + canvas.drawText( + bottomTexts[i - 1].toString(), + left + (right - left) / 2, + top + (bottom - top) / 2, + xAxis.labels.build() + ) + } } } if (bottomTexts.isNotEmpty() && numberOfLabels > 0) { @@ -206,7 +231,6 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute // Log.i(TAG, "$size / max($numberOfLabels - 2, 2) = $items") for (s in bottomTexts) { - if ((numberOfLabels <= 2 || i % items != 0) && i != 1 && i != size) { i++ continue @@ -246,17 +270,16 @@ class BarChartView @JvmOverloads constructor(context: Context?, attrs: Attribute override fun onZoomChanged(scale: Float) { Log.d(TAG, "New Zoom: $scale") - zoom = scale + zoom = (scale * 1.2).toFloat() refresh() } private fun getXOffset(): Int { - return min(max(0, xAxis.baseOffset + movementOffset), list.size - 1 - getDisplayedEntries()) + return min(max(0, xAxis.baseOffset + movementOffset), list.size - getDisplayedEntries()) } private fun getDisplayedEntries(): Int { // Log.d(TAG, "Number of entries displayed ${list.size}, ${xAxis.entriesDisplayed} + (($zoom - 100) * 10) = ${xAxis.entriesDisplayed + ((zoom - 100) * 10).toInt()}") return max(1, min(list.size, xAxis.entriesDisplayed + ((zoom - 100) * 10).toInt())) } - } diff --git a/charts/src/main/java/com/dzeio/charts/views/BaseChart.kt b/charts/src/main/java/com/dzeio/charts/views/BaseChart.kt index 777cd2c..6a77faa 100644 --- a/charts/src/main/java/com/dzeio/charts/views/BaseChart.kt +++ b/charts/src/main/java/com/dzeio/charts/views/BaseChart.kt @@ -6,7 +6,6 @@ import android.view.MotionEvent import android.view.MotionEvent.INVALID_POINTER_ID import android.view.ScaleGestureDetector import android.view.View -import androidx.core.view.MotionEventCompat abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) : View(context, attrs) { @@ -37,7 +36,6 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { - if (currentZoom != detector.scaleFactor) { currentZoom = detector.scaleFactor onZoomChanged(lastZoom + -currentZoom + 1) @@ -51,7 +49,8 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att lastZoom += -currentZoom + 1 } - }) + } + ) /** * Code mostly stolen from https://developer.android.com/training/gestures/scale#drag @@ -63,6 +62,7 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att when (ev.actionMasked) { MotionEvent.ACTION_DOWN -> { + onToggleScroll?.invoke(false) ev.actionIndex.also { pointerIndex -> // Remember where we started (for dragging) lastTouchX = ev.getX(pointerIndex) @@ -78,8 +78,7 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att val (x: Float, y: Float) = ev.findPointerIndex(activePointerId).let { pointerIndex -> // Calculate the distance moved - ev.getX(pointerIndex) to - ev.getY(pointerIndex) + ev.getX(pointerIndex) to ev.getY(pointerIndex) } posX += x - lastTouchX @@ -92,10 +91,11 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att lastTouchY = y } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + onToggleScroll?.invoke(true) activePointerId = INVALID_POINTER_ID } MotionEvent.ACTION_POINTER_UP -> { - + onToggleScroll?.invoke(true) ev.actionIndex.also { pointerIndex -> ev.getPointerId(pointerIndex) .takeIf { it == activePointerId } @@ -103,13 +103,22 @@ abstract class BaseChart @JvmOverloads constructor(context: Context?, attrs: Att // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. val newPointerIndex = if (pointerIndex == 0) 1 else 0 - lastTouchX = MotionEventCompat.getX(ev, newPointerIndex) - lastTouchY = MotionEventCompat.getY(ev, newPointerIndex) - activePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex) + lastTouchX = ev.getX(newPointerIndex) + lastTouchY = ev.getY(newPointerIndex) + activePointerId = ev.getPointerId(newPointerIndex) } } } } return true } -} \ No newline at end of file + + private var onToggleScroll: ((Boolean) -> Unit)? = null + + /** + * @param ev if input is false disable scroll + */ + fun setOnToggleScroll(ev: (Boolean) -> Unit) { + onToggleScroll = ev + } +}