Bagaimana cara menambahkan gambar ke JPanel?

341

Saya memiliki JPanel yang ingin saya tambahkan gambar JPEG dan PNG yang saya hasilkan dengan cepat.

Semua contoh yang saya lihat sejauh ini dalam Tutorial Swing , khususnya dalam contoh Swing menggunakan ImageIcons.

Saya menghasilkan gambar-gambar ini sebagai array byte, dan mereka biasanya lebih besar dari ikon umum yang mereka gunakan dalam contoh, di 640x480.

  1. Apakah ada masalah (kinerja atau lainnya) dalam menggunakan kelas ImageIcon untuk menampilkan gambar sebesar JPanel?
  2. Apa cara yang biasa dilakukan?
  3. Bagaimana cara menambahkan gambar ke JPanel tanpa menggunakan kelas ImageIcon?

Sunting : Pemeriksaan tutorial dan API yang lebih cermat menunjukkan bahwa Anda tidak dapat menambahkan ImageIcon langsung ke JPanel. Sebaliknya, mereka mencapai efek yang sama dengan mengatur gambar sebagai ikon JLabel. Rasanya tidak benar ...

Leonel
sumber
1
Bergantung pada bagaimana Anda menghasilkan array byte, mungkin lebih efisien untuk menggunakan MemoryImageSourcedaripada mengubahnya menjadi format JPEG atau PNG dan kemudian membaca dengan ImageIOseperti yang disarankan sebagian besar jawaban. Anda dapat memperoleh Imagedari hasil MemoryImageSourcekonstruksi dengan data gambar Anda dengan menggunakan createImage, dan menampilkan seperti yang disarankan dalam salah satu jawaban.
Alden
Periksa jawaban saya stackoverflow.com/questions/43861991/…
tommybee

Jawaban:

252

Inilah cara saya melakukannya (dengan sedikit info lebih lanjut tentang cara memuat gambar):

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            
    }

}
Brendan Cashman
sumber
17
-1 untuk implementasi paintComponent yang tidak valid (@Dogmatixed kemungkinan besar itulah sebabnya Anda mengalami masalah penggambaran ulang) - ia harus menjamin untuk menutupi area lengkapnya jika melaporkan buram (yang merupakan default), paling mudah dicapai dengan memanggil super.paintComponent
kleopatra
5
@kleopatra, Terima kasih, saya tidak menyadari bahwa ... menurut javadoc: "Selanjutnya, jika Anda tidak menerapkan super invoker, Anda harus menghormati properti buram, yaitu jika komponen ini buram, Anda harus sepenuhnya mengisi latar belakang dalam warna non-buram. Jika Anda tidak menghormati properti buram Anda kemungkinan akan melihat artefak visual. " Saya akan memperbarui jawabannya sekarang.
Brendan Cashman
8
Harap selalu menghormati metode Principle of Encapsulationsementara Super Class, Penentu Akses daripaintComponent(...)protectedpublic
Penentu
1
Ini tidak baik, itu tidak melakukan apa pun tentang ukuran panel ke gambar. Lebih banyak kode harus ditambahkan kemudian lakukan berurusan dengan ini. Jauh lebih mudah untuk menggunakan jawaban JLabel.
Keilly
2
@ Jonathan: Saya benar-benar minta maaf, mengenai beberapa sumber Principle of Encapsulation. Hanya ingat saat pemrograman, apa yang harus disembunyikan dari sisa kode, perlu dipertahankan seperti itu, daripada terlihat oleh semuanya dalam kode. Sepertinya itu adalah sesuatu yang datang dengan pengalaman, seperti yang dipelajari setiap hari. Buku apa pun dapat memberikan ide dasar apa itu enkapsulasi, tetapi bagaimana kami menerapkan kode kami yang memutuskan seberapa banyak kami berpegang pada konsep tersebut.
NIcE cOw
520

Jika Anda menggunakan JPanels, maka mungkin bekerja dengan Swing. Coba ini:

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

Gambar sekarang merupakan komponen ayun. Itu menjadi tunduk pada kondisi tata letak seperti komponen lainnya.

Fred Haslam
sumber
16
bagaimana skala gambar sesuai dengan ukuran JLabel?
coding_idiot
1
Kode yang bagus! Saya tidak terlalu berpengalaman dengan Swing tetapi saya tidak bisa membuatnya bekerja. Apakah ada yang mencobanya di jdk 1.6.0_16?
ATorras
3
@ATorras Saya tahu Anda menanyakan ini beberapa waktu lalu, tetapi jika ada pemula lain yang memiliki masalah, ingat untuk picLabel.setBounds ();
kyle
Apakah saya harus membuat objek JLabel baru setiap kali foto dimuat ulang?
0x6B6F77616C74
2
@coding_idiot JLabel baru (ImageIcon baru (backgroundImage.getScaledInstance (lebar, tinggi, Image.SCALE_FAST)));
Davide
37

