- PR -

swingでグラフィックコンテキストは取得できますか?

投稿者投稿内容
バンブー
会議室デビュー日: 2003/09/22
投稿数: 12
投稿日時: 2003-10-13 13:38
こんにちは、バンブーです。
JPanelに文字列を出力したいのですが、どの参考書を見ても、paintをオーバーライド
するように書かれています。
過去スレを見ても、同じような解決法だったような気がします。
が、文字列データが複雑なため、どうしても、グラフィックコンテキストを取得して、
ほかの、処理ルーチンの中で、出力できないものかといろいろ試しましたが、結局だめ
でした。以下が、処理データを簡略化して作成しただめなサンプルコードです。

コード:
import javax.swing.*;
import java.awt.*;

public class testText2 extends javax.swing.JApplet {
    boolean isData = false;
    int maxdata;
    myData[] myD;

    public testText2() {
        myD = new myData[64];
        for(int i = 0; i<3; i++) {
            myD[i] = new myData();
            myD[i].str = "test";
            myD[i].x = 20+i*50;
            myD[i].y = 20+i*50;
        }
        isData = true;
        maxdata = 3;
        myPanel myP = new myPanel();
        myP.setGr();
        getContentPane().add(myP);
        myP.setBounds(0,0,320,300);
        for(int i = 0; i<maxdata; i++) {
            myP.printText(myD[i].str, myD[i].x, myD[i].y);
        }
    }

    class myPanel extends JPanel {
        Graphics gr;
        public void setGr() {
            gr = getGraphics();   //////////どうもグラフィックコンテキストが取得できないみたいだ。
        }
        public void paint(Graphics g) {
        }
        public void printText(String str, int x, int y) {
            gr.drawString(str, x, y);////必ずエラーになる。
        }
        
    }                
            
    class myData {
        int x;
        int y;
        String str;
    }
    
}


次にあげるのは、上記の目的をpaint内部の処理で実現したコードです。
じゃあ、これを使えばいいじゃないかって.......そうはいかないので困っているのです。
どなたか解決法を教えてください。

コード:
/*
 * testText1.java
 *
 * Created on 2003/10/13, 11:29
 */


import javax.swing.*;
import java.awt.*;

public class testText1 extends javax.swing.JApplet {
    boolean isData = false;
    int maxdata;
    myData[] myD;

    public testText1() {
        myD = new myData[64];
        for(int i = 0; i<3; i++) {
            myD[i] = new myData();
            myD[i].str = "test";
            myD[i].x = 20+i*50;
            myD[i].y = 20+i*50;
        }
        isData = true;
        maxdata = 3;
        myPanel myP = new myPanel();
        getContentPane().add(myP);
        myP.setBounds(0,0,320,300);
    }
    class myPanel extends JPanel {
        public void paint(Graphics g) {
            if (isData) {
                for (int i = 0; i < maxdata; i++) {
                    g.drawString(myD[i].str,myD[i].x,myD[i].y);
                }
        
            }
        }
    }                
            
    class myData {
        int x;
        int y;
        String str;
    }
    
}



Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-10-13 15:54
引用:

バンブーさんの書き込み (2003-10-13 13:38) より:
コード:
public void setGr() {
    gr = getGraphics();   ////どうもグラフィックコンテキストが取得できないみたいだ。
}



java.awt.Component#getGraphics()メソッドはAPIの記述にもあるように、そのコンポーネントが現在表示可能でない場合はnull を返します。従ってアプレットのコンストラクタからsetGr()を呼び出しても、getGraphics()は常にnullを返します。

引用:

次にあげるのは、上記の目的をpaint内部の処理で実現したコードです。
じゃあ、これを使えばいいじゃないかって.......そうはいかないので困っているのです。


私にはこれで言いように思いますが、『そうはいかない』のはなぜですか?
バンブー
会議室デビュー日: 2003/09/22
投稿数: 12
投稿日時: 2003-10-13 16:31
Wata様のおっしゃってるとおりなのですが、文字列データの入っているクラスの
配列の生成とその内容が、リアルタイムで、変化するような仕様の、アプリを、計画中
なのです。つまり、表示したいデータが、常に変化しているような場合、はたして、
paintルーチンにまかせていいものかどうか不安なのです。
くわしくは知らないのですが、paintルーチンというのは、Windowの状態が変化したときに最後に一度呼ばれるルーチンだとどこかで読んだ気がするのです。
自分で呼び出すことのできないルーチンに表示をまかせるのは、納得がいかないのです。
こんな私はjavaがよく分かっていないのでしょうか?
Wata
ぬし
会議室デビュー日: 2003/05/17
投稿数: 279
投稿日時: 2003-10-13 17:28
引用:

バンブーさんの書き込み (2003-10-13 16:31) より:
Wata様のおっしゃってるとおりなのですが、文字列データの入っているクラスの
配列の生成とその内容が、リアルタイムで、変化するような仕様の、アプリを、計画中
なのです。つまり、表示したいデータが、常に変化しているような場合、はたして、
paintルーチンにまかせていいものかどうか不安なのです。


