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

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

ただいまの
回答率

89.99%

描いた絵をリセットしたい。(Graphicsをうまく扱いたい)

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 4,899

toutou

score 1638

現在画面をすべて消すリセットボタンを作ろうとしています。
それにつきましてまだ、Graphicsの理解が浅く扱いがうまくいかずできないです。

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test1 {
      public static void main(String[] args) {
      main a = new main();
      a.setVisible(true);
      }

}

class main extends JFrame implements ActionListener{
       main(){
           super("描写テスト");//タイトル
           this.setSize(400,400);//大きさ
           this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//終了
           JPanel kabe = new JPanel();//パネル作成
           JButton osu  =new JButton("押す");//ボタン作成
           remove(osu);
            osu.addActionListener(this);//ボタンにイベントをプラス
            kabe.setLayout(new BorderLayout());//壁をセット
            kabe.add(osu,BorderLayout.EAST);//壁にボタンを配置
            getContentPane().add(kabe,BorderLayout.EAST);//本体にパネルを設置
            this.add(new  event(), BorderLayout.CENTER);//真ん中にイベントを設置
        }
       public void actionPerformed(ActionEvent e){//ボタンイベント
            //  System.out.println("aaa");//確認用
             event gura =  new event();
              gura.kanri();
            gura.repaint();


       }


}


class  event extends  JPanel implements MouseListener, MouseMotionListener{
    int lastMouseX = -100;//移動距離
    int lastMouseY = -100;
    int start_x = -100;//スタート地点座標
    int start_y = -100;
    static boolean flag = false;//全体の管理
    BasicStroke BStroke = new BasicStroke(5.0f);//筆の大きさを設定
       BufferedImage seve_field = new BufferedImage(600,600, BufferedImage.TYPE_4BYTE_ABGR);//メモリの確保
       Graphics seve = seve_field.createGraphics();//メモリへの窓口
       event()
     {
               addMouseListener(this);//中央にマウスイベントを配置
               addMouseMotionListener(this);//同上

     }

         @Override
        public void paintComponent(Graphics g)
        {
               if(flag)
               {
                     System.out.println("ifルート");//確認用
                     flag = false;//戻し
                     seve_field = null;//メモリの中を破棄
                      seve = null;//seveを破棄
                      g.dispose() ;//gの中身を破棄
                      repaint();
               }else{
                    seve = seve_field.createGraphics();//メモリへの窓口
                   // System.out.println("描写");//確認用
                     Graphics2D g2= (Graphics2D)g;//2Dに変更
                     g2.setStroke(BStroke);//線の設定
                     g2.setColor(Color.RED);//線の色
                    Graphics2D seve2= (Graphics2D)seve;//メモリ加工
                    seve2.setStroke(BStroke);//線の設定
                    seve2.setColor(Color.RED);//線の色
                    if(start_x==-100||start_y== -100||lastMouseX==-100||lastMouseY==-100)
                     {
                          lastMouseX  = lastMouseY = start_x = start_y = -100;
                      }
                       g.drawImage(seve_field,200,300,this);//g2の中身をメモリの大きさだけゲット
                       seve2.drawImage(seve_field,0,0,this);//メモリの絵を投下
                       seve2.drawLine(start_x, start_y, lastMouseX, lastMouseY);//線を引く
                       g2.drawLine(start_x, start_y, lastMouseX, lastMouseY);//メモリに線を引く
                       start_x = lastMouseX;//スタート地点の変更
                       start_y = lastMouseY;}//同上
        }
        public boolean kanri()
        {
             lastMouseX  = lastMouseY = start_x = start_y = -100;
             flag = true;//フラグ設定
             return flag;
        }

          @Override
       public void mousePressed(MouseEvent e)
       {
          /*初期位置の保存*/
          start_x = lastMouseX = e.getX();
          start_y = lastMouseY = e.getY();
          flag = false;
       }
       @Override
       public void mouseReleased(MouseEvent e)
       {
             lastMouseX  = lastMouseY = start_x = start_y = -100;
       }
       @Override
       public void mouseDragged(MouseEvent e)
       {
           lastMouseX = e.getX();
           lastMouseY = e.getY();
           repaint();
        }
          @Override
       public void mouseClicked(MouseEvent e)
       {
       }
       @Override
       public void mouseEntered(MouseEvent e)
       {
       }
       @Override
       public void mouseExited(MouseEvent e)
       {
       }
       @Override
       public void mouseMoved(MouseEvent e)
       {
       }

}


自分の頭の中では、

1、ボタンを押すとfalseを返す。
2、gとseveの中身を消している。
3、repaint()でgが空っぽなのでまっさらな画面になっている。
4その後trueになってdrawImageをしてもgおよびメモリの中身がないのでまっさらになってる。

