Wear OS show image with horizontal scroll - android

I need to show long image like this on Galaxy 5 watch
Where can I find code examples for this job? Or which component I can use?

Use Coil to render the image.
AsyncImage(
model = "https://example.com/image.jpg",
contentDescription = null
)
You can use a horizontally scrollable Row in Compose.
Row(
modifier = Modifier.horizontalScroll(scrollState)
) {
// ...
}
https://developer.android.com/jetpack/compose/lists
You can make it scroll horizontally with RSB/Bezel with
public fun Modifier.scrollableRow(
focusRequester: FocusRequester,
scrollableState: ScrollableState
): Modifier = composed {
val coroutineScope = rememberCoroutineScope()
onPreRotaryScrollEvent {
coroutineScope.launch {
// events are vertical, but apply horizontally
scrollableState.scrollBy(it.verticalScrollPixels)
scrollableState.animateScrollBy(0f)
}
true
}
.focusRequester(focusRequester)
.focusable()
}
Here is full code example https://github.com/enginer/wear-os-horizontal-scroll-image

Related

Using CoilImage in Jetpack Compose, how can I remove the top and bottom padding after the image is downloaded

I have a simple CoilImage implementation in my project where I download an image through an url, and then proceed to move the image state into an Image Composable. That's going smooth so far, but I'm facing a small problem where undesirable paddings are being added to the image. The image composable is inside a column with no fixed height. How can I get rid of these paddings while retaining the image dimension?
I've tried a couple of solutions such as using wrapContentHeight, putting it in a box and clipping it, using contentScale etc, but I just can't seem to get rid of the green spaces.
#Composable
fun HorizontalTextWithObject(enumerationItem: TextWithObjectPresentable) {
Row(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.Top) {
Image(modifier = Modifier, enumerationItem)
}
Column(modifier = Modifier.weight(2f)) {
Title(modifier = Modifier, enumerationItem)
Description(enumerationItem)
}
}
}
#Composable
fun Image(modifier: Modifier,
enumerationItem:TextWithObjectPresentable) {
CoilImage(
{ request },
success = { imageState ->
imageState.imageBitmap?.let {
val width = it.width.dp
val height = ((width / 5) * 3)
Image(bitmap = it, contentDescription = "",
modifier = Modifier
.width(width)
.height(height)
.background(SDColor.green),
)
}
},
)
}
I tried ContentScale.Crop but it is not what I'm looking for as it will just fill the paddings heightwise

Compose material3 OverScroll Shows in all 4 directions

I am using meterial3 with Compose . I found that all the Scrollable Composable Showing overscroll effect in all 4 directions regardless of scrolling direction. that goes for Column, LazyColumn, LazyVerticalGrid etc ..
I m not using anything custom to override overscroll effect. i can not figure out whats causing this behavior . i will add an example code below with LazyVerticalGrid which is causing this behavior . Since this is vertical grid it should show over scroll only vertically(to and bottom) but it also show it horizontally (left and right)..
Any direction on this will be appreciated.
#Composable
#Destination
fun FavoritesScreen(navigator: DestinationsNavigator) {
val favoritesViewModel: FavoritesViewModel = hiltViewModel()
val favoritesLiveData =
favoritesViewModel.favoritesLiveData.observeAsState(initial = null)
if (favoritesLiveData.value != null) {
if (favoritesLiveData.value!!.isEmpty()) {
// Show empty view
} else {
LazyVerticalGrid(
contentPadding = PaddingValues(
12.dp,
12.dp,
12.dp,
20.dp
),
columns = GridCells.Fixed(2),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
item(span = { GridItemSpan(2) }) {
Spacer(modifier = Modifier.windowInsetsTopHeight(WindowInsets.statusBars))
Text(
text = stringResource(id = R.string.favorites),
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(top = 20.dp)
)
ColumnSpacer(value = 10)
}
items(favoritesLiveData.value!!) { game ->
// grid Item
}
}
}
}
}
You may need to update your Compose version. This was a known issue in Compose 1.3.0-alpha01, which was fixed subsequently.

How to track nested lazy composable scroll direction

