Kotlin: setting custom labels on x axis in line chart Mpandroidchart - android

i have an arrayList of String with lots of dates (4500 app), i need put all this dates on the xAxis of my chart create with MPAndroidChart, how can do this i Kotlin?
Here is my code:
val chart= ChartUtils()
val mChart:LineChart = root.findViewById(R.id.lineChartEvolCuota) as LineChart
mChart.isDragEnabled = true
mChart.setScaleEnabled(false)
mChart.axisRight.isEnabled=false
val test : MutableList<object> = db.Object()
val yArray= ArrayList<Entry>()
val xLabel = ArrayList<String>()
var c = 0f
for (i in test){
yArray.add(Entry(c, i.value!!.toFloat()))
xLabel.add(i.date.toString())
c+=1f
}
val xaxis:XAxis = mChart.xAxis
xaxis.granularity=1f
val set1 = LineDataSet(yFondoA, "TEST")
set1 .color = Color.parseColor(color)
set1 .setDrawCircles(false)
set1 .setDrawValues(false)
val data = LineData(set1)
mChart.data = data
mChart.setVisibleXRangeMaximum(365f)
mChart.moveViewToX(c)
SOLUTION
val xaxis:XAxis = mChart.xAxis
xaxis.granularity=1f
xaxis.setValueFormatter(object : ValueFormatter() {
val pattern = "dd MMM yy"
private val mFormat = SimpleDateFormat(pattern)
private val inputFormat = SimpleDateFormat("yyyy-MM-dd")
override fun getFormattedValue(value: Float): String {
val millis = TimeUnit.HOURS.toMillis(value.toLong())
return mFormat.format(inputFormat.parse(xLabel[value.toInt()]))
}
})

I found a nice java example here that I think will help you:
xAxis.setValueFormatter(new ValueFormatter() {
private final SimpleDateFormat mFormat = new SimpleDateFormat("dd MMM HH:mm", Locale.ENGLISH);
#Override
public String getFormattedValue(float value) {
long millis = TimeUnit.HOURS.toMillis((long) value);
return mFormat.format(new Date(millis));
}
});
When copying the java segment to your code, your jetbrains IDE will ask you to automatically convert to kotlin.
Using a custom formatter is cleaner than manually setting the labels.
Depending on how your dates are extracted from the data you will possibly need to adjust the following line from the example:
long millis = TimeUnit.HOURS.toMillis((long) value);
Also note that you added your labels to a list but never use them afterwards, which is why you are not seeing them.

Related

How to filter object list based on date property on kotlin

What I am trying to do is given a list of objects responseList<Object> in which the objects have a date field on string format "2015-01-16", filter the list to only get objects form last month or the current year and add the filtered objects to a new list.
The first thing that I have done is convert the string into date and the result is "Fri Jan 16 00:00:00 GMT-05:00 2015".
using:
val dateformated = getFormatedDate(object.date)
val cal = Calendar.getInstance()
cal.time = dateFormated; //date formatted is the string converted to date
var responseMonth = cal.get(Calendar.MONTH)
var responseYear = cal.get(Calendar.YEAR)
I am able to retrieve both month and year of the response and using this:
val now: LocalDate = LocalDate.now()
val lastMonth = now.minusMonths(1).month
val thisYear = now.year
I get the current year and month
What I am failing to is filtering the object list based on the requirement to create another list.
Any help or suggestions would be great, thanks
#Test
fun filterDateListByLastMonthOrThisYear() {
val fakeDateOne = LocalDate.of(2023, 2, 1)
val fakeDateTwo = LocalDate.of(2020, 3, 12)
val fakeDateThree = LocalDate.of(1999, 12, 31)
val fakeDateFour = LocalDate.of(2023, 2, 2)
val fakeDateFive = LocalDate.of(2023, 1, 3)
val datesList = listOf(
fakeDateOne,
fakeDateTwo,
fakeDateThree,
fakeDateFour,
fakeDateFive
)
val today = LocalDate.now()
val lastMonth = today.month.minus(1)
val thisYear = today.year
val expected = listOf(fakeDateOne, fakeDateFour, fakeDateFive)
val actual = datesList.filter { it.year == thisYear || it.month == lastMonth }
assertEquals(expected, actual)
}

