JPanelに画像を追加する方法


341

私が持っているJPanelの私はその場で生成することをJPEGやPNG画像を追加したい先のを。

Swingチュートリアルでこれまでに見たすべての例、特にSwingの例では、ImageIconsを使用しています。

私はこれらの画像をバイト配列として生成しています。これらの画像は通常、例で使用している一般的なアイコンよりも大きく、640x480です。

  1. ImageIconクラスを使用してJPanelでそのサイズの画像を表示する際に(パフォーマンスまたはその他の)問題はありますか?
  2. それを行う通常の方法は何ですか?
  3. ImageIconクラスを使用せずに画像をJPanelに追加する方法は?

編集:チュートリアルとAPIをさらに注意深く調べると、ImageIconをJPanelに直接追加できないことがわかります。代わりに、画像をJLabelのアイコンとして設定することにより、同じ効果を実現します。これは正しくないと思います...


1
バイト配列の生成方法によってはMemoryImageSource、を使用してJPEGまたはPNG形式に変換しImageIO、ほとんどの回答が示唆するように読み取る方が効率的です。を使用して画像データで構成されたImageからを取得し、回答の1つで提案されているように表示できます。MemoryImageSourcecreateImage
Alden

私の答えを確認してくださいstackoverflow.com/questions/43861991/…–
tommybee

回答:


252

ここに私がそれをする方法があります(画像をロードする方法についてのもう少し詳しい情報付き):

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage image;

    public ImagePanel() {
       try {                
          image = ImageIO.read(new File("image name and path"));
       } catch (IOException ex) {
            // handle exception...
       }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters            
    }

}

17
-1 paintComponentの無効な実施のため(のは、なぜあなたはそれらの再描画の問題が発生していることをおそらく@Dogmatixed) -それは必要があり、それは(デフォルト)という不透明を報告した場合、最も簡単なsuper.paintComponentを呼び出すことによって達成その完全なエリアをカバーするgarantee
クレオパトラ

5
@kleopatra、ありがとう、私はそのことに気づきませんでした... javadocによると:「さらに、スーパーの実装を呼び出さない場合は、不透明なプロパティを尊重する必要があります。つまり、このコンポーネントが不透明な場合、完全に入力する必要があります。背景は不透明ではない色です。不透明なプロパティを尊重しないと、視覚的なアーティファクトが発生する可能性があります。」答えを更新します。
ブレンダンキャッシュマン2012

8
Principle of Encapsulationスーパークラスのメソッドをオーバーライドする間は常に尊重してください。メソッドのアクセスpaintComponent(...)protectedpublic
指定子

1
これはダメです、それはパネルを画像に合わせてサイズ変更することについて何もしません。これに対処するには、後でコードを追加する必要があります。JLabelの回答を使用する方がはるかに簡単です。
Keilly、2015

2
@ジョナサン:いくつかのソースについて、私は本当に申し訳ありませんPrinciple of Encapsulation。プログラミング中に覚えておくべきことは、コードの他の部分から隠すべきものは、コード内のすべてのものから見えるのではなく、そのように維持する必要があるということです。毎日学ぶので、それは経験に伴うもののようです。どんな本でもカプセル化とは何かについての基本的な考えを与えることができますが、コンセプトにどれだけ固執するかを決定するのはコードの実装方法です。
nIcE cOw 2016

520

JPanelsを使用している場合は、おそらくSwingを使用しています。これを試して:

BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);

これで画像がSwingコンポーネントになりました。他のコンポーネントと同様にレイアウト条件の影響を受けます。


16
JLabelのサイズに応じて画像をスケーリングする方法
coding_idiot

1
素敵なコード!Swingの経験はあまりありませんが、うまく動作しません。誰かがjdk 1.6.0_16でそれを試しましたか?
ATorras 2012年

3
@ATorrasしばらく前にこれを尋ねたと思いますが、他の初心者が私の問題を抱えている場合は、picLabel.setBounds();を忘れないでください。
kyle 2014年

写真を再読み込みするたびに新しいJLabelオブジェクトを作成する必要がありますか?
0x6B6F77616C74 2015年

2
@coding_idiot new JLabel(new ImageIcon(backgroundImage.getScaledInstance(width、height、Image.SCALE_FAST)));
Davide

