[an error occurred while processing this directive] [an error occurred while processing this directive]

(2) 画像をロードして、表示する

画像をアップしたディレクトリのファイル一覧 (こんなの) を読み込んで、画像のパスを取得します。 リンクなっていて、.jpg または .JPG で終わるファイル一覧を取得し、ロードします。 画像の URL はソースコードに決め打ちです。 前回 から変わったのは、以下の6点です。

[New] Env: 色々な定数を入れるクラス

定数をまとめたクラスです。グローバル変数的に使っています。
class Env {
    public static int    W, H;
    public static double WH_RATIO;
    public static String URL;

    /** 各種パラメータのデフォルト値を設定 */
    static {
        W = 800;
        H = 600;
        WH_RATIO = 1.333333;
        URL = "http://funini.com/kei/ivy/narita/";
    }
}

Ivylet: メインのアプレットのクラス

アプレットのメインのクラスです。 新しく増えたところは色を変えました。
extractPaths() の引数に URL (例えばHttp://funini.com/kei/ivy/narita/)を入れると、画像のパスのリスト (例えば ["http://funini.com/kei/ivy/narita/00.jpg", "http://funini.com/kei/ivy/narita/01.jpg",..] が返ってきます。
extractFiles() は、BufferedReader から一行ずつ読み、得られた画像の URL をfiles に追加していきます。
public class Ivylet extends JApplet {
    public void init() {
        String[] imgPaths = extractPaths(Env.URL);
        IvyPanel tp = new IvyPanel(imgPaths);
        setContentPane(tp);
    }

 
    /** 指定されたパスから、画像の URL 一覧を取得 */
    public String[] extractPaths(String urlStr) {
        // urlBase には、URLの / 以前の部分が入る ( http://funini.com/kei/ など)
        String urlBase = urlStr.substring(0, urlStr.lastIndexOf("/") + 1);
        ArrayList files = new ArrayList();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    new URL(urlStr).openConnection().getInputStream()));
            extractFiles(files, urlBase, br);
        } catch (IOException ie) {
            ie.printStackTrace();
        }
        return (String[]) files.toArray(new String[0]);
    }

    /** br から全体を読み込み、画像の URL 一覧を取得 */
    void extractFiles(ArrayList files, String urlBase, BufferedReader br) throws IOException{
        Pattern pattern = Pattern.compile("^(.+\\.(jpg|JPG))<.*");
        for(;;) {
            String line = br.readLine();
            if(line == null) break;
            String[] targets = line.split(">");
            for(int i = 0; i < targets.length; i++) {
                Matcher m = pattern.matcher(targets[i]);
                if(m.matches()) files.add(urlBase + m.group(1));
            }
        }
    }

}

IvyPanel: メインのパネル

メインのパネルです。
前回から変わったのは、アイコンが 1 つだけだったのが、アイコンのリストになっただけです。
class IvyPanel extends JPanel {
    IconList    iconList; // 各画像の重ね順です。

    public IvyPanel(String[] urls) {
        /** コンストラクタ 引数は画像が入っているパス */
        setBackground(new Color(0x33, 0x33, 0x33)); // 背景色設定
        setPreferredSize(new Dimension(Env.W, Env.H)); // サイズ設定
        iconList = new IconList(urls);
    }


    /** 描画を行う */
    public void paintComponent(Graphics g) {
        super.paintComponent(g); // ウィンドウを表示
        Graphics2D g2 = (Graphics2D) g; // アンチエイリアスの設定など
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        for(int i = 0; i < iconList.size(); i++)
            iconList.get(i).draw(g2); // 各アイコンを表示
    }
}

[New] IconList: アイコンのリスト、重ね順、位置リストなどを保持するクラス

新しく増えたクラスです。アイコンのリストを持ちます。また、後に出てくる PiledPosition (各アイコンの大きさ・場所を持つクラス)のインスタンスを持っていて、 pile() メソッドでそれぞれのアイコンの場所・大きさをセットできます。
全てのアイコンはIcon[] icons という配列に格納されていて、この順番がアイコンの番号になっています。 また、int[] ordersという変数には重なっている順番が入っていて、この順番が描画の順番になっています。(orders[0] に入っている番号のアイコンが一番最初に描画される) このことは上のIvyPanel.paintComponent() に書いてあります。
class IconList {
    int           nIcons;       // アイコンの数
    Icon[]        icons;   // アイコン (画像1枚につき、一つのアイコンです)
    int[]         orders;
    PiledPosition piledPos; // 重ねた表示の時の各画像の位置

    public IconList(String[] imgPaths) {
        /** 引数: ロードされた画像リスト */
        nIcons = imgPaths.length;
        icons = new Icon[nIcons];
        for(int i = 0; i < nIcons; i++)
            icons[i] = new Icon(imgPaths[i]);
        orders = new int[nIcons];
        piledPos = new PiledPosition(nIcons);// 一覧表示での位置を計算
        pile();                               // 初期表示(重ね表示)に移行
    }

    /** 重ね表示にアイコンを移動 */
    public void pile() {
        for(int i = 0; i < nIcons; i++) {
            // 重ね順を設定。orders は描画順なので、一番新しい画像を最後にする
            orders[i] = nIcons - i - 1; 
            icons[i].setPosition(piledPos.getPos(i));
        }
    }

