Skip to content

Commit f6275de

Browse files
committed
TimeBar Double
1 parent 737bc8c commit f6275de

File tree

5 files changed

+287
-0
lines changed

5 files changed

+287
-0
lines changed

app/src/androidTest/kotlin/info/appdev/chartexample/StartTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import org.junit.Test
4242
import org.junit.rules.TestName
4343
import org.junit.runner.RunWith
4444
import timber.log.Timber
45+
import kotlin.jvm.java
4546

4647

4748
@RunWith(AndroidJUnit4::class)
@@ -278,6 +279,7 @@ class StartTest {
278279
contentItem.clazz == HorizontalBarFullComposeActivity::class.java ||
279280
contentItem.clazz == MultiLineComposeActivity::class.java ||
280281
contentItem.clazz == GradientActivity::class.java ||
282+
contentItem.clazz == TimeBarActivity::class.java ||
281283
contentItem.clazz == TimeLineActivity::class.java
282284
) {
283285
// These charts have less clickable area, so skip further clicks

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
<activity android:name="info.appdev.chartexample.HalfPieChartActivity" />
6161
<activity android:name="info.appdev.chartexample.SpecificPositionsLineChartActivity" />
6262
<activity android:name="info.appdev.chartexample.TimeLineActivity" />
63+
<activity android:name="info.appdev.chartexample.TimeBarActivity" />
6364
</application>
6465

6566
</manifest>
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package info.appdev.chartexample
2+
3+
import android.Manifest
4+
import android.R.attr.entries
5+
import android.content.Intent
6+
import android.content.pm.PackageManager
7+
import android.graphics.Color
8+
import android.os.Bundle
9+
import android.view.Menu
10+
import android.view.MenuItem
11+
import android.widget.SeekBar
12+
import android.widget.SeekBar.OnSeekBarChangeListener
13+
import androidx.core.content.ContextCompat
14+
import androidx.core.content.res.ResourcesCompat
15+
import androidx.core.net.toUri
16+
import info.appdev.chartexample.DataTools.Companion.getValues
17+
import info.appdev.chartexample.TimeLineActivity.Companion.TIME_OFFSET
18+
import info.appdev.chartexample.databinding.ActivityHorizontalbarchartBinding
19+
import info.appdev.chartexample.formatter.UnixTimeAxisValueFormatter
20+
import info.appdev.chartexample.formatter.UnixTimeRelative2NowAxisValueFormatter
21+
import info.appdev.chartexample.formatter.UnixTimeValueFormatter
22+
import info.appdev.chartexample.notimportant.DemoBase
23+
import info.appdev.charting.components.Description
24+
import info.appdev.charting.components.Legend
25+
import info.appdev.charting.components.XAxis.XAxisPosition
26+
import info.appdev.charting.data.BarData
27+
import info.appdev.charting.data.BarDataSet
28+
import info.appdev.charting.data.BarEntryDouble
29+
import info.appdev.charting.data.BarEntryFloat
30+
import info.appdev.charting.interfaces.datasets.IBarDataSet
31+
import timber.log.Timber
32+
33+
class TimeBarActivity : DemoBase(), OnSeekBarChangeListener {
34+
35+
private lateinit var binding: ActivityHorizontalbarchartBinding
36+
37+
override fun onCreate(savedInstanceState: Bundle?) {
38+
super.onCreate(savedInstanceState)
39+
binding = ActivityHorizontalbarchartBinding.inflate(layoutInflater)
40+
setContentView(binding.root)
41+
42+
binding.seekBarX.setOnSeekBarChangeListener(this)
43+
binding.seekBarY.setOnSeekBarChangeListener(this)
44+
45+
binding.chart1.isLogging = true
46+
binding.chart1.isDrawBarShadow = false
47+
binding.chart1.isDrawValueAboveBar = true
48+
binding.chart1.description.isEnabled = true
49+
binding.chart1.description = Description().apply {
50+
text = "Time Bar Line"
51+
}
52+
53+
// if more than 60 entries are displayed in the chart, no values will be drawn
54+
binding.chart1.setMaxVisibleValueCount(60)
55+
56+
// scaling can now only be done on x- and y-axis separately
57+
binding.chart1.isPinchZoom = false
58+
59+
// draw shadows for each bar that show the maximum value
60+
// chart.setDrawBarShadow(true);
61+
binding.chart1.setDrawGridBackground(false)
62+
63+
binding.chart1.xAxis.apply {
64+
position = XAxisPosition.BOTTOM
65+
typeface = tfLight
66+
isDrawAxisLine = true
67+
isDrawGridLines = true
68+
granularity = 10f
69+
// valueFormatter = UnixTimeAxisValueFormatter("HH:mm:ss")
70+
}
71+
72+
// binding.chart1.axisLeft.apply {
73+
// typeface = tfLight
74+
// isDrawAxisLine = true
75+
// isDrawGridLines = true
76+
// axisMinimum = 0f // this replaces setStartAtZero(true)
77+
// }
78+
79+
binding.chart1.axisRight.apply {
80+
typeface = tfLight
81+
isDrawAxisLine = true
82+
isDrawGridLines = false
83+
axisMinimum = 0f // this replaces setStartAtZero(true)
84+
valueFormatter = UnixTimeRelative2NowAxisValueFormatter("HH:mm:ss")
85+
}
86+
87+
binding.chart1.setFitBars(true)
88+
binding.chart1.animateY(2500)
89+
90+
// setting data
91+
binding.seekBarX.progress = 4
92+
binding.seekBarY.progress = 12
93+
94+
binding.chart1.legend.apply {
95+
verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM
96+
horizontalAlignment = Legend.LegendHorizontalAlignment.LEFT
97+
orientation = Legend.LegendOrientation.HORIZONTAL
98+
setDrawInside(false)
99+
formSize = 8f
100+
xEntrySpace = 4f
101+
}
102+
}
103+
104+
private fun setData(count: Int) {
105+
val barWidth = 9f
106+
val spaceForBar = 10.0
107+
val values = ArrayList<BarEntryDouble>()
108+
val sampleValues = getValues(100).map { (it!! * 100).toInt() }
109+
110+
var previousTimeOffset: Double = 0.0 //TIME_OFFSET.toDouble()
111+
for (i in 0..<count) {
112+
Timber.d("add ${sampleValues[i]}s to $previousTimeOffset")
113+
val yValue = sampleValues[i] + previousTimeOffset
114+
val value = BarEntryDouble(
115+
x = i * spaceForBar,
116+
vals = doubleArrayOf(previousTimeOffset, yValue),
117+
icon = ResourcesCompat.getDrawable(resources, R.drawable.star, null)
118+
)
119+
values.add(value)
120+
previousTimeOffset = yValue
121+
}
122+
123+
Timber.d(values.joinToString(separator = "\n"))
124+
125+
val set1: BarDataSet
126+
127+
if (binding.chart1.barData != null &&
128+
binding.chart1.barData!!.dataSetCount > 0
129+
) {
130+
set1 = binding.chart1.barData!!.getDataSetByIndex(0) as BarDataSet
131+
@Suppress("UNCHECKED_CAST")
132+
set1.entries = values as MutableList<BarEntryFloat>
133+
binding.chart1.barData?.notifyDataChanged()
134+
binding.chart1.notifyDataSetChanged()
135+
} else {
136+
@Suppress("UNCHECKED_CAST")
137+
set1 = BarDataSet(values as MutableList<BarEntryFloat>, "Bar DataSet")
138+
set1.setColors(
139+
Color.GREEN,
140+
Color.BLUE,
141+
Color.YELLOW
142+
)
143+
144+
set1.isDrawIcons = false
145+
146+
val dataSets = ArrayList<IBarDataSet>()
147+
dataSets.add(set1)
148+
149+
val data = BarData(dataSets)
150+
data.setValueTextSize(10f)
151+
data.setValueTypeface(tfLight)
152+
data.setValueFormatter(UnixTimeValueFormatter("HH:mm:ss"))
153+
data.barWidth = barWidth
154+
binding.chart1.data = data
155+
}
156+
}
157+
158+
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
159+
menuInflater.inflate(R.menu.bar, menu)
160+
return true
161+
}
162+
163+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
164+
when (item.itemId) {
165+
R.id.viewGithub -> {
166+
val i = Intent(Intent.ACTION_VIEW)
167+
i.data =
168+
"https://github.com/AppDevNext/AndroidChart/blob/master/app/src/main/java/info/appdev/chartexample/HorizontalBarChartActivity.kt".toUri()
169+
startActivity(i)
170+
}
171+
172+
R.id.actionToggleValues -> {
173+
binding.chart1.barData?.dataSets?.forEach {
174+
it.isDrawValues = !it.isDrawValues
175+
}
176+
binding.chart1.invalidate()
177+
}
178+
179+
R.id.actionToggleIcons -> {
180+
binding.chart1.barData?.dataSets?.forEach { set ->
181+
set.isDrawIcons = !set.isDrawIcons
182+
}
183+
binding.chart1.invalidate()
184+
}
185+
186+
R.id.actionToggleHighlight -> {
187+
binding.chart1.barData?.let { data ->
188+
data.isHighlight = !data.isHighlight
189+
binding.chart1.invalidate()
190+
}
191+
}
192+
193+
R.id.actionTogglePinch -> {
194+
binding.chart1.isPinchZoom = !binding.chart1.isPinchZoom
195+
binding.chart1.invalidate()
196+
}
197+
198+
R.id.actionToggleAutoScaleMinMax -> {
199+
binding.chart1.isAutoScaleMinMax = !binding.chart1.isAutoScaleMinMax
200+
binding.chart1.notifyDataSetChanged()
201+
}
202+
203+
R.id.actionToggleBarBorders -> {
204+
binding.chart1.barData?.dataSets?.map { it as BarDataSet }?.forEach { set ->
205+
set.barBorderWidth = if (set.barBorderWidth == 1f) 0f else 1f
206+
}
207+
binding.chart1.invalidate()
208+
}
209+
210+
R.id.animateX -> {
211+
binding.chart1.animateX(2000)
212+
}
213+
214+
R.id.animateY -> {
215+
binding.chart1.animateY(2000)
216+
}
217+
218+
R.id.animateXY -> {
219+
binding.chart1.animateXY(2000, 2000)
220+
}
221+
222+
R.id.actionSave -> {
223+
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
224+
saveToGallery()
225+
} else {
226+
requestStoragePermission(binding.chart1)
227+
}
228+
}
229+
}
230+
return true
231+
}
232+
233+
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
234+
binding.tvXMax.text = binding.seekBarX.progress.toString()
235+
binding.tvYMax.text = binding.seekBarY.progress.toString()
236+
237+
setData(binding.seekBarX.progress)
238+
binding.chart1.setFitBars(true)
239+
binding.chart1.invalidate()
240+
}
241+
242+
override fun saveToGallery() {
243+
saveToGallery(binding.chart1, "HorizontalBarChartActivity")
244+
}
245+
246+
override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
247+
248+
override fun onStopTrackingTouch(seekBar: SeekBar?) = Unit
249+
250+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package info.appdev.chartexample.formatter
2+
3+
import info.appdev.charting.data.BarEntryFloat
4+
import info.appdev.charting.data.EntryFloat
5+
import info.appdev.charting.formatter.IValueFormatter
6+
import info.appdev.charting.utils.ViewPortHandler
7+
import timber.log.Timber
8+
import java.text.SimpleDateFormat
9+
import java.util.Locale
10+
11+
class UnixTimeValueFormatter(val format: String = "yyyy-MM-dd'T'HH:mm:ss'Z'") : IValueFormatter {
12+
13+
val simpleDateFormat = SimpleDateFormat(format, Locale.getDefault())
14+
15+
// For bar/line values
16+
override fun getFormattedValue(value: Float, entryFloat: EntryFloat?, dataSetIndex: Int, viewPortHandler: ViewPortHandler?): String {
17+
return if (entryFloat is BarEntryFloat) {
18+
Timber.d("$value ${simpleDateFormat.format(entryFloat.yVals?.get(0)!!.toLong()) +
19+
" - " +
20+
simpleDateFormat.format(entryFloat.yVals?.get(entryFloat.yVals?.size!! - 1)!!.toLong()) +
21+
"\n" +
22+
entryFloat.yVals?.get(0) +
23+
"-" +
24+
entryFloat.yVals?.get(entryFloat.yVals?.size!! - 1)} $entryFloat")
25+
simpleDateFormat.format(entryFloat.yVals?.get(0)!!.toLong()) +
26+
" - " +
27+
simpleDateFormat.format(entryFloat.yVals?.get(entryFloat.yVals?.size!! - 1)!!.toLong())
28+
}
29+
else
30+
simpleDateFormat.format(value.toLong())
31+
}
32+
}

