Reputation: 527
I have an existing app made with XML and Kotlin. When updating, I wanted to learn Jetpack Compose, so I added a HorizontalPager inside a parent XML screen using ComposeView.
In XML
<androidx.compose.ui.platform.ComposeView
android:id="@+id/my_composable"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Then in Kotlin I did
binding.value.myComposable.setContent {
IntroPager()
}
My Compose Function
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun IntroPager(){
val pagerState = rememberPagerState(pageCount = {
3
})
Box(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { page ->
when (page){
0-> Page1("")
1-> Page2("")
2-> Page3("")
else -> Page1(" from else $page")
}
}
Row(
Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.BottomCenter)
.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.Center
) {
repeat(pagerState.pageCount) { iteration ->
val color =
if (pagerState.currentPage == iteration) Color(R.color.theme_color_secondary) else Color(R.color.dark_grey)
Box(
modifier = Modifier
.padding(2.dp)
.clip(CircleShape)
.background(color)
.size(16.dp)
)
}
}
}
}
Now, based on some user interaction in the XML part of the screen, I want to pass a click event from an XML onClickListener to go to the next page in my HorizontalPager in Compose. Basically Passing XML clickEvent to Compose.
I could not find any documentation on how to achieve this.
I tried implementing interface callback, but was unsuccessful
Upvotes: 1
Views: 611
Reputation: 527
The only working solution I could figure out was to use a shared ViewModel. Here is what I did:
I created a ViewModel class with a function to increment the page index:
class PagerViewModel : ViewModel() {
val page = mutableIntStateOf(0)
fun nextPage(){
if (page.value<=2)
page.value++
}
}
I then linked this ViewModel in my parent Activity. On click events in XML, I called the pagerViewModel.nextPage()
function.
Then in my Compose code:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun IntroPager(pagerViewModel: PagerViewModel){
val pagerState = rememberPagerState(pageCount = {
3
})
Box(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
) { page ->
Log.d("IntroPager", "IntroPager: "+pagerViewModel.page.value)
when (page){
0-> {
Page1("")
}
1-> {
Page2("")
}
2-> {
Page3("")
}
else -> Page1(" from else $page")
}
}
Row(
Modifier
.wrapContentHeight()
.fillMaxWidth()
.align(Alignment.BottomCenter)
.padding(bottom = 8.dp),
horizontalArrangement = Arrangement.Center
) {
repeat(pagerState.pageCount) { iteration ->
val color =
if (pagerState.currentPage == iteration) Color(R.color.theme_color_secondary) else Color(R.color.dark_grey)
Box(
modifier = Modifier
.padding(2.dp)
.clip(CircleShape)
.background(color)
.size(16.dp)
)
}
}
LaunchedEffect(pagerViewModel.page.value) {
pagerState.animateScrollToPage( pagerViewModel.page.value)
}
LaunchedEffect(pagerState){
snapshotFlow { pagerState.currentPage }.collect { page ->
pagerViewModel.page.intValue = page
}
}
}
}
This links the ViewModel page index to the HorizontalPager state. LaunchedEffect(pagerViewModel.page.value) {}
Launches a coroutine whenever the value of pageIndex changes, it animates scrolling to that page.
I wanted a straight forward way to connect click events between XML and Compose. But I couldn't find a simple solution, so I resorted to using the shared ViewModel to update state.
Upvotes: 1
Reputation: 1491
Define pagerState
at the beginning of setContent
and send it as a parameter to IntroPager
.
var pagerState = rememberPagerState(pageCount = { 3 })
fun IntroPager(pagerState: PagerState)
use this for page switching
val scope = rememberCoroutineScope()
scope.launch {
pagerState.scrollToPage(value)
}
You must add the xml clickListener after setContent
. Otherwise you will get this error.
@Composable invocations can only happen from the context of a @Composable
Upvotes: 0