つらねの日記

プログラムの進捗やゲームをプレイした感想などを書き連ねる日記。

JavaFX width fit ImageView in ListView

前書き

画像ファイルを分類分けして、それらをフォルダに入れておく(しかも階層が深いものもある)と画像があるディレクトリまで移動してプレビュー画面を開いてと、面倒くさいときがある。
そういった問題を解決すること、昨日のLTでのJava8のStreamの話を聞いて熱があったという理由から、8の書き方を少しだけ意識しながらJavaFXを使って画像Viewerを作った。

全てのソースはgithubにupしている。github.com

要求

  • ディレクトリ内にはいくつかのフォルダが存在する。
  • フォルダ内には複雑な階層で画像ファイルが点在する。
  • フォルダごと、消したり移動したりしたい。
  • ただし中にどのような画像が入っているかは推測が不可能。
  • フォルダ内の画像が含まれているのかを確認したい。
  • フォルダは手軽に連続的に確認したい。

新しい世界だった

StreamやLambda式のみならず、今まではほぼJava6 程度までしかほぼ触っていなかったため完全に新しい世界であった。
File を使おうとしたところ、nio2 というものがあるらしく、いろいろと便利になっていた。
というか、Stream.filterが完全に神だった。

Files.list(Paths.get(dir))
.filter(Files::isDirectory)
.forEach(System.out::println);

こんな感じでdir 内のディレクトリのみを取得できた。
今までのString の代わりにPathを使い、Files をstatic に使う感じになってるのかなという感じ。
Files::isDirectory などといった記法は中の関数に順々に直接投げ込める感じがあるととても良いと思う。

Files.find(f, Integer.MAX_VALUE, (path, attr) -> path.getFileName().toString().matches(".*\\.(jpg)*(png)*"))
.filter(path->!Files.isDirectory(path))
.forEach(path->System.out.println(path));

フォルダ内のjpgとpngを探してきてくれる。
正規表現拡張子判断して画像か判断しているが、もっと良い記法が有りそう。
しかしながら、いまいち切り時がわからず、横に長いなという感想。

長らく困らせてくれた部分

ListView 内部にImageView を適当に放り込むと、画像サイズがそのままで横スクロールも必要になる。
これは面倒。
どうにかしたかったがなかなかどうにもならなかった。
初めListView 側の設定かなと思って調べていたが、
ListView (JavaFX 8)
これをやったら、表示部分が小さくなるだけであった。


以下の記事に漂流したが、書いてみたらなんか違う。stackoverflow.com

@Override
public void initialize(URL location, ResourceBundle resources) {
    ListView<ImageView> lv = new ListView(items);
    
    lv.setCellFactory(new Callback<ListView<ImageView>, ListCell<ImageView>>() {
        
        @Override
        public ListCell<ImageView> call(ListView<ImageView> param) {
            ListCell lc = new ListCell<ImageView>() {
                
                @Override
                protected void updateItem(ImageView item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty)
                        setGraphic(null);
                    else
                        setGraphic(item);
                }
            };
            lc.prefWidthProperty().bind(lv.widthProperty().subtract(4));
            return lc;
        }
    });
}

確かにListView 横スクロールはしなくなってはいるが、画像のサイズがそのままでむしろ見えなくなった。

fit だとか画像のresize だとかというワードで以下の記事がかかった。stackoverflow.com
しかしながら横のみがぴったりになり、縦が長いままという始末。
最終的には
setPreserveRatio(true)で解決できた。

InputStream is = Files.newInputStream(path, StandardOpenOption.READ);
ImageView iv = new ImageView(new Image(is));
iv.fitWidthProperty().bind(imgs.widthProperty().subtract(20));
iv.setPreserveRatio(true);

感想

画像の大きさ問題を乗り越え、無事動くものを作ることができた。
これでわざわざそのディレクトリに行くことなく、画像を閲覧することができる。
展望としては画像読み込みをnext クリック時に行っているため若干の遅さがあるという点を改善したい。
(但し、事前に指定したディレクトリ内のすべての画像を読み込むのはmemory error でダメだった。)