I have to display realtime data in the app. I chose MPAndroidChart as a library for charts.
But without settings initial dataset with entries or setting axis minimum and axis maximum (x axis) my chart isn't displayed (with this settings I can't scroll chart). I almost already copied all code from the example, but it's still invisible (example starts without bounds of x axis and works good):
https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartExample/src/com/xxmassdeveloper/mpchartexample/RealtimeLineChartActivity.java
Here is example with fixed max/min X
Here is example of bug, no data shown:
Everything is inside the fragment in the viewpager:
Here is code of the initing line chart:
line_chart.apply {
description.apply {
isEnabled = true
text = "Live chart"
}
setTouchEnabled(true)
isDragEnabled = true
setScaleEnabled(true)
setDrawGridBackground(false)
setPinchZoom(true)
data = LineData().apply {
setValueTextColor(Color.BLACK)
}
// THIS:
// data = LineData(dataset("Simple").apply {
// for (i in 1..10) {
// addEntry(Entry(i.toFloat(), i.toFloat()))
// }
// })
legend.apply {
form = LINE
textColor = Color.BLACK
}
xAxis.apply {
textColor = Color.BLACK
setDrawGridLines(false)
setAvoidFirstLastClipping(true)
isEnabled = true
granularity = 5f
axisMinimum = 0f
axisMaximum = 20f
setValueFormatter { value, _ -> Date(value.toLong()).simpleSecondsFormat() }
}
axisLeft.apply {
textColor = Color.BLACK
// OR THIS:
//axisMinimum = 0f
//axisMaximum = 12f
setDrawGridLines(true)
}
axisRight.isEnabled = false
setVisibleXRangeMaximum(100f)
}
Code for creating dataset:
fun dataset(label: String): LineDataSet = LineDataSet(null, label).apply {
axisDependency = LEFT
color = Color.BLACK
setCircleColor(Color.RED)
lineWidth = 2f
circleRadius = 4f
fillAlpha = 65
fillColor = Color.RED
highLightColor = Color.GREEN
valueTextColor = Color.RED
valueTextSize = 9f
setDrawValues(true)
}
Data comes from the store, using RxJava2:
.observeOn(AndroidSchedulers.mainThread())
.doOnNext { Timber.d("Entry: $it, timestamp: ${it.timestamp}") }
.doOnNext {
line_chart.data.apply {
val dataSet = getDataSetByLabel(it.macAddress, true) ?: dataset(
it.macAddress).also { addDataSet(it) }
addEntry(Entry(it.timestamp.toFloat(), it.energy),
getIndexOfDataSet(dataSet))
notifyDataChanged()
}
}
.doOnNext {
line_chart.apply {
notifyDataSetChanged()
setVisibleXRangeMaximum(120f)
invalidate()
}
}
Thanks!
Accidentally fixed issue, by removing line setVisibleXRangeMaximum(100f) from the first snippet.
Related
my problem is how to know or define the other(couple) value of the y-axis or x-axis.
My code :
formatterValueY = new ValueFormatter() {
#Override
public String getAxisLabel(float valueY, AxisBase axis) {
//how to find the valueX of valueY ??
// need it to return string
//note: I know the search solution(data loop
//search),
// it is useless if there is two equal values y1=y2
//this is example of what I wanna achieve,
// this is simple example,
float x = findTheRealXOfY(valueY);
//or
//float x = findTheRealXOfY(valueY,axis);
if(x %2==0)
{
return "Pair:"+valueY;
}
else{
return "inPair:"+valueY;
}
}
}
YAxis yAxis = myBarChar.getYAxis();
yAxis.setValueFormatter(formatterValueY);
so if there is way to find the real pair value of Y using the valueY and axis.
This is an example of what I want
Depending on where you want these labels you have a couple options. If you want to set the labels along the bottom of the chart, you can use the IndexAxisValueFormatter(labels) on the x axis. If you want to add labels on the top of the bars you can set a ValueFormatter on the BarDataSet. Here is an example that sets "L" or "M" on the lower axis depending on the bar height, and "Less" or "More" on the bar label itself - also depending on the bar height.
For setting the x axis labels, the relevant command is
xaxis.valueFormatter = IndexAxisValueFormatter(labels)
and for the bar labels
barDataSet.valueFormatter = object : ValueFormatter() {
override fun getBarLabel(barEntry: BarEntry?): String {
var label = ""
barEntry?.let { e ->
// here you can access both x and y
label = if( e.y > 25 ) {
"More"
} else {
"Less"
}
}
return label
}
}
If you want the labels above the bars, you can use
barChart.setDrawValueAboveBar(true)
Complete Example
val barChart = findViewById<BarChart>(R.id.bar_chart)
// the values on your bar chart - wherever they may come from
val yVals = listOf(10f, 20f, 30f, 40f, 50f, 60f)
val valueSet1 = ArrayList<BarEntry>()
val labels = ArrayList<String>()
// You can create your data sets and labels at the same time -
// then you have access to the x and y values and can
// determine what labels to set
for (i in yVals.indices) {
val yVal = yVals[i]
val entry = BarEntry(i.toFloat(), yVal)
valueSet1.add(entry)
if( yVal > 20) {
labels.add("M")
}
else {
labels.add("L")
}
}
val dataSets: MutableList<IBarDataSet> = ArrayList()
val barDataSet = BarDataSet(valueSet1, " ")
barChart.setDrawBarShadow(false)
barChart.setDrawValueAboveBar(false)
barChart.description.isEnabled = false
barChart.setDrawGridBackground(false)
val xaxis: XAxis = barChart.xAxis
xaxis.setDrawGridLines(false)
xaxis.position = XAxis.XAxisPosition.BOTTOM
xaxis.granularity = 1f
xaxis.isGranularityEnabled = true
xaxis.setDrawLabels(true)
xaxis.setDrawAxisLine(false)
xaxis.valueFormatter = IndexAxisValueFormatter(labels)
xaxis.textSize = 15f
val yAxisLeft: YAxis = barChart.axisLeft
yAxisLeft.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
yAxisLeft.setDrawGridLines(true)
yAxisLeft.setDrawAxisLine(true)
yAxisLeft.isEnabled = true
yAxisLeft.textSize = 15f
barChart.axisRight.isEnabled = false
barChart.extraBottomOffset = 10f
barChart.extraLeftOffset = 10f
val legend: Legend = barChart.legend
legend.isEnabled = false
barDataSet.color = Color.CYAN
barDataSet.valueTextSize = 15f
// You can also set a value formatter on the bar data set itself (and enable
// setDrawValues). In this you get a BarEntry which has both the x position
// and bar height.
barDataSet.setDrawValues(true)
barDataSet.valueFormatter = object : ValueFormatter() {
override fun getBarLabel(barEntry: BarEntry?): String {
var label = ""
barEntry?.let { e ->
// here you can access both x and y
// values with e.x and e.y
label = if( e.y > 25 ) {
"More"
} else {
"Less"
}
}
return label
}
}
dataSets.add(barDataSet)
val data = BarData(dataSets)
barChart.data = data
barChart.invalidate()
example of what I needed, this is the best solution, with no over-code
this is the code:
BarData data = new BarData(colorBarSet);
data.setValueFormatter(new ValueFormatter() {
#Override
public String getBarLabel(BarEntry barEntry) {
Log.e("TAG", "this is the X value: " + barEntry.getX());
Log.e("TAG", "this is the Y value: " + barEntry.getY());
// this will return "Girl" for red color with x value=1,
// value of x is the index of the color in bar chart
return getMajeureVoteOfColor(barEntry.getX);
}
});
Was wondering if it is possible to change the line and fill color for the line chart based on if the y-axis values are positive or negative. An example of it is below
Below is what i could achieve with the following code
private fun setUpLineChart() {
val lineData = getDataSet()
view.lineChart.apply {
data = lineData
description.isEnabled = false
setScaleEnabled(false)
setTouchEnabled(false)
legend.isEnabled = false
axisLeft.apply {
setDrawLabels(false)
setDrawGridLines(false)
setDrawAxisLine(false)
spaceBottom = 30f
}
axisRight.apply {
setDrawLabels(false)
setDrawGridLines(false)
setDrawAxisLine(false)
}
xAxis.apply {
setDrawLabels(false)
setDrawGridLines(false)
setDrawAxisLine(false)
}
animateXY(700, 1000, Easing.EaseInOutQuad)
}
}
private fun getDataSet(): LineData {
val entries = mutableListOf<Entry>()
val dataList = listOf(1, 20, -20, 33, 54, 7, -18, 2)
dataList.forEachIndexed { index, element ->
entries.add(Entry(index.toFloat(), element.toFloat()))
}
val dataSet = LineDataSet(entries, "")
dataSet.apply {
setDrawCircles(false)
valueTextSize = 0f
lineWidth = 3f
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
color = ContextCompat.getColor(view.context, R.color.colorOnSurface)
setDrawFilled(true)
fillColor = ContextCompat.getColor(view.context, R.color.colorSurface2)
}
return LineData(dataSet)
}
The line and fill colors are bind to a specific LineDataSet. So to achieve the result you want according to the above example you have to separate your current dataSet into 4 LineDataSets (2 positive and 2 negative) and by doing this each one then can have its own fill and line colors and you are flexible to have as many colors you want for each dataSet. Of course you have to do your own logic to separate the positive LineDataSets from negative LineDataSets. I have modified your getDataSet() function to give you an example of how to achieve the separation of positive LineDataSets from negative LineDataSets each one having its own line and fill colors.
private fun getDataSet(): LineData? {
val dataSets: MutableList<ILineDataSet> = ArrayList()
val yArray = floatArrayOf(1f, 20f, -20f, 33f, 54f, 7f, -18f, 2f)
var entries = ArrayList<Entry?>()
var prevValueIsPositive = false
var prevValueIsNegative = false
val step = 1f
for (i in yArray.indices) {
val y = yArray[i]
//positive y values
if (y >= 0) {
//we are changing to positive values so draw the current negative dataSets
if (prevValueIsNegative) {
//calculate the common mid point between a positive and negative y
val midEntry = Entry(i.toFloat() - step / 2, 0f)
entries.add(midEntry)
//draw the current negative dataSet to Red color
dataSets.add(getLineDataSet(entries, android.R.color.holo_red_dark, android.R.color.holo_purple))
//and initialize a new DataSet starting from the above mid point Entry
entries = ArrayList()
entries.add(midEntry)
prevValueIsNegative = false
}
//we are already in a positive dataSet continue adding positive y values
entries.add(Entry(i.toFloat(), y))
prevValueIsPositive = true
//not having any other positive-negative changes so add the remaining positive values in the final dataSet
if (i == yArray.size - 1) {
dataSets.add(getLineDataSet(entries, android.R.color.holo_green_light, android.R.color.holo_orange_dark))
}
} else {
//we are changing to negative values so draw the current positive dataSets
if (prevValueIsPositive) {
//calculate the common mid point between a positive and negative y
val midEntry = Entry(i.toFloat() - step / 2, 0f)
entries.add(midEntry)
//draw the current positive dataSet to Green color
dataSets.add(getLineDataSet(entries, android.R.color.holo_green_light, android.R.color.holo_orange_dark))
//and initialize a new DataSet starting from the above mid point Entry
entries = ArrayList()
entries.add(midEntry)
prevValueIsPositive = false
}
//we are already in a negative dataSet continue adding negative y values
entries.add(Entry(i.toFloat(), y))
prevValueIsNegative = true
//not having any other positive-negative changes so add the remaining negative values in the final dataSet
if (i == yArray.size - 1) {
dataSets.add(getLineDataSet(entries, android.R.color.holo_red_dark, android.R.color.holo_purple))
}
}
}
return LineData(dataSets)
}
with the usage of the below helper function to prepare a new LineDataSet with its specified line and fill colors:
private fun getLineDataSet(entries: ArrayList<Entry?>, fillColor: Int, lineColor: Int): LineDataSet {
val dataSet = LineDataSet(entries, "")
dataSet.setDrawCircles(false)
dataSet.valueTextSize = 0f
dataSet.lineWidth = 3f
dataSet.mode = LineDataSet.Mode.HORIZONTAL_BEZIER
dataSet.color = ContextCompat.getColor(this, lineColor)
dataSet.setDrawFilled(true)
dataSet.fillColor = ContextCompat.getColor(this, fillColor)
return dataSet
}
I'm creating a Weather app and i'm trying to display an MPChart which will use the hourly temps as Y points & the Hour (in "HH" format) as the XAxisLabels/ X points.
I'm creating the LineData for the chart in my Fragment's viewModel and expose it to the Fragment via liveData. I also create the XAxisLabels for the chart on my Fragment. The LineData seem to contain the right entries (viewModel: [Entry, x: 0.0 y: 39.07, Entry, x: 1.0 y: 38.68, Entry, x: 2.0 y: 38.16, Entry, x: 3.0 y: 37.29, Entry, x: 4.0 y: 36.2, Entry, x: 5.0 y: 35.22,........]), but the chart isn't even displaying any of the data. What am I missing here?
Here's what is rendered:
Here's the code:
ViewModel.kt
class WeatherViewModel(
private val forecastRepository: ForecastRepository,
private val weatherUnitConverter: WeatherUnitConverter,
context: Context
) : ViewModel() {
val hourlyWeatherEntries = forecastRepository.getHourlyWeather()
val hourlyChartData = hourlyWeatherEntries.switchMap { hourlyData ->
liveData {
Log.d("ViewModel hourly data", "$hourlyData")
val task = viewModelScope.async(Dispatchers.Default) {
createChartData(hourlyData)
}
emit(task.await())
}
}
/**
* Creates the line chart's data and returns them.
* #return The line chart's data (x,y) value pairs
*/
private fun createChartData(hourlyWeather: List<HourWeatherEntry>?): LineData {
if (hourlyWeather == null) return LineData()
Log.d("ViewModel class", hourlyWeather.toString())
val unitSystemValue = preferences.getString(UNIT_SYSTEM_KEY, "si")!!
val values = arrayListOf<Entry>()
for (i in hourlyWeather.indices) { // init data points
// format the temperature appropriately based on the unit system selected
val hourTempFormatted = when (unitSystemValue) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> hourlyWeather[i].temperature
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> weatherUnitConverter.convertToFahrenheit(
hourlyWeather[i].temperature
)
else -> hourlyWeather[i].temperature
}
// Create the data point
values.add(
Entry(
i.toFloat(),
hourTempFormatted.toFloat(),
appContext.resources.getDrawable(
determineSummaryIcon(hourlyWeather[i].icon),
null
)
)
)
}
Log.d("MainFragment viewModel", "$values")
// create a data set and customize it
val lineDataSet = LineDataSet(values, "")
val color = appContext.resources.getColor(R.color.black, null)
val offset = MPPointF.getInstance()
offset.y = -35f
lineDataSet.apply {
valueFormatter = YValueFormatter()
setDrawValues(true)
fillDrawable = appContext.resources.getDrawable(R.drawable.gradient_night_chart, null)
setDrawFilled(true)
setDrawIcons(true)
setCircleColor(color)
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
this.color = color // line color
iconsOffset = offset
lineWidth = 3f
valueTextSize = 9f
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
valueTypeface = appContext.resources.getFont(R.font.work_sans_medium)
}
}
// create a LineData object using our LineDataSet
val data = LineData(lineDataSet)
data.apply {
setValueTextColor(R.color.colorPrimary)
setValueTextSize(15f)
}
return data
}
}
MainFragment.kt
class MainFragment : Fragment() {
// Lazy inject the view model
private val viewModel: WeatherViewModel by viewModel()
private lateinit var hourlyChart: LineChart
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpChart()
lifecycleScope.launch {
// Shows a lottie animation while the data is being loaded
//scrollView.visibility = View.GONE
//lottieAnimView.visibility = View.VISIBLE
bindUIAsync().await()
// Stops the animation and reveals the layout with the data loaded
//scrollView.visibility = View.VISIBLE
//lottieAnimView.visibility = View.GONE
}
}
#SuppressLint("SimpleDateFormat")
private fun bindUIAsync() = lifecycleScope.async(Dispatchers.Main) {
// fetch hourly chart line data
val hourlyChartData = viewModel.hourlyChartData
hourlyChartData.observe(viewLifecycleOwner, Observer { lineData ->
if(lineData == null) {
Log.d("HOURLY CHART DATA", "Line data = null")
return#Observer
}
hourlyChart.data = lineData
hourlyChart.invalidate()
})
// fetch hourly weather
val hourlyWeather = viewModel.hourlyWeatherEntries//viewModel.hourlyWeatherEntries
// Observe the hourly weather live data
hourlyWeather.observe(viewLifecycleOwner, Observer { hourly ->
if (hourly == null) return#Observer
val xAxisLabels = arrayListOf<String>()
val sdf = SimpleDateFormat("HH")
for (i in hourly.indices) {
val formattedLabel = sdf.format(Date(hourly[i].time * 1000))
xAxisLabels.add(formattedLabel)
}
setChartAxisLabels(xAxisLabels)
Log.d(TAG, "label count = ${hourlyChart.xAxis.labelCount.toString()}")
})
return#async true
}
private fun setChartAxisLabels(labels: ArrayList<String>) {
// Populate the X axis with the hour labels
hourlyChart.xAxis.valueFormatter = IndexAxisValueFormatter(labels)
}
/**
* Sets up the chart with the appropriate
* customizations.
*/
private fun setUpChart() {
hourlyChart.apply {
description.isEnabled = false
setNoDataText("Data is loading...")
// enable touch gestures
setTouchEnabled(true)
dragDecelerationFrictionCoef = 0.9f
// enable dragging
isDragEnabled = true
isHighlightPerDragEnabled = true
setDrawGridBackground(false)
axisRight.setDrawLabels(false)
axisLeft.setDrawLabels(false)
axisLeft.setDrawGridLines(false)
// disable zoom functionality
setScaleEnabled(false)
setPinchZoom(false)
isDoubleTapToZoomEnabled = false
// disable the chart's legend
legend.isEnabled = false
// append extra offsets to the chart's auto-calculated ones
setExtraOffsets(0f, 0f, 0f, 10f)
data = LineData()
data.isHighlightEnabled = false
setVisibleXRangeMaximum(6f)
setBackgroundColor(resources.getColor(R.color.bright_White, null))
}
// X Axis setup
hourlyChart.xAxis.apply {
position = XAxis.XAxisPosition.BOTTOM
textSize = 14f
setDrawLabels(true)
setDrawAxisLine(false)
setDrawGridLines(false)
isEnabled = true
granularity = 1f // one hour
spaceMax = 0.2f // add padding start
spaceMin = 0.2f // add padding end
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
typeface = resources.getFont(R.font.work_sans)
}
textColor = resources.getColor(R.color.black, null)
}
// Left Y axis setup
hourlyChart.axisLeft.apply {
setDrawLabels(true)
setDrawGridLines(false)
setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
// temperature values range (higher than probable temps in order to scale down the chart)
axisMinimum = 0f
axisMaximum = when (getUnitSystemValue()) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> 50f
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> 150f
else -> 50f
}
}
// Right Y axis setup
hourlyChart.axisRight.apply {
setDrawGridLines(false)
isEnabled = false
}
}
}
I have a problem updating the data in a chart. When I change the settings and come back to this fragment or when I go out of the app and come back, the chart apparently changes its size and hide the labels in its axis. However, if I navigate through the app and come back, the chart is displaying well.
Screenshot of correct display
Screenshot of axis labels disappearing
My context is the following:
I have a fragment where I observe the viewmodel in onViewCreated. When the data is successfully retrieved, I update the chart. I have tried also to make a flag to make sure that the call to set up the chart is only called once, but that doesn't work either. So the problem is not related to how many times this call is made.
viewModel.forecast.observe(viewLifecycleOwner, Observer { resource ->
if (resource != null && resource.status.isSuccessful()){
ChartUtilities.setUpChart(
viewModel.forecastOfNextHours().subList(0, 9),
binding.lineChart,
"Temperature of the next 24 hours",
isMetric
)
}
In ChartUtilities.kt:
object ChartUtilities {
fun setUpChart(list: List<ForecastListItem>, lineChart: LineChart, label: String, isMetric: Boolean){
//lineChart.clear()
val (values, yValues, xAxisValues) = fillData(list, isMetric)
val data = configureDataSet(values, label)
customSettings(lineChart, data, xAxisValues, yValues)
//lineChart.notifyDataSetChanged()
}
private fun configureDataSet(
values: ArrayList<Entry>,
label: String
): LineData {
val set1 = LineDataSet(values, label)
set1.axisDependency = YAxis.AxisDependency.LEFT
set1.color = ColorTemplate.getHoloBlue()
set1.valueTextColor = ColorTemplate.getHoloBlue()
set1.lineWidth = 4.0f
set1.setDrawCircles(false)
set1.setDrawValues(false)
set1.fillAlpha = 65
set1.fillColor = Color.RED
set1.highLightColor = Color.rgb(244, 117, 117)
set1.setDrawCircleHole(false)
set1.mode = LineDataSet.Mode.HORIZONTAL_BEZIER
// create a data object with the data sets
val data = LineData(set1)
data.setValueTextColor(Color.WHITE)
data.setValueTextSize(9f)
return data
}
private fun fillData(list: List<ForecastListItem>, isMetric: Boolean): Triple<ArrayList<Entry>, ArrayList<Double>, ArrayList<String>> {
val values = ArrayList<Entry>()
val yValues = ArrayList<Double>()
val xAxisValues = arrayListOf<String>()
list.forEachIndexed { index, forecastItem ->
xAxisValues.add(forecastItem.getHourOfDay())
var temperature = forecastItem.getTemperature()!!
if (!isMetric)
temperature = WeatherUtils.celsiusToFahrenheit(temperature)
yValues.add(temperature)
values.add(Entry(index.toFloat(), temperature.toFloat()))
}
return Triple(values, yValues, xAxisValues)
}
private fun customSettings(chart: LineChart, data: LineData, xAxisValues: ArrayList<String>, yAxisValues: ArrayList<Double>){
chart.data = data
customChart(chart)
customLegend(chart)
customXAxis(chart, xAxisValues)
customYAxis(chart, yAxisValues)
val rightAxis = chart.axisRight
rightAxis.isEnabled = false
}
private fun customChart(chart: LineChart) {
chart.description.isEnabled = false
chart.setTouchEnabled(false)
//chart.dragDecelerationFrictionCoef = 0.9f
// enable scaling and dragging
chart.isDragEnabled = false
chart.setScaleEnabled(false)
chart.setDrawGridBackground(false)
chart.isHighlightPerDragEnabled = true
// set an alternative background color
chart.setBackgroundResource(R.color.colorPrimaryDark)
chart.setViewPortOffsets(0f, 0f, 0f, 0f)
}
private fun customLegend(chart: LineChart) {
val l = chart.legend
l.isEnabled = true
l.verticalAlignment = Legend.LegendVerticalAlignment.TOP
l.textColor = Color.WHITE
}
private fun customXAxis(
chart: LineChart,
xAxisValues: ArrayList<String>
) {
val xAxis = chart.xAxis
xAxis.position = XAxis.XAxisPosition.BOTTOM
xAxis.textSize = 12f
xAxis.setDrawAxisLine(true)
xAxis.setDrawGridLines(false)
xAxis.textColor = Color.WHITE
//xAxis.setCenterAxisLabels(true)
xAxis.setAvoidFirstLastClipping(false)
xAxis.granularity = 2f // three hours
xAxis.axisMaximum = xAxisValues.size.toFloat()
xAxis.labelCount = xAxisValues.size
xAxis.valueFormatter = IndexAxisValueFormatter(xAxisValues)
xAxis.isEnabled = true
}
private fun customYAxis(
chart: LineChart,
yAxisValues: ArrayList<Double>
) {
val leftAxis = chart.axisLeft
leftAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
leftAxis.textColor = Color.WHITE
leftAxis.setDrawGridLines(true)
leftAxis.isGranularityEnabled = true
leftAxis.axisMinimum = yAxisValues.min()?.minus(10)?.toFloat()!!
leftAxis.axisMaximum = yAxisValues.max()?.plus(10)?.toFloat()!!
leftAxis.yOffset = -9f
leftAxis.isEnabled = true
}
}
I realise this is a very late answer, but I had exactly the same issue and stumbled upon your question. I'd intermittently see the axes disappear when scrolling between different charts (I had them in a RecyclerView).
I solved it by deleting the following line, which I see you also have in your code:
chart.setViewPortOffsets(0, 0, 0, 0);
The javadoc for that method says that we should avoid manually setting it unless we know what we're doing (which I don't!).
I also found that increasing the values to the following worked for me:
chart.setViewPortOffsets(60, 0, 50, 60);
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