Bir JPanel'e görüntü nasıl eklenir?


341

Anında oluşturduğum JPEG ve PNG görüntülerini eklemek istediğim bir JPanel var .

Swing Tutorials'da şimdiye kadar gördüğüm tüm örnekler , özellikle Swing örneklerindeImageIcon s kullanın .

Bu görüntüleri bayt dizileri olarak üretiyorum ve genellikle 640x480'deki örneklerde kullandıkları ortak simgeden daha büyükler.

  1. Bir JPanel boyutunda bir görüntü görüntülemek için ImageIcon sınıfını kullanırken herhangi bir (performans veya başka bir sorun) var mı?
  2. Bunu yapmanın olağan yolu nedir ?
  3. ImageIcon sınıfını kullanmadan bir JPanel'e görüntü nasıl eklenir?

Düzenleme : Öğreticilerin ve API'nın daha dikkatli bir incelemesi, bir JPanel'e doğrudan bir ImageIcon ekleyemeyeceğinizi gösterir. Bunun yerine, görüntüyü bir JLabel'in simgesi olarak ayarlayarak aynı efekti elde ederler. Bu doğru gelmiyor ...


1
Bayt dizilerini nasıl oluşturduğunuza bağlı olarak, MemoryImageSourcebunları JPEG veya PNG biçimine dönüştürmekten ve daha sonra ImageIOçoğu cevabın önerdiği şekilde okumaktan daha verimli olabilir . Bir alabilir Imagebir gelen MemoryImageSourcekullanarak görüntü verileri ile inşa createImagecevapların birinde söylediği gibi, ve ekran.
Alden

Yanıtlar:


252

Bunu nasıl yapacağım (bir görüntünün nasıl yükleneceği hakkında biraz daha fazla bilgi ile):

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 geçersiz uygulanması için (en neden olanlar yeniden çizilmesi konusunda sorunlar yaşıyorsanız o büyük olasılıkla @Dogmatixed) - bu olmalı o varlık opak (varsayılan) bildiriyorsa onun tam alanı kapsayacak garantee, en kolay super.paintComponent arayarak elde
kleopatra

5
@kleopatra, Teşekkürler, bunu fark etmedim ... javadoc'a göre: "Ayrıca, süper uygulamanın uygulanmasını istemezseniz, opak özelliği onurlandırmalısınız, yani bu bileşen opaksa, tamamen doldurmalısınız Opak özelliği onurlandırmazsanız, muhtemelen görsel eserler göreceksiniz. " Cevabı şimdi güncelleyeceğim.
Brendan Cashman

8
Lütfen her zaman Principle of EncapsulationSuper Class'ın geçersiz kılma yöntemlerine saygı paintComponent(...)protectedpublic
gösterin

1
Bu iyi değil, paneli görüntüye boyutlandırma hakkında hiçbir şey yapmıyor. Daha fazla kodun daha sonra eklenmesi gerekecektir. JLabel yanıtını kullanmak çok daha basit.
Keilly

2
@Jonathan: Bazı kaynaklar için gerçekten üzgünüm Principle of Encapsulation. Programlama sırasında, kodun geri kalanından gizlenmesi gerekenler, koddaki her şeye görünür olmak yerine bu şekilde korunmalıdır. Her gün öğrendiği gibi, deneyimle gelen böyle bir şey gibi görünüyor. Herhangi bir kitap, kapsüllemenin ne olduğu hakkında temel bir fikir verebilir, ancak bu konsepte ne kadar bağlı olduğumuza karar veren kodumuzu nasıl uyguladığımızdır.
Güzel

520

JPanels kullanıyorsanız muhtemelen Swing ile çalışıyorsunuzdur. Bunu dene:

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

Görüntü artık bir salınım bileşenidir. Diğer bileşenler gibi yerleşim koşullarına tabi olur.


16
JLabel büyüklüğüne göre görüntü nasıl ölçeklendirilir?
coding_idiot

1
Güzel kod! Swing konusunda pek tecrübeli değilim ama çalışamıyorum. 1.6.0_16 JDK'da deneyen var mı?
ATorras

3
@ATorras Ben bunu bir süre önce sordum biliyorum ama başka yeni başlayanlar benim sorunları vardı, picLabel.setBounds () unutmayın;
kyle

Her fotoğraf yeniden yüklendiğinde yeni JLabel nesnesi oluşturmam gerekir mi?
0x6B6F77616C74

2
@coding_idiot yeni JLabel (yeni ImageIcon (backgroundImage.getScaledInstance (genişlik, yükseklik, Image.SCALE_FAST)));
Davide

