ちょっと自分専用のデスクトップキャラクター作ってみたお話(2)
今回も前々から別ブログで書き溜めていた"自分専用のデスクトップキャラを作るお話"の続きになります.
自分が色々忘れてしまう前にざくざく書いておかねば……
前回までのお話
- javaで何か目に見える形で作品残すために自分専用のデスクトップキャラクター作ってみよう.
- せっかく完成できたんだからもっと自分専用の機能とかつけてオリジナル感出していきたいね!
- そうだ.自分からもメッセージを送れるようにまずは新規ウィンドウ増やそう!!!
…ということで,今回は新しく文字の入力ができるウィンドウを作っていきたいと思います.
突然ですが,前回書いた(ほぼ参考サイト様の)ウィンドウ用のソースコードがこちら.
(参考サイト様は前回のブログに掲載)
import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.image.*; import javax.swing.*; import javax.swing.event.*; import java.io.*; import com.sun.awt.AWTUtilities; class MakeWindow extends JWindow{ JLabel label = null; Canvas canvas = null; Container contentPane = null; MakeWindow(){ } //---------------通常のwindow作成---------------------- public JFrame makeWindow(int width, int height){ JFrame mainFrame = new JFrame("サンプル"); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setSize(width,height); mainFrame.setLocationRelativeTo(null); //JFrameよりContentPaneを取得 Container contentPane = mainFrame.getContentPane(); //ラベルのインスタンスを生成 label = new JLabel("これはラベルです"); //ラベルをContentPaneに配置 contentPane.add(label,BorderLayout.CENTER); mainFrame.setVisible(true); return mainFrame; } //----------------画像windowの作成---------------------- public JFrame makeImageWindow(String imagePath){ JFrame frame = new JFrame(); frame.setUndecorated(true); //各種リスナー追加 addListener(frame); frame = viewImage(frame,imagePath); return frame; } //----------------window表示メイン---------------------- public JFrame viewImage(JFrame frame,String imagePath){ ImageIcon icon = new ImageIcon(imagePath); frame.setSize(icon.getIconWidth(),icon.getIconHeight()); setImagePosition(frame,icon.getIconWidth(),icon.getIconHeight()); Shape shape = getImageShape(icon); frame.setShape(shape); frame.setBackground(new Color(0,0,0,0)); Image image = icon.getImage(); contentPane = frame.getContentPane(); //canvasがない時は作成 if(canvas == null){ canvas = new Canvas(image); contentPane.add(canvas); frame.setVisible(true); }else{ //canvasがある時は再描写 canvas.image = image; canvas.repaint(); } //setAlwaysOnTop(true); return frame; } public JFrame makeMessageWindow(String imagePath){ JFrame frame = new JFrame("new"); frame.setUndecorated(true); addListener(frame); ImageIcon icon = new ImageIcon(imagePath); frame.setSize(icon.getIconWidth(),icon.getIconHeight()); setImagePosition(frame,icon.getIconWidth(),icon.getIconHeight() + 30); Shape shape = getImageShape(icon); frame.setShape(shape); //Image image = icon.getImage(); //Canvas canvas = new Canvas(image); JPanel p = new JPanel(); ////JPanel p1 = new JPanel(); //JLabel label = new JLabel("うにゃー!!"); //p.add(label); //p.setBackground(Color.WHITE); String aisa; aisa = timeevent.time(); label = new JLabel(aisa); ////JTextField text = new JTextField(8); label.setHorizontalAlignment(JLabel.CENTER); ////text.setHorizontalAlignment(JLabel.CENTER); p.add(label); ////p1.add(text); Container contentPane = frame.getContentPane(); contentPane.add(label,BorderLayout.CENTER); ////contentPane.add(text,BorderLayout.CENTER); frame.setVisible(true); return frame; } //----------------------------各種処理系------------------------------ //------ドラッグ可能にして,ダブルクリックで終了するようにする-------- class DragWindowListener extends MouseAdapter{ private MouseEvent start; private Point loc; private Window window; public DragWindowListener(Window w){ super(); window = w; } @Override public void mousePressed(MouseEvent me){ if(me.getClickCount() >= 2){ System.exit(0); } start = me; } @Override public void mouseDragged(MouseEvent me){ loc = window.getLocation(loc); int x = loc.x - start.getX() + me.getX(); int y = loc.y - start.getY() + me.getY(); window.setLocation(x,y); } } //-------------------ラベルの再描写----------------------- public void redrawLabel(String message){ label.setText(message); repaintWindowAncestor(label); } //再描写 public void repaintWindowAncestor(JComponent c){ JRootPane root = c.getRootPane(); if(root == null){ return; } Rectangle r = SwingUtilities.convertRectangle(c,c.getBounds(),root); root.repaint(100,r.x,r.y,r.width,r.height); } //各種リスナー追加 private void addListener(JFrame frame){ DragWindowListener dwl = new DragWindowListener(frame); frame.addMouseListener(dwl); frame.addMouseMotionListener(dwl); } //画面端に配置 private void setImagePosition(JFrame frame, int width, int height){ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setBounds(screenSize.width - width,screenSize.height - height,width,height); } //アルファ値が0である箇所以外を全てshapeに追加してそれを返す public Shape getImageShape(ImageIcon icon){ GeneralPath shape = new GeneralPath(); final BufferedImage bi = new BufferedImage(icon.getIconWidth(),icon.getIconHeight(),BufferedImage.TYPE_INT_ARGB); icon.paintIcon(null,bi.createGraphics(),0,0); ColorModel cm = bi.getColorModel(); for(int y=0; y<bi.getHeight(); y++){ for(int x=0; x<bi.getWidth(); x++){ //完全に透過していなければshapeに追加 if(cm.getAlpha(bi.getRGB(x,y)) > 0){ int start = x; int width = 0; while((x<bi.getWidth()) && (cm.getAlpha(bi.getRGB(x++,y)) > 0)){ width++; } shape.append(new Rectangle(start,y,width,1),true); } } } return shape; } //Canvasクラスを改造する private class Canvas extends JPanel{ private static final long serialVersionUID = 1L; private Image image = null; public Canvas(Image image){ super(); this.image = image; } public void paintComponent(Graphics g){ if(image == null) return; g.drawImage(image,0,0,this); } } }
……実を言うとまだ完璧に理解できた,というわけではない為,自分で少々無駄なところを勝手に付け足してしまっているかもしれません.
(でも動くから多分大丈夫…!!ソースを最適化するのはとりあえずプログラムが動いた後で…!!!)
では次に,ここに今回のメインである文字入力専用のウィンドウを作成するコードを書いていきます.
上のソースに書き加えたのが以下になります.
//chatWindow作成 JTextField text; public JFrame makeChatWindow(String imagePath){ JFrame frame = new JFrame("chat"); frame.setUndecorated(true); //frame.setVisible(true); addListener(frame); ImageIcon icon = new ImageIcon(imagePath); frame.setSize(icon.getIconWidth(),icon.getIconHeight()); setImagePosition2(frame,icon.getIconWidth(),icon.getIconHeight()); Shape shape = getImageShape(icon); frame.setShape(shape); JPanel p = new JPanel(); //p.setBackground(Color.WHITE); text = new JTextField(9); text.setHorizontalAlignment(JLabel.CENTER); p.add(text); Container contentPane = frame.getContentPane(); contentPane.add(text,BorderLayout.CENTER); frame.setVisible(true); text.addKeyListener(new KeyInput()); return frame; } //Enterキーが押された時の処理 class KeyInput extends KeyAdapter { public void keyPressed(KeyEvent e){ if(e.getKeyCode() == KeyEvent.VK_ENTER) { String txt = text.getText(); //------------類語変換------------- txt = vocabulary.chenge(txt); //------------返答分岐------------- String responce; responce = answer.ans(txt); redrawLabel(responce); System.out.println(responce); //------------終了コマンド--------- if(txt.equals("n")){ System.exit(0); } } } } //chatの画面配置 private void setImagePosition2(JFrame frame, int width, int height){ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setBounds(screenSize.width - width, screenSize.height - height \ - 400,width,height); }
ちょっとコードがごちゃごちゃしているので順を追って説明していくと,
まず初めに,文字を入力する為にJTextFieldを用意.それをメッセージウィンドウと同様にして作ったウィンドウにp.add()して貼り付けています. これでとりあえず文字を入力する形は完成!
次に入力した文字をエンターキーで受け取ることができるようにaddKeyListener(new KeyInput());
を使ってエンターキーが押された時のイベントを作っています.
また,KeyInputクラスではエンターキーが押された時点でのTextFieldにある文字を取得し,各処理を行ってキャラクターの返事を再描写しています.
因みに,返事は別コードのanswer.java
で処理されていますが,「こんにちは」と挨拶を入れてあげると時間帯にあった挨拶を返してくれるようになっています!
とりあえずanswer.java
のコード説明は次回以降に回します.
そして,今回このコードを書くにあたってはこちらのサイト様を参考にさせていただきました. https://teratail.com/questions/8253
最後に,チャット用ウィンドウの表示場所ですが,他の二つのウィンドウと同じに設定してしまうとキャラクターのセリフウィンドウと丸かぶりしてしまう為わざと初期表示位置を他のウィンドウと分けています.
……とまぁいろいろごちゃごちゃと勢いだけで書き込んでいったのですが,今回追加実装できた子はこうなりました!!!
ちゃんと挨拶できてる…!!!!!!!!
やっぱり画面に居るだけじゃなくて言葉に反応してくれるようになるとますます愛着が湧いてきますね…!!!!
(ちょっと次はSiriみたいにアプリ起動してくれる機能つけてみる予定…)
そして実は,ここまで作った段階でMakeWindow.java 以外にも
があるのですが,次回ではその部分の説明ができたら良いなと思ってます!!