异步加载 JButton 图标

Load JButton Icons Asynchronously

我有一个应用程序在启动时从 API 中提取数据并将它们显示在 JButton 网格中。我已根据 API 响应成功显示并生成 JButton,但我的问题是每个 Button 都需要有一个图标,该图标也从 API 直接拉入 JButton 图标。

实现这一点很好,但我的问题是应用程序需要大约 10 分钟才能启动,因为它同步地一个一个地拉出每个图像图标。 P.S。我正在使用 OkHttp 作为我的 http 客户端。

有人可以帮我找到一种方法来加载按钮,然后在图像完全从 API 中完全拉出时逐渐加载图像。

我该怎么做? 如何异步执行此操作?

我尝试到处寻找,但未能成功地将我找到的答案应用到我的问题中。

这是这个问题的代码段。

提前致谢!

static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

// api post method
String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
        .url(url)
        .post(body)
        .build();
    try (Response response = client.newCall(request).execute()) {
        return response.body().string();
    }
}

// api call
try {
        String json = "";
        String response = post("http://myapi.com", json);

        JSONObject JsonResponse = new JSONObject(response);
        JSONArray devices = JsonResponse.getJSONArray("response");

        int rows = (devices.length() / 4) + 3;
        resultsPanel.setLayout(new GridLayout(rows, 0, 20, 20));

        for(int d = 0; d < devices.length(); d++){

            // store device details
            JSONObject selectedDevice = devices.getJSONObject(d);

            // create button
            JButton device = new JButton();

            // style button
            Border lineBorder = new LineBorder(new java.awt.Color(238, 238, 238));
            Border padding = new EmptyBorder(0, 10, 0, 0);

            device.setIcon(new javax.swing.ImageIcon(getClass().getResource("/assets/shield_32.png")));

            device.setIconTextGap(20);
            device.setText(devices.getJSONObject(d).get("name").toString());
            device.setPreferredSize(new Dimension(100, 100));
            device.setBackground(new java.awt.Color(255, 255, 255));
            device.setFont(new java.awt.Font("Arial", 0, 12));
            device.setVerticalTextPosition(SwingConstants.BOTTOM);
            device.setHorizontalTextPosition(SwingConstants.CENTER);
            device.setBorder(new CompoundBorder(lineBorder, padding));
            device.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
            device.setFocusable(false);

            // event handling
            device.addActionListener((java.awt.event.ActionEvent evt) -> {
                System.out.println("Selected: " + ((JButton) evt.getSource()).getText());
                deviceNameLbl.setText(((JButton) evt.getSource()).getText());

                deviceImgLbl.setIcon(new javax.swing.ImageIcon(getClass().getResource("/assets/shield_350.png")));
                getDevicePattern(selectedDevice.get("deviceModelID").toString());

                CardLayout card = (CardLayout)mainPanel.getLayout();
                card.show(mainPanel, "material_design");
            });

            // add button to panel
            resultsPanel.add(device);
        }

    } catch (IOException ex) {
        Logger.getLogger(ISoD.class.getName()).log(Level.SEVERE, null, ex);
    }

我终于自己解决了这个问题。

我使用 SwingWorker class 在从 API 拉取图像图标时异步设置图像图标。这就是我一直在寻找的完美效果!

完整的解决方案如下:

主要Class(JFrame)

static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
        .url(url)
        .post(body)
        .build();
    try (Response response = client.newCall(request).execute()) {
        return response.body().string();
    }
}

try {
        String json = "";
        String response = post("http://my-api.com/images", json);

        JSONObject JsonResponse = new JSONObject(response);
        JSONArray devices = JsonResponse.getJSONArray("response");

        int rows = (devices.length() / 4) + 3;
        resultsPanel.setLayout(new GridLayout(rows, 0, 20, 20));

        for(int d = 0; d < devices.length(); d++){

            // store device details
            JSONObject selectedDevice = devices.getJSONObject(d);

            // create button
            JButton device = new JButton();

            // extracting icon url from api response
            URL imageURL = new URL(devices.getJSONObject(d).get("brandImage").toString());

            // style button
            Border lineBorder = new LineBorder(new java.awt.Color(238, 238, 238));
            Border padding = new EmptyBorder(0, 10, 0, 0);

            device.setIcon(new javax.swing.ImageIcon(getClass().getResource("/assets/stock_image.png")));
            device.setIconTextGap(20);
            device.setText(devices.getJSONObject(d).get("name").toString());
            device.setPreferredSize(new Dimension(100, 100));
            device.setBackground(new java.awt.Color(255, 255, 255));
            device.setFont(new java.awt.Font("Arial", 0, 12));
            device.setVerticalTextPosition(SwingConstants.BOTTOM);
            device.setHorizontalTextPosition(SwingConstants.CENTER);
            device.setBorder(new CompoundBorder(lineBorder, padding));
            device.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
            device.setFocusable(false);

            // add button to panel
            resultsPanel.add(device);

            // asynchronously load images from api response
            new ImageWorker(imageURL, device).execute();
        }

    } catch (IOException ex) {
        Logger.getLogger(ISoD.class.getName()).log(Level.SEVERE, null, ex);
    }


SwingWorker Class

public class ImageWorker extends SwingWorker<ImageIcon, Void>{

URL imageURL;
ImageIcon brandImage;
JButton device;

public ImageWorker(URL imageURL, JButton device){
    this.imageURL = imageURL;
    this.device = device;
}

@Override
protected ImageIcon doInBackground() throws Exception {
    brandImage = new ImageIcon(imageURL);
    Image rawBrandImage = brandImage.getImage();
    Image newimg = rawBrandImage.getScaledInstance(32, 32,  java.awt.Image.SCALE_SMOOTH);
    brandImage = new ImageIcon(newimg);
    return brandImage;
}

@Override
protected void done() {
    // leave as default zagg shield icon if no brand icon is returned by api
    if (brandImage.getIconWidth() == 32 && brandImage.getIconHeight() == 32) {
        device.setIcon(brandImage);
    }
  }
}