วิธีเพิ่มรูปภาพไปยัง JPanel


341

ฉันมีJPanelที่ฉันต้องการเพิ่มภาพ JPEG และ PNG ที่ฉันสร้างได้ทันที

ตัวอย่างทั้งหมดที่ฉันเคยเห็นในบทแนะนำของ Swingโดยเฉพาะในตัวอย่างของ Swingใช้ImageIcons

ฉันกำลังสร้างภาพเหล่านี้เป็นอาร์เรย์แบบไบต์และโดยปกติจะมีขนาดใหญ่กว่าไอคอนทั่วไปที่ใช้ในตัวอย่างที่ 640x480

  1. มีปัญหา (ประสิทธิภาพหรืออื่น ๆ ) ในการใช้คลาส ImageIcon เพื่อแสดงรูปภาพที่มีขนาดใน JPanel หรือไม่?
  2. อะไรคือปกติวิธีการทำมันได้หรือไม่
  3. วิธีเพิ่มรูปภาพไปยัง JPanel โดยไม่ใช้คลาส ImageIcon

แก้ไข : การตรวจสอบบทเรียนเพิ่มเติมอย่างระมัดระวังและ API แสดงว่าคุณไม่สามารถเพิ่ม ImageIcon ลงใน JPanel ได้โดยตรง แต่จะให้เอฟเฟกต์เดียวกันโดยตั้งค่าภาพเป็นไอคอนของ JLabel แค่รู้สึกไม่ถูกต้อง ...


1
ขึ้นอยู่กับวิธีที่คุณสร้างอาร์เรย์ไบต์มันอาจมีประสิทธิภาพมากกว่าการใช้การMemoryImageSourceแปลงให้เป็นรูปแบบ JPEG หรือ PNG แล้วอ่านด้วยImageIOคำตอบที่แนะนำมากที่สุด คุณสามารถได้รับImageจากการMemoryImageSourceสร้างด้วยข้อมูลภาพของคุณโดยใช้createImageและแสดงตามที่แนะนำในหนึ่งในคำตอบ
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 ส่วนใหญ่อาจเป็นเหตุผลว่าทำไมคุณกำลังมีปัญหา redrawing เหล่านั้น) - มันต้องรับประกันเพื่อให้ครอบคลุมพื้นที่ที่สมบูรณ์ถ้ามันเป็นรายงานทึบแสง (ซึ่งเป็นค่าเริ่มต้น) ที่ง่ายที่สุดที่ประสบความสำเร็จโดยการเรียก super.paintComponent
kleopatra

5
@kleopatra ขอบคุณฉันไม่ได้ตระหนักว่า ... ตาม javadoc: "นอกจากนี้ถ้าคุณไม่เรียกใช้ super ของการดำเนินการคุณต้องให้เกียรติกับคุณสมบัติทึบแสงนั่นคือถ้าองค์ประกอบนี้เป็นทึบคุณต้องกรอกอย่างสมบูรณ์ พื้นหลังในสีที่ไม่ทึบถ้าคุณไม่เคารพคุณสมบัติทึบแสงคุณอาจเห็นสิ่งประดิษฐ์ที่มองเห็น " ฉันจะอัปเดตคำตอบทันที
Brendan Cashman

8
โปรดเคารพวิธีการPrinciple of Encapsulationที่เหนือกว่าของ Super Class เสมอตัวระบุการเข้าถึงของpaintComponent(...)วิธีนี้protectedไม่ใช่public:-)
nIcE cOw

1
มันไม่ดีมันไม่ได้ทำอะไรเกี่ยวกับการปรับขนาดพาเนลให้กับรูปภาพ รหัสเพิ่มเติมจะต้องถูกเพิ่มในภายหลังจะจัดการกับสิ่งนี้ ง่ายกว่ามากในการใช้คำตอบ JLabel
Keilly