Cara Fred Haslam bekerja dengan baik. Saya mengalami masalah dengan filepath, karena saya ingin referensi gambar dalam toples saya. Untuk melakukan ini, saya menggunakan:

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

Karena saya hanya memiliki jumlah gambar terbatas (sekitar 10) yang saya perlu memuat menggunakan metode ini, itu bekerja dengan cukup baik. Ia mendapat file tanpa harus memiliki relatif relatif file yang benar.

shawalli
sumber
1
Saya mengganti baris pertama dengan BufferedImage previewImage = ImageIO.read(new URL(imageURL));untuk mendapatkan gambar saya dari server.
intcreator
Untuk merujuk gambar dari tabung, coba ini stackoverflow.com/a/44844922/3211801
Nandha
33

Saya pikir tidak perlu subkelas apa pun. Cukup gunakan Jlabel. Anda dapat mengatur gambar menjadi Jlabel. Jadi, ubah ukuran Jlabel lalu isi dengan gambar. Tidak apa-apa. Inilah yang saya lakukan.

CoderTim
sumber
20

Anda dapat menghindari menggulirkan subkelas Komponen Anda sepenuhnya dengan menggunakan kelas JXImagePanel dari SwingX gratis pustaka .

Unduh

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

sumber
8

Anda dapat mensubklasifikasikan JPanel - ini adalah ekstrak dari ImagePanel saya, yang menempatkan gambar di salah satu dari 5 lokasi, atas / kiri, atas / kanan, tengah / tengah, bawah / kiri atau bawah / kanan:

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); }
    }
Lawrence Dol
sumber
8
  1. Seharusnya tidak ada masalah (selain masalah umum yang Anda miliki dengan gambar yang sangat besar).
  2. Jika Anda berbicara tentang menambahkan banyak gambar ke satu panel, saya akan menggunakan ImageIcons. Untuk satu gambar, saya akan berpikir tentang membuat subkelas kustom JPaneldan mengganti paintComponentmetodenya untuk menggambar.
  3. (lihat 2)
Michael Myers
sumber
6

JPanelhampir selalu kelas yang salah untuk subkelas. Mengapa Anda tidak akan subkelasJComponent ?

Ada sedikit masalah dengan ImageIconkonstruktor yang memblokir membaca gambar. Tidak terlalu masalah saat memuat dari toples aplikasi, tetapi mungkin jika Anda berpotensi membaca melalui koneksi jaringan. Ada banyak contoh era AWT untuk menggunakan MediaTracker, ImageObserverdan teman-teman, bahkan dalam demo JDK.

Tom Hawtin - tackline
sumber
6

Saya melakukan sesuatu yang sangat mirip dalam proyek pribadi yang sedang saya kerjakan. Sejauh ini saya telah menghasilkan gambar hingga 1024x1024 tanpa masalah (kecuali memori) dan dapat menampilkannya dengan sangat cepat dan tanpa masalah kinerja.

Mengganti metode cat subclass JPanel adalah berlebihan dan membutuhkan lebih banyak pekerjaan daripada yang harus Anda lakukan.

Cara saya melakukannya adalah:

Class MapIcon implements Icon {...}

ATAU

Class MapIcon extends ImageIcon {...}

Kode yang Anda gunakan untuk menghasilkan gambar akan berada di kelas ini. Saya menggunakan BufferedImage untuk menarik ketika paintIcon () dipanggil, gunakan g.drawImvge (bufferedImage); Ini mengurangi jumlah flashing yang dilakukan saat Anda menghasilkan gambar, dan Anda dapat mengaitkannya.

Berikutnya saya memperpanjang JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Ini karena saya ingin meletakkan gambar saya pada panel gulir, yaitu menampilkan bagian dari gambar dan meminta pengguna untuk menggulirkan sesuai kebutuhan.

Jadi saya menggunakan JScrollPane untuk memegang MapLabel, yang hanya berisi MapIcon.

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

scrollPane.getViewport ().add (mapLabel);

Tapi untuk skenario Anda (hanya perlihatkan seluruh gambar setiap waktu). Anda perlu menambahkan MapLabel ke JPanel atas, dan pastikan untuk mengukur semuanya ke ukuran penuh gambar (dengan menimpa GetPreferredSize ()).

Thomas Jones-Low
sumber
5

