質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JFrame

JFrameはJFC/Swingフレームワークのコンポーネントであり、トップレベルのコンテナです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

Q&A

解決済

2回答

1736閲覧

Swingにおけるコンポネント描画、イベント処理の方法で行き詰まり

JavaTakashi

総合スコア2

JFrame

JFrameはJFC/Swingフレームワークのコンポーネントであり、トップレベルのコンテナです。

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Eclipse

Eclipseは、IBM社で開発された統合開発環境のひとつです。2001年11月にオープンソース化されました。 たくさんのプラグインがあり自由に機能を追加をすることができるため、開発ツールにおける共通プラットフォームとして位置づけられています。 Eclipse自体は、Javaで実装されています。

1グッド

0クリップ

投稿2020/08/13 06:42

編集2020/08/13 07:12

前提・実現したいこと

Swingのコンポネント描画やイベント処理などは、イベントディスパッチスレッド(EDT)上で行なうルールとされていますが、
「コンポネント描画」や「イベント処理」をEDT上で行う具体的な手順・方法がわからず、次項「発生している問題」欄のとおり行き詰ってしまいました。

発生している問題・エラーメッセージ

『該当のソースコード』を実行すると、「choicel」「choice2」「choice3」「textArea」の表示に不具合が発生します。
例えば、「マイホーム」で「本を読む」を選択すると、テキストエリアには「何を読もう。」というテキストが表示され、choicel〜3もそれぞれの表示に変更されます。
そこまでは良いのですが、本を読むのをやめてマイホームへ戻ってみると、メッセージウィンドウもchoice1〜3の表示も「本を読む」の状態のまま変化してくれません。
他の選択肢「何か食べる」「買い物に行く」も同様です。表示されないか、表示されたとしても、描写にもたつきが発生します。

該当のソースコード

