私はSwingWorkerのは、私を助けることを願っている問題がありますが、私は私のプログラムでそれを統合する方法はかなりわかりません。
問題:
CardLayout Iでカード2を開き、カード1上のボタンを持っています。カード2は、平均1〜6の画像に表示されるカスタム・レンダラ(延出JLabelの)とするJListを有します。
- PNG画像
- サイズは500キロバイトの周りに
- カードの変更とImageIOに介してロード
レンダラは、JLabelのアイコンなどの画像イメージのスケーリングやにじみ等の設定より重い操作を適用します。
約6イメージが頻繁それでもその無反応感触の臨時一瞬悪く起こらないである、レンダリングする必要がある場合、これは、ほぼ秒かかることがあります。
今、私はSwingWorkerのはここに役立つかもしれないと思ったが、私は徹底的に私はそれを統合する必要がありますどのようにと混同しています。
私たちは、このコードスニペットを持っていたと仮定すると、
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class Example {
private JPanel mainPanel = new JPanel();
private JList<Product> list = new JList();
private JScrollPane scroll = new JScrollPane();
private Map<String, Color> colorMap = new HashMap<>();
public Example() {
colorMap.put("red", Color.red);
colorMap.put("blue", Color.blue);
colorMap.put("cyan", Color.cyan);
colorMap.put("green", Color.green);
colorMap.put("yellow", Color.yellow);
mainPanel.setBackground(new Color(129, 133, 142));
scroll.setViewportView(list);
scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(80,200));
list.setCellRenderer(new CustomRenderer());
DefaultListModel model = new DefaultListModel();
model.addElement(new Product("red"));
model.addElement(new Product("yellow"));
model.addElement(new Product("blue"));
model.addElement(new Product("red"));
model.addElement(new Product("cyan"));
list.setModel(model);
mainPanel.add(scroll);
}
public static void main(String[] args) throws IOException {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("WorkerTest");
frame.setContentPane(new Example().mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(300, 300);
frame.setMinimumSize(new Dimension(160, 255));
frame.setVisible(true);
}
});
}
class CustomRenderer extends JLabel implements ListCellRenderer<Product> {
private Product product;
public CustomRenderer() {
setOpaque(false);
}
@Override
public Component getListCellRendererComponent(JList<? extends Product> list, Product product, int index, boolean isSelected, boolean cellHasFocus) {
this.product = product;
/**
* in the actual code image is png with alpha channel respectively named to the productID of the JList object
*
* String id = product.getId();
* image = ImageIO.read(getClass().getResource("../../resources/images/" + id + ".png"));
*/
BufferedImage image1 = new BufferedImage(80, 50, BufferedImage.TYPE_INT_RGB);
BufferedImage image2 = new BufferedImage( 80, 75, BufferedImage.TYPE_INT_RGB);
Graphics g = image2.getGraphics();
/**
* this is only an example, in the actual code I might also apply gaussian blurs or rescale several time
*/
g.drawImage(image1,0,0,null);
setIcon(new ImageIcon(image2));
return this;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(colorMap.get(product.getColor()));
g.fillRect(0,0,80,75);
}
}
class Product {
String productID;
String color;
public Product(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public String getProductID() {
return productID;
}
}
}
私は、画像の操作を引き継ぐために、すべてのgetListCellRendererComponent呼び出しからSwingWorkerのを呼び出す必要がありますか?
SwingWorkerのは、この問題のためにも、適切なツールですか?
私は私のGUIのこの部分を高速化することができますどのようになどの任意の助けをいただければ幸いです。
EDIT:ウナギのホバークラフトフル画像をプリロードする助けとレンダラから画像をロードする根本的に間違っていることができることを述べました。
この他の質問へのリードを私に:
私は3000の周りに、各オブジェクトは、8キロバイトのJPGサムネイル(レンダリング中にも)のオブジェクトIDを介して負荷であるを持っているオブジェクトを持つと同時に、これらのサムネイルの12に6周りのリストが表示されます(レッツ・コール、それはLIST1)リストを持っている(起因しますリストの寸法)
ユーザーがオブジェクトを選択したとき、彼はCardlayoutからカード2を表示するには、ボタンを押すことができます元の質問に言及し、それのリスト(リスト2)オブジェクトで、すべてが、それは非サムネイル表示(500キロバイトのPNG +重い画像操作)でオブジェクトの関連です。今、私はオブジェクトの非サムネイル画像をプリロードすることが可能だと思うし、それが周りに1-6の画像になります最初のリストで選択した関係です。私はウナギのホバークラフトのフルが言ったことを正確に理解している場合、私はLIST1からオブジェクトを選択した後、これらのイメージをロードするためにSwingWorkerのを使用することができます。
しかし、どのようなリスト1から約3000の画像について、プログラムは一見スローダウンや、彼らはサイズがかなり小さく、サムネイルには重い操作は存在しないが、彼らはまだリスト1のレンダラを形成するロードされているため、応答しなくなっていません。それは数千のサムネイルをプリロードするために意味をなすだろうか?
ところで。質問の編集この種のを望んされていない場合は私に言うこと自由に感じ、それは自分自身の問題になされるべきである場合。
一つのアプローチは、次のようになります。
特定の要素(製品)のためのセルレンダリングコンポーネントが要求されるたびに、あなたは、一致する画像がすでにロードされているかどうか確認してください。そうでない場合は、バックグラウンドで画像をロードし、処理の作業を行うスイングワーカーを開始します。作業員が終了すると、画像は、後の検索のためにキャッシュに置かれています。一方で、あなたはレンダラがちょうど言わせ"Loading..."
か何か。
非常に迅速な実装はここにあります:
そしてMCVEとして:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingWorker;
public class LazyImageLoadingCellRendererTest
{
private JPanel mainPanel = new JPanel();
private JList<Product> list = new JList<Product>();
private JScrollPane scroll = new JScrollPane();
public LazyImageLoadingCellRendererTest()
{
mainPanel.setBackground(new Color(129, 133, 142));
scroll.setViewportView(list);
scroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(80, 200));
list.setCellRenderer(new LazyImageLoadingCellRenderer<Product>(list,
LazyImageLoadingCellRendererTest::loadAndProcessImage));
DefaultListModel<Product> model = new DefaultListModel<Product>();
for (int i=0; i<1000; i++)
{
model.addElement(new Product("id" + i));
}
list.setModel(model);
mainPanel.add(scroll);
}
public static void main(String[] args) throws IOException
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
JFrame frame = new JFrame("WorkerTest");
frame.setContentPane(
new LazyImageLoadingCellRendererTest().mainPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(300, 300);
frame.setMinimumSize(new Dimension(160, 255));
frame.setVisible(true);
}
});
}
private static final Random random = new Random(0);
private static BufferedImage loadAndProcessImage(Product product)
{
String id = product.getProductID();
int w = 100;
int h = 20;
BufferedImage image =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, w, h);
g.setColor(Color.BLACK);
g.drawString(id, 10, 16);
g.dispose();
long delay = 500 + random.nextInt(3000);
try
{
System.out.println("Load time of " + delay + " ms for " + id);
Thread.sleep(delay);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return image;
}
class Product
{
String productID;
public Product(String productID)
{
this.productID = productID;
}
public String getProductID()
{
return productID;
}
}
}
class LazyImageLoadingCellRenderer<T> extends JLabel
implements ListCellRenderer<T>
{
private final JList<?> owner;
private final Function<? super T, ? extends BufferedImage> imageLookup;
private final Set<T> pendingImages;
private final Map<T, BufferedImage> loadedImages;
public LazyImageLoadingCellRenderer(JList<?> owner,
Function<? super T, ? extends BufferedImage> imageLookup)
{
this.owner = Objects.requireNonNull(
owner, "The owner may not be null");
this.imageLookup = Objects.requireNonNull(imageLookup,
"The imageLookup may not be null");
this.loadedImages = new ConcurrentHashMap<T, BufferedImage>();
this.pendingImages =
Collections.newSetFromMap(new ConcurrentHashMap<T, Boolean>());
setOpaque(false);
}
class ImageLoadingWorker extends SwingWorker<BufferedImage, Void>
{
private final T element;
ImageLoadingWorker(T element)
{
this.element = element;
pendingImages.add(element);
}
@Override
protected BufferedImage doInBackground() throws Exception
{
try
{
BufferedImage image = imageLookup.apply(element);
loadedImages.put(element, image);
pendingImages.remove(element);
return image;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
@Override
protected void done()
{
owner.repaint();
}
}
@Override
public Component getListCellRendererComponent(JList<? extends T> list,
T value, int index, boolean isSelected, boolean cellHasFocus)
{
BufferedImage image = loadedImages.get(value);
if (image == null)
{
if (!pendingImages.contains(value))
{
//System.out.println("Execute for " + value);
ImageLoadingWorker worker = new ImageLoadingWorker(value);
worker.execute();
}
setText("Loading...");
setIcon(null);
}
else
{
setText(null);
setIcon(new ImageIcon(image));
}
return this;
}
}
注意:
これは本当に一般的なアプローチを示すだけの簡単な例です。もちろん、これは多くの方法で改善することができました。実際のロードプロセスがすでにに引き出されているがFunction
(これのために、それは一般的に適用すること任意のそれはしようとします:かかわらず、それはどこから来るの、画像の一種)、一つの大きな注意点があることであるロードすべての画像を。素敵な拡張は、ここにいくつかの粋を追加すること、そしてそれがいることを確認してくださいだろう唯一の細胞は、現在表示されている画像をロードします。あなたが1000個の要素のリストを持って、そして最後の10個の要素を見たいと思ったときにたとえば、あなたは990個の要素がロードされるのを待つ必要はありません。最後の要素はpriorizedしなければならない高いですそして最初にロードされました。しかし、このために、少し大きめのインフラ(主に:自分のタスクキュー、リストとそのスクロールペインにはいくつかの強力な接続)が必要になることがあります。(...それは素晴らしく、面白いものが持っているかもしれないので、私はおそらく、この1日取り組むだろうが、それまで、上記の例では、それを行う可能性があります)