ListView with ImageViews for cells very bugged?

John Hendrikx john.hendrikx at gmail.com
Tue Jul 11 23:22:15 UTC 2023


It's not the most efficient code for sure, as so far its bare bones 
seeing if the app works at all.  I already updated it a bit, adding the 
ImageView as a field of the cell factory, but it made little 
difference.  I'll work up a full example tomorrow with pre-loaded images 
and everything (and without my util helpers) so others can try.

--John

On 12/07/2023 00:58, Kevin Rushforth wrote:
> What you have should work, but will create redundant images and 
> ImageView ojbects. The recommendation is to create the nodes 
> (ImageView in this case) in the constructor of the cell factory and 
> call setGraphic with either null or that factory's node.
>
> Having said that, I doubt that this is related to the visual problems 
> you are seeing. Maybe Ajit or Andy have some ideas.
>
> -- Kevin
>
>
> On 7/11/2023 3:50 PM, John Hendrikx wrote:
>> I tend to avoid ListView, because for some reason it seems to just 
>> never do what I want unless used for its mundane rows of text use 
>> case. I so far always just implemented my own skin for ListView that 
>> deals with displays that have many images that need to scroll 
>> smoothly, but...
>>
>> Recently, I've again attempted to use ListView, this time for showing 
>> (so far) fixed size ImageViews with images exactly 512x512 pixels in 
>> size:
>>
>> The cell factory looks like this, which I think is a correct 
>> implementation:
>>
>>       listView.setCellFactory(lv -> {
>>         return new ListCell<>() {
>>           protected void updateItem(ImageHandle imageHandle, boolean 
>> empty) {
>>             if(empty) {
>>               setGraphic(null);
>>             }
>>             else {
>>               try {
>>                 setGraphic(new ImageView(new Image(new 
>> ByteArrayInputStream(imageHandle.getImageData()))));
>>               }
>>               catch(IOException e) {
>>                 e.printStackTrace();
>>               }
>>             }
>>           }
>>         };
>>       });
>>
>> Now, this is with JavaFX 21-ea-24 (but also happens on 19).  I 
>> spotted the following IMHO bugs:
>>
>> 1) When the ImageView is so large that a single row is only partially 
>> visible, the scrollbar is incorrect (it shows a full bar, even if 
>> half of a row is displayed).
>>
>> 2) The vertical scrollbar doesn't respond to a click in the non-thumb 
>> area at all (you'd expect to make it scroll a page up or down that way)
>>
>> 3) When jerkily resizing the window, I can sometimes have a vertical 
>> + horizontal scrollbar when there's a full row visible + anywhere 
>> from 0 to 10 extra empty pixels (probably the potential height of a 
>> horizontal scrollbar) with the vertical scroll bar still visible.  
>> The algorithm to hide the scrollbar apparently is not deterministic 
>> and depends on mouse movement speed.
>>
>> 4) When a row is less than half visible, scrolling the window down to 
>> another ImageView will jump the view and hide the first cell that 
>> should still be visible for the bottom 0-49% of its height.
>>
>> 5) It's possible to get the vertical scroll bar in such a state that 
>> it doesn't respond to the mouse any more at all, even by dragging the 
>> thumb.  Some keyboard action and mouse scrolling sometimes restores 
>> it to relatively normal functioning.
>>
>> 6) Mouse wheel scrolling acts weird.  With one large cell visible and 
>> two rows available, scrolling only **down** on the mouse wheel will 
>> scroll down, then jump back up, and scroll down again... it's 
>> impossible to scroll all the way down to get to a point where it 
>> stops scrolling as it keeps jumping back.
>>
>> This is with ImageView's.  I suppose they're known to have 
>> problems... but then I switched to a Region, which contain an 
>> ImageView with proper min/pref/max implementations.  This improved 
>> the situation somewhat.  The scrollbar was much more reliable, but I 
>> still saw almost all of the above issues, but somewhat more reliable 
>> than before.  Still rows would disappear when (for the most part) 
>> invisible, those and the jumps of the view are just totally 
>> unacceptable.
>>
>> I'm not quite sure what to make of this, it seems the control is not 
>> meant for arbitrary graphics.
>>
>> --John
>>
>>
>>
>> package org.int4.sdui.ui;
>>
>> import hs.mediasystem.util.image.ImageHandle;
>> import hs.mediasystem.util.javafx.control.Containers;
>>
>> import java.io.ByteArrayInputStream;
>> import java.io.IOException;
>> import java.util.Base64;
>> import java.util.List;
>>
>> import javafx.application.Application;
>> import javafx.scene.Scene;
>> import javafx.scene.control.Button;
>> import javafx.scene.control.ListCell;
>> import javafx.scene.control.ListView;
>> import javafx.scene.control.TextArea;
>> import javafx.scene.image.Image;
>> import javafx.scene.image.ImageView;
>> import javafx.scene.layout.HBox;
>> import javafx.scene.layout.Priority;
>> import javafx.stage.Stage;
>>
>> import kong.unirest.core.Unirest;
>>
>> public class Main {
>>
>>   public static void main(String[] args) {
>>     Application.launch(UI.class, args);
>>   }
>>
>>   public static class UI extends Application {
>>
>>     @Override
>>     public void start(Stage primaryStage) {
>>       TextArea prompt = new TextArea("a flower on Mars");
>>       Button button = new Button("Submit");
>>       ListView<ImageHandle> listView = new ListView<>();
>>
>>       listView.setCellFactory(lv -> {
>>         return new ListCell<>() {
>>           protected void updateItem(ImageHandle imageHandle, boolean 
>> empty) {
>>             if(empty) {
>>               setGraphic(null);
>>             }
>>             else {
>>               try {
>>                 setGraphic(new ImageView(new Image(new 
>> ByteArrayInputStream(imageHandle.getImageData()))));
>>               }
>>               catch(IOException e) {
>>                 e.printStackTrace();
>>               }
>>             }
>>           }
>>         };
>>       });
>>
>>       button.setOnAction(e -> {
>>         Txt2ImgResponse response = textToImage(prompt.getText());
>>
>>         for(String image : response.images) {
>>           listView.getItems().add(new 
>> InMemoryImageHandle(Base64.getDecoder().decode(image)));
>>         }
>>       });
>>
>>       HBox hbox = Containers.hbox(Containers.vbox(prompt, button), 
>> listView);
>>
>>       HBox.setHgrow(listView, Priority.ALWAYS);
>>
>>       Scene scene = new Scene(hbox);
>>
>>       primaryStage.setScene(scene);
>>       primaryStage.show();
>>     }
>>
>>     private Txt2ImgResponse textToImage(String prompt) {
>>       return Unirest.post("http://127.0.0.1:7860/sdapi/v1/txt2img")
>>         .body(new Txt2Img(prompt, 5))
>>         .contentType("application/json")
>>         .asObject(Txt2ImgResponse.class)
>>         .getBody();
>>     }
>>   }
>>
>>   static class InMemoryImageHandle implements ImageHandle {
>>     private final byte[] data;
>>
>>     public InMemoryImageHandle(byte[] data) {
>>       this.data = data;
>>     }
>>
>>     @Override
>>     public byte[] getImageData() {
>>       return data;
>>     }
>>
>>     @Override
>>     public String getKey() {
>>       return null;
>>     }
>>
>>     @Override
>>     public boolean isFastSource() {
>>       return true;
>>     }
>>   }
>>
>>   record Txt2Img(String prompt, int steps) {}
>>   record Txt2ImgResponse(List<String> images) {}
>> }
>>
>>
>>
>


More information about the openjfx-dev mailing list