37

フレッド・ハスラムのやり方はうまくいきます。jarファイル内の画像を参照したいので、ファイルパスに問題がありました。これを行うために、私は使用しました:

BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));

この方法を使用してロードする必要があるのは有限数(約10)の画像しかないため、非常にうまく機能します。正しい相対ファイルパスがなくてもファイルを取得します。


1
BufferedImage previewImage = ImageIO.read(new URL(imageURL));サーバーから画像を取得するために最初の行を置き換えました。
intcreator 2015

jarからの画像を参照するには、このstackoverflow.com/a/44844922/3211801を
Nandha

33

何もサブクラス化する必要はないと思います。Jlabelを使用するだけです。Jlabelに画像を設定できます。したがって、Jlabelのサイズを変更してから、画像で埋めます。大丈夫です。これが私のやり方です。



11
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));

8

JPanelをサブクラス化できます-これは私のImagePanelからの抽出で、画像を上/左、上/右、中央/中央、下/左、または下/右の5つの場所のいずれかに配置します。

protected void paintComponent(Graphics gc) {
    super.paintComponent(gc);

    Dimension                           cs=getSize();                           // component size

    gc=gc.create();
    gc.clipRect(insets.left,insets.top,(cs.width-insets.left-insets.right),(cs.height-insets.top-insets.bottom));
    if(mmImage!=null) { gc.drawImage(mmImage,(((cs.width-mmSize.width)/2)       +mmHrzShift),(((cs.height-mmSize.height)/2)        +mmVrtShift),null); }
    if(tlImage!=null) { gc.drawImage(tlImage,(insets.left                       +tlHrzShift),(insets.top                           +tlVrtShift),null); }
    if(trImage!=null) { gc.drawImage(trImage,(cs.width-insets.right-trSize.width+trHrzShift),(insets.top                           +trVrtShift),null); }
    if(blImage!=null) { gc.drawImage(blImage,(insets.left                       +blHrzShift),(cs.height-insets.bottom-blSize.height+blVrtShift),null); }
    if(brImage!=null) { gc.drawImage(brImage,(cs.width-insets.right-brSize.width+brHrzShift),(cs.height-insets.bottom-brSize.height+brVrtShift),null); }
    }

8
  1. 問題はないはずです(非常に大きな画像で発生する可能性のある一般的な問題以外)。
  2. 1つのパネルに複数の画像を追加する場合は、ImageIcons を使用します。単一の画像の場合、画像を描画するメソッドのカスタムサブクラスを作成JPanelしてオーバーライドすることを検討しますpaintComponent
  3. (2を参照)

6

JPanelほとんどの場合、サブクラスに対して間違ったクラスです。なぜサブクラス化しないのですかJComponentか?

ImageIconコンストラクターが画像の読み取りをブロックするという点で、わずかな問題があります。アプリケーションjarからロードする場合は問題ありませんが、ネットワーク接続を介して読み取りを行っている可能性があります。JDKのデモにもMediaTracker、AWT時代の例ImageObserverや、およびその友人の例がたくさんあります。


6

私が取り組んでいるプライベートプロジェクトで非常によく似たことをしています。これまでのところ、1024x1024までの画像を(メモリを除いて)問題なく生成し、非常に迅速に、パフォーマンスの問題なしに表示できました。

JPanelサブクラスのpaintメソッドのオーバーライドはやり過ぎであり、必要以上の作業が必要です。

私のやり方は:

Class MapIcon implements Icon {...}

または

Class MapIcon extends ImageIcon {...}

画像の生成に使用するコードは、このクラスに含まれます。私はBufferedImageを使用して描画し、paintIcon()が呼び出されたら、g.drawImvge(bufferedImage);を使用します。これにより、イメージの生成中に行われるフラッシュの量が減少し、スレッド化できます。

次に、JLabelを拡張します。

Class MapLabel extends Scrollable, MouseMotionListener {...}

これは、画像をスクロールペインに配置したいためです。つまり、画像の一部を表示し、必要に応じてユーザーにスクロールしてもらいます。

そこで、JScrollPaneを使用して、MapIconのみを含むMapLabelを保持します。

