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

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

ただいまの
回答率

90.03%

java:frameは使いまわし、panelを描画・削除してページ遷移したいです

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 492

makoto-n

score 382

javaでアプリケーション作成中です。
ページ遷移をしたいのですが、frameを消してnewをして~を繰り返しては移動させたframeが初期の位置に戻ってしまいます。
なのでpanelに部品を埋め込み、遷移するさいにはpanelをremoveAllして新規panelをnewしたいと思ったのですが、
panelを消せずに詰まっています。
どうか改善点・または別の案を押してください。

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

class SSample3_1 extends JFrame {
    JButton btn1, btn2, btn3, btn4;
    JPanel p;
    SSample3_1 dodai2;

    public static void main(String args[]) {
        SSample3_1 frame = new SSample3_1("タイトル");
        frame.setVisible(true);
    }

    SSample3_1(String title) {
        setTitle(title);
        setBounds(100, 100, 300, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        p = new JPanel();

        btn1 = new JButton("Button");
        btn2 = new JButton("Button");
        btn3 = new JButton("Button");

        p.add(btn1);
        p.add(btn2);
        p.add(btn3);

        btn1.addActionListener(null);
        btn2.addActionListener(null);
        btn3.addActionListener(null);

        Container contentPane = getContentPane();
        contentPane.add(p, BorderLayout.CENTER);
    }
    private void junbi2() {

        JTextField text1 = new JTextField();
        text1.setText("aaaaa");

        p.add(text1);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btn1) {
            p.removeAll();
            dodai2 = new SSample3_1("title");
            this.junbi2();
            dodai2.setVisible(true);
        } else if (e.getSource() == btn2) {
            p.removeAll();
        } else if (e.getSource() == btn3) {
            p.removeAll();
        }
    }
}

removeAllです。失礼しました。

イベントを追加についてですが、
btn1.addActionListener(null)
をthisに変更するとEclipseではエラーになってしまいます。
イベントはゆくゆくは別のファイルに記述したいと考えておりますが、
現状は汚くても実装させることを先に考えています。

btn1...にイベントをaddできてもいません。


(A) インターフェースActionListenerを実装したクラスのインスタンス

package db_test;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

class SSample3_1 extends JFrame implements ActionListener {
    JButton btn1, btn2, btn3, btn4;
    JPanel p;
//    SSample3_1 dodai2;

    public static void main(String args[]) {
        SSample3_1 frame = new SSample3_1("タイトル");
        frame.setVisible(true);
    }