import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.UIManager; public class HogeQuest extends JFrame{ JFrame frame; Container contentPane; JPanel titleNamePanel; //ゲームのタイトル画面 JLabel titleNameLabel; JPanel startButtonPanel; //スタートボタン JPanel textPanel; //メッセージウィンドウ JTextArea textArea; //メッセージを表示 Font titleFont = new Font(Font.SERIF, Font.PLAIN, 100); //ゲームタイトル用のフォント Font normalFont = new Font(Font.SERIF, Font.PLAIN, 20); String position, text; JPanel choiceButtonPanel; //選択肢のコマンドボタンを格納 JButton choice1, choice2, choice3, startButton; //コマンドボタン、スタートボタン JPanel imagePanel; //画像を表示 JLabel imageLabel; ImageIcon image; StartButtonHandler startButtonHandler = new StartButtonHandler(); //スタートボタンを押したときの動作 ChoiceHandler choiceHandler = new ChoiceHandler(); //コマンドボタン(選択肢)を押したときの動作 public static void main(String[] args){ new HogeQuest(); } public HogeQuest(){ //macで起動した場合に発生する表示の不具合(ボタンの背景色が設定通りに反映されない)を防ぐためのもの try { UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName() ); } catch (Exception e) { e.printStackTrace(); } frame = new JFrame("HogeQuest"); frame.setBounds(0,0,1300,950); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().setBackground(Color.black); frame.setLayout(null); frame.setVisible(true); contentPane = frame.getContentPane(); //// ゲーム開始直後のタイトル画面。スタートボタンを押すと「マイホーム」に画面遷移 titleNamePanel = new JPanel(); titleNamePanel.setBounds(100,300,1000,200); titleNamePanel.setBackground(Color.black); titleNameLabel = new JLabel("Hoge Quest"); //ゲームタイトル titleNameLabel.setForeground(Color.white); titleNameLabel.setFont(titleFont); titleNamePanel.add(titleNameLabel); startButtonPanel = new JPanel(); startButtonPanel.setBounds(485, 600, 200, 120); startButtonPanel.setBackground(Color.black); startButton = new JButton("Start"); startButton.setBackground(Color.black); startButton.setForeground(Color.white); startButton.setBorder(null); //ボタンの枠線を消す startButton.setFont(normalFont); startButton.addActionListener(startButtonHandler); startButton.setFocusPainted(false); startButtonPanel.add(startButton); contentPane.add(titleNamePanel); contentPane.add(startButtonPanel); } ////////////////////////////////////////////// ここからはマイホーム public void myHome(){ position = "myHome"; titleNamePanel.setVisible(false); //タイトル画面を消す startButtonPanel.setVisible(false); //スタートボタンも消す imagePanel = new JPanel(); //画像が表示されるエリア imagePanel.setBounds(40,40,720,480); imagePanel.setBackground(Color.black); imageLabel = new JLabel(); image = new ImageIcon("./image01.jpg"); imageLabel.setIcon(image); imagePanel.add(imageLabel);// contentPane.add(imagePanel); textPanel = new JPanel(); //メッセージが表示されるエリア textPanel.setBounds(60, 540, 680, 200); textPanel.setBackground(Color.black); textArea = new JTextArea(); textArea.setBounds(60, 520, 680, 200); textArea.setBackground(Color.black); textArea.setForeground(Color.white); textArea.setFont(normalFont); textArea.setLineWrap(true); //テキストをパネル内に収まるよう折り返す textPanel.add(textArea); contentPane.add(textPanel); choiceButtonPanel = new JPanel(); //コマンドボタン choiceButtonPanel.setBounds(800, 550, 380, 200); choiceButtonPanel.setBackground(Color.black); choiceButtonPanel.setLayout(new GridLayout(3,1)); contentPane.add(choiceButtonPanel); choice1 = new JButton("本を読む"); choice1.setBackground(Color.black);//ボタンの色は背景と同じ黒 choice1.setForeground(Color.white);//文字の色は白 choice1.setFont(normalFont); choice1.setFocusPainted(false); choice1.addActionListener(choiceHandler); choice1.setActionCommand("c1"); choiceButtonPanel.add(choice1); choice2 = new JButton("何か食べる"); choice2.setBackground(Color.black); choice2.setForeground(Color.white); choice2.setFont(normalFont); choice2.setFocusPainted(false); choice2.addActionListener(choiceHandler); choice2.setActionCommand("c2"); choiceButtonPanel.add(choice2); choice3 = new JButton("買い物に行く"); choice3.setBackground(Color.black); choice3.setForeground(Color.white); choice3.setFont(normalFont); choice3.setFocusPainted(false); choice3.addActionListener(choiceHandler); choice3.setActionCommand("c3"); choiceButtonPanel.add(choice3); } /////////////////////////////////////////////////////// 本を読む public void book(){ position = "book"; textArea.setText("何を読もう。"); choice1.setText("スッキリわかるJava入門"); choice2.setText("オブジェクト指向でなぜつくるのか"); choice3.setText("やめる"); } /////////////////////////////////////////////////////// 何か食べる public void eat(){ position = "eat"; textArea.setText("何か食べよう。"); choice1.setText("ラーメン"); choice2.setText("カレーライス"); choice3.setText("ああ、おなかいっぱい"); } ///////////////////////////////////////////////////// 買い物に行く public void shop(){ position = "shop"; image = new ImageIcon("./image02.jpg"); imageLabel.setIcon(image); textArea.setText("何をお買い求めになられますか?"); choice1.setText("ラーメン"); choice2.setText("カレーライス"); choice3.setText("店を出る"); } /////////////////////////////////////// スタートボタンを押した時の動作 public class StartButtonHandler implements ActionListener{ public void actionPerformed(ActionEvent event){ myHome(); } } /////////////////////////////////////// 選択肢のボタンを押した時の動作 public class ChoiceHandler implements ActionListener{ public void actionPerformed(ActionEvent event){ String yourChoice = event.getActionCommand(); switch(position){ case "myHome": //マイホーム switch(yourChoice){ case "c1": book();break; case "c2": eat();break; case "c3": shop();break; } break; case "book": //本を読む switch(yourChoice){ case "c1": textArea.setText("ふむふむ。"); break; //「スッキリわかるJava入門」を選択 case "c2": textArea.setText("なるほどわからん。"); break; //「オブジェクト指向でなぜつくるのか」を選択 case "c3": myHome();break; //「やめる」を選択 } break; case "eat": //何か食べる switch(yourChoice){ case "c1": textArea.setText("おいしい!"); break; //「ラーメン」を選択 case "c2": textArea.setText("からい!"); break; //「カレーライス」を選択 case "c3": myHome();break; //「ああ、おなかいっぱい」を選択 } break; case "shop": //買い物に行く switch(yourChoice){ case "c1": textArea.setText("ラーメンですね。ありがとうございます。"); break; //「ラーメン」を選択 case "c2": textArea.setText("カレーですね。ありがとうございます。"); break; //「カレーライス」を選択 case "c3": myHome();break; //「店を出る」を選択 } break; } } } }

試したこと

ネットにて症状を検索。どうやら複数の別個のスレッドから描写の変更を命令したことによる競合が原因であると推測し、SwingUtilities、SwingWorker、EDTなど調べてみたのですが、それらを当該コードにどう適用すればいいのか分からず独学の限界を感じております。

補足情報(FW/ツールのバージョンなど)

■参考にしたもの:
How to Make Text Adventure Game with GUI in Java FINAL - Complete the game
https://www.youtube.com/watch?v=AyB3LiF-XyA&list=PL_QPQmz5C6WUMB0xEMZosWbyQo_Kil0Fb&index=6

■環境:
java 1.8

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

問題点はshiketaさんの通りと思います(ボタン等が複数個重なっておかしくなっている)

HogeQuestクラスのコード量が増えてきて、わかりにくくなってきているのではないでしょうか?

  • 表示内容がガラッと変わるような場合は、CardLayoutで遷移させるのがおすすめ(タイトル → マイホーム)
  • タイトル・マイホームはJPanelを継承し専用のクラスにすると、見通しが良いのと単独でテストしやすい
  • フィールド(メンバー変数)はできるだけ減らし、可能ならfinalにする

以上でだいぶすっきりすると思います。

Java

1import java.awt.CardLayout; 2import java.awt.Color; 3import java.awt.Container; 4import java.awt.Font; 5import java.awt.GridLayout; 6import java.awt.event.ActionEvent; 7import java.awt.event.ActionListener; 8import java.util.function.Consumer; 9import javax.swing.ImageIcon; 10import javax.swing.JButton; 11import javax.swing.JFrame; 12import javax.swing.JLabel; 13import javax.swing.JPanel; 14import javax.swing.JTextArea; 15import javax.swing.SwingUtilities; 16import javax.swing.UIManager; 17 18 19public class HogeQuest extends JFrame { 20 public static final Font titleFont = new Font(Font.SERIF, Font.PLAIN, 100); 21 public static final Font normalFont = new Font(Font.SERIF, Font.PLAIN, 20); 22 23 public static void main(String[] args) { 24 try { 25 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 30 new HogeQuest().setVisible(true); 31 } 32 33 public HogeQuest() { 34 setTitle("HogeQuest"); 35 setBounds(0, 0, 1300, 950); 36 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 37 setLayout(new CardLayout()); // 紙芝居のようにカードを追加していくようなレイアウト 38 39 getContentPane().setBackground(Color.BLACK); 40 41 add(new Title()); // タイトル画面を追加(最初に表示される 42 add(new Home()); // ゲーム?画面を追加(最初は表示されない 43 } 44} 45 46class Title extends JPanel { // タイトル画面 47 public Title() { 48 setLayout(null); 49 setBackground(Color.BLACK); 50 51 JLabel title = new JLabel("Hoge Quest"); 52 title.setFont(HogeQuest.titleFont); 53 title.setForeground(Color.WHITE); 54 title.setBounds(380, 270, 500, 120); 55 add(title); 56 57 JButton start = new JButton("Start"); 58 start.setBorder(null); 59 start.setFont(HogeQuest.normalFont); 60 start.setFocusPainted(false); 61 start.setBackground(Color.BLACK); 62 start.setForeground(Color.WHITE); 63 start.setBounds(570, 610, 40, 20); 64 start.addActionListener(new StartButtonHandler()); 65 add(start); 66 } 67 68 private class StartButtonHandler implements ActionListener { 69 @Override public void actionPerformed(ActionEvent event) { 70 JFrame frame = (JFrame) SwingUtilities.getRoot(Title.this); // 親フレームの取得 71 Container contentPane = frame.getContentPane(); 72 CardLayout layout = (CardLayout) contentPane.getLayout(); // レイアウトの取得 73 layout.next(contentPane); // 次のカードに遷移 74 } 75 } 76} 77 78class Home extends JPanel { // ゲーム?画面 79 private final JTextArea textArea; 80 private final JButton choice1; 81 private final JButton choice2; 82 private final JButton choice3; 83 private final JLabel imageLabel; 84 private final ImageIcon image1 = new ImageIcon("./image01.jpg"); 85 private final ImageIcon image2 = new ImageIcon("./image02.jpg"); 86 87 // 関数型インターフェース 引数String 戻り値なし 88 // 選択肢を選んだ後の処理を記述 89 private Consumer<String> choiceAction; 90 91 public Home() { 92 setLayout(null); 93 setBackground(Color.BLUE); 94 95 JPanel imagePanel = new JPanel(); 96 imagePanel.setBounds(40, 40, 720, 480); 97 imagePanel.setBackground(Color.BLACK); 98 imageLabel = new JLabel(image1); 99 imagePanel.add(imageLabel); 100 add(imagePanel); 101 102 JPanel textPanel = new JPanel(); 103 textPanel.setBounds(60, 540, 680, 200); 104 textPanel.setBackground(Color.BLACK); 105 textArea = new JTextArea(); 106 textArea.setBounds(60, 520, 680, 200); 107 textArea.setBackground(Color.BLACK); 108 textArea.setForeground(Color.WHITE); 109 textArea.setFont(HogeQuest.normalFont); 110 textArea.setLineWrap(true); 111 textPanel.add(textArea); 112 add(textPanel); 113 114 JPanel choicePanel = new JPanel(); 115 choicePanel.setBounds(800, 550, 380, 200); 116 choicePanel.setBackground(Color.BLACK); 117 choicePanel.setLayout(new GridLayout(3, 1)); 118 add(choicePanel); 119 120 ChoiceHandler choiceHandler = new ChoiceHandler(); 121 122 choice1 = new JButton(); 123 choice1.setBackground(Color.BLACK); 124 choice1.setForeground(Color.WHITE); 125 choice1.setFont(HogeQuest.normalFont); 126 choice1.setFocusPainted(false); 127 choice1.addActionListener(choiceHandler); 128 choice1.setActionCommand("c1"); 129 choicePanel.add(choice1); 130 131 choice2 = new JButton(); 132 choice2.setBackground(Color.BLACK); 133 choice2.setForeground(Color.WHITE); 134 choice2.setFont(HogeQuest.normalFont); 135 choice2.setFocusPainted(false); 136 choice2.addActionListener(choiceHandler); 137 choice2.setActionCommand("c2"); 138 choicePanel.add(choice2); 139 140 choice3 = new JButton(); 141 choice3.setBackground(Color.BLACK); 142 choice3.setForeground(Color.WHITE); 143 choice3.setFont(HogeQuest.normalFont); 144 choice3.setFocusPainted(false); 145 choice3.addActionListener(choiceHandler); 146 choice3.setActionCommand("c3"); 147 choicePanel.add(choice3); 148 149 myHome(); 150 } 151 152 153 private void myHome() { 154 textArea.setText(""); 155 choice1.setText("本を読む"); 156 choice2.setText("何か食べる"); 157 choice3.setText("買い物に行く"); 158 159 // 匿名クラスで 160 choiceAction = new Consumer<String>() { 161 @Override public void accept(String yourChoice) { 162 switch (yourChoice) { 163 case "c1": book(); break; 164 case "c2": eat(); break; 165 case "c3": shop(); break; 166 } 167 } 168 }; 169 } 170 171 private void book() { 172 textArea.setText("何を読もう。"); 173 choice1.setText("スッキリわかるJava入門"); 174 choice2.setText("オブジェクト指向でなぜつくるのか"); 175 choice3.setText("やめる"); 176 177 // 冗長なので以下ラムダで 178 choiceAction = yourChoice -> { 179 switch (yourChoice) { 180 case "c1": textArea.setText("ふむふむ。"); break; 181 case "c2": textArea.setText("なるほどわからん。"); break; 182 case "c3": myHome(); break; 183 } 184 }; 185 } 186 187 private void eat() { 188 textArea.setText("何か食べよう。"); 189 choice1.setText("ラーメン"); 190 choice2.setText("カレーライス"); 191 choice3.setText("ああ、おなかいっぱい"); 192 193 choiceAction = x -> { 194 switch (x) { 195 case "c1": textArea.setText("おいしい!"); break; 196 case "c2": textArea.setText("からい!"); break; 197 case "c3": myHome(); break; 198 } 199 }; 200 } 201 202 private void shop() { 203 imageLabel.setIcon(image2); 204 textArea.setText("何をお買い求めになられますか?"); 205 choice1.setText("ラーメン"); 206 choice2.setText("カレーライス"); 207 choice3.setText("店を出る"); 208 209 choiceAction = x -> { 210 switch (x) { 211 case "c1": textArea.setText("ラーメンですね。ありがとうございます。"); break; 212 case "c2": textArea.setText("カレーですね。ありがとうございます。"); break; 213 case "c3": myHome(); break; 214 } 215 }; 216 } 217 218 private class ChoiceHandler implements ActionListener { 219 @Override public void actionPerformed(ActionEvent event) { 220 String yourChoice = event.getActionCommand(); 221 choiceAction.accept(yourChoice); 222 } 223 } 224}

できるだけ元のレイアウトを保持したつもりですが、ずれていたらすいません。
選択肢と感想?が離れたところになってしまうのがちょっと気になりますね。


追記
関数型インターフェースを使用してちょっと近づけてみました。

投稿2020/08/13 21:26

編集2020/08/14 05:04
TN8001

総合スコア9315

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

JavaTakashi

2020/08/14 01:00

ご回答ありがとうございます。しかも、コードの手直しまでしていただき大変恐縮です。 今朝、手直ししていただいたコードをさっそくコピペ(すいません)して実行してみたところ、問題となっていた表示のバグが無くなっており、「うおおお」と思わず声が出ました。 提示していただいたコードの仕組みをこれから解読、勉強させていただきます。 >選択肢と感想?が離れたところになってしまうのがちょっと気になりますね。 (実際の?)Hoge Questはかなりボリュームがありまして、選択肢と感想はもっと離れており、私もそこは気になっているのですが、現在のJAVA経験値ではこうするより他なさそうです…。
JavaTakashi

2020/08/14 07:00

>追記 >関数型インターフェースを使用してちょっと近づけてみました。 関数型インターフェースまで勉強が行き着いていない初学者の私にとって、この結果は衝撃です。まさにパラダイムシフトというか、自分の作成したコードがこれほど分かりやすくなるとは…。 私がラムダ式の文法を理解できるようになるのはまだ先になると思いますが、たいへん刺激になります。重ねてありがとうございます。
TN8001

2020/08/14 07:14

例を見たらやっている意味は大体分かりますよね? 使わないと覚えないので少しずつ試してみてください。 私もそんなに得意なわけではないんで、毎回検索したりしてます^^; いいゲームができるといいですね。がんばってください^^
JavaTakashi

2020/08/14 08:17

正直なところ、未熟な自分が一足飛びでこれを活用してもいいのかという葛藤はありますが、あきらかに便利そうなので、ご助言いただいたように実際のプログラムで試してみようと思います。ありがとうございます。
guest

0

ボタン押下時など、自力でThreadを作成している様子はないので、このソースの中の処理は、ほぼすべてイベントディスパッチスレッド上で実行されています。

たとえば、ボタン押下時に、重い処理をバックグラウンドで実行させるためにThreadを作成して処理を実行中に、進捗状況を画面上に表示したいときなどに、SwingUtilities#invokeLater()を使います。

java

1 JButton button = new JButton("..."); 2 JTextArea progress = new JTextArea(); 3 button.addActionListener(ev -> { 4 while (true) { 5 // 重い処理 6 SwingUtilities.invokeLater(() -> { 7 progress.append("..."); 8 }); 9 } 10 });

うえのように書いてもprogressは更新されません。ほかにもいろいろと問題がでてきます。重い処理をイベントディスパッチスレッド上で実行しているからです。
progressをきちんと更新してほしければ、つぎのように書きます。

java

1 button.addActionListener(ev -> { 2 new Thread() { 3 @Override 4 public void run() { 5 while (true) { 6 // 重い処理 7 SwingUtilities.invokeLater(() -> { 8 progress.append("..."); 9 }); 10 } 11 } 12 }.start(); 13 });

で、件のソースですが、myHome()メソッドが何度も呼び出されているようです。myHome()メソッドが呼び出される都度、LabelやTextAreaを生成して、コンポーネントに追加しています。これがどんどん積み重なって、もたっとしてくるのではないでしょうか。

java

1 public void myHome() { 2 imagePanel = new JPanel(); 3 ... 4 contentPane.add(imagePanel); 5 6 textPanel = new JPanel(); 7 ... 8 contentPane.add(textPanel); 9 10 choiceButtonPanel = new JPanel(); 11 ... 12 contentPane.add(choiceButtonPanel);

投稿2020/08/13 10:49

編集2020/08/13 23:43
shiketa

総合スコア3971

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

JavaTakashi

2020/08/14 00:35

さっそく助言をいただきありがとうございます。 >myHome()メソッドが呼び出される都度、LabelやTextAreaを生成して、コンポーネントに追加しています。これがどんどん積み重なって、もたっとしてくるのではないでしょうか。 これは目から鱗でした。 マイホームに戻るたび、myHome()メソッドに入力していたimagePanelやtextPanel、choiceButtonPanelが新たに生成されていくわけなのですね。 なるほど。今まで、パネルやラベルの数は変わらずラベルの内容だけが上書きされるという勘違いをしておりました。 SwingUtilitiesの解説もありがとうございます。 お恥ずかしながら、重い処理がバックグラウンドで実行されるようなHoge Questではございませんので、「SwingUtilities、SwingWorker。ここにカギがあるのではないか」という推測も少しずれていたようです。 とても参考になります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問