How to add multiple items to Accompanist HorizontalPager? - android-jetpack-compose

When I create
HorizontalPager(count = 3) {
page -> ComposableName()
}
It just shows same composable multiple times, i can't add few pages and if I try to
HorizontalPager(count = 3) {
Image()
Image()
Image()
}
elements are just stacked on top of each other and also 3 times.
Is there any way to make smth like
LazyColumn()
{
item {}
item {}
item {}
}
If not what's the simplest solution?

In the content block of HorizontalPager you get index of the page that should be displayed. So you can do something like this:
HorizontalPager(count = 3) { index ->
if (index == 0) {
Image0()
} else if (index == 1) {
Image1()
}
...
}

Related

How can I remember the list position in a Compose LazyColumn using Paging 3 LazyPagingItems?

I have a function like:
#Composable
fun LazyElementList(data: Flow<PagingData<Element>>) {
val scrollState = rememberLazyListState()
val elements = data.collectAsLazyPagingItems()
LazyColumn(state = scrollState) {
items(elements) {
DisplayElement(it)
}
}
}
I would like when navigating to another screen and back to maintain the place in the list.
Unexpectedly, the value of scrollState is maintained when visiting child screens. If it wasn't, it should be hoisted, probably into the ViewModel.
In the code in the question scrollState will be reset to the beginning of the list because there are no items in the list on the first composition. You need to wait to display the list until the elements are loaded.
#Composable
fun LazyElementList(data: Flow<PagingData<Element>>) {
val scrollState = rememberLazyListState()
val elements = data.collectAsLazyPagingItems()
if (elements.isLoading) {
DisplayLoadingMessage()
} else {
LazyColumn(state = scrollState) {
items(elements) {
DisplayElement(it)
}
}
}
}
fun LazyPagingItems.isLoading(): Boolean
get() = loadState.refresh is LoadState.Loading

How to show a composable just for e few seconds?

I have a Text composable within a Box:
Box(modifier = Modifier)
) {
Text(text = "BlaBla" )
}
How can show the Box/Text for a few seconds, only?
You can use LaunchedEffect and delay with a boolean flag and set it false after time specified
#Composable
private fun TimedLayout() {
var show by remember { mutableStateOf(true) }
LaunchedEffect(key1 = Unit){
delay(5000)
show = false
}
Column(modifier=Modifier.fillMaxSize()) {
Text("Box showing: $show")
if(show){
Box{
Text(text = "BlaBla" )
}
}
}
}
There are tons of way you can achieve this, for instance:
You can use AnimatedVisibility in case you want to choose which animation you want.
Example:
AnimatedVisibility(visible = yourFlag) {
Box{
Text(text = "BlaBla")
}
}
If you are using a ViewModel or any pattern and you can observe you can create two states and then inside of the ViewModel / Presenter / Whatever you start the timer you want (doing this you can test it):
sealed class TextState {
object Visible: TextState()
object Invisible: TextState()
}
And then inside the #Composable you do this
val state by presenter.uiState.collectAsState()
when (val newState = state) {
Visible -> {}
Invisible -> {}
}
You can change it also playing with the Alpha
Modifier.alpha(if(isVisible) 1f else 0f)
Another valid option is what #Thracian commented, using Side-Effects
for example :
#Composable
fun YourComposable() {
LaunchedEffect(key1 = Unit, block = {
try {
initTimer(SECONDS_HERE) {
//Timer has passed so you can show or hide
}
} catch(e: Exception) {
//Something wrong happened with the timer
}
})
}
suspend fun initTimer(time: Long, onEnd: () -> Unit) {
delay(timeMillis = time)
onEnd()
}

Vapor 4 - Approaching an issue - Return type of a function

