MPhiljay Barchart in android - mpandroidchart

In Barchart show x values from column belongs to one label more than 2 two but should be one.
viewModel.reportMounthData.observe(this, Observer { reportModelList->
val barChart = binding.idBarChartMounth
barChart.setScaleEnabled(false)
val entries: ArrayList<BarEntry> = ArrayList()
var i=0
reportModelList.forEach { reportModel->
entries.add(BarEntry(reportModel.YigilganBonus.toFloat(), i))
i++
}
val bardataset = BarDataSet(entries, "Oylik maoshlar miqdori")
val labels = ArrayList<String>()
reportModelList.sortedBy { it.oy }.forEach { reportModel ->
labels.add(mounthSalary(reportModel.oy))
}
val data = BarData(labels, bardataset)
barChart.data = data // set the data and list of labels into chart
barChart.setDescription("\uD83D\uDEE0 Oylik maosh statistikasi") // set the description
// bardataset.setColors(ColorTemplate.COLORFUL_COLORS)
barChart.animateY(2500)
})
enter image description here

it works perfectly until the values ​​reach 6. If there are more than 6, a defect may occur naturally. they said that the main reason for this is that it will not fit on the screen

Related

How to set Xaxis and Yaxis using data class using kotlin in MpAndroid bar chart

Halo i am trying to create barchart using MPAndroid Library but icant how to use it when i send json from php
echo json_encode($output);
and the output contain 2 data that is hari and total_jual
$rowdata[]=array('hari'=>$row['hari'],
'total_jual'=>$row['total_jual']);
in android studio i am using volley to catch jason
for(i in 0 until arr.length()){
val obj = arr.getJSONObject(i)
dataListPenjualan.add(
ClassPenjualan(
obj.getString("hari").toString(),
obj.getString("total_jual").toString()
)
)
val entries = ArrayList<BarEntry>()
val barDataSet = BarDataSet(entries, "Cells")
val labels = ArrayList<String>()
labels.add(dataListPenjualan[i].hari)
//barDataSet.setColors(ColorTemplate.COLORFUL_COLORS)
barDataSet.color = resources.getColor(R.color.black)
chartPemasukan.animateY(5000)
}
the data i catch using volley i send it into class
this is my class
data class ClassPenjualan (val hari:String,
val totalPenjualan:String)
how can i create barchart using data i catch from php. I already try to search but many explanation is in java.
this is what i try
val entries = ArrayList<BarEntry>()
entries.add(BarEntry(dataListPenjualan[i].hari.toFloat(), i))
val barDataSet = BarDataSet(entries, "Cells")
val labels = ArrayList<String>()
labels.add(dataListPenjualan[i].hari)
val data = BarData(labels, barDataSet)
chartPemasukan.data = data // set the data and list of lables into chart
chartPemasukan.setDescription("Set Bar Chart Description") // set the description
//barDataSet.setColors(ColorTemplate.COLORFUL_COLORS)
barDataSet.color = resources.getColor(R.color.black)
chartPemasukan.animateY(5000)
Your code entries.add(BarEntry(dataListPenjualan[i].hari.toFloat(), i)) is wrong.
just try entries.add(BarEntry(i,dataListPenjualan[i].hari.toFloat()))
below code is my demo
val values = ArrayList<BarEntry>()
var i = 0
while (i < xValueCount) {
val yValue = (Math.random() * (100)).toFloat()
values.add(BarEntry(i.toFloat(), yValue))
i++
}
val set1: BarDataSet
if (chart.data != null &&
chart.data.dataSetCount > 0) {
set1 = chart.data.getDataSetByIndex(0) as BarDataSet
set1.values = values
chart.data.notifyDataChanged()
chart.notifyDataSetChanged()
} else {
set1 = BarDataSet(values, "speed")
//绘制图标
set1.setDrawIcons(false)
//绘制数值
set1.setDrawValues(false)
set1.color = ContextCompat.getColor(mContext, getBarHighColorByDataType(false))
set1.highLightColor = ContextCompat.getColor(mContext, getBarHighColorByDataType(true))
set1.highLightAlpha = 255
val dataSets = ArrayList<IBarDataSet>()
dataSets.add(set1)
val data = BarData(dataSets)
data.setValueTextSize(10f)
//barWith = 柱宽度/(柱宽度+旁边一处空白宽度)
data.barWidth = when (dataType) {
0 -> 0.37f
1 -> 0.52f
2 -> 0.3f
else -> 0.43f
}
chart.data = data
}

Merge properties of a list to another based on properties objects

