I am getting some issue Bar Graph with Dynamic values in X-Axis values setValueFormatter:
I checked with a list view, recycler view and view inflation but always getting issues in the values in X-Axis Formatter that it doesn't work with value when list size is 3 or less than 3.
With View Inflation:
Requirement: I want to set the values in a graph with a set of 8. Like, If my list size is 14,
So the graph view inflates 2 times like:
If my list size is 10, it will inflate 2 times, first with 8 values of bar graph and the second graph only with 2 values. It works for the same when the list size is 9, 8 values for the first graph and 1 value for the second graph.
But the issue is that when the remaining value for the last graph is less than 4 like:
when the list size is 11, the remaining value is 3
when the list size is 25, the remaining value is 1 for the fourth graph
It gives the repeated or duplicated value in the x Axis like this:
Here is the issue:
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
var size : String = ""
try {
if(value>=0&&!mMonths[value.toInt()/10 % mMonths.size].equals("")&&value<mMonths.size*10) {
size = mMonths[0 / 10 % mMonths.size]
}
}catch (e : IndexOutOfBoundsException){
xAxis.isGranularityEnabled=false
}
return size
}
})
Note: This will work fine when mMonths list size is greater than 3 as you can see in the first screenshot but it gives issues for list size 1, 2 or 3.
Source code:
private fun setBarChart(mList:ArrayList) {
var calender_size: Float = 0.0f
if(mList.
size>0){
if(mList.size>8){
if(mList.size/8==0){
calender_size = (mList.size / 8).toFloat()
}else{
calender_size = (mList.size / 8).toFloat() + 1
}
}else{
calender_size = 1f
}
}
var section_variable: Int = 0
var tillvalue: Int = 0
var list_size: Int = 1
allGraphs.clear()
multiplegraphviewtmp.removeAllViews()
for (i in 0 until Math.round(calender_size)) {
val newLayout = layoutInflater.inflate(R.layout.new_functionalgraph, null, false)
multiplegraphviewtmp.addView(newLayout)
allGraphs.add(newLayout)
newLayout.tv_categoryname.setText(mList[0].description)
val entriesnew = ArrayList<BarEntry>()
entriesnew.clear()
var mMonths : ArrayList<String> = ArrayList()
mMonths.clear()
if(mList.size>8){
if (i > 0) {
if (mList.size / 8 == 0) {
list_size = (i + 1) * 8
} else {
val temp = mList.size % 8
if ((i + 1) * 8 < mList.size) {
list_size = (i + 1) * 8
} else {
// section_variable = section_variable + temp
list_size = list_size + temp
}
}
} else {
list_size = 8
}
for (j in section_variable until list_size) {
entriesnew.add(BarEntry((tillvalue * 10).toFloat(), mList[j].percentage.toFloat()))
mMonths.add(mList[j].itemName)
section_variable++
tillvalue++
}
tillvalue = 0
}else{
list_size=1
for (j in 0 until mList.size) {
entriesnew.add(BarEntry((tillvalue * 10).toFloat(), mList[j].percentage.toFloat()))
mMonths.add(mList[j].itemName)
//section_variable++
tillvalue++
}
tillvalue = 0
}
newLayout.barChartmore.legend.isEnabled = false
newLayout.barChartmore.description.isEnabled = false
var leftAxisfornewgraphs = newLayout.barChartmore.getAxisLeft()
leftAxisfornewgraphs.setAxisMaxValue(100f)
leftAxisfornewgraphs.setAxisMinValue(0f)
leftAxisfornewgraphs.setStartAtZero(true)
leftAxisfornewgraphs.setAxisLineWidth(0f)
leftAxisfornewgraphs.setLabelCount(11, true)
leftAxisfornewgraphs.setDrawGridLines(true)
leftAxisfornewgraphs.axisLineColor = activity!!.resources.getColor(R.color.graph_textColor)
leftAxisfornewgraphs.textColor = activity!!.resources.getColor(R.color.graph_textColor)
leftAxisfornewgraphs.typeface = Typeface.DEFAULT_BOLD
leftAxisfornewgraphs.textSize = 12f
leftAxisfornewgraphs.gridColor = activity!!.resources.getColor(R.color.view)
leftAxisfornewgraphs.axisLineColor = activity!!.resources.getColor(R.color.view)
// leftAxisfornewgraphs.setValueFormatter(MyYAxisValueFormatter()) //////////to set labels in %
//////////////////to change Right line/////////////////
var rightAxis = newLayout.barChartmore.axisRight
rightAxis.setDrawGridLines(false)
rightAxis.setDrawLabels(false)
rightAxis.setLabelCount(40, true)
rightAxis.axisLineColor = activity!!.resources.getColor(R.color.view)
var xAxis = newLayout.barChartmore.getXAxis()
xAxis.textSize = 9f
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
xAxis.setLabelRotationAngle(-90f)
xAxis.textColor = activity!!.resources.getColor(R.color.graph_textColor)
xAxis.typeface = Typeface.DEFAULT_BOLD
xAxis.gridColor = activity!!.resources.getColor(R.color.view)
xAxis.axisLineColor = activity!!.resources.getColor(R.color.view)
xAxis.granularity=1f
newLayout.barChartmore.setPinchZoom(false)
newLayout.barChartmore.getLegend().setWordWrapEnabled(true)
newLayout.barChartmore.setScaleEnabled(false)
newLayout.barChartmore.isDoubleTapToZoomEnabled=false
val labels = ArrayList<String>()
labels.clear()
for (i in 0 until mMonths.size){
labels.add("18-Jan")
}
xAxis.setValueFormatter(object : IAxisValueFormatter {
override fun getFormattedValue(value: Float, axis: AxisBase?): String {
var size : String = ""
try {
if(value>=0&&!mMonths[value.toInt()/10 % mMonths.size].equals("")&&value<mMonths.size*10) {
size = mMonths[0 / 10 % mMonths.size]
mTotalvalues++
}
}catch (e : IndexOutOfBoundsException){
xAxis.isGranularityEnabled=false
}
return size
}
})
xAxis.spaceMin = 6f
xAxis.spaceMax = 6f
val barDataSet = BarDataSet(entriesnew, "Cells")
barDataSet.setDrawValues(false)
val list: ArrayList<Int> = ArrayList<Int>()
list.clear()
list.add(activity!!.resources.getColor(R.color.graph_skyblue))
list.add(activity!!.resources.getColor(R.color.graph_orange))
list.add(activity!!.resources.getColor(R.color.graph_navyBlue))
barDataSet.setColors(list)
val data = BarData(barDataSet);
if(mMonths.size<2){
data.barWidth= 1f
}else{
data.barWidth= 5f
}
newLayout.barChartmore.data = data
newLayout.barChartmore.invalidate()
newLayout.export_single.setOnClickListener(View.OnClickListener {
Commonclass.createPdf(activity, newLayout.barChartmore.chartBitmap)
})
//newLayout.barChartmore.clear()
}
}
Same Issue getting with recycler view and Listview. please help:
I am getting some issue Bar Graph with Dynamic values in XAxis values setValueFormatter
Related
I made a stat calculator and I need to store 10 user inputted values to be stored in an array where I can get the average and lowest number. How do I go about getting the values on the array to get the maximum and minimum values?
var values = ArrayList<Int>()
var count = 0
var arrCount = 0
addButt.setOnClickListener {
values.add(statNum.text.toString().toInt())
count++
arrCount++
var i = 0
statNum.setText("")
statArray.text = ""
for (i in 0..arrCount - 1) {
statArray.text = statArray.text as String + values[i] + ","
}
}
avgBut.setOnClickListener{
val statArray = doubleArrayOf(4.0, 7.0, 11.0, 12.0, 9.0)
var sum = 0.0
for (num in statArray) {
sum += num
}
val average = sum / statArray.size
finalAnswer.text = average.toString()
}
minmaxBut.setOnClickListener{
fun findMin(list: List<Int?>): Int? {
return list.sortedWith(compareBy { it }).first()
}
fun findMax(list: List<Int?>): Int? {
return list.sortedWith(compareBy { it }).last()
}
fun main() {
val list = listOf(10, 4, 2, 7, 6, 9)
val min = findMin(list)
finalAnswer.text = "Min Vale = $min"
val max = findMax(list)
println(max) // 10
}
It's very easy to get min, max and average number in the arraylist. I did not understand your code clearly but i will share how you can do these. After you may use that on your array:
val list = arrayListOf<Int>(1,2,3,4,5,6,7,8,9,10)
Log.e("min", list.min().toString())
Log.e("max", list.max().toString())
Log.e("average", list.average().toString())
This will give you below output in logcat:
E/min: 1
E/max: 10
E/average: 5.5
I want to show the Min and Max value on the line chart i am using the library MPChart Android library.
I want to show min max as like this.
My Code for the Line chart
val lineDataSet = LineDataSet(values, "")
lineDataSet.lineWidth = 2f
lineDataSet.setDrawValues(true)
lineDataSet.setDrawFilled(true)
// lineDataSet.fillAlpha = 85
lineDataSet.setDrawCircles(false)
// lineDataSet.setCircleColor(Color.BLACK)
lineDataSet.color = resources.getColor(R.color.green)
// lineDataSet.fillColor = resources.getColor(R.color.colorPrimary)
lineDataSet.highLightColor = resources.getColor(R.color.coral);
val elevationMarker = ChartMarkerView2(activity)
binding!!.chart.markerView = elevationMarker
lineDataSet.setDrawHighlightIndicators(true)
lineDataSet.setDrawHorizontalHighlightIndicator(false)
lineDataSet.setDrawCircleHole(true)
lineDataSet.circleHoleRadius = 5f
lineDataSet.setCircleColor(
resources.getColor(R.color.green)
)
val drawable = ContextCompat.getDrawable(requireContext(), R.drawable.fade_graph_bg)
lineDataSet.fillDrawable = drawable
lineDataSet.axisDependency = YAxis.AxisDependency.LEFT
// lineDataSet.cubicIntensity = 0f
// lineDataSet.mode = LineDataSet.Mode.HORIZONTAL_BEZIER;
val dataSets = ArrayList<ILineDataSet>()
dataSets.add(lineDataSet) // add the datasets
// create a data object with the datasets
val data = LineData(dataSets)
// data.setDrawValues(false)
// set data
binding!!.chart.data = data
// binding!!.chart.animateX(100)
binding!!.chart.invalidate()
you can get Max- Min your Dataset and in ValueFormatter Modify your value like this :
val numArr1 = intArrayOf(10000, 10100, 12200,12700, 9036, 11200, 10200)
val max = numArr1.maxOrNull() ?: 0
val min = numArr1.minOrNull() ?: 0
valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
if (value == min.toFloat() || value == max.toFloat())
{
return "$value $"
}
return ""
}
}
Here is the code snippet to set minimum and maximum value in Linechart using MPAndroidChart library.
val chartEntities: ArrayList<Entry> = ArrayList()
val values = Array(5) {0f}
values[0] = 5f
values[1] = 2.2f
values[2] = 7.2f
values[3] = 9.5f
values[4] = 4.5f
values[5] = 6.4f
var i = 0
for (entry in values) {
var value = values[i]
chartEntities.add(Entry(i.toFloat(), value))
i++
}
val lineDataSet = LineDataSet(chartEntities, "")
val max = values.maxOrNull() ?: 0
val min = values.minOrNull() ?: 0
val valueFormatter = object : ValueFormatter() {
override fun getFormattedValue(value: Float): String {
if (value == min.toFloat() || value == max.toFloat())
{
return "$value"
}
return ""
}
}
lineDataSet.valueFormatter = valueFormatter
lineDataSet.setDrawValues(true)
With this sample snippet you can set minimum and maximum values in linechart using MPAndroidChart.
Create float array of items to display in the line chart
find out minimum and maximum values from the arraylist.
create ValueFormatter and set values there.
set valueFormatter property to lineDataSet
set setDrawValues(true) in lineDataSet
Hope this helps. Happy coding.
I have a table with fifteen rows. Each row have three columns and a total column. I want to get the total per row, the grand total, and the overall average.
The user may not enter data for all rows, and the user may skip a row.
So the code checks if the user have entered data in one of three fields of each row.
If the row is blank, ignore it.
If some of the fields are filled-up, tell the user to fill up the rest of the row.
If all the fields in a row is filled up, sum all its fields and increment the divider.
I have only pasted the codes for Rows 1 & 2 for brevity, but it shows the gist of what I'm trying to achieve:
The code:
var a1 = 0
var a2 = 0
var total = 0
var divider = 0
// Row 1
if (b1p1.text.isNotEmpty() or b2p1.text.isNotEmpty() or b3p1.text.isNotEmpty()) {
var y = 0
listOf(b1p1, b2p1, b3p1).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p1, b2p1, b3p1).forEach {
a1 += it.text.toString().toInt()
}
total1.text = a1.toString()
total += a1
e2 = 1
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
// Row 2
if (b1p2.text.isNotEmpty() or b2p2.text.isNotEmpty() or b3p2.text.isNotEmpty()) {
var y = 0
listOf(b1p2, b2p2, b3p2).forEach {
if (it.text.isEmpty()) {
it.error = "Fill up empty fields!"
y = 1
}
}
if (y == 0) {
divider++
listOf(b1p2, b2p2, b3p2).forEach {
a2 += it.text.toString().toInt()
}
total2.text = a2.toString()
total += a2
} else {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
}
if (e2 == 1) {
grandTotalTextView.text = total.toString()
average = total.toDouble()/divider
val decimalFormatter = DecimalFormat("#,###.##")
averageTextView.text = decimalFormatter.format(average).toString()
cyeSingleton.anct3b = decimalFormatter.format(average).toString()
} else {
Toast.makeText(activity, "Error 2", Toast.LENGTH_SHORT).show()
}
The table:
This is the best I could come up with. Should there be no other suggestion, I will settle for this.
Thanks in advance!
**EDIT: Thanks to ** https://stackoverflow.com/users/3736955/jemshit-iskenderov
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
private fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>): TotalResult {
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews as List<EditText>, totalTextViews[index])
if(!rowResult.ignoreRow){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
Toast.makeText(
activity,
"$divider, $allTotal, $showError", Toast.LENGTH_SHORT)
.show()
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
private fun calculateRowResult(rowTextViews:List<EditText>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.text.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
fun main(){
val totalResult = calculateTotalResult(
allTextViews = listOf(
listOf(t11,t12,t13),
listOf(t21,t22,t23)
),
totalTextViews = listOf(totalView1, totalView2)
)
// single Toast error
if(totalResult.showError){
// showToast(error)
}
// use totalResult.divider, totalResult.allTotal
}
data class TotalResult(val divider:Int, val allTotal:Int, val showError:Boolean)
fun calculateTotalResult(allTextViews:List<List<TextView>>, totalTextViews:List<TextView>){
var divider = 0
var allTotal = 0
var showError=false
allTextViews.forEachIndexed{index, rowTextViews->
val rowResult = calculateRowResult(rowTextViews, totalTextViews[index])
if(!rowResult.ignore){
if(rowResult.allFieldsFilled){
divider+=1
allTotal+=rowResult.rowTotal
}else{
showError = true
}
}
}
return TotalResult(divider, allTotal, showError)
}
data class RowResult(val ignoreRow:Boolean, val allFieldsFilled:Boolean, val rowTotal:Int)
fun calculateRowResult(rowTextViews:List<TextView>, totalTextView:TextView): RowResult {
val ignore = rowTextViews.filter{it.isBlank()}.count() == rowTextViews.size
if(ignore)
return RowResult(true, false, 0)
var emptyFieldCount = 0
var total = 0
rowTextViews.forEach {textView ->
if (textView.text.isEmpty()) {
textView.error = "Fill up empty fields!"
emptyFieldCount +=1
}else{
val fieldValue:Int? = textView.text.toString().toIntOrNull() // or toIntOrElse{0}
if(fieldValue!=null) total+=fieldValue
}
}
if(emptyFieldCount==0)
totalTextView.text = total.toString()
return RowResult(false, emptyFieldCount==0, total)
}
Extracted calculateTotalResult() and calculateRowResult() so multiple rows and columns do not need to repeat same code.
calculateRowResult() processes singlet row of TextViews. I had to iterate rowTextViews twice, one to calculate ignore, the other to show error on TextView if not ignore. We don't show Toast Error here yet.
calculateTotalResult() iterates through all rows and gets total result. We show only one Toast Error (if required) after this step.
Code is pseudo-code, not tested.
I want to set the negative values of the bars to the left of the bars but at present some of the bars are overlapping these values.
How can i remove this overlapping of the negative values and if i set horizonatal_chart.setDrawValueAboveBar(true) then the positive bar will overlap the positive values.
Here's the code:
private fun filterHorizontalBarData(myList: List<Response>?) {
try {
val brand = ArrayList<String>()
var index = 0f
var color_index = 0
val data = BarData()
val values_adapter = ArrayList<String>()
for (item in myList!!) {
if (item.kPI.equals("Value % Growth") || item.kPI.equals("Volume % Growth")) {
val kpiValue = ArrayList<BarEntry>()
kpiValue.add(BarEntry(index,item.kPIvalue.toFloat()))
brand.add(item.brand)
values_adapter.add(item.kPIvalue.toString())
val barDataSet = BarDataSet(kpiValue, item.brand)
if(color_index<7)
barDataSet.setColor(getColor(horizonatal_chart.context, getColorID(color_index)))
else
barDataSet.setColor(getColorID(color_index))
barDataSet.valueTextSize = 8f
// barDataSet.setDrawValues(false)
data.addDataSet(barDataSet)
index++
color_index++
}
}
// values_adapter.sortDescending()
// first_bar_values.adapter = AdapterValuesHorizontalBar(values_adapter)
setHorizontalChart(data, brand)
}catch (e: Exception){
e.printStackTrace()
}
}
private fun setHorizontalChart(data : BarData, brand: ArrayList<String>){
horizonatal_chart.setDrawBarShadow(false)
val description = Description()
description.text = ""
horizonatal_chart.description = description
horizonatal_chart.legend.setEnabled(false)
horizonatal_chart.setPinchZoom(false)
horizonatal_chart.setScaleEnabled(false)
horizonatal_chart.setDrawValueAboveBar(true)
//Display the axis on the left (contains the labels 1*, 2* and so on)
val xAxis = horizonatal_chart.getXAxis()
xAxis.setDrawGridLines(false)
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM)
xAxis.setEnabled(true)
xAxis.setDrawAxisLine(false)
xAxis.textColor = Color.parseColor("#a1a1a1")
xAxis.xOffset = 5f
val yLeft = horizonatal_chart.axisLeft
//Set the minimum and maximum bar lengths as per the values that they represent
yLeft.axisMaximum = 100f
yLeft.axisMinimum = 0f
yLeft.isEnabled = false
yLeft.setDrawZeroLine(true)
yLeft.granularity = 1f
//Now add the labels to be added on the vertical axis
xAxis.valueFormatter = IAxisValueFormatter { value, axis ->
if (value.toInt() < brand.size) {
brand.get(value.toInt())
} else {
"0"
}
}
val yRight = horizonatal_chart.axisRight
yRight.setDrawAxisLine(true)
yRight.setDrawGridLines(false)
yRight.isEnabled = false
//Set bar entries and add necessary formatting
horizonatal_chart.axisLeft.setAxisMinimum(data.yMin)
data.barWidth = 0.9f
// val myCustomChartRender = MyCustomChartRender(horizonatal_chart, horizonatal_chart.animator, horizonatal_chart.viewPortHandler)
// //Add animation to the graph
// horizonatal_chart.renderer = myCustomChartRender
horizonatal_chart.animateY(2000)
horizonatal_chart.data = data
horizonatal_chart.setTouchEnabled(false)
horizonatal_chart.invalidate()
}
Please help me !
The problem seems that you've not provided minimum and maximum cap for rightAxis.
horizonatal_chart.axisRight.setAxisMinimum(-100)
horizonatal_chart.axisRight.setAxisMaximum(100)
Reference : The same issue is mentioned at issue and was solved by providing axis minimum and maximum
I am trying to show the data using LineChart, and ran into issues displaying x axis labels. I use data.start and data.end to set the x-axis bounds in my chart. So let's say API returns data for two days only (Thursday and Friday), start will be Monday and end will be Saturday.
Note:X values are ALL unix timestamps.
here's my code:
class MyChart(chart: LineChart?, val data: MyData) {
private val startValue = data.start.toEpochSecond(ZoneOffset.UTC)
init {
chart?.run {
setScaleEnabled(false)
legend.isEnabled = false
description.isEnabled = false
axisRight.run {
setDrawGridLines(true)
setDrawLabels(false)
axisMinimum = 0.1f // hide 0.0 values
}
axisLeft.run {
setDrawGridLines(true)
axisMinimum = 0.1f // hide 0.0 values
}
setupXAxis(xAxis)
Log.e("MPTEST, startValue: ", startValue.toString())
val entries = ArrayList<Entry>()
data.list?.forEachIndexed { _, data ->
val timeInSeconds = data.time.toEpochSecond(ZoneOffset.UTC)
val xIndex = (timeInSeconds - startValue).toFloat()
Log.e("MPTEST, xIndex: ", xIndex.toString())
entries.add(Entry(xIndex, data.value))
}
val data = LineData(LineDataSet(entries, ""))
setData(data)
invalidate()
}}
private fun setupXAxis(xAxis: XAxis?) {
xAxis?.run {
position = XAxis.XAxisPosition.BOTTOM
val startInSeconds = data.start.toEpochSecond(ZoneOffset.UTC)
axisMinimum = (startInSeconds - startValue).toFloat()
Log.e("MPTEST, axisMinimum: ", axisMinimum.toString())
val endInSeconds = data.end.toEpochSecond(ZoneOffset.UTC)
axisMaximum = (endInSeconds - startValue).toFloat()
Log.e("MPTEST, axisMaximum: ", axisMaximum.toString())
setCenterAxisLabels(true)
setDrawGridLines(false)
// I need to fix something here I think
setLabelCount(7, true)
valueFormatter = IndexAxisValueFormatter(data.labels)
}
} }
Here's the logcat output:
E/MPTEST, axisMinimum:: 0.0
E/MPTEST, axisMaximum:: 604799.0
E/MPTEST, startValue:: 1546732800
E/MPTEST, xIndex:: 366180.0
E/MPTEST, xIndex:: 467893.0
That's what I get - https://i.ibb.co/YD2DnhJ/Screen-Shot-2019-01-16-at-7-20-23-AM.png
(as you can see only first x axis label is displayed)
and this is what I am trying to achieve - https://i.ibb.co/M8b9pyP/Screen-Shot-2019-01-16-at-7-24-15-AM.png