MapIcon map = new MapIcon (); 
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();

scrollPane.getViewport ().add (mapLabel);

しかし、あなたのシナリオでは(毎回全体の画像を表示するだけです)。MapLabelを一番上のJPanelに追加し、それらすべてを(GetPreferredSize()をオーバーライドして)画像のフルサイズにサイズ変更する必要があります。


5

プロジェクトディレクトリにソースフォルダーを作成します。この場合は、Imagesと呼びます。

JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();

5

独自ComponentのsおよびSwingXライブラリとImageIOクラスの使用を回避できます。

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));

4

この回答は@shawalliの回答を補足するものです...

私も自分のjar内の画像を参照したかったのですが、BufferedImageを使用する代わりに、これを単純に行いました。

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));

1

OPの3つの質問に実際に対処するのではなく、多くの答えを見ることができます。

1)パフォーマンスについて一言:ディスプレイアダプターの現在の解像度と色深度に一致する正確なピクセルバイト順序を使用できない場合、バイト配列はおそらく非効率です。

最高の描画パフォーマンスを実現するには、画像を現在のグラフィックス構成に対応するタイプで生成されたBufferedImageに変換するだけです。https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.htmlで createCompatibleImageを参照してください

これらの画像は、何もプログラミングせずに数回描画した後、ディスプレイカードのメモリに自動的にキャッシュされます(これはJava 6以降のSwingの標準です)。したがって、実際の描画にかかる時間はごくわずかです。です画像を変更しなかった。

イメージを変更すると、メインメモリとGPUメモリ間のメモリ転送が追加されますが、これは低速です。したがって、画像をBufferedImageに「再描画」することは避け、getPixelとsetPixelは絶対に行わないでください。

たとえば、ゲームを開発している場合、すべてのゲームアクターをBufferedImageに描画してからJPanelに描画するのではなく、すべてのアクターを小さいBufferedImagesとしてロードして、JPanelコードで1つずつ描画する方がはるかに高速です。それらの適切な位置-この方法では、キャッシュ用の画像の初期転送を除いて、メインメモリとGPUメモリの間で追加のデータ転送は行われません。

ImageIconは内部でBufferedImageを使用しますが、基本的には適切なグラフィックスモードでBufferedImageを割り当てることが重要であり、これを正しく行うための努力はありません。

2)これを行う通常の方法は、JPanelのオーバーライドされたpaintComponentメソッドでBufferedImageを描画することです。Javaは、GPUメモリにキャッシュされたVolatileImagesを制御するバッファーチェーンなど、追加の優れた機能を多数サポートしていますが、GPUアクセラレーションのこれらの詳細のすべてを公開することなくかなり良い仕事をするJava 6以降、これらを使用する必要はありません。

GPUアクセラレーションは、半透明画像のストレッチなどの特定の操作では機能しない場合があることに注意してください。

3)追加しないでください。上記のように塗ってください:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, this); 
}

「追加」は、画像がレイアウトの一部である場合に意味があります。JPanelを塗りつぶす背景または前景の画像としてこれが必要な場合は、paintComponentで描画してください。画像を表示できる汎用のSwingコンポーネントを作成したい場合は、同じストーリーです(JComponentを使用してpaintComponentメソッドをオーバーライドできます)。これをGUIコンポーネントのレイアウトに追加します。

4)配列をBufferedimageに変換する方法

バイト配列をPNGに変換してからロードすると、かなりのリソースを消費します。より良い方法は、既存のバイト配列をBufferedImageに変換することです。

そのため:forループ使用せず、ピクセルをコピーします。それは非常に遅いです。代わりに:

  • BufferedImageの優先バイト構造を学習します(現在、ピクセルごとに4バイトであるRGBまたはRGBAを想定しても安全です)。
  • 使用中のスキャンラインとスキャンサイズを学習します(たとえば、142ピクセル幅の画像があるかもしれませんが、実際には256ピクセル幅のバイト配列として格納されます。これは、GPUハードウェアで処理して未使用のピクセルをマスクする方が速いためです。 )
  • 次に、これらの原則に従って配列を作成したら、BufferedImageのsetRGB配列メソッドで配列をBufferedImageにコピーできます。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.