diff --git a/app/src/main/kotlin/info/appdev/chartexample/DataTools.kt b/app/src/main/kotlin/info/appdev/chartexample/DataTools.kt index 84c7deb04..a3f09c098 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/DataTools.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/DataTools.kt @@ -158,6 +158,10 @@ class DataTools { return result } + fun getSawtoothValues(size: Int): Array { + return Array(size) { i -> if (i % 2 == 0) -1.0 else 1.0 } + } + fun setData(context: Context, lineChart: LineChart, count: Int = VAL_COUNT, range: Float = VAL_RANGE) { Timber.d("count=$count range=$range") val values = ArrayList() diff --git a/app/src/main/kotlin/info/appdev/chartexample/TimeLineActivity.kt b/app/src/main/kotlin/info/appdev/chartexample/TimeLineActivity.kt index 2d6bfe064..b6793d4ed 100644 --- a/app/src/main/kotlin/info/appdev/chartexample/TimeLineActivity.kt +++ b/app/src/main/kotlin/info/appdev/chartexample/TimeLineActivity.kt @@ -8,6 +8,8 @@ import android.view.MenuItem import androidx.core.net.toUri import androidx.lifecycle.lifecycleScope import info.appdev.chartexample.DataTools.Companion.generateSineWaves +import info.appdev.chartexample.DataTools.Companion.getSawtoothValues +import info.appdev.chartexample.custom.TimeMarkerView import info.appdev.chartexample.databinding.ActivityLinechartNoseekbarBinding import info.appdev.chartexample.formatter.UnixTimeAxisValueFormatter import info.appdev.chartexample.notimportant.DemoBase @@ -25,6 +27,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import timber.log.Timber +import java.text.SimpleDateFormat +import java.util.Locale class TimeLineActivity : DemoBase() { private var menuItemMove: MenuItem? = null @@ -77,31 +82,66 @@ class TimeLineActivity : DemoBase() { binding.chart1.axisRight.isEnabled = false - setData(60f, TIME_OFFSET) + val timeMarkerView = TimeMarkerView(this, R.layout.custom_marker_view, "HH:mm:ss.sss") + timeMarkerView.chartView = binding.chart1 + binding.chart1.marker.add(timeMarkerView) + + setData(60f, TIME_OFFSET, true) binding.chart1.invalidate() } @Suppress("SameParameterValue") - private fun setData(range: Float, timeOffset: Long) { + private fun setData(range: Float, timeOffset: Long, sinus: Boolean) { - val sampleEntries = generateSineWaves(3, 30) - .mapIndexed { index, data -> + val sampleEntries = if (sinus) + generateSineWaves(3, 30).mapIndexed { index, data -> val valueY = (data.toFloat() * range) + 50 Entry(timeOffset + index.toFloat() * 1000, valueY) - }.toMutableList() + } + else { + var previousEntry: Entry? = null + getSawtoothValues(14).mapIndexed { index, data -> + val valueY = data.toFloat() * 20 + val entry = previousEntry?.let { + // nay third value is 0, so we add here more then 1 second, otherwise we have a one second entry + if (index % 3 == 0) { + Entry(it.x + 3000, valueY) + } else + Entry(it.x + 1000, valueY) + } ?: run { + Entry(timeOffset + index.toFloat() * 1000, valueY) + } + previousEntry = entry + // Now you can use 'prev' which holds the previous Entry + entry + } + } + + val simpleDateFormat = SimpleDateFormat("HH:mm:ss.sss", Locale.getDefault()) + sampleEntries.forEach { entry -> + val entryText = "Entry: x=${simpleDateFormat.format(entry.x)} x=${entry.x}, y=${entry.y}" + Timber.d(entryText) + } val set1: LineDataSet if (binding.chart1.lineData.dataSetCount > 0) { set1 = binding.chart1.lineData.getDataSetByIndex(0) as LineDataSet - set1.entries = sampleEntries + set1.entries = sampleEntries.toMutableList() + if (sinus) + set1.lineMode = LineDataSet.Mode.LINEAR + else + set1.lineMode = LineDataSet.Mode.STEPPED binding.chart1.lineData.notifyDataChanged() binding.chart1.notifyDataSetChanged() } else { // create a dataset and give it a type - set1 = LineDataSet(sampleEntries, "DataSet 1") - + set1 = LineDataSet(sampleEntries.toMutableList(), "DataSet 1") + if (sinus) + set1.lineMode = LineDataSet.Mode.LINEAR + else + set1.lineMode = LineDataSet.Mode.STEPPED set1.axisDependency = YAxis.AxisDependency.LEFT set1.color = Color.rgb(255, 241, 46) set1.isDrawCircles = false @@ -151,6 +191,17 @@ class TimeLineActivity : DemoBase() { true }.isCheckable = true } + menuItemMove = menu?.add("Show sinus data")?.apply { + this.isChecked = true + setOnMenuItemClickListener { menuItem -> + menuItem.isChecked = !menuItem.isChecked + lifecycleScope.launch { + setData(60f, TIME_OFFSET, menuItem.isChecked) + binding.chart1.invalidate() + } + true + }.isCheckable = true + } return true } diff --git a/app/src/main/kotlin/info/appdev/chartexample/custom/TimeMarkerView.kt b/app/src/main/kotlin/info/appdev/chartexample/custom/TimeMarkerView.kt new file mode 100644 index 000000000..c4d99bb56 --- /dev/null +++ b/app/src/main/kotlin/info/appdev/chartexample/custom/TimeMarkerView.kt @@ -0,0 +1,40 @@ +package info.appdev.chartexample.custom + +import android.annotation.SuppressLint +import android.content.Context +import android.widget.TextView +import info.appdev.chartexample.R +import info.appdev.charting.components.MarkerView +import info.appdev.charting.data.Entry +import info.appdev.charting.highlight.Highlight +import info.appdev.charting.interfaces.datasets.IDataSet +import info.appdev.charting.utils.PointF +import java.text.SimpleDateFormat +import java.util.Locale + +@SuppressLint("ViewConstructor") +class TimeMarkerView(context: Context?, layoutResource: Int, val format: String = "yyyy-MM-dd'T'HH:mm:ss'Z'") : MarkerView(context, layoutResource) { + + private val simpleDateFormat = SimpleDateFormat(format, Locale.getDefault()) + private val tvContent: TextView = findViewById(R.id.tvContent) + + @SuppressLint("SetTextI18n") + override fun refreshContent(entry: Entry, highlight: Highlight) { + @Suppress("UNCHECKED_CAST") + val dataset = this.chartView?.data?.dataSets[0] as? IDataSet + val myIndex = dataset?.getEntryIndex(entry) + val nextEntry = myIndex?.let { + if (it < dataset.entryCount - 1) + dataset.getEntryForIndex(myIndex + 1) + else + null + } ?: run { null } + + val duration = if (nextEntry != null) " - duration:${(nextEntry.x - entry.x)}" else "" + tvContent.text = "${simpleDateFormat.format(entry.x)}$duration" + super.refreshContent(entry, highlight) + } + + override var offset: PointF = PointF() + get() = PointF(-(width / 2).toFloat(), -height.toFloat()) +}