Add x axis as datetime label in MPAndroidChart? (Kotlin)

I believe this question and answer explains how to format time series data into readable date labels in Java. How do you do the same thing in Kotlin?
You could create a custom formatter class extending the IAxisValueFormatter:
class MyCustomFormatter() : IAxisValueFormatter
{
override fun getFormattedValue(value: Float, axis: AxisBase?): String
{
val dateInMillis = value.toLong()
val date = Calendar.getInstance().apply {
timeInMillis = dateInMillis
}.time
return SimpleDateFormat("dd MMM", Locale.getDefault()).format(date)
}
}
Then assign it to your chart with
chart?.xAxis?.valueFormatter = MyCustomFormatter()
Using version 3.0+ of the MPAndroidChart:
Set formatter to the x axis:
// Formatter to adjust epoch time to readable date
lineChart.xAxis.valueFormatter = LineChartXAxisValueFormatter()
Create a new class LineChartXAxisValueFormatter:
class LineChartXAxisValueFormatter : IndexAxisValueFormatter() {
override fun getFormattedValue(value: Float): String? {
// Convert float value to date string
// Convert from seconds back to milliseconds to format time to show to the user
val emissionsMilliSince1970Time = value.toLong() * 1000
// Show time in local version
val timeMilliseconds = Date(emissionsMilliSince1970Time)
val dateTimeFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
return dateTimeFormat.format(timeMilliseconds)
}
}
When the entries are added to the ChartDataArray they are added in seconds, not milliseconds, to avoid potential precision issues with inputting as a float (i.e. milliseconds divided by 1000).
chartDataArray.add(Entry(secondsSince1970.toFloat(), yValue.toFloat()))
Happy coding!

MPAndroidChart - How to display a date only once on x axis with data points in milliseconds?