I have a function that triggers when I hit a certain endpoint, that contains some sort of a condition inside
if room exists {
return room
} else {
create a new room and return it
}
The issue is, that sometimes I return an EventLoopFuture (if the room doesn't exist), and sometimes I return a Room type without an EventLoopFuture (if a room exists), so I am not quite sure what the return type of the function should be.
Now the way I fix it is with a hacky workaround, where I always return an EventLoopFuture, and if the room exists, I update it and return it, which is totally unnecessary as there's nothing to update.
Would the right way be to save the newly created room and return it separately, so the condition will always return a Room type and not an EventLoopFuture?
The original function:
func findEmpty(req:Request) throws -> EventLoopFuture<Room> {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
return Room.query(on: req.db)
.filter(\.$category == category)
.all()
.flatMap { relevantRooms in
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room.update(on: req.db)
.map {
return room
}
}
}
}
let newRoom = Room(...//initializing room)
return newRoom.save(on: req.db)
.map {
return newRoom
}
}
}
With the introduction of async, you can choose to return either the Future or the resolved value. However, in most cases, the async pattern results in simpler code. If you do want to return a Future of your room, then it should be as simple as replacing your existing return room with return req.eventLoop.makeSucceededFuture(room) instead of the unnecessary update. so:
return room.update(on: req.db).map {
return room
}
Becomes:
return req.eventLoop.makeSucceededFuture(room)
Converting code using Future pattern to async is relatively straightforward (and if it isn't that's probably a sign you should leave it as Future). In your case:
func findEmpty(req:Request) async throws -> Room {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
let relevantRooms = try await Room.query(on: req.db)
.filter(\.$category == category)
.all()
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room
}
}
}
let newRoom = Room(...//initializing room)
try await newRoom.save(on: req.db)
return newRoom
}
Just an observation, but your inner for could be replaced with something simpler:
if let unoccupied = room.availablePositions.first { !$0.isOccupied } {
return unoccupied
}

Jetpack Compose:composable that have too many composables can cause the lazyColumn get stuck when it scroll

I want a graph like this.
The graph is composable in LazyColumn's item. And the lazyColumn get stuck when the graph shows and hides. How to optimize it?
code:
#Composable
fun List(){
LazyColumn(){
item{
Graph()
}
items{
// other items
}
}
}
#Composable
fun Graph(){
Row(Modifer.height(200.dp)) {
// other simple Composables,
if(isShow)
repeat(30){
Column() {
repeat(7) {
Box(Modifer.size(16.dp).background(Blue))
}
}
}
}
}
I don't know much about compose.I found a way to fixme it.
Use Side-effect.
#Composable
fun List(){
val isShow = remember { mutableStateOf(true) }
val listState = rememberLazyListState()
LaunchedEffect(listState) {
// Graph is first item
snapshotFlow { listState.firstVisibleItemIndex }.collect {
if (it != 0) {
isShow.value = false
}else{
isShow.value = true
}
}
}
LazyColumn(state = listState){
item{
Graph(isShow.value)
}
items{
// other items
}
}
}
#Composable
fun Graph(isShow:Boolean){
Row(Modifer.height(200.dp)) {
// other simple Composables,
if(isShow)
repeat(30){
Column() {
repeat(7) {
Box(Modifer.size(16.dp).background(Blue))
}
}
}
}
}
There are simple codes.
Is a good way?
You need to use LazyColumn's items(...) or item { ..composable..} to take advantage of its lazy-ness. For each item on your column, you need to surround it with item to tell compose that is a row inside that column.

JavaFX8 - How to remove a TableCell which has a tooltip?

I want to remove TableCells out of my TableView. Therefore I call this line:
table.getItems().remove(item);
The TableCell is removed correctly, but the ToolTip will be shown on the next TableCell.
For example: When I remove the first Cell, the ToolTip will be shown on the second Cell (which is now the first).
How do I avoid this issue?
My CellFactory looks like that:
column.setCellFactory(c -> {
TableCell<DataItem, DataItem> cell = new TableCell<DataItem, DataItem>() {
#Override
protected void updateItem(DataItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getName());
if (item.getDescription() != null && !item.getDescription().isEmpty()) {
setTooltip(new Tooltip(item.getDescription()));
}
}
}
};
return cell;
});
Handle the case where item is null. If you think in terms of "removing data from the table model", instead of "removing cells" (you're not removing any cells at all, you're just changing the data displayed by the existing cells), this should make sense.
column.setCellFactory(c -> {
TableCell<DataItem, DataItem> cell = new TableCell<DataItem, DataItem>() {
#Override
protected void updateItem(DataItem item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
setText(null);
setTooltip(null);
} else {
setText(item.getName());
if (item.getDescription() != null && !item.getDescription().isEmpty()) {
setTooltip(new Tooltip(item.getDescription()));
} else {
// may need something here, depending on your application logic...
}
}
}
};
return cell;
});

Resources