の想定です。
しかしなっていません。
どこら辺の考えが間違っているのでしょうか。
理解を深めたいのでアドバイスをお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

+1

mainクラスの初期化においてeventのインスタンス(※)をCENTERへ配置していますが、そのインスタンスとボタンを押したときにnewしているインスタンスは別物でありどちらか一方へ描画した内容がもう一方へ影響を及ぼすといった関係はありません。

ボタンを押したときにeventインスタンスをnewし、それに対してrepainしていますが、このインスタンスはGUI階層中に存在しない(JFrameを最上位とするコンポーネント階層中に現れない)ので画面への表示には何の効果も及ぼさないと思います。

※のインスタンスの表示内容を消す(背景色で塗りつぶす)ならこのインスタンスのpaintメソッド中でそれをする必要があります。paintメソッド以外から表示内容を更新することはできないと思ってください。

自分の頭の中では、...

質問者さんは「2でgやseveの中身を消している」と表現されているのでGraphicsの中身に描画結果があると想定されているように見えます。しかし、Graphicsクラスは描画対象のコンポーネントへ何かを描画するための「ペンとしての属性」という意味合いのものであり描画結果を保持するようなものではありません。


蛇足:

  • paintComponentの中でrepaintを呼び出すこと
    やったことがないので何が起こるかはわかりませんが多分よした方がいいでしょう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/11 23:43

    こっちのイメージとそちらのイメージが合わないのは、こちらが悪いせいですから気になさらずに。なぜならそっちはちゃんと出来てるイメージでありみんながやってることなので、ちゃんとした思考です。それに比べてこっちは合わないということは妄想と一緒です。勝手に間違ったイメージを作ってます。

    キャンセル

  • 2017/05/11 23:57

    すいません。あと今唐突に発見したのですが前回の線が途切れる現象は、setBounds(600,600,500,500)を使ってるせいか最大化してその中の範囲から外に描くように描くと、途切れる模様です。原因をさぐってくれとかじゃなく報告だけしておきます。

    キャンセル

  • 2017/05/12 06:20

    setBoundsなど直接サイズを指定するコードとLayoutManger(BorderLayoutなど)を併用すると今一つレイアウトが自然に行えないので自分は全てコンテナのレイアウト機能に任せるように心がけてます。JFrameのコンストラクターの最終行は常にpack()にし、setSize/setBoundsは決して使わないと決めている感じです。(w;

    キャンセル

checkベストアンサー

0

既に問題を解決できる回答をKSwordOfHasteさんが提示していらっしゃいますが

描画コンポーネントをクリアしたいという要件だけでしたら。
※オフスクリーンの扱いについては余談欄に記入しました。

class main

class main extends JFrame implements ActionListener {

    private final event gura;

    main() {
        super("描写テスト");//タイトル
        this.setSize(400, 400);//大きさ
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//終了
        JPanel kabe = new JPanel();//パネル作成
        JButton osu = new JButton("押す");//ボタン作成
        remove(osu);
        osu.addActionListener(this);//ボタンにイベントをプラス
        kabe.setLayout(new BorderLayout());//壁をセット
        kabe.add(osu, BorderLayout.EAST);//壁にボタンを配置
        getContentPane().add(kabe, BorderLayout.EAST);//本体にパネルを設置
        gura = new event();
        this.add(gura, BorderLayout.CENTER);//真ん中にイベントを設置
    }

    @Override
    public void actionPerformed(ActionEvent e) {//ボタンイベント
        //  System.out.println("aaa");//確認用
        gura.kanri();
        gura.repaint();
    }

}


event#paintComponent

   @Override
    public void paintComponent(Graphics g) {
        if (flag) {
            System.out.println("ifルート");//確認用
            flag = false;//戻し
            super.paintComponent(g);
            // BufferedImageを再度作成。
            seve_field = new BufferedImage(600, 600, BufferedImage.TYPE_4BYTE_ABGR);
            // インスタンス変数 seveへの代入は下記行で行っているので、省略
            return;
        }

        seve = seve_field.createGraphics();//メモリへの窓口
        // System.out.println("描写");//確認用
        Graphics2D g2 = (Graphics2D) g;//2Dに変更
        g2.setStroke(BStroke);//線の設定
        g2.setColor(Color.RED);//線の色
        Graphics2D seve2 = (Graphics2D) seve;//メモリ加工
        seve2.setStroke(BStroke);//線の設定
        seve2.setColor(Color.RED);//線の色
        if (start_x == -100 || start_y == -100 || lastMouseX == -100 || lastMouseY == -100) {
            lastMouseX = lastMouseY = start_x = start_y = -100;
        }
        g.drawImage(seve_field, 200, 300, this);//g2の中身をメモリの大きさだけゲット
        seve2.drawImage(seve_field, 0, 0, this);//メモリの絵を投下
        seve2.drawLine(start_x, start_y, lastMouseX, lastMouseY);//線を引く
        g2.drawLine(start_x, start_y, lastMouseX, lastMouseY);//メモリに線を引く
        start_x = lastMouseX;//スタート地点の変更
        start_y = lastMouseY;
    }

余談という確認事項

1点目、event#paintComponentメソッド内でsuper.paintComponent(g);を行っていないため、描画内容はクリアされません。
オフスクリーンメモリ(seve_field)はどういう意図を持って使用されたのでしょうか?

描画内容を保持したいという点だけでしたら、オフスクリーンは不要です。g.drawImage、seve2.drawImage、seve2.drawLine、g2.drawLineは以下の一行だけで要件は満たせます。

g.drawLine(start_x, start_y, lastMouseX, lastMouseY);//線を引く

2点目、オフスクリーンメモリの扱いと描画順序について
event#paintComponentメソッド内でseve2.drawLine、g2.drawLineとJPanelとメモリの2箇所に対してdrawLineを行っていますが

  1. オフスクリーン(seve_field)にdrawLine
  2. オフスクリーンをevent#paintComponent関数で渡されたGraphicsに対してg.drawImage
    オフスクリーンを使う目的から勝手に想像すると、こーなると思うのですが。

3点目、インスタンス変数start_xとstart_yと2つのint変数で宣言していますが、
座標を扱う時はjava.awt.Pointjava.awt.Rectangleを使うと管理しやすくなります。

4点目、あるコンポーネントのペイント領域を知りたい時はSwingUtilities#calculateInnerAreaが使用できます。
自分でオフスクーンを管理する時はJFrame(mainクラス)のウィンドウのサイズ変更イベントを契機にしてオフスクーンのサイズをコンポーネントのペイント領域と一致させる必要があります。面倒ならmainクラスのコンストラクタでthis.setResizable(false);を設定し、ウィンドウサイズを変更不可にしてください。

5点目、インスタンス変数seve について。
インスタンス変数に代入箇所が2箇所あります、

  1. Graphics seve = seve_field.createGraphics();
  2. event#paintComponentメソッド内

Graphics にはdisposeメソッドがあるため
使い終わったらseve.dispose();を呼び出してください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/11 07:11

    umyuさんの指摘は色々勉強になると思います。>質問者さん
    ただ、前の質問からの流れで考えると質問者さんはseve_fieldをオフスクリーンバッファという意味ではなく「お絵かきの結果としての画像ドキュメント」と想定されていると思います。つまり必ずしも画面の大きさに追従するべきとは言えないかもしれません。

    むしろ、表示内容を変更する際の線の描画などは全てpaintComponent以外の場所でseve_fieldへ直接描画し、event#paintComponentの中ではdrawImage(seve_field, 0, 0, this);しかやらないというのが素直な実装と思います。ただ、そこを指摘し始めるとかなり根本的な部分からの指摘になってくるので自分の回答ではGraphicsの扱いという点にだけ絞って回答しました。そのためかなり中途半端なコメントになってしまいましたが…

    キャンセル

  • 2017/05/11 09:56

    ありがとうございます。
    seve_fieldは自分のイメージではスクリーンショットのようなものが入っている記録装置だと思い、それをdrawImageで投下してる感じです。いつの間にかミスって消えてしまったようですが、 Graphics seve = seve_field.createGraphics();//メモリへの窓口 seve2に変換前に入ってました。結果意味ないので影響がなかったという感じです。ここらへんの間違いを少し考えます。
    また、線を引くg.drawLineについてなんですが、その記述だけだと一本線が引かれるだけで他がすべて消えてしまう、そう考えたので他もいろいろ足してます。
    java.awt.Point、java.awt.Rectangleは自分には未知の世界なので、これから習得って感じになります。
    SwingUtilities#calculateInnerAreaも同様です。
    disposeメソッドは破棄のところで使っていますが使い方がおかしいです。これもがんばります。

    キャンセル

  • 2017/05/11 23:47

    おかげで実装できました。ほぼ書いてもらったのであまり考えてませんが。余談の部分はまだぜんぜん理解してませんが、これらの部分は自分で解析をして学ぶところです。ですので本題の部分はおおむね解決したということでこの質問は閉めさせてもらいます。またわからなくなったら質問するのでそのときによろしくお願いします。

    キャンセル

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

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

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