    /** アイコンの数を返すメソッド */
    public int size() { return nIcons; }

    /** 重ね順が(下から) i 番目のアイコンを返すメソッド */
    public Icon get(int i){ return icons[orders[i]]; }
}

[New] IconPosition: アイコンの位置を持つクラス

アイコンの位置を持つクラスです。アイコン N 個それぞれに対応したx, w, w, h (x y 座標、幅・ 高さ)を保持します。 getPos() メソッドで、指定された番号のアイコンのx, y, w, hを長さ4の配列に入れて返します。
今回はアイコンを重ねて表示するモードしか作っていませんが、後ほど別の表示モードも作るので、まずどんな配置でも 共通なクラスをまとめて IconPosition クラスを作りました。
abstract class IconPosition {
    /** 各アイコンの配置を示すクラス */
    int[] X, Y; // アイコンの X, Y, W, H
    int   w, h;

    public IconPosition(int n) {
        /** 各アイコンの座標、大きさ */
        X = new int[n];
        Y = new int[n];
    }

    /** 指定されたインデックスのアイコン位置を返す */
    public int[] getPos(int i) 
    {   return new int[] { X[i], Y[i], w, h }; }

    /** 指定された幅/高さ 最大値を超えないように、幅・高さを設定する */
    public void setWH(int wMax, int hMax) {
        w = wMax; // アイコンの(最大)幅
        h = (int) ((double) w / Env.WH_RATIO); // アイコンの(最大)高さ
        if(h > hMax) { // 幅を基準にしたら、高さがhMaxをはみ出てしまった場合
            h = hMax;
            w = (int) (h * Env.WH_RATIO);
        }
    }
}

[New] PiledPosition: 重ね表示 の時の位置情報を持つクラス

重ね表示の場所を決めています。 等間隔ではなく、手前に来るほど間隔があいて、しかも左上から右下に向けて配置するために 二次関数を使っています。それがX[i] = と Y[i] = という2行です。
class PiledPosition extends IconPosition {
    int zw, zh;

    /** コンストラクタ (引数: 画像の数) */
    public PiledPosition(int n) {
        super(n);
        setWH(Env.W / 2, Env.H / 2);

        X[0] = Env.W / 2 - Env.W / 10; // 一番手前のアイコンのX座標
        Y[0] = Env.H / 2 - Env.H / 10; // 一番手前のアイコンのY座標
        for(int i = 1; i < n; i++) {
            X[i] = X[i - 1] - (int) ((10 - i) * (10 - i) * 0.9);
            Y[i] = Y[i - 1] - (int) ((10 - i) * (10 - i) * 0.4 + 20);
        }
    }
}

Rect: パネルに置く色んな部品の基本クラス

ほぼ前回と同じですが、Color の変数を追加しました。
abstract class Rect {
    int[]   P;     // X,Y座標, 幅、高さ
    Image   img;
    int     imgW, imgH; // 画像の幅、高さ
    Color   col;

    public Rect(int x, int y, int w, int h) {
        P = new int[] { x, y, w, h };
    }

    /** 画像をロード。path がhttp:// で始まる場合、HTTPで取得する */
    public void loadImage(String path){ 
        img = ImageLoader.load(path); 
        imgW = img.getWidth(null);
        imgH = img.getHeight(null);
    }
    
    public void setPosition(int[] P){
        for(int i = 0; i < 4; i++)
            this.P[i] = P[i];
    }
}

Icon: アイコンクラス

描画の部分で、周りに透明な枠を描いて、その中に少し余白をあけて画像を描くように変えました。 透明な枠を描くには、アルファ値を設定した Colorオブジェクトを作っています。new Color(0.85f, 0.85f, 0.75f, 0.3f) という行の一番最後の 0.3f が透明度です。
class Icon extends Rect {
    public Icon(String imgPath) {
        super(0, 0, 0, 0);
        loadImage(imgPath);
        col = new Color(0.85f, 0.85f, 0.75f, 0.3f);
    }

    /** アイコンを描画する */
    void draw(Graphics2D g) {
        g.setColor(col);
        // アイコンの周りの、少し透明な枠を描画する
        g.fillRect(P[0], P[1], P[2], P[3]);
        // 少し余白をあけて、アイコンを描画する
        int marginX = P[2] / 30;
        int marginY = P[3] / 30;
        g.drawImage(img, P[0] + marginX, P[1] + marginY, P[0] + P[2] - marginX,
                P[1] + P[3] - marginY, 0, 0, imgW, imgH, null);
    }
}

ImageLoader: 画像を読み込むためのクラス

前回から変更無しです。
class ImageLoader {
    /** 画像をロード。path がhttp:// で始まる場合、HTTPで取得する */
    public static Image load(String path) {
        try {
            if(path.startsWith("http://")){
                URLConnection urlc = new URL(path).openConnection();
                return ImageIO.read(urlc.getInputStream());
            }
            return ImageIO.read(new File(path));
        } catch (IOException ie) { ie.printStackTrace();}
        return null;
    }
}
[an error occurred while processing this directive]