I have a LazyGrid composable nested within a vertically scrolling layout. I want to be able to detect whenever the nested grid scrolls up and as a result scroll the outer layout to a particular point on the screen.
I tried doing something like this:
#Composable
fun MyComposable(viewModel: ViewModel) {
val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var scrollToPosition by remember {
mutableStateOf(0F)
}
ConstraintLayout(
modifier = Modifier
.padding(8.dp)
.fillMaxSize()
.verticalScroll(scrollState) {
val (ref1, ref2, ref3, gridView) = createRefs()
if (viewModel.scrollUp.value){
coroutineScope.launch {
scrollState.animateScrollTo(scrollToPosition.roundToInt())
}
}
Row(){//some view}
//Target scrol point
Row(modifier = Modifier
.height(200.dp)
.padding(top = 8.dp)
.onGloballyPositioned { coordinates ->
scrollToPosition = coordinates.positionInParent().y
}){ ... }
//row housing nested grid
Row(){ GridView(viewModel)}
}
}
In the grid view I have:
#Composable
fun GridView (viewModel: ViewModel) {
//monitor scroll position
val lazyListState = rememberLazyListState()
val pOffset = remember {
lazyListState.firstVisibleItemScrollOffset
}
val direction = lazyListState.firstVisibleItemScrollOffset - pOffset
val scrollDown = direction >0
LazyVerticalGrid( state = lazyListState ...) {
if (scrollUp) {
viewModel.scrollDown.value = true
...
}
}
}
The scroll variable in the view model is just:
val scrollUp = mutableStateOf(false)
Now, the issue is, this setup is not very responsive and I need to scroll up and down in the grid view before the system detects it.
Is there a more efficient way to set this up to track even the minutest scrolls?
I thought of using this but since it was a composable, i couldn't use it with my view model as before and when I called it directly from the outer layout scope, it kept scrolling the outer layout to the target point before the nested grid was touched.

Jetpack Compose - HorizontalPager item spacing/padding for items with max width

Using Jetpack Compose and the accompanist pager, I'm trying to create a HorizontalPager where:
Edges of items to the left and right of current item are shown
There is a max width to the Pager items
As an example, I wrote the following code (for simplicities sake, I made Text items, but in reality, I'm actually making more complex Card items):
#Composable
fun MyText(modifier: Modifier) {
Text(
text = LOREM_IPSUM_TEXT,
modifier = modifier
.wrapContentHeight()
.border(BorderStroke(1.dp, Color.Red))
)
}
#ExperimentalPagerApi
#Composable
fun MyPager(pagerItem: #Composable () -> Unit = {}) {
Scaffold {
Column(
modifier = Modifier
.fillMaxSize()
// In case items in the VP are taller than the screen -> scrollable
.verticalScroll(rememberScrollState())
) {
HorizontalPager(
contentPadding = PaddingValues(32.dp),
itemSpacing = 16.dp,
count = 3,
) {
pagerItem()
}
}
}
}
#ExperimentalPagerApi
#Preview
#Composable
fun MyPager_200dpWidth() {
MyPager { MyText(modifier = Modifier.widthIn(max = 200.dp)) }
}
#ExperimentalPagerApi
#Preview
#Composable
fun MyPager_500dpWidth() {
MyPager { MyText(modifier = Modifier.widthIn(max = 500.dp)) }
}
#ExperimentalPagerApi
#Preview
#Composable
fun MyPager_FillMaxWidth() {
MyPager { MyText(modifier = Modifier.fillMaxWidth()) }
}
The issue I'm having is that when I make the item have a max width that seems to be smaller than the screen width (see MyPager_200dpWidth), I no longer see the items on the side anymore. On the other hand, using items with larger max widths (See MyPager_500dpWidth) or fillMaxWidth (See MyPager_FillMaxWidth) allows me to see the items on the side.
It seems weird to me that items with the smaller max width are actually taking up more horizontal space than the items with the larger max width. This can be shown in the Preview section...
See how the images on the left (MyPager_500dpWidth) and middle (MyPager_FillMaxWidth) show the next item, while the image on the right (MyPager_200dpWidth`) takes up the whole screen? Here's another comparison when hovering my mouse over the items to see the skeleton boxes...
Just wondering if someone could help me figure out how I can solve this use case. Is it possible that there's a bug in Compose?
The page size is controlled by the HorizontalPager.contentPadding parameter.
Applying widthIn(max = 200.dp) to your text only reduces the size of your element inside the page, while the page size remains unchanged.
Applying more padding should solve your problem, for example, contentPadding = PaddingValues(100.dp) looks like this:
You can fix this by adding your contentPadding like this
val horizontalPadding = 16.dp
val itemWidth = 340.dp
val screenWidth = LocalConfiguration.current.screenWidthDp
val contentPadding = PaddingValues(start = horizontalPadding, end = (screenWidth - itemWidth + horizontalPadding).dp)

Scroll multiple lists at different speeds in Jetpack Compose

Currently I'm trying to develop a special kind of image carousel which basically has two rows (one at the top with the images, and another one (shorter in width) at the bottom that use names for reference)
The thing is that I need to scroll the top row faster than the bottom one to achieve this with Jetpack Compose (can be done in regular Android with just some scroll listeners)
I was able to achieve scroll the rows simultaneously but they are scrolling at the same speed. I need to scroll the first one twice as fast to achieve the effect I want.
Here's the code I tried.
val scrollState = rememberScrollState()
Row(modifier = Modifier.horizontalScroll(scrollState)) {
Images()
}
Row(modifier = Modifier.horizontalScroll(scrollState)) {
Legend()
}
If you only need one Row to be scrollable, you can create new ScrollState each time.
val scrollState = rememberScrollState()
Row(modifier = Modifier.horizontalScroll(scrollState)) {
repeat(100) {
Text(it.toString())
}
}
Row(
modifier = Modifier.horizontalScroll(
ScrollState(scrollState.value * 2),
enabled = false
)
) {
repeat(200) {
Text(it.toString())
}
}
Note that this solution may not be the best in terms of performance, as it creates a class on each pixel scrolled.
An other solution is to sync them with LaunchedEffect. It'll also allow you both way scrolling synchronization.
#Composable
fun TestScreen(
) {
val scrollState1 = rememberScrollState()
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.horizontalScroll(scrollState1)
) {
repeat(100) {
Text(it.toString())
}
}
val scrollState2 = rememberScrollState()
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
modifier = Modifier.horizontalScroll(scrollState2)
) {
repeat(200) {
Text(it.toString())
}
}
SyncScrollTwoWay(scrollState1, scrollState2, 2f)
}
#Composable
fun SyncScrollTwoWay(scrollState1: ScrollState, scrollState2: ScrollState, multiplier: Float) {
SyncScroll(scrollState1, scrollState2, multiplier)
SyncScroll(scrollState2, scrollState1, 1 / multiplier)
}
#Composable
fun SyncScroll(scrollState1: ScrollState, scrollState2: ScrollState, multiplier: Float) {
if (scrollState1.isScrollInProgress) {
LaunchedEffect(scrollState1.value) {
scrollState2.scrollTo((scrollState1.value * multiplier).roundToInt())
}
}
}
But it also have one downside: there'll be little delay in scrolling, so the second scroll view will always be one frame behind the scrolling one.

Categories

Resources