37

Fred Haslam'ın yolu iyi çalışıyor. Dosya yoluyla ilgili sorunum vardı, çünkü kavanozumdaki bir görüntüye başvurmak istiyorum. Bunu yapmak için kullandım:

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

Bu yöntemi kullanarak yüklemem gereken yalnızca sınırlı sayıda (yaklaşık 10) görüntüye sahip olduğum için oldukça iyi çalışıyor. Doğru göreceli dosya yoluna sahip olmak zorunda kalmadan dosya alır.


1
BufferedImage previewImage = ImageIO.read(new URL(imageURL));Resimlerimi bir sunucudan almak için ilk satırı değiştirdim .
intcreator

Kavanozdan görüntüleri yönlendirmek için şu stackoverflow.com/a/44844922/3211801
Nandha

33

Bence hiçbir şeyin alt sınıfa tabi tutulmasına gerek yok. Sadece bir Jlabel kullanın. Jlabele bir görüntü ayarlayabilirsiniz. Yani, Jlabel'i yeniden boyutlandırın ve bir görüntü ile doldurun. Tamam. Ben böyle yapıyorum.


20

Ücretsiz SwingX kütüphanelerinden JXImagePanel sınıfını kullanarak kendi Bileşen alt sınıfınızı tamamen yuvarlamaktan kaçınabilirsiniz .

İndir


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

8

JPanel'i alt sınıflandırabilirsiniz - işte ImagePanel'imden bir görüntü, üst / sol, üst / sağ, orta / orta, alt / sol veya alt / sağ 5 konumdan birine yerleştirilir:

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. Herhangi bir sorun olmamalıdır (çok büyük görüntülerde karşılaşabileceğiniz genel sorunların dışında).
  2. Tek bir panele birden fazla resim eklemekten bahsediyorsanız, ImageIcons'yi kullanırdım . Tek bir görüntü için, görüntüyü çizmek için özel bir alt sınıf oluşturmayı JPanelve paintComponentyöntemini geçersiz kılmayı düşünürdüm .
  3. (bkz. 2)

6

JPanelneredeyse her zaman alt sınıfa yanlış sınıftır. Neden alt sınıf yapmıyorsunuz JComponent?

ImageIconYapıcı görüntüyü okumayı engellemede küçük bir sorun var . Uygulama kavanozundan yükleme yaparken gerçekten bir sorun değil, belki de bir ağ bağlantısı üzerinden okuyorsanız. AWT dönemi kullanma örnekleri bol var MediaTracker, ImageObserverve arkadaşlar bile JDK demolar halinde.


6

Üzerinde çalıştığım özel bir projede çok benzer bir şey yapıyorum. Şimdiye kadar 1024x1024'e kadar (bellek hariç) sorunsuz görüntüler ürettim ve bunları çok hızlı ve performans sorunu olmadan gösterebilirim.

JPanel alt sınıfının boya yöntemini geçersiz kılmak çok fazladır ve yapmanız gerekenden daha fazla çalışma gerektirir.

Bunu yapmamın yolu:

Class MapIcon implements Icon {...}

VEYA

Class MapIcon extends ImageIcon {...}

Görüntüyü oluşturmak için kullandığınız kod bu sınıfta olacaktır. Daha sonra paintIcon () çağrıldığında üzerine çizmek için bir BufferedImage kullanın, g.drawImvge (bufferedImage) kullanın; Bu, görüntülerinizi oluştururken yapılan yanıp sönme miktarını azaltır ve iş parçacığı oluşturabilirsiniz.

Sonra JLabel genişletmek:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Bunun nedeni, görüntümü kaydırma bölmesine koymak, görüntünün bir bölümünü görüntülemek ve kullanıcının gerektiği gibi kaydırmasını sağlamaktır.

O zaman ben sadece MapIcon içeren MapLabel tutmak için bir JScrollPane kullanın.

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

scrollPane.getViewport ().add (mapLabel);

Ancak senaryonuz için (her seferinde görüntünün tamamını gösterin). MapLabel öğesini en üstteki JPanel'e eklemeniz ve tümünü görüntünün tam boyutuna boyutlandırdığınızdan emin olmanız gerekir (GetPreferredSize () geçersiz kılınarak).


5

Proje dizininizde bir kaynak klasör oluşturun, bu durumda Görüntüler adını verdim.

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

Kendi Components ve SwingX kütüphanelerini ve ImageIOsınıflarını kullanmaktan kaçınabilirsiniz :

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

4

Bu cevap @ shawalli'nin cevabının bir tamamlayıcısıdır ...

