自定义视图未在 BottomSheetDialogFragment 中绘制,有时在无效后调用 onSizeChanged
Custom View is not drawing in BottomSheetDialogFragment, when sometimes onSizeChanged is called after invalidate
我正在开发一个自定义图表视图,显示在底部 sheet 对话框片段中,它没有绘制,当我添加数据并在调用 onSizeChanged()
之前调用 invalidate()
时.有时会出现这种情况,我不知道如何解决。
我在我的 BottomSheetDialogFragment 的 onViewCreated(...)
中添加了数据,它是 LiveData,所以在 Observer 调用 ChartView 的 setDataPoints()
之前肯定需要一些时间。
我还在两个普通片段中使用了图表,在那里工作正常。
我的自定义视图:
class MonthlyTrendChart @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
//region Variables
private val data = mutableListOf<DateDataPoint>()
private val points = mutableListOf<PointF>()
private val path = Path()
private val pathPaint: Paint
//endregion
//region Constructor
init {
pathPaint = Paint().apply {
isAntiAlias = true
color = Color.WHITE
strokeWidth = 5.0f
style = Paint.Style.STROKE
}
setWillNotDraw(false)
}
//endregion
//region View
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
println("MB: onSizeChanged: ${data.size}")
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
computePath()
canvas?.drawPath(path, pathPaint)
}
//endregion
//region Methods
fun setDataPoints(dataPoints: List<DateDataPoint>) = GlobalScope.launch(Dispatchers.Main) {
val min = dataPoints.minByOrNull { it.value }?.value ?: 0.0f
val positiveDataPoints: List<DateDataPoint> = List(dataPoints.size) { i -> DateDataPoint(dataPoints[i].date, dataPoints[i].value - min) }
data.clear()
data.addAll(positiveDataPoints)
computePoints()
postInvalidate()
println("MB: invalidate: ${data.size}")
}
private fun computePoints() {
points.clear()
val graphHeight = height.toFloat()
val maxValue: Float = data.maxByOrNull { it.value }!!.value
val xStep: Float = width.toFloat() / (data.size - 1)
for (i in 0 until data.size) {
val xVal = xStep * i
val yVal = graphHeight - data[i].value / maxValue * graphHeight
points.add(PointF(xVal, yVal))
}
}
private fun computePath() {
if (points.isEmpty()) return
with(path) {
reset()
moveTo(points.first().x, points.first().y)
for (i in 0 until points.size) {
lineTo(points[i].x, points[i].y)
}
}
}
//endregion
companion object {
}
}
这是输出:
2021-02-19 18:13:25.210 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:30.146 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:30.153 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:32.424 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:32.429 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:34.897 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:34.900 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:37.077 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:37.083 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:38.689 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28 <---- this is the case when it doesn't draw
2021-02-19 18:13:38.697 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 28 <---- doesn't draw
我已经尝试在 onSizeChanged()
中调用 invalidate()
,设置 setWillNotDraw(false)
或使用 postInvalidateDelayed(100)
延迟 invalidate()
的调用。
我找到了解决该问题的方法:
为了检查 onSizeChanged
是否已经被调用,我在我的方法 setDataPoints
.
中检查 height * width > 0
如果这个条件不成立,我会设置一个标志来重新计算尺寸变化时的绘图数据。
在 onSizeChanged
中,我检查了那个标志并再次调用了我的 setDataPoints
方法,这现在有效了。
我正在开发一个自定义图表视图,显示在底部 sheet 对话框片段中,它没有绘制,当我添加数据并在调用 onSizeChanged()
之前调用 invalidate()
时.有时会出现这种情况,我不知道如何解决。
我在我的 BottomSheetDialogFragment 的 onViewCreated(...)
中添加了数据,它是 LiveData,所以在 Observer 调用 ChartView 的 setDataPoints()
之前肯定需要一些时间。
我还在两个普通片段中使用了图表,在那里工作正常。
我的自定义视图:
class MonthlyTrendChart @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
//region Variables
private val data = mutableListOf<DateDataPoint>()
private val points = mutableListOf<PointF>()
private val path = Path()
private val pathPaint: Paint
//endregion
//region Constructor
init {
pathPaint = Paint().apply {
isAntiAlias = true
color = Color.WHITE
strokeWidth = 5.0f
style = Paint.Style.STROKE
}
setWillNotDraw(false)
}
//endregion
//region View
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
println("MB: onSizeChanged: ${data.size}")
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
computePath()
canvas?.drawPath(path, pathPaint)
}
//endregion
//region Methods
fun setDataPoints(dataPoints: List<DateDataPoint>) = GlobalScope.launch(Dispatchers.Main) {
val min = dataPoints.minByOrNull { it.value }?.value ?: 0.0f
val positiveDataPoints: List<DateDataPoint> = List(dataPoints.size) { i -> DateDataPoint(dataPoints[i].date, dataPoints[i].value - min) }
data.clear()
data.addAll(positiveDataPoints)
computePoints()
postInvalidate()
println("MB: invalidate: ${data.size}")
}
private fun computePoints() {
points.clear()
val graphHeight = height.toFloat()
val maxValue: Float = data.maxByOrNull { it.value }!!.value
val xStep: Float = width.toFloat() / (data.size - 1)
for (i in 0 until data.size) {
val xVal = xStep * i
val yVal = graphHeight - data[i].value / maxValue * graphHeight
points.add(PointF(xVal, yVal))
}
}
private fun computePath() {
if (points.isEmpty()) return
with(path) {
reset()
moveTo(points.first().x, points.first().y)
for (i in 0 until points.size) {
lineTo(points[i].x, points[i].y)
}
}
}
//endregion
companion object {
}
}
这是输出:
2021-02-19 18:13:25.210 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:30.146 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:30.153 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:32.424 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:32.429 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:34.897 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:34.900 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:37.077 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 0
2021-02-19 18:13:37.083 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28
2021-02-19 18:13:38.689 29400-29400/at.guger.moneybook.dev I/System.out: MB: invalidate: 28 <---- this is the case when it doesn't draw
2021-02-19 18:13:38.697 29400-29400/at.guger.moneybook.dev I/System.out: MB: onSizeChanged: 28 <---- doesn't draw
我已经尝试在 onSizeChanged()
中调用 invalidate()
,设置 setWillNotDraw(false)
或使用 postInvalidateDelayed(100)
延迟 invalidate()
的调用。
我找到了解决该问题的方法:
为了检查 onSizeChanged
是否已经被调用,我在我的方法 setDataPoints
.
height * width > 0
如果这个条件不成立,我会设置一个标志来重新计算尺寸变化时的绘图数据。
在 onSizeChanged
中,我检查了那个标志并再次调用了我的 setDataPoints
方法,这现在有效了。