I got 2 lists with x objects inside , for example:
data class Model(
var token: String = "",
var id: String = "",
var name: String = "",
var image: Int = 0,
)
array is initialized and filled, the other list has x objects also that contains the objects of the first list but with different values in their properties!
what I want to do is to change the properties of the first array by the second one if they got the same object.name
var arr1 = ArrayList<Model>() // locale
var arr2 = ArrayList<Model>() // from db
the first array I got for example
[Model(name = "David", token = "" , image = 0)]
the second array I got
[Model(name = "David", token = "1asd5asdd851", image = 1)]
How do I make the first array take the missing token?
I tried with .filter{} and with .map{}. groupBy {} for hours because Name is the only properties that are the same but I'm more and more confused.
We can first group the second array by name using associateBy() and then iterate over first array and reassign properties:
val arr2ByName = arr2.associateBy { it.name }
arr1.forEach { item1 ->
arr2ByName[item1.name]?.let { item2 ->
item1.token = item2.token
item1.image = item2.image
}
}
Alternatively, if you don't need to modify items in arr1, but create another array and you can use items from both arr1 and arr2, then it will be much easier:
val arr3 = arr1.map { arr2ByName[it.name] ?: it }
One possible way would be to use fold() as follows:
fun main(args: Array<String>) {
val arr1 = listOf(Model(name = "David", token = "" , image = 0))
val arr2 = listOf(Model(name = "David", token = "1asd5asdd851", image = 1))
val mergedModels = arr2.fold(arr1) { localModels, dbModel ->
localModels.map { localModel ->
if (localModel.name == dbModel.name) localModel.copy(token = dbModel.token, image = dbModel.image)
else localModel
}
}
println(mergedModels)
}
If you want to reuse arr1 variable then you can do the following (but I would still use the previous option):
fun main(args: Array<String>) {
var arr1 = listOf(Model(name = "David", token = "" , image = 0))
val arr2 = listOf(Model(name = "David", token = "1asd5asdd851", image = 1))
arr1 = arr2.fold(arr1) { localModels, dbModel ->
localModels.map { localModel ->
if (localModel.name == dbModel.name) localModel.copy(token = dbModel.token, image = dbModel.image)
else localModel
}
}
println(arr1)
}

Kotlin ListView IndexOutOfBoundsException

(new to kotlin) I'm making my own music app but i'm having an error that i don't understand :
in the bit of code where i try to access a random view (shuffle), i get the java.lang.IndexOutOfBoundsException: Index:96 Size:11.
96 in this example is the view in the listview that i'm trying to access.
It happens in this line : var iView = listViewMusic.get(idShuffle)
Same if i use listViewMusic[idShuffle]
EDIT : Turns out that 11 is only the 11 visible items on screen at any given moment, even if the list contains hundreds of items. When i use listViewMusic.smoothscrolltoposition(idShuffle) it works, but the size of 11 now relates to the 11 on screen after the scrolling
The function playNext() inside the activity, called when clicking on the shuffle button:
fun playNext(){
try {
// Find the order of the next song to play
var idShuffle = musicAdapter!!.getIdofItem(listMusicShuffled.get(musicNextToPlay).title)
// Find the right record in the list
//var iView = listViewMusic[idShuffle]
//toastIt(applicationContext, "adapter count ${listViewMusic.adapter.count}")
//toastIt(applicationContext, "listview count ${listViewMusic.count}")
var iView = listViewMusic.get(idShuffle) //throws the error
// Play it
playOrPause(iView)
// Prepare the next track
musicNextToPlay += 1
if (musicNextToPlay >= listMusicShuffled.size) {
musicNextToPlay = -1
}
} catch (e:Exception) {toastException(applicationContext, "playnext", e) }
}
The part of the function onCreate that fills in the listViewMusic:
// Retrieve the data
val mediaStoreUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query(mediaStoreUri, null, "", null, null)
//Browse the data
listMusic = mutableListOf()
val listMusicJson = getMusicFromJson()
while (cursor!!.moveToNext()) {
val musicName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val musicArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val musicUrl = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val musicType:String =
if (musicArtist.contains("lmdmf", true)) { "LMDMF" }
else if (musicName.contains("slack", true)) { "SLACK" }
else { "MUSIC" }
listMusic.add(
MusicTrack(
musicName,
musicPlayCount,
musicUrl,
musicType
)
)
}
cursor.close()
musicAdapter = MusicAdapter(listMusic.sortedWith(compareBy<MusicTrack>{ it.title }).filter { it.type == "MUSIC"}.toMutableList())
listViewMusic.adapter = musicAdapter

MPAndroidChart - make xaxis spacing relative