    SSample3_1(String title) {
        setTitle(title);
        setBounds(100, 100, 300, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        p = new JPanel();

        btn1 = new JButton("Button");
        btn2 = new JButton("Button");
        btn3 = new JButton("Button");

        p.add(btn1);
        p.add(btn2);
        p.add(btn3);

        btn1.addActionListener(this);
        btn2.addActionListener(this);
        btn3.addActionListener(this);

        Container contentPane = getContentPane();
        contentPane.add(p, BorderLayout.CENTER);
    }

    private void junbi2() {

        JTextField text1 = new JTextField();
        text1.setText("aaaaa");

        p.add(text1);
        setDefaultCloseOperation(EXIT_ON_CLOSE);

    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btn1) {
            this.p.removeAll();
            //            dodai2 = new SSample3_1("title");
            //            this.junbi2();
            //            dodai2.setVisible(true);
        } else if (e.getSource() == btn2) {
            p.removeAll();
        } else if (e.getSource() == btn3) {
            p.removeAll();
        }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • asahina1979

    2019/01/03 11:44

    イベントを登録してないボタンで何をしたいの?
    問題のあるイベントを登録した最新のソースを添付してください。

    キャンセル

  • KSwordOfHaste

    2019/01/03 12:24

    (A)「panelをremoveする」
    (B)「panelから全ての子供コンポーネント(ボタン3つのことです)をremoveする」
    コードは(B)に見えますが質問文の内容は(A)です。文章は正確にお願いします。読む側が混乱し質問者さんの意図をつかみにくくなります。

    キャンセル

  • makoto-n

    2019/01/04 02:33

    すみません、問題のあるソースはこれ一つのみです。
    イベントを追加できていない、もしくは、remobeAllの使い方が変なのかわかりません。
    removeAllです、失礼しました。

    キャンセル

回答 1

checkベストアンサー

+1

最初にタイトルの主題から外れた内容であることをお詫びします。

質問内容は画面遷移に関してなのですが、ボタンをクリックした際のActionListenerの登録自体に躓いておられるようでしたのでそこに焦点をあててコメントいたします。

ActionListenerを実装したクラス(あるいはそのインスタンス)

JButtonのメソッドaddActionListenerの引数に指定すべきはActionListenerですがこれはJava言語仕様で「関数型インターフェース(functional interface)」と呼ばれているものです。これに適合するような値の指定方法は見かけ上様々なバリエーションがありますが、本質的には以下の3通りぐらいです。

Java7以前の仕様
(A) インターフェースActionListenerを実装したクラスのインスタンス

Java8で追加された仕様
関数型インターフェースActionListenerの要実装メソッドactionPerformedのシグネチャに適合するような
(B) lambda式
(C) メソッド参照

質問文に「thisに変更すると...エラーになってしまいます」とあります。その書き方は(A)に該当します。しかし質問者さんがそこで躓いたのはクラスが特定のインターフェースを「実装」することの正確な意味を把握しておられないことが原因のようです。

(A)の「実装した」の意味は大雑把に言えば「クラスの宣言部にActionListenerからの継承であることを明記しているもの、あるいはその派生」といえ、SSample3_1の宣言部が

class SSample3_1 implements ActionListener

となっていない点が問題なのです。ここまでは、swingあるいはEclipseの知識ではなくJavaの言語仕様に関する知識です。

swingにおけるActionListenerの指定バリエーション

SSample3_1の宣言部に「implements ActionListener」と書けばとりあえず

btn1.addActionListener(this);

と書けるのですが、単一のクラス(典型的にはJFrameの派生)に複数のボタンのアクションを全部まかせるのはどちらかといえば「悪手」になりかねないと思います。actionPerformedメソッドの中で「どのボタンが押されたかいちいち切り分けなければならない」からです(質問者さんのコードはそうなってます)。

切り分けの煩雑さを避けるためのJava7以前での主要な方法は「名前ありの個別の具象クラス」あるいは「匿名クラス」を用いることでしたが、Java8が出て久しい現在前述の(B)(C)を活用できる機会も多いのではないでしょうか。

class MyFrame extends JFrame { // ActionListenerは実装しない
  JButton button1 = new JButton("button 1");
  JButton button2 = new JButton("button 2");
  ...

  MyFrame() {
    ...

    // (1)lambda式(だいたい3行以上にはなる)
    // 短い処理の場合いちいちメソッドを定義せずに済む。
    button1.addActionListener(event -> {
      System.out.println("ボタン1がおされた");
    });

    // (2)メソッド参照(だいたい1行)
    // アクションの記述が長いとき、lambda式でだらだら書くとアクション登録と
    // アクション処理本体がごちゃまぜになり画面構築コードの見通しが悪くなりがち。
    // アクション処理をメソッドに分離した場合はメソッド参照により簡潔に記述できる。
    button2.addActionListener(this::onClick2);
    ...

    // (3)lambda式(1行しか要しないパターン)
    // ActionEventを必要としない場合onClick3の引数を書かずに済ませる目的でこうすることも。
    button3.addActionListener(e -> onClick3());
    ...
  }

  private void onClick2(ActionEvent e) {
    // 何行かにわたる長めの処理
    ... 
  }

  private void onClick3() {
    // 何行かにわたる長めの処理
    ... 
  }
}

上記はイベント処理を実装するのに必要な言語仕様とその応用の一部の紹介にすぎません。実際にコードを書く際にはアクセスしたい情報(変数や別のインスタンス)をどこに定義しておくかなど言語仕様やオブジェクト指向設計のトピックが色々あります。

応用プログラムを書こうとしている段階で「言語仕様の再確認」なんて言われてもまどろっこしく感じると思いますが、基礎をかためておかないと具体的機能(画面遷移とか)に取り組もうとしても迷走しがちだと思います。失礼ながら本Q&Aはそんな印象を持ちました。swingを用いた機能の実現法だけでなくこういう基礎の部分にも目を向けるとよいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/06 11:37

    詳しい解説をしていただきありがとうございます。

    先にAから取り掛かろうと思いましたの
    >> (A) インターフェースActionListenerを実装したクラスのインスタンス
     class SSample3_1 extends JFrame implements ActionListener
    を試したところエラーにはならないようです。
    が、removeAllが適切ではないのか、ボタンクリック後にパネルが消えません。
    仰る「Javaの言語仕様に関する知識」に関するところですが、
    すみません、ここで躓いてしまいました。
    パネルを消すには
    this.p.removeAll
    では不適切でしょうか?

    キャンセル

  • 2019/01/06 11:50 編集

    p.removeAllで3つのボタンはパネルの子供コンポーネントではなくなるので方向は間違ってません。しかしswingは「状態が変わっても再描画しないとその結果が画面に反映されないケースがある」という点に注意が必要です。まずは
    p.removeAll();
    p.repaint(); //<=これを追加
    としてみてください。画面上からもボタンが消えるはずです。
    ---
    うるさいことをいうようですが「パネルを消す」はちょっと曖昧です。
    (A)ウィンドウからそのパネル自体を消すこと
    (B)そのパネル上にある他の子どもコンポーネントを消す
    のどちらにも受け取れるからです。プログラミングの会話をする際には「極力正確な表現」を心がけてください。(A)なのか(B)なのかで意味もすべき処理も違いますので。

    キャンセル

  • 2019/01/06 14:28

    なるほど、ありがとうございます。
    特性があったのですね、再描画できました。

    (B) lambda式
    (C) メソッド参照
    について試行錯誤してみようと思います。

    また、正確な表現につき助言してくださってありがとうございます、今後に生かします。

    キャンセル

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

  • ただいまの回答率 90.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る