Tobias Gronbach
03/23/2022, 10:24 AMmyanmarking
03/23/2022, 12:13 PMmyanmarking
03/23/2022, 12:14 PMRonald Wolvers
03/23/2022, 5:21 PMAlertDialog()
) or from your AndroidView()
? If AndroidView()
is creating the dialogs then what comes to mind is to use the compose variant. You would have to propagate state to the composable somehow though, so you could use a StateFlow
and call collectAsState()
on it, which will give you exact control over the recomposition. I guess that's a lot of work but that's the approach I use a lot and does get the job done so to speak.Ronald Wolvers
03/23/2022, 5:22 PMRonald Wolvers
03/23/2022, 5:41 PMTobias Gronbach
03/23/2022, 5:50 PM@Composable
fun AnalyseScreen(
viewModel: AnalyseViewModel = hiltViewModel(),
) {
val chartDataPackage by viewModel.chartDataPackageFlow.collectAsState()
val screenState by viewModel.screenState.collectAsState(AnalyseScreenState())
val menuBackgroundColor = Color(0xFF957B1B)
val menuBarHeight = 50.dp
var isMenuOpen by remember { mutableStateOf(false) }
var showTimeSelectionDialog by remember { mutableStateOf(false) }
if (showTimeSelectionDialog) {
// ... calls Dialog
}
if (screenState.showDiagramSelectionDialog) {
//... calls Dialog
}
Column(
modifier = Modifier.background(Color(0xFF423F34)),
verticalArrangement = Arrangement.SpaceBetween
) {
Row(
modifier = Modifier.fillMaxWidth().height(200.dp)
) {
Chart(chartDataPackage) // opens AndroidView
}
Row(modifier = Modifier
.height(menuBarHeight)
.fillMaxWidth()
.background(menuBackgroundColor)
.padding(5.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
// .... more composables
}
if (isMenuOpen) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(0.dp, 0.dp, 0.dp, menuBarHeight),
contentAlignment = Alignment.BottomEnd
) {
// Menu
}
}
}
}
As soon as a dialog is shown / hidden or Menu is shown / hidden the Chart also gets recomposed and I don't want that.Ronald Wolvers
03/23/2022, 6:33 PMremember[...]()
was called and then after the if
switches simply hits the part of the function again where the chart is drawn. If you would put the dialogs logic after drawing the chart, I'm pretty sure it won't get recomposed.Ronald Wolvers
03/23/2022, 6:33 PMRonald Wolvers
03/23/2022, 6:47 PMTobias Gronbach
03/23/2022, 8:07 PMTobias Gronbach
03/23/2022, 8:24 PM@Composable
private fun <T> Chart(chartDataPackage: ChartDataPackage<T>) {
when (chartDataPackage.diagram) {
Diagram.OverviewChart -> {
val stats = chartDataPackage.data as List<StatisticsData>
StatsOverviewChart(stats.first())
}
Diagram.BarChart_XNameValence_YAmount,
-> {
val chartData = chartDataPackage.data as List<BarEntry>
val meta = chartDataPackage.meta
meta?.let {
BarChartCompose(chartData, it)
}
}
Diagram.PieChart_XNameValence_YAmount,
-> {
val meta = chartDataPackage.meta
val chartData = chartDataPackage.data as List<PieEntry>
meta?.let {
PieChartCompose(chartData, meta)
}
}
}
This is PieChart with AndroidView as example
@Composable
fun PieChartCompose(chartData: List<PieEntry>, meta: ChartMetaData) {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
PieChart(context)
},
update = { pieChart ->
pieChart.apply {
// ... PieChart Settings
}}})
Ronald Wolvers
03/23/2022, 8:51 PMView
being re-drawn is putting a LaunchedEffect()
in the Chart()
composable and then seeing if that gets executed. If it doesn't then it must have something to do with AndroidView()
of which I don't know the internals. How Compose decides whether or not to re-draw an old school View
is territory probably best avoided until there's no other option, lest you would have to re-do the whole thing in Compose 😬Ronald Wolvers
03/23/2022, 8:53 PMTobias Gronbach
03/23/2022, 9:04 PMLaunchedEffect(chartDataPackage) { //used chartDataPackage as Key
println("Recomposition Launched Effect")
}
println("Recomposition") // added this as well outside of launchedeffect
Output is:
"Recomposition Launched Effect" when diagramm opened first time
"Recompostion" after show/hide of dialogs and menus
If I interpret this correctly I would say it's Compose recomposingRonald Wolvers
03/23/2022, 9:16 PMAndroidView()
. Just wanted to double check too that the dialogs don't touch the state?Ronald Wolvers
03/23/2022, 9:21 PMTobias Gronbach
03/23/2022, 9:23 PMRonald Wolvers
03/23/2022, 9:26 PMcollectAsState()
call?Ronald Wolvers
03/23/2022, 9:29 PMTobias Gronbach
03/23/2022, 9:30 PM@Composable
fun AnalyseScreen(
viewModel: AnalyseViewModelCompose = hiltViewModel(),
) {
val chartDataPackage by viewModel.chartDataPackageFlow.collectAsState()
println("AnalyseScreen Recomposition")
Box(
modifier = Modifier.fillMaxSize().padding(0.dp, 0.dp, 0.dp, 50.dp)
) {
Chart(chartDataPackage)
}
.... // rest of states and composables
}
Ronald Wolvers
03/23/2022, 9:32 PMval chartDataPackage (...)
?Ronald Wolvers
03/23/2022, 9:33 PMTobias Gronbach
03/23/2022, 9:34 PMRonald Wolvers
03/23/2022, 9:34 PMRonald Wolvers
03/23/2022, 9:35 PMTobias Gronbach
03/23/2022, 9:38 PMRonald Wolvers
03/23/2022, 9:39 PMcollectAsState()
is happening is still called, it must be something that AndroidView()
is doing where it recomposes the whole thingRonald Wolvers
03/23/2022, 9:39 PMTobias Gronbach
03/23/2022, 9:39 PMRonald Wolvers
03/23/2022, 9:40 PMTobias Gronbach
03/23/2022, 9:42 PMmyanmarking
03/23/2022, 9:49 PMmyanmarking
03/23/2022, 9:53 PMTobias Gronbach
03/24/2022, 9:58 AM@Composable
fun AnalyseScreen(
viewModel: AnalyseViewModelCompose = hiltViewModel(),
) {
Surface {
println("AnalyseScreen Recomposition")
Box(
modifier = Modifier.fillMaxSize().padding(0.dp, 0.dp, 0.dp, 50.dp)
) {
Chart(viewModel)
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.BottomEnd
) {
ChartMenu(viewModel)
}
}
}
As you can see I took the content (a lot of nested Composables) below the Chart and added it to a seperate @Composable-Annotated-Function. I passed to both
@Composable-Functions (Chart and ChartMenu) my viewModel (which could be done in other ways as well). In Zachs article he writes
"Functions are a natural delimiter for re-executable chunks of code, because they already have well-defined entry and exit points.". So
I thought this was a good idea and suddenly it worked as expected BUT I still didn't understand why?
Because I thought the content of the ChartMenu was also before wrapped in Columns and Rows (and wondered if this aren't good entry and exit points), etc. and so I still wondered why a Menu Popup or Dialog Popup would force
the whole Screen to recompose. Then again in the article Zach writes "You might be surprised to find out (and I often forget) that common layouts like Column, Row, and Box are all inline functions."
and "Because the body of inline composable functions are simply copied into their call sites, such functions do not get their own recompose scopes."
That got me interested and I changed the function declaration from
fun ChartMenu(viewModel : AnalyseViewModelCompose)
to
inline fun ChartMenu(viewModel : AnalyseViewModelCompose)
... and guess what? Right, again it didn't work! So it's really important to understand recompose scopes and realize that Column, Row, Box etc. are inline functions without own recompose scopes!
I hope I got it right and I hope you could also learn something! Thanks again so much for your kind help, i really appreciate that!Ronald Wolvers
03/24/2022, 10:35 AM