実際に試してみないことには確実なことはいえませんが、その文字列データが変更されたタイミングで、パネルのrepaint()を呼び出してやれば十分な気がします。
repaintを呼び出すと、しかるべき手順を踏んだ後にpaintが呼び出されます。

引用:

こんな私はjavaがよく分かっていないのでしょうか?


誰もが最初はわかっていないですし、javaやSwingの詳細な仕組みまでわかっている人など、ほんの一握りしかいません。私も詳細はわかりません。
悲観せずに、APIに載っている自分のまだ知らないクラスやメソッドを一つずつ試して、わかっていけばよいと思います。
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-10-13 22:16
やっている内容からして、JPanelではなく、JListをしようしたほうがよかぁないですか?
コード:
        for(int i = 0; i<3; i++) {
            myD[i] = new myData();
            myD[i].str = "test";
            myD[i].x = 20+i*50;
            myD[i].y = 20+i*50;
        }


の箇所で、描画位置の指定が少々厄介そうですが、ListCellRendererをうまく実装すればできそうな気がします。

後で、文字列データを追加するのか、修正するのかは分からないですが、どちらの場合においても、AppletでListModelの実装(たいていDefaultListModel)を保持しておき、表示を切り替えたいときに、ListModelに変更を通知(変更だの、追加だの、削除だの)すれば、描画まで勝手にやってくれて楽です。

Webで検索かければ、JListの使用方法はいくらでも見つかると思うので、試してみてはいかがでしょう。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-10-14 11:01
unibon です。こんにちわ。

引用:

バンブーさんの書き込み (2003-10-13 16:31) より:
くわしくは知らないのですが、paintルーチンというのは、Windowの状態が変化したときに最後に一度呼ばれるルーチンだとどこかで読んだ気がするのです。
自分で呼び出すことのできないルーチンに表示をまかせるのは、納得がいかないのです。



ちなみに Swing ならば、「自分で呼び出すことのできるルーチン」として、
http://java.sun.com/j2se/1.4/ja/docs/ja/api/javax/swing/JComponent.html#paintImmediately(int,%20int,%20int,%20int)
が使えるはずです。
しかし、通常は paint メソッドを設けて、
それを repaint メソッドで呼び出すことが普通です。

なお、paintImmediately にしろ paint にしろ、
原理的には必ず画面の描画を更新するはずですが、
実際にはビデオドライバとの相性(バグ?)などもあり、
必ず描画されるとは限らないはずです。
#Java でも Windows でも、画面に描画のゴミが残るのは
#良く遭遇することだと思います。
しかし、これを言い出すと、キリがないので、
普通はそこまでクリティカルなことは問わないのが普通です。
#「普通」と言ってで良いのか?

#以下、かなりあいまいな説明ですが。
なお、paint メソッドの引数として渡される Graphics は、一定ではないことと、
なんらかの方法である段階で取得した Graphics に描画しても、
paint メソッドでやるべき描画(責務を負った描画)になるとは限りません。
普通は、paint メソッドの外で描画しても、画面には反映されません。
ただし、ダブルバッファと呼ばれる手法は良く使われますが、
これも結局は paint メソッドの引数の Graphics にコピーしてますので、
結局は paint メソッドの引数の Graphics に描画していることに変わりはありません。
バンブー
会議室デビュー日: 2003/09/22
投稿数: 12
投稿日時: 2003-10-17 06:08
unibon様、かずくん様、Wata様、アドバイスありがとうございました。
おかげさまで、4日ほどかかりましたが、ほぼ期待通りの仕上がりとなりました。

それにしても、ダブルバッファの実装やら、repaint()の埋め込みやら、repaint()を
呼び込むためのEventの実装やら、勉強にはなりましたが、けっこう大変でした。

実は、VBの移植をやっているのですが、文字列の出力がこんなに大変だとは、
思ってもみませんでした。
思えば、
VBのActiveXカスタムコントロールをbeanに移植できた時点で、これはいけるぞと
思ってとりかかったのですが、スクロールや文字列出力でこんなにつまずくとは!
この先、印刷やら、タブレット入力やら、つまずきそうな所が山積です。今後とも
よろしくおねがいいたします。

ところで、初歩的な質問ですが、Jpanelには、ダブルバッファが実装済みだと聞き
ましたが、どの参考書をみても、ユーザーが実装して、それを利用するように
なっています。システムが持っているダブルバッファはユーザーからは利用できない
のでしょうか?まあ、ダブルバッファを二重に持っても支障はないのですが...
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-10-17 10:17
SDKのversionは何?

J2SDK1.4であれば、swingの内部ではVolatileImageを使用してDouble Bufferしてくれているので、ユーザーコードからはそのことを意識する必要はないです。
ただし、このオフスクリーン描画はpaint()メソッドで行っているので、paint()メソッドをオーバーライドしてしまうと、やってもらえなくなります。代わりにpaintComponent()をオーバーライドするようにしてください。

それより以前のversinは良く知りません。
たぶん http://java.sun.com にあげられたrelese noteに書かれているでしょう

[ メッセージ編集済み 編集者: かずくん 編集日時 2003-10-17 10:27 ]

スキルアップ/キャリアアップ(JOB@IT)