アプリ版:「スタンプのみでお礼する」機能のリリースについて

JAVAの初心者です、宜しくお願いします。
下のようなプログラムを書きました。

「public void paint(Graphics g){
~ repaint();」とすると、画像がちらつきます、しかし、「repaint();」を消すとちらつかなくなります、この原因が分かりません。
一体何故このような現象が起こるのでしょうか、宜しくお願いします。

==========================================================

import java.applet.Applet;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class Oval_Rec_1_071128 extends Applet implements MouseListener , ActionListener
{
int x1 ;
int y1 ;
int xh = 50 ;
int yv = 50 ;

Button bt1 ;
Button bt2 ;

public void actionPerformed(ActionEvent ae){}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseReleased(MouseEvent e){}

public void init()
{
setBackground(Color.yellow);

addMouseListener(this);

bt1 = new Button("push");
bt1.setFont(new Font("SansSerif", Font.BOLD, 20));
bt1.setBackground(Color.black);
bt1.setForeground(Color.lightGray);
add(bt1);
bt1.addActionListener(this);

bt2 = new Button("change_circle");
add(bt2);
bt2.addActionListener(this);
}

public void mousePressed(MouseEvent e)
{
x1 = e.getX();
y1 = e.getY();
xh = e.getX();
yv = e.getY();

//repaint(); //comment out
}

public void paint(Graphics g)
{
g.setColor(Color.red);
g.setFont(new Font("Serif",Font.BOLD,24));
g.drawString("Hello Applet World ! !" , 50 , 50);

g.setColor(Color.blue);
g.drawLine( 100 , 100 , 500 , 500);

g.setColor(Color.cyan);
g.fillOval( x1 , y1 , xh , yv);

g.setColor(Color.magenta);
g.fillRect( xh , yv, x1/10 , y1/10 );

repaint();
}
}

A 回答 (8件)

update()は必ず画面の消去を行うわけではありません。


参考書によっては「update()は描画面を背景色で塗りつぶす」と書いてあるものもありますが、実際にはそこまで単純なものではありません。じゃあ実際はどうなのか、は細かく説明しだすと芋づる式に難しい用語や概念が出てくるので、ここでの回答は難しいです。理解していないと困る場面もそうそうないでしょうし、「基本的な」描画の流れからは外れた高度な内容になってきます。

なのでまだ勉強を始めたばかりであれば、まずは「直接update()を呼ぶことはほとんどない」「描画をしてほしいタイミングでrepaint()を呼べばよい」「描画処理そのものはpaint()内に書けばよい」ということを覚えておき、ある程度Javaに慣れてから、Javaのソースコードを直接自分で読んでみるなり(JDKにsrc.zipというのが入っています)、描画プロセスについて細かく書かれた参考書等を読むなりされた方がよいと思います。

で、今回の背景塗りつぶしに関しては、入門書でよく見かけるのは以下の2つの方法です。お手持ちの参考書なり、勉強サイトなりにどちらか書かれていませんか?

1.Graphics.setColor()で描画色を設定し、Graphics.fillRect()で画面全体を塗りつぶす。
2.Graphics.setBackground()で背景色を設定しておき、Graphics.clearRect()で画面全体を塗りつぶす。

要は自分で描画する内容についての単純な更新は自分でやろうね、ということです。質問者様が今回書かれたclear()メソッドの最初の2行がちょうど1.に該当しますが、基本的にはそれが正しいやり方だと思っておいてよいと思いますよ。
    • good
    • 0

うわ、失礼。

#6ですが#7様の回答が正解。私のは無視してください。

forループに気づかなかった…アニメーションはpaint()の中でforループ使って実現してはいけません。どういう参考書をお使いなのか分かりませんが、Runnableインターフェースのことがたぶんそのうち出てくるので、それまでアニメーションには手をつけない方がいいです。
    • good
    • 0
この回答へのお礼

みなさん、ありがとうございます。
ひとまず締め切らせていただきます。
貴重な回答ありがとうござます。
自分でももっと勉強します。
今後とも宜しくお願いします。

お礼日時:2009/10/27 19:20

 #2です。



 アニメーションに手を出してしまいましたか。
 まだ、早いと思うのですが。

>なにか、基本的な作成方法とかアルゴリズムみたいなものが
>間違っているのでしょうか、宜しくご教示お願い致します。

 残念ながら、間違っています。

 clearメソッドを実装すれば、アニメーションらしきものが出てきますが、根本的に間違っています。
 試しに、アニメーションが終わった後にブラウザを最小化して元に戻してみてください。
 なぜか、また、アニメーションが始まります。
 アプレットビューワーなら、ウィンドウの大きさを変更しただけで、アニメが始まります。
 これは思い描いていた挙動ではないはずです。

 ちなみにAWTでアプレットを作ったので、アニメらしきものが出ましたが、Swingで実装していれば、それすらも出ないはずです。

 もし、アニメーションをしたいのであれば、

 イベントディスパッチスレッドはどんな仕事をしているのか。
 paintメソッドはどんなタイミングで呼ばれるのか。

 そういった、基本的なことを理解しないと無理だと思います。
    • good
    • 0

 #2です。



> updateが単独で呼ばれる場合は(基本的には?)
>ないという考えで良いのでしょうか。

 そうですね。
 まとめると、

1. updateメソッド、paintメソッドは、システム(Java仮想マシン)が呼ぶもの。プログラマが明示的に呼ぶことはまずない。

2. 再描画したいならrepaintメソッドを使うべき。

3. プログラマにできることは、コンポーネントの見た目や振る舞いを変更するために、paintメソッド(SwingならpaintComponentメソッド)を書き換えること。でも、そのメソッドを呼ぶのは、あくまで、システム。

 もちろん、例外はありますが、あくまで基本はこういう形です。
    • good
    • 0
この回答へのお礼

PecoPlusさん、何度も丁重な回答ありがとうございます。
何とか理解できました、有り難う御座いました。
ところで、下のような連続的に円が右に移動するプログラムを作成しました。
paint()は呼び出されるときは、必ず古いwindowにある絵を消して描くと思ったのですが、
そうはならなくて円が連続してしまいます。
色々と考えて「clear(g);」書いてやりましたが、「clear(g);をコメント」にすると円が連続します。
paint()は呼び出されるときには、必ず今windowにある絵を消して描くと考えたのですが、そのようにはなってくれませんでした。
メソッドの基本的な機能は何となく理解出来るのですが、実はこの辺のプログラム全体の流れ?みたいなものがなかなか理解出来ません。
何故「clear(g);」が必要なのでしょうか。宜しくお願いします。

また、「clear(g);」を書かなくても出来る方法はあるのでしょうか。
やはり、どこかに「repaint」を入れると思ったとおりに動くのでしょうか。
なにか、基本的な作成方法とかアルゴリズムみたいなものが間違っているのでしょうか、宜しくご教示お願い致します。

================================================================

public class Ball_Idou extends Applet
{
int x ;

public void paint(Graphics g)
{
for(x = 0 ; x < 180 ; ++ x)
{
//clear(g) ;
g.drawOval(x , 90 , 19 , 19) ;
sleep() ;
}
}

public void clear(Graphics g)
{
g.setColor(Color.white) ;
g.fillRect(0 , 80 , 200 , 40) ;
g.setColor(Color.black) ;
}

public void sleep(Graphics g)
{
double s = 0.0 ;

for (int j = 1 ; j<100 ; ++ j)
{
for (int k = 1 ; k < 100 ; ++ k)
{
s =+ Math.sin((double) j ) ;
}
}
}
}

お礼日時:2009/10/24 11:48

 #2です。



 ちょっと、例えが悪かったようです。

>#新しく図形を描く命令をすると必ずパネル全体を一旦クリアして、
>背景色を再描画した後に新しく、新しい図形を描くのではないのですか。
>その場合には古い絵は残らないと思うのですが

 その通りです。
 私が出した例えは、「もし、repaintがupdateを呼ばなかったとしたら、こんなことになってしまうよ」という仮の話です。

># 基本的には、パネルだけの再描画だけがPaint、
>パネルに貼り付けられているものも含めた再描画が
>Updateという考えでよいのでしょうか。

 違います。
 端的に言えば、

update : 消しゴム
paint : 鉛筆

 とでも、考えていただければ、いいと思います。
 すでに画用紙に何か書かれていれば、いったん消しゴムで消してから、書かなくてはいけない場合もあれば、画用紙が白紙なので、消しゴムを使う必要がなくいきなり鉛筆で書ける場合もあるということです。

 もっとも、自分で、コンポーネントを自作したり、アニメーションをさせようとしたりしない限り、この辺のことはあまり深く考える必要はないです。
 普通にやっていれば、updateやpaintを明示的に呼ぶことは、まずないと思います。
 それより、repaintの使い方を覚えることが先決です。


 「イベントディスパッチスレッド」については、ここで一から説明するのは、ちょっと無理かもしれません。
 いったん、勉強してみて、疑問点があれば、新しく質問をたててみてください。
    • good
    • 0
この回答へのお礼

PecoPlusさん、回答ありがとうございます。

>すでに画用紙に何か書かれていれば、いったん消しゴムで消してから、書かなくてはいけない場合もあれば、画用紙が白紙なので、消しゴムを使う必要がなくいきなり鉛筆で書ける場合もあるということです。

 もっとも、自分で、コンポーネントを自作したり、アニメーションをさせようとしたりしない限り、この辺のことはあまり深く考える必要はないです。
 普通にやっていれば、updateやpaintを明示的に呼ぶことは、まずないと思います。

# ということは、repainを呼んでやれば、パネルの諸状況を判断して
paintが呼ばれる場合、updateが呼ばれる場合があるということでしょうか。

 updateが単独で呼ばれる場合は(基本的には?)ないという考えで良いのでしょうか。
 再度ご教示宜しくお願いします。

お礼日時:2009/10/23 19:49

 #2です。



 updateメソッドは何をしているかというと、コンポーネントの塗りつぶしを行ってから、paintメソッドを呼びます。

 repaintを呼ぶと何が起こるかというと、

repaint() (再描画命令、updateメソッドを呼ぶ)
  ↓
update() (コンポーネントを背景色で塗りつぶしたあと、paintメソッドを呼ぶ)
  ↓
paint()  (コンポーネントを描画)

 もし、repaint()が、update()を呼ばず、直接paint()を呼んでいたら、どうなるでしょう?

 今回を例に挙げれば、図形の座標が変えられ、repaint()→paint()となれば、そのまま、新しい図形を上書きしますが、上書きされなかった古い図形の断片は消されることなく、残り続けてしまいます。
 これでは、いけません。
 完全に、背景色で塗りつぶし、白紙の状態にしてから、一からすべて描き直す。
 これが、Javaでのコンポーネント描画の基本です。

 しかし、それが基本なら、なぜ、updateメソッドとpaintメソッドが一つのメソッドとして実装されておらず、別々のメソッドにしてあるのでしょう?

 たとえば、パネルにボタンが配置してあるとします。
 そのパネルのrepaint()が呼ばれたとします。
 まず、updateメソッドにより、パネル全体が背景色で塗りつぶされます。(当然ボタンも消えます)
 そのあと、パネルのpaintメソッドが呼ばれ、パネル内の子コンポーネントを描画しようとします。
 そのとき、子のボタンをもう一回背景色で塗りつぶす必要があるでしょうか?
 もうすでに塗りつぶされているのですから、それは冗長です。
 ここでは、描画だけされればいいはずです。
 よって、ボタンのpaintメソッドのみが呼ばれます。

 このように、場合場合によって

塗りつぶし→描画
描画

 と、するべき事柄が違うので、分けてあるのです。


>コードによっては直接Updatを呼んでいる場合もあります。

 イベント処理のコードの中で、updateメソッドやpaintメソッドを直接呼ぶことは基本的にないと思います。
 repaintメソッドを呼んで、イベントディスパッチスレッドに適切なタイミングでまとめて描画してもらうよう依頼するのが基本です。
    • good
    • 0
この回答へのお礼

PecoPlusさん、何度も明快な回答ありがとうございます。

>repaint() (再描画命令、updateメソッドを呼ぶ)
  ↓
update() (コンポーネントを背景色で塗りつぶしたあと、paintメソッドを呼ぶ)
  ↓
paint()  (コンポーネントを描画)

>今回を例に挙げれば、図形の座標が変えられ、repaint()→paint()となれば、そのまま、新しい図形を上書きしますが、上書きされなかった古い図形の断片は消されることなく、残り続けてしまいます。

#新しく図形を描く命令をすると必ずパネル全体を一旦クリアして、背景色を再描画した後に新しく、新しい図形を描くのではないのですか。
その場合には古い絵は残らないと思うのですが。
(>まず、updateメソッドにより、パネル全体が背景色で塗りつぶされます。(当然ボタンも消えます))

>その辺の原理というか、描画の仕組みが良く理解できていないのですが。
何か詳しい良書があるでしょうか。

> たとえば、パネルにボタンが配置してあるとします。
 そのパネルのrepaint()が呼ばれたとします。
 まず、updateメソッドにより、パネル全体が背景色で塗りつぶされます。(当然ボタンも消えます)

# 基本的には、パネルだけの再描画だけがPaint、パネルに貼り付けられているものも含めた再描画がUpdateという考えでよいのでしょうか。

>repaintメソッドを呼んで、イベントディスパッチスレッドに適切なタイミングでまとめて描画してもらうよう依頼する

# 「イベントディスパッチスレッド」とは、具体的にどのような意味でしょうか。

再度宜しくご教示お願い致します。

お礼日時:2009/10/23 18:39

 こんにちは。



>この場合、そもそもrepaint()を書いてやる必要はあるのでしょうか。
>また、書くとすれば一体どのように(どこに)書いてやると良いのでしょうか。

 #1さんの理由から paint()メソッド内に書くのはおかしいです。
 書く必要があるのは、コメントアウトされていますが、mousePressedイベントのまさにそこです。

「図形の座標を変更したので、書き直しておいてね」って、ことです。

 こうすることによって、一連のイベントが処理され一段落したところで、イベントディスパッチスレッドによって、再描画が必要とされたところだけが描き直されます。
(ここでは、アプレット全体が描き直されます)
    • good
    • 0
この回答へのお礼

PecoPlusさん、明快な回答有り難うございました。
うまく動くようになりました。

ところで、「repaint」と「update」とはどう違うのでしょうか。
APIを調べると、
>repaint
public void repaint()このコンポーネントを再びペイントします。
このメソッドは、このコンポーネントの update メソッドを可能なかぎり速やかに呼び出します。

>repaint
public void repaint(long tm)このコンポーネントを再びペイントします。tm ミリ秒以内に update メソッドを呼び出します。

>UPDATE - class java.awt.event.PaintEvent の static 変数
再描画イベントタイプです。
>UPDATE - interface javax.accessibility.AccessibleTableModelChange の static 変数
既存のデータに対する変更を識別します。
>UPDATE - class javax.swing.event.TableModelEvent の static 変数
既存のデータに対する変更を識別します。
========================================================

上記のような解説がありますが、呼んでもよく理解できません。
コードによっては直接Updatを呼んでいる場合もあります。
その使い分けが良く理解できません。

何と宜しくご教示お願いいたします。

お礼日時:2009/10/23 12:45

repaint()は簡単にいえば「手が空いたらpaint()で描画してね」と描画スレッドに合図を送るためのメソッドです。

なので描画メソッドであるpaint()の中で呼ぶのはおかしいです。画面を描画している人が自分に対してもう一度描画するように依頼するようなものですから。
画面描画のサイクルの中で再描画をしようとするので、描画が間に合わないかタイミングがおかしくなるかでちらつくのでしょう。

しかしpaint()の中でrepaint()を呼ぶコードを初めて見ました。なにげに斬新です。
    • good
    • 0
この回答へのお礼

 komi1341さん、回答有り難う御座います。
 この場合、そもそもrepaint()を書いてやる必要はあるのでしょうか。
 また、書くとすれば一体どのように(どこに)書いてやると良いのでしょうか。
なにとぞ宜しくお願いします。

お礼日時:2009/10/23 01:09

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!