2
@ Jonathan: Principle of Encapsulationฉันขอโทษจริงๆเกี่ยวกับแหล่งที่มาของบางอย่างสำหรับ เพียงจำไว้ว่าในขณะที่การเขียนโปรแกรมสิ่งที่ควรซ่อนไว้จากส่วนที่เหลือของรหัสจะต้องมีการบำรุงรักษาด้วยวิธีนั้นแทนที่จะมองเห็นทุกอย่างในรหัส ดูเหมือนว่ามันเป็นสิ่งหนึ่งที่มาพร้อมกับประสบการณ์เช่นเดียวกับที่เรียนรู้ในแต่ละวัน หนังสือทุกเล่มสามารถให้แนวคิดพื้นฐานว่าการห่อหุ้มคืออะไร แต่เป็นวิธีที่เราใช้รหัสของเราที่ตัดสินว่าเรายึดถือแนวคิดมากน้อยแค่ไหน
nIcE cOw

520

หากคุณใช้ JPanels แสดงว่าอาจใช้งานได้กับ Swing ลองสิ่งนี้:

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

ภาพตอนนี้เป็นองค์ประกอบการแกว่ง มันจะอยู่ภายใต้เงื่อนไขการจัดวางเช่นส่วนประกอบอื่น ๆ


16
จะขยายภาพตามขนาดของ JLabel ได้อย่างไร?
coding_idiot

1
รหัสที่ดี! ฉันไม่ค่อยมีประสบการณ์กับ Swing มากนัก แต่ก็ใช้งานไม่ได้ มีใครลองใช้งานใน jdk 1.6.0_16 ไหม
ATorras

3
@Atras ฉันรู้ว่าคุณถามคำถามนี้ไปซักพัก แต่ถ้ามือใหม่คนอื่นมีปัญหาของฉันอย่าลืม picLabel.setBounds ();
kyle

ฉันต้องสร้างวัตถุ JLabel ใหม่ทุกครั้งเมื่อโหลดภาพถ่ายใหม่หรือไม่
0x6B6F77616C74

2
@coding_idiot ใหม่ JLabel (ImageIcon ใหม่ (backgroundImage.getScaledInstance (ความกว้างความสูง Image.SCALE_FAST)));
Davide

37

วิธีของ Fred Haslam ทำงานได้ดี ฉันมีปัญหากับพา ธ ไฟล์เนื่องจากฉันต้องการอ้างอิงรูปภาพภายในโถของฉัน เมื่อต้องการทำสิ่งนี้ฉันใช้:

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));เพื่อรับภาพจากเซิร์ฟเวอร์
บุกรุกใน

สำหรับการอ้างอิงรูปภาพจาก jar ลองใช้stackoverflow.com/a/44844922/3211801
Nandha

33

ฉันคิดว่าไม่จำเป็นต้อง subclass ของอะไร เพียงใช้ Jlabel คุณสามารถตั้งค่ารูปภาพเป็น Jlabel ดังนั้นปรับขนาด Jlabel แล้วเติมด้วยภาพ ไม่เป็นไร. นี่คือวิธีที่ฉันทำ



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

8

คุณสามารถ subclass 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. หากคุณกำลังพูดถึงการเพิ่มหลายภาพลงในแผงเดียวฉันจะใช้ImageIcons สำหรับภาพเดียวฉันจะคิดเกี่ยวกับการสร้างคลาสย่อยที่กำหนดเองJPanelและแทนที่paintComponentวิธีการในการวาดภาพ
  3. (ดู 2)

6

JPanelเป็นคลาสที่ไม่ถูกต้องในคลาสย่อยเสมอ ทำไมคุณไม่ subclass JComponent?

มีปัญหาเล็กน้อยImageIconในการที่ผู้สร้างบล็อกการอ่านภาพ ไม่ใช่ปัญหาเมื่อทำการโหลดจาก jar ของแอปพลิเคชั่น แต่บางทีถ้าคุณกำลังอ่านผ่านการเชื่อมต่อเครือข่าย มีมากมายของตัวอย่าง AWT ยุคของการใช้เป็นMediaTracker, ImageObserverและเพื่อน ๆ แม้ในการสาธิต JDK


6

ฉันกำลังทำสิ่งที่คล้ายกันมากในโครงการส่วนตัวที่ฉันกำลังทำอยู่ ป่านนี้ฉันได้สร้างภาพถึง 1024x1024 โดยไม่มีปัญหาใด ๆ (ยกเว้นหน่วยความจำ) และสามารถแสดงได้อย่างรวดเร็วและไม่มีปัญหาประสิทธิภาพใด ๆ

