How to force compose' LazyColumn to act like traditional scrollable elements like RecyclerView or ListView?
Useful when want to scroll with mouse, e.g. with vysor.
The solution is to add filter to modifier
const val VERTICAL_SCROLL_MULTIPLIER = -4
// Helps with scrolling with mouse wheel (just like recycler view/list view without compose)
#ExperimentalComposeUiApi
#Composable
fun MyLazyColumn(
modifier: Modifier = Modifier,
state: LazyListState, // rememberLazyListState()
content: LazyListScope.() -> Unit
) {
LazyColumn(
state = state,
modifier = modifier
.pointerInteropFilter {
if (it.action == MotionEvent.ACTION_SCROLL) {
state.dispatchRawDelta(it.getAxisValue(MotionEvent.AXIS_VSCROLL) * VERTICAL_SCROLL_MULTIPLIER)
}
false
},
content = content
)
}
Related
I'm working with Compose but have Fragments. I need to switch to second page when user click IconButton.
With only xml:
_binding = FragmentFirstScreenBinding.inflate(inflater, container, false)
val viewPager = activity?.findViewById<ViewPager2>(R.id.viewPager)
binding.firstFragmentButton.setOnClickListener{
viewPager?.currentItem = 1
}
In Compose:
AndroidView(factory = { context ->
val viewPager = activity?.findViewById<ViewPager2>(R.id.viewPager)
val view = LayoutInflater.from(context)
.inflate(R.layout.fragment_view_pager, null, false)
viewPager?.currentItem = 1
view
})
Box(
modifier = Modifier
.fillMaxSize()
) {
IconButton(
onClick = { },
modifier = Modifier
.height(90.dp)
.width(90.dp)
.align(Alignment.TopEnd)
) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_keyboard_arrow_right_24),
contentDescription = "",
)
}
}
Problem with this code is that automatically I switch to second page.
I tried to put viewPager in IconButton on click but I dont get view back and got null when user clicks icon. My question is how to get that viewPager into compose part.
EDIT:
Just put viewPager in onCreateView before
val viewPager = activity?.findViewById<ViewPager2>(R.id.viewPager)
return ComposeView(requireContext()).apply {
setContent {
I practicing jetpack compose, where I have a LazyVerticalGrid, a Text and a lazyColumn composeables, which I put in inside another composable which I called HomeContentScreen and gave it a scroll function, and as you know, the LasyVerticalGrid already has a built-in scroll function, which I tried to disable but it affected the scrolling function.
To Summarize; the three composeables scroll differently, depending on which part of the screen I touch.
So my question is how do I make them all follow the same scroll function (vertically)
Here's the codes.
|
Text Composeable;
#Composable
fun HomeQuote() {
Text(
text = stringResource(R.string.Lorem_ipsum),
style = MaterialTheme.typography.h2,
modifier = Modifier
.paddingFromBaseline(top = 24.dp, bottom = 18.dp)
.padding(horizontal = 18.dp)
.heightIn(min = 56.dp)
)
}
|
LazyVerticalGrid Composable;
#Composable
fun SecondBodyGrid(
modifier: Modifier = Modifier,
onHomeCardClick: (Int) -> Unit = {},
) {
LazyVerticalGrid(
columns = GridCells.Fixed(1),
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.height(1450.dp)
//.disabledVerticalPointerInputScroll()
.....
|
LazyRow Composable
#Composable
fun FirstBodyRow(
onHomeCardClick: (Int) -> Unit = {},
) {
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(horizontal = 16.dp),
modifier = Modifier
) {
.....
|
And Finally the Composable I arranged everything, HomeContentScreen;
#Composable
fun HomeContentScreen(
modifier: Modifier = Modifier,
onHomeCardClick: () -> Unit
) {
Column(
modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp)) {
HomeQuote()
HomeTitleSection(title = R.string.favorite_collections) {
SecondBodyGrid {onHomeCardClick()}
}
HomeTitleSection(title = R.string.align_your_body) {
FirstBodyRow {onHomeCardClick()}
}
}
Please bear with me I don't know how to properly phrase my question, but I'll try my best.
I made the screen above by putting muliple composables together.
I gave the composable carrying the row an onClick function;
#Composable
fun MenuGrid(
modifier: Modifier = Modifier,
onMenuCardClick: (Int) -> Unit = {},
) {
LazyHorizontalGrid(
rows = GridCells.Fixed(3),
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.height(200.dp)
) {
items(MenuData) { item ->
MenuCard(
drawable = item.drawable,
text = item.text,
modifier = Modifier
.height(56.dp)
.clickable { onMenuCardClick(item.drawable + item.text) }
)
}
}
}
Like I said earlier I put multiple composables together, to form the image above. I arranged the multiple composables in a composable, I called MenuContentScreen;
#Composable
fun MenuContentScreen(modifier: Modifier = Modifier) {
Column(
modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp)) {
MenuQuote()
MenuContentSection(title = R.string.favorite_collections) {
MenuGrid()
}
}
}
Then I referenced/called the MenuContentScreen on the main Composable of that screen MenuScreen. (The one defined in NavHost)
#Composable
fun MenuScreen() {
MenuContentScreen(Modifier.padding())
}
Which is where the problem is, since the onClick function was defined in another composable, I can't use the onClick function for the MenuScreen;
So MY QUESTION is there a way I can link the onClick function on MenuGrid to MenuScreen, maybe creating an parameter on MenuScreen and assigning it to the MenuGrid's onClick function (which I have tried and got val cannot be assigned), or Any thing at all.
I will greatly appreciate any help. I have been on this like forever. No information is too small please.
Thanks in Advance.
You need to carry onClick lambda till your MenuScreen Composable such as
#Composable
fun MenuContentScreen(modifier: Modifier = Modifier,
onMenuCardClick: () -> Unit
) {
Column(
modifier
.verticalScroll(rememberScrollState())
.padding(vertical = 16.dp)) {
MenuQuote()
MenuContentSection(title = R.string.favorite_collections) {
MenuGrid(){
onMenuCardClick()
}
}
}
#Composable
fun MenuScreen(
onMenuCardClick: () -> Unit
) {
MenuContentScreen(Modifier.padding(), onClick= onMenuCardClick)
}
And in you nav graph without passing navController to MenuScreen
call
MenuContentScreen {
navController.navigate()
}
Why the composable Icon does not appear?
The Cardcontent composable is FillMaxWidth
I want the "Cardcontent" composable to do fillMaxWidth inside the "Cardtest" composable taking into account the "Icon" composable
#Preview
#Composable
fun Icon () {
Image(painter = painterResource(id = R.drawable.icon),
contentDescription ="Icon",
modifier = Modifier.
size(width = 40.dp,
height = 40.dp) )
}
#Preview
#Composable
fun Cardcontent (){
Row (horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth())
{
Avatar()
TextDesc()
}
}
#Preview
#Composable
fun CardTest(){
Row(Modifier
.width(310.dp)
.padding(start = 16.dp)
.padding(vertical = 13.dp),
verticalAlignment = Alignment.CenterVertically)
{
Cardcontent()
Icon()
}
}
Agree with ADM opinion, you must pass the modifier as an argument of CardContent() and that's mandatory, because in order to asign a weight(1f) modifier to your CardContent component, the declaration must be within the scope of a Column or Row, because of possible reusability, you CardContent component could be used in a Box or scaffold in the future, and weight doesn't apply for this components, so imagine that having it declared inside of the CardContent as default weight and then placing it inside of those examples would be useless and give you a sintax error, so finally you must do something like this:
fun CardTest(){
Row(
Modifier
.width(310.dp)
.padding(start = 16.dp)
.padding(vertical = 13.dp),
verticalAlignment = Alignment.centerVertically
)
{
Cardcontent(modifier = Modifier.weight(1f))
Icon()
}
}
You should in CardContend insert argument wherein you hand over component Icon
For some reason LazyColumns do not scroll with a mouse click and move gesture. It only works with the mouse wheel so far. For LazyRows it is also not possible to scroll with the mouse wheel. It seems that lazy row is useless for Compose for desktop.
Are there any possiblities to enable a click and move gesture on LazyRow and LazyColum. And if not is it at least possible to enable to scroll through a LazyRow with the mouse wheel?
I used this minimal reproducible example to test the scrolling
#Composable
#Preview
fun App() {
var text by remember { mutableStateOf("Hello, World!") }
MaterialTheme {
LazyRow(modifier = Modifier.fillMaxSize()) {
repeat(100) {
item {
Text("Test Test Test Test $it ")
}
}
}
}
}
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
App()
}
}
This is the intended behavior.
All scrollable components (including LazyColumn) work (for now) only with mouse wheel scroll events on the desktop.The scrollable components should not respond to mouse drag/move events.
Here's a basic example of how you can add drag support to your components:
val scrollState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
LazyRow(
state = scrollState,
modifier = Modifier
.draggable(
orientation = Orientation.Horizontal,
state = rememberDraggableState { delta ->
coroutineScope.launch {
scrollState.scrollBy(-delta)
}
},
)
) {
items(100) {
Text("Test Test Test Test $it")
}
}