I have a LineChart in my app. The x value of the data points I use are milliseconds that I display as a date using ValueFormatter. This works fine, except I only want the date to be displayed once. I tried the following code, and it almost works, however it only works for the first date displayed. Any help appreciated!
Here is an example of my problem:
This is my most successful code, I use kotlin:
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
val dateFormatter = SimpleDateFormat("dd/MM")
var lastDate = ""
val formatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
val date = dateFormatter.format(Date(value.toLong()))
if (date==lastDate){
return ""
}
lastDate = date
return date
}
}
xAxis.valueFormatter = formatter
The following is the whole function, the json is temporary and is only there for testing purposes, the app will receive similar jsons.
private fun chart(){
val json = JSONObject("""{"payload":{"serie":{"data":
[[1564129484189,3287],[1564129933626,3287],[1564130385185,3287],[1564130834622,3280],[1564131284854,3280],[1564131736401,3280],
[1564132184652,3280],[1564132634092,3280],[1564133083925,3280],[1564133534963,3287],[1564133984117,3287],[1564134434677,3287],
[1564134883854,3280],[1564135333830,3280],[1564135785080,3280],[1564136234991,3280],[1564136685793,3280],[1564137134667,3280],
[1564137584570,3280],[1564138034367,3280],[1564138484236,3280],[1564138934104,3273],[1564139384690,3273],[1564139834549,3273],
[1564140284417,3280],[1564140734298,3280],[1564141183544,3280],[1564141634006,3273],[1564142083871,3273],[1564142533644,3273],
[1564142984818,3273],[1564143433797,3273],[1564143883681,3273],[1564144334485,3273],[1564144785207,3273],[1564145234378,3273],
[1564145685180,3280],[1564146134070,3280],[1564146584966,3280],[1564147034886,3280],[1564147484703,3280],[1564147934612,3280],
[1564148384456,3280],[1564148834343,3280],[1564149283943,3280],[1564149734825,3280],[1564150184715,3280],[1564150634494,3280],
[1564151084377,3287],[1564151534299,3287],[1564151984188,3287],[1564215691264,3287],[1564216142234,3287],[1564216591762,3287],
[1564217041687,3287],[1564217491103,3287],[1564217941061,3287],[1564218391529,3287],[1564218841469,3287],[1564219291586,3287],
[1564219741426,3287],[1564220192557,3287],[1564220641356,3287],[1564221091465,3280],[1564221541440,3280],[1564221991829,3280],
[1564222441313,3287],[1564222893010,3287],[1564223344202,3287],[1564223792494,3280],[1564224696695,3280],[1564225144701,3280],
[1564225591575,3280],[1564226047071,3280],[1564226492027,3280],[1564226951626,3280],[1564227392573,3280],[1564227841277,3280],
[1564228292385,3287],[1564228741844,3287],[1564229192885,3287],[1564229642831,3287],[1564230092311,3287],[1564230550690,3287],
[1564230993205,3287],[1564231443101,3287],[1564231892541,3287],[1564232344078,3280],[1564232793143,3280],[1564233255081,3280]]}}}""")
val payload = json.get("payload") as JSONObject
val serie = payload.get("serie") as JSONObject
val dataPoints = serie.get("data") as JSONArray
val values = arrayListOf<Entry>()
for (i in 0 until dataPoints.length()) {
val innerData = dataPoints[i] as JSONArray
values.add (Entry(
innerData[0].toString().toFloat() ,
innerData[1].toString().toFloat()
))
}
val chart = findViewById<LineChart>(R.id.graph2)
chart.setTouchEnabled(true)
chart.setPinchZoom(true)
chart.isScaleYEnabled = false
val dataSet = LineDataSet(values, "Service")
dataSet.color = ContextCompat.getColor(this, R.color.colorAccent)
dataSet.setCircleColor(ContextCompat.getColor(this, R.color.colorPrimaryDark))
dataSet.setDrawValues(false)
dataSet.lineWidth = 2f
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
val dateFormatter = SimpleDateFormat("dd/MM")
var lastDate = ""
val formatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
val date = dateFormatter.format(Date(value.toLong()))
if (date==lastDate){
return ""
}
lastDate = date
return date
}
}
xAxis.valueFormatter = formatter
val yAxisRight = chart.axisRight
yAxisRight.isEnabled = false
val yAxisLeft = chart.axisLeft
yAxisLeft.granularity = 1f
val desc = Description()
desc.text = "custom description"
chart.description = desc
val data = LineData(dataSet)
chart.data = data
chart.animateX(1000)
chart.invalidate()
}
I don't know in kotlin but in java or for some idea you can refer to my answer in the given link- DateTime axis in MPAndroidChart
I'm attaching the code in which I've used IndexAxisValueFormatter. Also declare dev String array in your class final String dev[] = new String[100]; and use it as in below code.
for (int i = 0; i < jsonArray1.length(); i++) {
JSONObject object1 = jsonArray1.getJSONObject(i);
dev[i] = object1.getString("block_time"); //for xaxis needed
XAxis bottomAxis = lineChart.getXAxis();
bottomAxis.setValueFormatter(new IndexAxisValueFormatter(dev));/*for x axis values*/
bottomAxis.setLabelCount(lineEntries.size());
bottomAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
bottomAxis.setDrawLabels(true); //to hide all xaxis values
bottomAxis.setDrawGridLines(false);
bottomAxis.setDrawAxisLine(true);}

Graph using mpAndroid Chart xAxis is not displaying the date i formatted