การเอาชนะวิธีการทาสีของคลาสย่อยของ JPanel นั้นเกินขีด จำกัด และต้องใช้งานมากกว่าที่คุณต้องทำ

วิธีที่ฉันทำคือ:

Class MapIcon implements Icon {...}

หรือ

Class MapIcon extends ImageIcon {...}

รหัสที่คุณใช้ในการสร้างภาพจะอยู่ในชั้นนี้ ฉันใช้ BufferedImage เพื่อวาดลงบนเมื่อเรียก paintIcon () ให้ใช้ g.drawImvge (bufferedImage) สิ่งนี้จะช่วยลดปริมาณการกระพริบที่เกิดขึ้นในขณะที่คุณสร้างภาพและคุณสามารถร้อยด้ายได้

ต่อไปฉันขยาย JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

นี่เป็นเพราะฉันต้องการวางภาพของฉันลงในบานหน้าต่างเลื่อน Ie แสดงส่วนของภาพและให้ผู้ใช้เลื่อนไปมาตามต้องการ

ดังนั้นฉันจึงใช้ JScrollPane เพื่อเก็บ MapLabel ซึ่งมีเพียง MapIcon

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

scrollPane.getViewport ().add (mapLabel);

แต่สำหรับสถานการณ์ของคุณ (เพียงแสดงภาพทั้งหมดทุกครั้ง) คุณต้องเพิ่ม MapLabel ลงใน JPanel ด้านบนและตรวจสอบให้แน่ใจว่าได้ปรับขนาดให้เต็มขนาดของรูปภาพ (โดยการแทนที่ GetPreferredSize ())


5

สร้างโฟลเดอร์ซอร์สในไดเรกทอรีโครงการของคุณในกรณีนี้ฉันเรียกมันว่ารูปภาพ

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ไลบรารีและImageIOคลาสSwingX ของตนเอง:

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

4

คำตอบนี้เป็นส่วนเติมเต็มให้กับคำตอบของ @ shawalli ...

ฉันต้องการอ้างอิงรูปภาพภายในไหมของฉันด้วย แต่แทนที่จะมี BufferedImage ฉันก็ทำสิ่งนี้ได้ง่าย:

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

1

ฉันเห็นคำตอบมากมายไม่ตอบคำถามทั้งสามข้อของ OP

1)คำเกี่ยวกับประสิทธิภาพ: อาร์เรย์ไบต์ไม่มีประสิทธิภาพเว้นแต่คุณสามารถใช้พิกเซลไบต์ที่แน่นอนซึ่งตรงกับอะแดปเตอร์จอแสดงผลความละเอียดปัจจุบันและความลึกของสี

เพื่อให้ได้ประสิทธิภาพการวาดที่ดีที่สุดเพียงแปลงภาพของคุณเป็น BufferedImage ซึ่งสร้างขึ้นด้วยประเภทที่สอดคล้องกับการกำหนดค่ากราฟิกปัจจุบันของคุณ ดู createCompatibleImage ที่https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

ภาพเหล่านี้จะถูกเก็บไว้โดยอัตโนมัติในหน่วยความจำการ์ดแสดงผลหลังจากการวาดภาพไม่กี่ครั้งไม่มีความพยายามเขียนโปรแกรมใด ๆ (นี้เป็นมาตรฐานในการสวิงตั้งแต่ Java 6) และดังนั้นจึงวาดภาพที่เกิดขึ้นจริงจะใช้จำนวนเล็กน้อยของเวลา - ถ้าคุณไม่ได้เปลี่ยนภาพ .

การเปลี่ยนแปลงภาพจะมาพร้อมกับการโอนหน่วยความจำเพิ่มเติมระหว่างหน่วยความจำหลักและหน่วยความจำ GPU ซึ่งช้า หลีกเลี่ยงการ "redrawing" รูปภาพลงใน BufferedImage ดังนั้นหลีกเลี่ยงการทำ getPixel และ setPixel เลย

