[an error occurred while processing this directive]
[an error occurred while processing this directive]
(3) クリックで場所が変わる
前回はディレクトリのファイル一覧 (
こんなの) を読み込んで、重ねて表示できるようにしました。
今回は、マウスでクリックすることで、奥の方に重なった写真を手前に移動できるようにします。
前回 から変わったのは、以下の点です。
- マウスクリックに反応し、アイコンの位置を変える処理を追加した
- 上を実現するため、クリックされたのが何番目のアイコンかを判断できるようにした
- クリックされたアイコンより手前のアイコンは、画面右下の外部に追い出すようにした
なお、新しいクラスは増えていません。
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: メインのアプレットのクラス
アプレットのメインのクラスです。前回から変化ありません。
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: メインのパネル
メインのパネルです。マウスを扱えるよう、新たに MouseListenerが implement され、
mousePressed イベントの中身にクリックされたときの処理が入りました。
クリックされたときは、重なっている順番に各アイコンの座標の内部かどうかを調べていきます。
クリックされたアイコンが見つかったら、そのアイコンをトップにもってきます。
もしどのアイコンもクリックされていないときには、初期表示(0番目のアイコンがトップ)に戻ります。
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: アイコンのリスト、重ね順、位置リストなどを保持するクラス
クリックされたとき、そのアイコンが何番目に重なっているかを返すメソッドを追加しました。
また、指定したアイコンをトップに、それより上のアイコンを右下の画面外に移動するメソッド(piledTop) を追加しました。
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: アイコンの位置を持つクラス
前回からそのままです。
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);
}
}
}
PiledPosition: 重ね表示 の時の位置情報を持つクラス
画面の右下にはみ出した位置 (outerX, outerY) を追加して、getOuterPos で取得できるようにしました。
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: パネルに置く色んな部品の基本クラス
指定された座標がこの長方形の内部かどうかを判断するメソッド (hit) を追加しました。
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];
}
}
Icon: アイコンクラス
ほぼ同じですが、アイコンが画面からはみ出している時には描画しないようにしました。
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);
}
}
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]