# 如何从文件或网络加载图像

> 从本地文件或从网络下载的字节加载 PNG/JPEG/GIF，并用 ImageView 显示它。

Source: https://crossfly.dev/zh/docs/cookbook/images/carregar-arquivo-rede — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何从文件或网络加载图像

**目的。** 从本地路径加载图像文件（PNG、JPEG 或 GIF），或从网络下载的字节
加载，并用 `ImageView` 在屏幕上显示它。这是显示照片、图标和插图最常见的
方式。

## 使用的 API
- `new Image(String path) throws ImageException, IOException` — 从文件路径加载图像。如果找不到文件（读取后 `width == 0`），则抛出 `ImageException`。
- `new Image(byte[] bytes) throws ImageException` — 从已在内存中的字节数组解码图像（PNG/JPEG/GIF）。对从网络下载的数据使用此构造函数。
- `new ImageView(Image image, ImageSize imageSize)` — 创建绘制图像的控件。使用 `ImageSize.RATIO` 时，图像会在保持比例的情况下均匀缩放（letter-boxing），不裁切。
- `ImageView.imageSize(ImageSize)` / `.alignX(Position)` / `.alignY(Position)` — 用于调整尺寸模式和对齐方式（默认 `Position.CENTER`）的流式 setter。

> Javadoc 与代码的偏差：`new Image(String)` 的 Javadoc 写的是 "@throws ImageException When
> the file was not found"，但实际签名还声明了 `IOException`。两者都要处理。

## 完整示例（复制、粘贴并运行）

```java
package com.exemplo;

import java.io.IOException;

import dev.crossfly.Main;                    // runOnPaintThreadAndWait
import dev.crossfly.ui.*;                     // Window, Component, ImageView, UIConstants.ImageSize
import dev.crossfly.ui.layout.*;             // VerticalLayout, LAY_FILL
import dev.crossfly.ui.image.*;              // Image, ImageException

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   private ImageView imageView;

   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // 1) 从本地文件加载
      try {
         Image img = new Image("/sdcard/foto.jpg");   // 可能抛出 ImageException / IOException
         ImageView iv = new ImageView(img, UIConstants.ImageSize.RATIO);
         add(LAY_FILL, iv);
      } catch (ImageException | IOException e) {
         // 文件未找到或已损坏
      }

      // 2) 从网络：在 paint thread 之外下载并解码，然后更新 widget
      imageView = new ImageView(null, UIConstants.ImageSize.RATIO);
      add(LAY_FILL, imageView);

      new Thread(() -> {
         try {
            byte[] data = baixarBytes("https://exemplo.com/foto.png"); // 你的 HTTP client
            Image img = new Image(data);              // 在 paint thread 之外解码
            Main.runOnPaintThreadAndWait(() -> imageView.image(img));
         } catch (Exception e) {
            // 处理网络/解码错误
         }
      }).start();
   }

   /** Stub：替换为你自己的 HTTP 客户端。 */
   private byte[] baixarBytes(String url) throws IOException
   {
      return new byte[0];
   }
}
```

> 下面的片段仅展示**核心部分**。要运行它，请把它放入上面的 `MainWindow` 骨架中。

## 最小片段

```java
// 已在源码核实：Image.java（403、432 行）和 ImageView.java（72 行）
import dev.crossfly.ui.image.*;   // Image, ImageException
import dev.crossfly.ui.*;         // ImageView, UIConstants.ImageSize

// 1) 从本地文件加载
try {
   Image img = new Image("/sdcard/foto.jpg");   // 可能抛出 ImageException / IOException
   ImageView iv = new ImageView(img, UIConstants.ImageSize.RATIO);
   add(iv, /* 你的容器的位置/布局 */);
} catch (ImageException | java.io.IOException e) {
   // 文件未找到或已损坏
}
```

### 从网络（在 paint thread 之外下载字节）

`new Image(...)` 会解码像素，是一项繁重的操作。**绝不要**在 paint
thread 上下载或解码。在工作线程中下载字节、在那里创建 `Image`，然后再更新控件。

```java
// 已在源码核实：new Image(byte[]) — Image.java 432 行
new Thread(() -> {
   try {
      byte[] data = baixarBytes("https://exemplo.com/foto.png"); // 你的 HTTP client
      Image img = new Image(data);              // 在 paint thread 之外解码
      // 现在在 paint thread 上安排 UI 更新
      Main.runOnPaintThreadAndWait(() -> imageView.image(img));
   } catch (Exception e) {
      // 处理网络/解码错误
   }
}).start();
```

## 深入了解
- [后台工作](/docs/data-and-services/background-work) — 如何离开 paint thread 来下载和解码。
- [ImageView 控件](/docs/ui/widgets/imageview) 和 [图像](/docs/ui/images-and-graphics/images) — `ImageSize` 的各模式与对齐。
- 姊妹配方：[imagem-de-fundo.md](/docs/cookbook/images/imagem-de-fundo)。