ตัวอย่างเช่นหากคุณกำลังพัฒนาเกมแทนที่จะวาดนักแสดงเกมทั้งหมดไปที่ BufferedImage และจากนั้นไปที่ JPanel จะเร็วกว่ามากที่จะโหลดนักแสดงทั้งหมดเป็น BufferedImages และวาดทีละตัวในรหัส JPanel ของคุณที่ ตำแหน่งที่เหมาะสม - วิธีนี้ไม่มีการถ่ายโอนข้อมูลเพิ่มเติมระหว่างหน่วยความจำหลักและหน่วยความจำ GPU ยกเว้นการถ่ายโอนภาพเริ่มต้นสำหรับการแคช

ImageIcon จะใช้ BufferedImage ใต้ฝากระโปรง - แต่โดยทั่วไปการจัดสรร BufferedImage ด้วยโหมดกราฟิกที่เหมาะสมคือกุญแจและไม่มีความพยายามที่จะทำสิ่งนี้

2)วิธีปกติในการทำเช่นนี้คือการวาด BufferedImage ในวิธีการเขียนทับสีแทนส่วนประกอบของ JPanel แม้ว่าจาวาจะรองรับสินค้าจำนวนมากเช่นโซ่บัฟเฟอร์ที่ควบคุม VolatileImages ที่แคชไว้ในหน่วยความจำ GPU แต่ก็ไม่จำเป็นต้องใช้สิ่งเหล่านี้ตั้งแต่ Java 6 ซึ่งทำงานได้ดีพอสมควรโดยไม่เปิดเผยรายละเอียดทั้งหมดของการเร่งความเร็วของ GPU

โปรดทราบว่าการเร่งความเร็ว GPU อาจไม่ทำงานสำหรับการทำงานบางอย่างเช่นการยืดภาพโปร่งแสง

3)อย่าเพิ่ม เพียงทาสีตามที่กล่าวไว้ข้างต้น:

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

"การเพิ่ม" เข้าท่าถ้ารูปภาพเป็นส่วนหนึ่งของเลย์เอาต์ หากคุณต้องการสิ่งนี้เป็นภาพพื้นหลังหรือภาพเบื้องหน้าที่เติม JPanel เพียงแค่วาดด้วยสีส่วนประกอบ ถ้าคุณชอบการต้มส่วนประกอบ Swing ทั่วไปซึ่งสามารถแสดงภาพของคุณได้มันเป็นเรื่องเดียวกัน (คุณอาจใช้ JComponent และแทนที่วิธีการ paintComponent ของมัน) - แล้วเพิ่มสิ่งนี้ลงในเค้าโครงของส่วนประกอบ GUI

4)วิธีแปลง Array ให้เป็น Bufferedimage

การแปลงอาร์เรย์ไบต์ของคุณเป็น PNG จากนั้นโหลดค่อนข้างมากทรัพยากร วิธีที่ดีกว่าคือการแปลงอาร์เรย์ไบต์ที่มีอยู่ของคุณเป็น BufferedImage

สำหรับที่: อย่าใช้สำหรับลูปและคัดลอกพิกเซล มันช้ามาก แทน:

  • เรียนรู้โครงสร้างไบต์ที่ต้องการของ BufferedImage (ทุกวันนี้มันปลอดภัยที่จะสมมติว่า RGB หรือ RGBA ซึ่งเป็น 4 ไบต์ต่อพิกเซล)
  • เรียนรู้ scanline และ scanize ในการใช้งาน (เช่นคุณอาจมีภาพกว้าง 142 พิกเซล - แต่ในชีวิตจริงที่จะถูกเก็บไว้เป็นอาร์เรย์ไบต์กว้าง 256 พิกเซลเนื่องจากมันเร็วกว่าในการประมวลผลและปิดบังพิกเซลที่ไม่ได้ใช้โดยฮาร์ดแวร์ GPU )
  • จากนั้นเมื่อคุณสร้างอาเรย์ตามหลักการเหล่านี้เมธอดอาร์เรย์ setRGB ของ BufferedImage สามารถคัดลอกอาเรย์ของคุณไปยัง BufferedImage
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.