app/src/main/kotlin/info/appdev/chartexample/notimportant/MainActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import info.appdev.chartexample.ScrollViewActivity
7575
import info.appdev.chartexample.SpecificPositionsLineChartActivity
7676
import info.appdev.chartexample.StackedBarActivity
7777
import info.appdev.chartexample.StackedBarActivityNegative
78+
import info.appdev.chartexample.TimeBarActivity
7879
import info.appdev.chartexample.TimeLineActivity
7980
import info.appdev.chartexample.compose.HorizontalBarComposeActivity
8081
import info.appdev.chartexample.compose.HorizontalBarFullComposeActivity
@@ -160,6 +161,7 @@ class MainActivity : ComponentActivity() {
160161
companion object {
161162
val menuItems = ArrayList<ContentItem<out DemoBase>>().apply {
162163
add(ContentItem("Line Charts"))
164+
add(ContentItem("TimeBar", "Show a time line with Unix timestamp", TimeBarActivity::class.java))
163165
add(ContentItem("Basic", "Simple line chart.", LineChartActivity::class.java))
164166
add(ContentItem("Multiple", "Show multiple data sets.", MultiLineChartActivity::class.java))
165167
add(ContentItem("Dual Axis", "Line chart with dual y-axes.", LineChartDualAxisActivity::class.java))

0 commit comments

Comments
 (0)