Buat folder sumber di direktori proyek Anda, dalam hal ini saya menyebutnya Gambar.

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

sumber
5

Anda dapat menghindari menggunakan Componentperpustakaan dan ImageIOkelas milik sendiri dan SwingX :

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

Jawaban ini adalah pelengkap dari jawaban @ shawalli ...

Saya ingin referensi gambar di dalam toples saya juga, tetapi alih-alih memiliki BufferedImage, saya sederhana melakukan ini:

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

Saya bisa melihat banyak jawaban, tidak benar-benar menjawab tiga pertanyaan OP.

1) Sebuah kata pada kinerja: array byte cenderung tidak efisien kecuali jika Anda dapat menggunakan pemesanan byte pixel yang tepat yang cocok dengan display adapter Anda resolusi saat ini dan kedalaman warna.

Untuk mencapai kinerja gambar terbaik, cukup konversikan gambar Anda ke BufferedImage yang dihasilkan dengan jenis yang sesuai dengan konfigurasi grafis Anda saat ini. Lihat createCompatibleImage di https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

Gambar-gambar ini akan secara otomatis di-cache pada memori kartu tampilan setelah menggambar beberapa kali tanpa upaya pemrograman (ini adalah standar dalam Swing sejak Java 6), dan oleh karena itu gambar yang sebenarnya akan memakan waktu yang dapat diabaikan - jika Anda tidak mengubah gambar .

Mengubah gambar akan disertai dengan transfer memori tambahan antara memori utama dan memori GPU - yang lambat. Hindari "menggambar ulang" gambar menjadi BufferedImage karena itu, hindari melakukan getPixel dan setPixel dengan segala cara.

Misalnya, jika Anda mengembangkan game, alih-alih menarik semua aktor game ke BufferedImage dan kemudian ke JPanel, jauh lebih cepat untuk memuat semua aktor sebagai BufferedImages yang lebih kecil, dan menggambar mereka satu per satu dalam kode JPanel Anda di posisi yang tepat - dengan cara ini tidak ada transfer data tambahan antara memori utama dan memori GPU kecuali transfer awal gambar untuk caching.

ImageIcon akan menggunakan BufferedImage di bawah tenda - tetapi pada dasarnya mengalokasikan BufferedImage dengan mode grafis yang tepat adalah kuncinya, dan tidak ada upaya untuk melakukan ini dengan benar.

2) Cara yang biasa dilakukan adalah dengan menggambar BufferedImage dalam metode paintComponent dari JPanel yang diganti. Meskipun Java mendukung sejumlah barang tambahan yang bagus seperti rantai penyangga yang mengendalikan VolatileImages yang di-cache dalam memori GPU, tidak perlu menggunakan semua ini karena Java 6 yang melakukan pekerjaan yang cukup baik tanpa memaparkan semua detail akselerasi GPU ini.

Perhatikan bahwa akselerasi GPU mungkin tidak berfungsi untuk operasi tertentu, seperti peregangan gambar yang tembus cahaya.

3) Jangan menambahkan. Cukup cat seperti yang disebutkan di atas:

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

"Menambahkan" masuk akal jika gambar merupakan bagian dari tata letak. Jika Anda membutuhkan ini sebagai gambar latar atau latar depan yang mengisi JPanel, cukup menggambar paintComponent. Jika Anda lebih suka membuat komponen Swing umum yang dapat menampilkan gambar Anda, maka itu adalah cerita yang sama (Anda dapat menggunakan JComponent dan mengganti metode paintComponentnya) - dan kemudian menambahkan ini ke tata letak komponen GUI Anda.

4) Cara mengonversi array menjadi Bufferedimage

Mengubah byte array Anda ke PNG, kemudian memuatnya cukup banyak sumber daya. Cara yang lebih baik adalah mengonversi array byte yang ada ke BufferedImage.

Untuk itu: jangan gunakan untuk loop dan menyalin piksel. Itu sangat lambat. Sebagai gantinya:

  • mempelajari struktur byte yang disukai dari BufferedImage (saat ini aman untuk menganggap RGB atau RGBA, yaitu 4 byte per piksel)
  • pelajari garis pemindaian dan scansize yang digunakan (mis. Anda mungkin memiliki gambar lebar 142 piksel - tetapi dalam kehidupan nyata itu akan disimpan sebagai array byte lebar 256 piksel karena lebih cepat memprosesnya dan menutupi piksel yang tidak digunakan oleh perangkat keras GPU )
  • kemudian setelah Anda memiliki array array sesuai dengan prinsip-prinsip ini, metode array setRGB dari BufferedImage dapat menyalin array Anda ke BufferedImage.
Wah Bee
sumber