【问题标题】:Make images fetched from server display in real time实时显示从服务器获取的图像
【发布时间】:2014-11-20 03:18:55
【问题描述】:

我有许多图像是通过一系列线程化的 HTTP 网络调用获取的。我正在使用 Callables 和 Futures 来管理这个过程。当每个图像从服务器返回时,我想在 JPanel 上显示它而不等待其他图像返回。

此代码有效,但 UI 直到所有图像都返回后才更新:

private void loadAndDisplayImages() throws InterruptedException, ExecutionException {      

    final List<Callable<Image>> partitions = new ArrayList<Callable<Image>>();

    for(final MediaFeedData data : imagesList) {
        partitions.add(new Callable<Image>() {
            public Image call() throws Exception {
                    String url = data.getImageUrl();
                    return ImageDisplayer.displayImageFromUrl(url, imageSize);
                }
            }        
        });
    }

    // for testing, use only a single thread to slow down rendering
    final ExecutorService executorPool = Executors.newFixedThreadPool(1); //numImages);    

    // run each callable, capture the results in a list of futures
    final List<Future<Image>> futureImages = 
            executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);

    for(final Future<Image> img : futureImages) {
        Image image = img.get(); // this will block the UI

        final ImageButton imageButton = new ImageButton(image, imageSize);

        SwingUtilities.invokeLater(new Runnable(){
            @Override public void run() {
                imagesPanel.add(imageButton);
                frame.validate();
                frame.setVisible(true);
            }
        });
    }

    executorPool.shutdown();
}

【问题讨论】:

  • 考虑使用从Callable 扩展而来的SwingWorker,它是done 方法,将图像添加到面板。不要忘记致电revalidaterepaint
  • 尝试使用SwingWorker 它将帮助您同时更新GUI。检查此链接docs.oracle.com/javase/tutorial/uiswing/concurrency/…
  • 我花了好几个小时尝试使用它。其实调用这个函数的方法就是一个SwingWorker。但我不知道如何让它更新我的用户界面。

标签: java swing


【解决方案1】:

考虑将SwingWorkerExecutorService 结合使用...

SwingWorker...

    public class ImageLoaderWorker extends SwingWorker<Image, Image> {

        private File source;
        private JPanel container;

        public ImageLoaderWorker(File source, JPanel container) {
            this.source = source;
            this.container = container;
        }

        @Override
        protected Image doInBackground() throws Exception {
            return ImageIO.read(source);
        }

        @Override
        protected void done() {
            try {
                Image img = get();
                JLabel label = new JLabel(new ImageIcon(img));
                container.add(label);
                container.revalidate();
                container.repaint();
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }

    }

ExecutorService...

ExecutorService executor = Executors.newFixedThreadPool(4);
File images[] = new File("...").listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        String name = pathname.getName().toLowerCase();
        return name.endsWith(".jpg") || name.endsWith(".png");
    }
});

for (File img : images) {

    executor.submit(new ImageLoaderWorker(img, this));

}

可运行示例...

这只是扫描一个目录并加载图像,但概念基本相同......

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestImageLoader {

    public static void main(String[] args) {
        new TestImageLoader();
    }

    public TestImageLoader() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new JScrollPane(new TestPane()));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel implements Scrollable {

        public TestPane() {
            setLayout(new GridLayout(0, 4));
            ExecutorService executor = Executors.newFixedThreadPool(4);
            File images[] = new File("...").listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    String name = pathname.getName().toLowerCase();
                    return name.endsWith(".jpg") || name.endsWith(".png");
                }
            });

            for (File img : images) {

                executor.submit(new ImageLoaderWorker(img, this));

            }
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return new Dimension(600, 600);
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 128;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return false;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

    }

    public class ImageLoaderWorker extends SwingWorker<Image, Image> {

        private File source;
        private JPanel container;

        public ImageLoaderWorker(File source, JPanel container) {
            this.source = source;
            this.container = container;
        }

        @Override
        protected Image doInBackground() throws Exception {
            return ImageIO.read(source);
        }

        @Override
        protected void done() {
            try {
                Image img = get();
                JLabel label = new JLabel(new ImageIcon(img));
                container.add(label);
                container.revalidate();
                container.repaint();
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }

    }

}

【讨论】:

  • 大多数人忘记(或没有意识到)SwingWorker ;)
  • 看了这个之后,我认为它基本上和我的代码是一样的。我的 ExecutorService 使用每个图像一个线程进行 http 获取,而且速度非常快。我的问题是在 ExecutorService 仍然有打开的线程时返回 UI。我可以用 SwingWorker 代替 SwingUtilities,但我认为这不会改变任何东西。事实上,我什至不确定我是否有问题。如果我尝试获取 500 张图片进行压力测试,UI 大约需要 1-2 秒才能完全重新绘制(从第一张图片显示开始)。
  • 我不知道这是否是我的代码按我想要的工作的线索,或者是否只是所有这些图像都需要时间才能在屏幕上呈现。我明白你的例子的要点了吗?再次感谢。我真的很感激!
  • SwingWorkerdone方法,这个是在doInBackground返回之后调用的,但是是在EDT的上下文中执行的
  • 是的,类似,SwingWorker 只是让它变得更容易;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多