I have a code to display a data in a line graph and I need to change the xAxis to date but the log have a error
java.lang.NumberFormatException: For input string: "2019-04-19 00:00:00"
Below is my code snippet for line graph
mLineChart = findViewById(R.id.chart)
importData()
val dataSet = LineDataSet(entries, "Water")
val lineData = LineData(dataSet)
mLineChart.axisRight.isEnabled = false
mLineChart.data = lineData
dataSet.color = Color.RED
dataSet.setDrawCircles(false)
dataSet.setDrawValues(false)
dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER
mLineChart.description.text = ""
mLineChart.legend.isEnabled = false
mLineChart.invalidate()
val leftAxis = mLineChart.axisLeft
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
leftAxis.axisMinimum = 0f
leftAxis.axisMaximum = 30f
val rightAxis = mLineChart.axisRight
rightAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
rightAxis.axisMinimum = 0f
rightAxis.axisMaximum = 30f
var xAxis = mLineChart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.setDrawGridLines(false)
xAxis.axisMinimum = 0f
xAxis.labelCount = 5
xAxis.axisMaximum = 400f
xAxis.granularity = 1f
xAxis.isGranularityEnabled = true
xAxis.valueFormatter = DateAxisValueFormatter(null)
Below is my code snippet for inserting data
private fun importData() {
for (data in dataList) {
val date = java.lang.Float.parseFloat(data.date)
val water = java.lang.Float.parseFloat(data.water)
entries.add(Entry(date, water))
}
Below is my code snippet for value formatter
internal inner class DateAxisValueFormatter(private val mValues: Array<String>?) : ValueFormatter(),
IAxisValueFormatter {
var sdf = SimpleDateFormat("hh:mm", Locale.ENGLISH)
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
return sdf.format(Date(value.toLong()))
}
}
Your exception is clear, you're trying to convert a string into Float, but this string is not a Number.
If you want to display a date, you have to format your date in milliseconds before.
Try something like that:
private fun importData() {
val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
for (data in dataList) {
val date = dateFormatter.parse(data.date)
val water = java.lang.Float.parseFloat(data.water)
//Here you get your date as a Long and you convert it in Float
entries.add(Entry(date.time.toFloat(), water))
}
EDIT:
In your xAxis set your own valueFormatter this way to convert your float back in date:
val x = chart.xAxis
x.setValueFormatter { value, _ ->
SimpleDateFormat("HH:mm:ss", Locale.ENGLISH).format(value.toLong())
}

Wrong values on x-axis on linechart

I'm using MPAndroidChart in my android application but the x-axis is wrong formatted and label are duplicated. Here my code
private fun lineChart(exams: List<Exam>) {
val formatter: DateFormat = SimpleDateFormat("dd/MM/yyyy")
val lineChart: LineChart = linechart
lineChart.animateY(1000)
val d = Description()
d.text = "TEST"
lineChart.description = d
val yValues: ArrayList<Entry> = ArrayList()
var count = 0
for (exam in exams.sortedByDescending { exam -> exam.date }) {
if (getString(R.string.scores_array_admitted) != exam.score)
yValues.add(Entry(count++.toFloat(), exam.score.toFloat()))
}
val lineDataSet = LineDataSet(yValues, getString(R.string.linechart_score_label))
val dataSet: ArrayList<ILineDataSet> = ArrayList()
dataSet.add(lineDataSet)
val lineData = LineData(dataSet)
val xAxis: XAxis = lineChart.xAxis
val values = ArrayList<String>()
for (exam in exams.sortedByDescending { exam -> exam.date }) {
if (getString(R.string.scores_array_admitted) != exam.score)
values.add(formatter.format(exam.date))
}
val array = arrayOfNulls<String>(values.size)
values.toArray(array)
var aa = array!!
xAxis.valueFormatter = MyAxisValueFormatter(aa)
lineChart.data = lineData
}
In debug I see that the value in the arrays are correct, 2 exams and in values array the label are the two date of the exam and they are exactly two, but when I run my application the result is this
In x axis I would have only two date

Categories

Resources