mirror of
https://github.com/ev-map/EVMap.git
synced 2026-05-19 12:26:20 -04:00
Add some labels to BarGraphView
This commit is contained in:
committed by
Johan von Forstner
parent
b0371c1b20
commit
f6afb2a8cb
@@ -2,19 +2,28 @@ package net.vonforst.evmap.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.DashPathEffect
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import net.vonforst.evmap.R
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class BarGraphView(context: Context, attrs: AttributeSet) : View(context, attrs) {
|
||||
|
||||
var zeroHeight = 4 * context.resources.displayMetrics.density
|
||||
var barWidth = 16 * context.resources.displayMetrics.density
|
||||
var barMargin = 2 * context.resources.displayMetrics.density
|
||||
var barWidth = (16 * context.resources.displayMetrics.density).roundToInt()
|
||||
var barMargin = (2 * context.resources.displayMetrics.density).roundToInt()
|
||||
var legendWidth = 12 * context.resources.displayMetrics.density
|
||||
var legendLineLength = 4 * context.resources.displayMetrics.density
|
||||
var legendLineWidth = 1 * context.resources.displayMetrics.density
|
||||
var dashLength = 4 * context.resources.displayMetrics.density
|
||||
|
||||
var barDrawableUnavailable =
|
||||
AppCompatResources.getDrawable(context, R.drawable.bar_graph_unavailable)!!
|
||||
@@ -26,35 +35,107 @@ class BarGraphView(context: Context, attrs: AttributeSet) : View(context, attrs)
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
var maxValue: Int? = null
|
||||
set(value) {
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var activeAlpha = 0.87f
|
||||
var inactiveAlpha = 0.60f
|
||||
|
||||
private val legendPaint = Paint().apply {
|
||||
val ta = context.theme.obtainStyledAttributes(intArrayOf(R.attr.colorControlNormal))
|
||||
color = ta.getColor(0, 0)
|
||||
strokeWidth = legendLineWidth
|
||||
textSize = legendWidth - legendLineLength
|
||||
}
|
||||
private val legendDashedPaint = Paint().apply {
|
||||
set(legendPaint)
|
||||
alpha = (inactiveAlpha * 255).roundToInt()
|
||||
style = Paint.Style.STROKE
|
||||
pathEffect = DashPathEffect(floatArrayOf(dashLength, dashLength), 0f)
|
||||
}
|
||||
|
||||
private lateinit var graphBounds: Rect
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
graphBounds = Rect(paddingLeft, paddingTop, w - paddingRight, h - paddingBottom)
|
||||
val bottom = (paddingBottom + legendWidth).roundToInt()
|
||||
val left = (paddingLeft + legendWidth).roundToInt()
|
||||
val right = (paddingRight + legendWidth).roundToInt()
|
||||
val top = (paddingTop + (legendWidth - legendLineLength) / 3 * 2).roundToInt()
|
||||
graphBounds = Rect(left, top, w - right, h - bottom)
|
||||
}
|
||||
|
||||
private val timeFormat = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
val data = data ?: return
|
||||
val maxValue = data.maxOf { it.value }
|
||||
val data = data?.toSortedMap() ?: return
|
||||
val maxValue = maxValue ?: data.maxOf { it.value }
|
||||
|
||||
canvas.apply {
|
||||
data.toSortedMap().entries.forEachIndexed { i, (t, v) ->
|
||||
drawLine(
|
||||
graphBounds.left.toFloat(),
|
||||
graphBounds.top.toFloat(),
|
||||
graphBounds.right.toFloat(),
|
||||
graphBounds.top.toFloat(),
|
||||
legendDashedPaint
|
||||
)
|
||||
|
||||
legendPaint.textAlign = Paint.Align.CENTER
|
||||
data.entries.forEachIndexed { i, (t, v) ->
|
||||
val drawable = if (v > 0) barDrawableAvailable else barDrawableUnavailable
|
||||
|
||||
val height =
|
||||
zeroHeight + (graphBounds.height() - zeroHeight) * v.toFloat() / maxValue
|
||||
val left = graphBounds.left + (barWidth + barMargin) * i
|
||||
|
||||
if (left + barWidth > graphBounds.right) return@forEachIndexed
|
||||
|
||||
drawable.setBounds(
|
||||
graphBounds.left + ((barWidth + barMargin) * i).roundToInt(),
|
||||
left,
|
||||
graphBounds.bottom - height.roundToInt(),
|
||||
graphBounds.left + ((barWidth + barMargin) * i + barWidth).roundToInt(),
|
||||
left + barWidth,
|
||||
graphBounds.bottom
|
||||
)
|
||||
drawable.alpha = (inactiveAlpha * 255).roundToInt()
|
||||
drawable.draw(canvas)
|
||||
|
||||
if (t.minute == 0) {
|
||||
val center = left.toFloat() + barWidth / 2
|
||||
drawLine(
|
||||
center, graphBounds.bottom.toFloat(),
|
||||
center, graphBounds.bottom + legendLineLength, legendPaint
|
||||
)
|
||||
drawText(
|
||||
t.withZoneSameInstant(ZoneId.systemDefault()).format(timeFormat),
|
||||
center, graphBounds.bottom + legendWidth, legendPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
drawLine(
|
||||
graphBounds.left.toFloat(),
|
||||
graphBounds.bottom.toFloat(),
|
||||
graphBounds.right.toFloat(),
|
||||
graphBounds.bottom.toFloat(),
|
||||
legendPaint
|
||||
)
|
||||
drawLine(
|
||||
graphBounds.left.toFloat(),
|
||||
graphBounds.bottom.toFloat(),
|
||||
graphBounds.right.toFloat(),
|
||||
graphBounds.bottom.toFloat(),
|
||||
legendPaint
|
||||
)
|
||||
|
||||
legendPaint.textAlign = Paint.Align.LEFT
|
||||
drawText(
|
||||
maxValue.toString(),
|
||||
graphBounds.right.toFloat() + legendLineLength,
|
||||
graphBounds.top + (legendWidth - legendLineLength) / 3,
|
||||
legendPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,16 +335,30 @@
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/prediction" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView8"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/availability_prediction"
|
||||
android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:textColor="?colorPrimary"
|
||||
app:goneUnlessAnimated="@{predictionGraph != null}"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView13" />
|
||||
|
||||
<net.vonforst.evmap.ui.BarGraphView
|
||||
android:id="@+id/prediction"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:goneUnless="@{predictionGraph != null}"
|
||||
android:layout_marginTop="8dp"
|
||||
app:goneUnlessAnimated="@{predictionGraph != null}"
|
||||
app:data="@{predictionGraph}"
|
||||
app:maxValue="@{charger.data.totalChargepoints}"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline2"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView13"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView8"
|
||||
tools:itemCount="3"
|
||||
tools:layoutManager="LinearLayoutManager"
|
||||
tools:listitem="@layout/item_connector"
|
||||
|
||||
@@ -270,4 +270,5 @@
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
<string name="about_contributors">Mitwirkende</string>
|
||||
<string name="about_contributors_text">Dank an alle Mitwirkenden für ihre Beiträge von Code und Übersetzungen für EVMap:</string>
|
||||
<string name="availability_prediction">Verfügbarkeitsprognose</string>
|
||||
</resources>
|
||||
@@ -269,4 +269,5 @@
|
||||
<string name="pref_provider_osm_mapbox">OpenStreetMap (Mapbox)</string>
|
||||
<string name="about_contributors">Contributors</string>
|
||||
<string name="about_contributors_text">Thanks to all contributors for their coding and translation contributions to EVMap:</string>
|
||||
<string name="availability_prediction">Availability prediction</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user