/* Javaで書いた、画像ビュワーです。 http://funini.com/kei/ivy/ */ import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.URL; import java.net.URLConnection; import javax.imageio.ImageIO; import javax.swing.JApplet; import javax.swing.JPanel; import javax.swing.Timer; import java.util.ArrayList; import java.util.regex.*; /** 各種定数の宣言 */ 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: アプレットのクラス。今回のメイン、IvyPanel が載っている。 ******************************************************************************/ 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: メインのパネル。画像を描画したり、クリック時の動作を記述したクラス。 ******************************************************************************/ class IvyPanel extends JPanel implements MouseListener { IconList iconList; // 各画像の重ね順です。 public IvyPanel(String[] urls) { /** コンストラクタ 引数は画像が入っているパス */ setBackground(new Color(0x33, 0x33, 0x33)); // 背景色設定 setPreferredSize(new Dimension(Env.W, Env.H)); // サイズ設定 iconList = new IconList(urls); addMouseListener(this); // マウスのイベントを扱えるようにする } public synchronized void mousePressed(MouseEvent e) { /** マウスがクリックされたときのイベント */ Point p = e.getPoint(); // ボタン以外がクリックされた時 for(int i = iconList.size() - 1; i >= 0; i--) { // クリックは表示と逆順で調べる Icon icon = iconList.get(i); // i 番目のアイコンについて if(icon.hit(p)){ // クリックされている場合 iconList.piledTop(icon); // icon をトップに表示 (重ね表示) repaint(); return; } } // ボタンもアイコンもクリックされていない場合、初期表示に戻る initView(); repaint(); } void initView() { /** 初期表示を行う */ iconList.pile(); // 重ね表示 } /** 描画を行う */ 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); // 各アイコンを表示 } /** MouseListener を実装するために必要なメソッド群 */ public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mouseClicked(MouseEvent e) {} } /******************************************************************************* * IconList: アイコンのリスト、重ね順、位置リストなどを保持するクラス ******************************************************************************/ 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)); } } /** アイコンのインデックス(何番目のアイコンか)を返す */ int searchIcon(Icon ic) { for(int i = 0; i < nIcons; i++) if(icons[i] == ic) return i; return -1; // 見つからなかった場合 } /** 指定されたインデックスが何番目に重なっているかを返す */ int searchOrder(int index) { for(int i = 0; i < nIcons; i++) if(orders[i] == index) return i; return -1; // 見つからなかった場合 } /** 重ね表示において、指定されたアイコンをトップに移動 */ public void piledTop(Icon ic) { int index = searchIcon(ic); // 指定されたアイコンより手前のものは、画面外に移動 for(int i = 0; i < index; i++) icons[i].setPosition(piledPos.getOuterPos()); // 指定されたアイコン以後のものを手前から順番に並べる for(int i = index; i < nIcons; i++) icons[i].setPosition(piledPos.getPos(i - index)); } /** アイコンの数を返すメソッド */ public int size() { return nIcons; } /** 重ね順が(下から) i 番目のアイコンを返すメソッド */ public Icon get(int i){ return icons[orders[i]]; } } /******************************************************************************* * IconPosition, PiledPosition, ListPosition: * アイコンの位置を持つクラス。PiledPositionが重ね表示、ListPositionが一覧表示。 ******************************************************************************/ 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); } } } class PiledPosition extends IconPosition { /** 重ね表示 の時の位置情報を持つクラス */ int zw, zh; int outerX, outerY; /** コンストラクタ (引数: 画像の数) */ 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); } outerX = Env.W; outerY = Env.H; } /** フォーカスアウトして、画面からはみ出た位置を返す */ public int[] getOuterPos() { return new int[] { outerX, outerY, w, h }; } } /******************************************************************************* * Rect, Icon, MyBtn, Background, Shadow: * 様々な長方形のGUI部品。位置・大きさ・アニメーション用の変数と draw() を備える ******************************************************************************/ /** ベースになる、長方形クラス */ 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 }; } /** 指定された座標 p が、この長方形の内部かどうかを返す */ public boolean hit(Point p) { return (P[0] <= p.x && p.x <= P[0] + P[2] && P[1] <= p.y && p.y <= P[1] + P[3]); } /** 画像をロード。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]; } } /** アイコンクラス。1枚の写真の情報を持ちます */ 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) { // まずは、画像が半分以上画面外にはみ出ている場合は描画しない if(P[0] + P[2] / 2 < 0 || P[1] + P[3] / 2 < 0 || Env.W <= P[0] || Env.H <= P[1]) return; 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); } } 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; } }