I have an MPAndroidChart line chart that shows dates on the xaxis & weights on the yaxis. As it is, the data points along the xaxis are all evenly spaced, but I'd like to make them relative to the time period between each weight entry. Try as I might, I can't find anything in the MPAndroidChart documentation that describes how to do this, or if it's even supported. Can anyone point me in the right direction here?
Here is the code to create the chart (with the result being even spacing):
private fun loadChart(weightList: List<FormattedWeight>) {
if (weightList.isEmpty()) { return }
val weights = ArrayList<Entry>()
val dates = ArrayList<Date>()
for (i in weightList.indices) {
val weight = weightList[i]
weights.add(Entry(i.toFloat(), weight.Weight!!.toFloat()))
dates.add(weight.WeightDate!!)
}
val dataSet = LineDataSet(weights, "Weights")
dataSet.mode = LineDataSet.Mode.LINEAR
val xaxis = binding.weightChart.xAxis
xaxis.granularity=2f
dataSet.color = Color.BLUE
dataSet.setCircleColor(Color.BLUE)
xaxis.position = XAxis.XAxisPosition.BOTTOM
xaxis.valueFormatter = object : ValueFormatter() {
private val mFormat = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT)
override fun getFormattedValue(value: Float): String {
return try {
mFormat.format(dates[value.toInt()])
} catch (e: Exception) {
""
}
}
}
binding.weightChart.data = LineData(dataSet)
binding.weightChart.description.isEnabled = false
binding.weightChart.legend.isEnabled = false
binding.weightChart.invalidate()
binding.weightChart.moveViewToX(weights[weights.size - 1].y)
}
Even spacing
Relative spacing
I figured out that the xaxis spacing is set in
Entry(float x, float y)
If you set the x parameter to evenly incremented numbers (eg: 1, 2, 3...) you will get evenly spaced data points. However if you set them to varying increments (eg: 1, 1.3, 2.2...), you'll get relative spacing. So here's the code I used to achieve this:
val spacing = calcRelativeSpacing(weightList)
for (i in weightList.indices) {
val weight = weightList[i]
weights.add(Entry(spacing[i], weight.Weight!!.toFloat()))
dates.add(weight.WeightDate!!)
}
Note the call to calcRelativeSpacing(weightList):
private fun calcRelativeSpacing(entries: List<FormattedWeight>): ArrayList<Float> {
val startDate = Instant(entries.get(0).WeightDate)
val endDate = Instant(entries.get(entries.lastIndex).WeightDate)
val days = Days.daysBetween(startDate, endDate).days
val dayInterval = (days / entries.size).toFloat()
val spacing = ArrayList<Float>()
spacing.add(1f)
for (i in 1 until entries.size) {
spacing.add(spacing[i-1] + (Days.daysBetween(Instant(entries.get(i-1).WeightDate), Instant(entries.get(i).WeightDate)).days / dayInterval))
}
return spacing
}
The result:

Fragment BarChart in Modal is not updating - TornadoFX

I've created a Fragment to hold a BarChart and a ScrollPane - the end result will be a scrollable histogram.
I'm creating these new fragments in a seperate modal using the openModal method.
The problem that i'm having is that the BarChart doesn't seem to be updating when I call my loadData method, as shown below:
class Histogram : Fragment() {
override val root = vbox{
hgrow = Priority.ALWAYS
vgrow = Priority.ALWAYS
style{
minWidth = 1280.px
minHeight = 180.px
}
hbox{
hgrow = Priority.ALWAYS
}
}
private val bar = ScrollBar()
private var barChart = barchart("bar", CategoryAxis(), NumberAxis()){
barGap = 0.0
categoryGap = -1.0
hgrow = Priority.ALWAYS
vgrow = Priority.ALWAYS
style{
minWidth = 640.px
minHeight = 240.px
maxHeight = 480.px
}
isLegendVisible = false
}
private val s = XYChart.Series<String, Number>()
init{
root.add(barChart)
root.add(bar)
}
fun loadData(h: AlignmentHistogram){
s.data.add(XYChart.Data<String, Number>("asd", 2))
barChart.title = h.rname
/* for(i in 0..MAX_DATASET_SIZE){
if(i > h.histogram.size){
break
}
val data = XYChart.Data<String, Int>((h.firstPosition + i + 1).toString(), (h.histogram[i]))
println(data)
s.data.add(data)
}*/
s.data.add(XYChart.Data<String, Number>("asasdd", 5))
s.name = h.rname
barChart.data.add(s)
}
}
AlignmentHistogram is just a POJO with the data for the histogram and a few other details.
I'm calling the fragment with the following code:
val stage = Stage()
stage.title = "${pointer.rname} - ${selectedFile.file.name}"
val view = Histogram()
view.loadData(h)
val parent = StackPane()
stage.scene = Scene(parent, 1280.0, 360.0)
view.openModal(StageStyle.UTILITY, Modality.NONE, false, stage, false, true)
The result of this is just an empty histogram in a new modal, despite calling barChart.data.add(s)
Any ideas? Thanks in advance!
Nevermind, I solved it by passing the POJO in as the scope and putting the set-up code into the init block.
This article helped with the scope part:
Tornadofx - How to pass parameter to Fragment on every instance