Ben de benim kavanoz içinde bir görüntü referans istedim, ama bir BufferedImage sahip olmak yerine, ben basit yaptım:

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

1

OP'nin üç sorusunu gerçekten ele almayan birçok cevap görebiliyorum.

1) Performansla ilgili bir kelime: bayt dizileri, ekran bağdaştırıcılarınızın geçerli çözünürlüğü ve renk derinliği ile eşleşen tam bir piksel bayt sırasını kullanmadıkça muhtemelen verimsizdir.

En iyi çizim performansını elde etmek için görüntünüzü mevcut grafik yapılandırmanıza karşılık gelen bir türle oluşturulan bir BufferedImage'a dönüştürmeniz yeterlidir. Https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html adresindeki createCompatibleImage sayfasına bakın.

Bu görüntüler otomatik olarak (bu Java 6 beri Swing standarttır) herhangi bir programlama çaba olmadan birkaç kez çizerek sonra ekran kartı belleğine önbelleğe edilecek ve dolayısıyla gerçek çizim zamanını önemsiz miktarda alacak - eğer görüntüyü değişmedi .

Görüntünün değiştirilmesi, ana bellek ile GPU belleği arasında ek bir bellek aktarımı ile birlikte gelir - bu yavaştır. Bu nedenle, görüntüyü BufferedImage'a "yeniden çizmekten" kaçının, getPixel ve setPixel'i hiçbir şekilde yapmaktan kaçının.

Örneğin, bir oyun geliştiriyorsanız, tüm oyun aktörlerini bir BufferedImage'a ve ardından bir JPanel'e çizmek yerine, tüm aktörleri daha küçük BufferedImages olarak yüklemek ve bunları JPanel kodunuzda tek tek çizmek çok daha hızlıdır. uygun konumları - bu şekilde, ana bellek ile GPU belleği arasında, önbelleğe alınacak görüntülerin ilk aktarımı dışında ek veri aktarımı olmaz.

ImageIcon, bufferedImage'ı kaputun altında kullanacaktır - ancak temel olarak bir BufferedImage'ı uygun grafik moduna atamak anahtardır ve bunu doğru yapmak için hiçbir çaba yoktur.

2) Bunu yapmanın genel yolu, JPanel'in geçersiz bir paintComponent yöntemine bir BufferedImage çizmektir. Java, GPU belleğinde önbelleğe alınan VolatileImages'ı kontrol eden tampon zincirler gibi iyi miktarda ek güzellikleri desteklese de, Java 6'nın tüm GPU hızlandırması ayrıntılarını göstermeden makul bir şekilde iyi bir iş çıkardığı için bunlardan herhangi birine gerek yoktur.

GPU hızlandırmanın yarı saydam görüntüleri genişletme gibi belirli işlemler için çalışmayabileceğini unutmayın.

3) Eklemeyin. Sadece yukarıda belirtildiği gibi boyayın:

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

"Ekleme", resim mizanpajın bir parçasıysa mantıklıdır. Buna JPanel'i dolduran bir arka plan veya ön plan görüntüsü olarak ihtiyacınız varsa, sadece paintComponent öğesini çizin. Görüntünüzü gösterebilecek genel bir Swing bileşenini demlemeyi tercih ediyorsanız, aynı hikaye (bir JComponent kullanabilir ve paintComponent yöntemini geçersiz kılabilirsiniz) - ve bunu GUI bileşenleri düzeninize ekleyebilirsiniz .

4) Diziyi bir Bufferedimage'e dönüştürme

Bayt dizilerinizi PNG'ye dönüştürmek, sonra yüklemek oldukça kaynak yoğundur. Daha iyi bir yol, mevcut bayt dizinizi BufferedImage'a dönüştürmektir.

Bunun için: döngüler ve kopya pikseller için kullanmayın . Bu çok çok yavaş. Yerine:

  • BufferedImage'ın tercih edilen bayt yapısını öğrenin (günümüzde piksel başına 4 bayt olan RGB veya RGBA'yı kabul etmek güvenlidir)
  • tarama çizgisini öğrenin ve kullanımdaki taramayı öğrenin (örn. 142 piksel genişliğinde bir görüntünüz olabilir - ancak gerçek hayatta 256 piksel genişliğinde bayt dizisi olarak depolanacak ve kullanılmayan pikselleri GPU donanımı tarafından maskelemek daha hızlıdır. )
  • daha sonra bu ilkelere göre bir dizi oluşturduğunuzda, BufferedImage'ın setRGB dizi yöntemi dizinizi BufferedImage'a kopyalayabilir.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.