Làm cách nào để thêm hình ảnh vào JPanel?


341

Tôi có một JPanel mà tôi muốn thêm hình ảnh JPEG và PNG mà tôi tạo ra khi đang di chuyển.

Tất cả các ví dụ tôi đã thấy cho đến nay trong Hướng dẫn xoay , đặc biệt trong các ví dụ Xoay sử dụng ImageIcons.

Tôi đang tạo những hình ảnh này dưới dạng mảng byte và chúng thường lớn hơn biểu tượng phổ biến mà chúng sử dụng trong các ví dụ, ở 640x480.

  1. Có bất kỳ vấn đề (hiệu suất hoặc khác) nào trong việc sử dụng lớp ImageIcon để hiển thị hình ảnh có kích thước trong JPanel không?
  2. Gì vậy thường cách để làm việc đó?
  3. Làm cách nào để thêm hình ảnh vào JPanel mà không cần sử dụng lớp ImageIcon?

Chỉnh sửa : Kiểm tra kỹ hơn các hướng dẫn và API cho thấy rằng bạn không thể thêm ImageIcon trực tiếp vào JPanel. Thay vào đó, họ đạt được hiệu ứng tương tự bằng cách đặt hình ảnh làm biểu tượng của JLabel. Điều này không cảm thấy đúng ...


1
Tùy thuộc vào cách bạn tạo các mảng byte, sử dụng hiệu quả MemoryImageSourcehơn là chuyển đổi chúng sang định dạng JPEG hoặc PNG và sau đó đọc với ImageIOhầu hết các câu trả lời gợi ý. Bạn có thể nhận được Imagetừ một MemoryImageSourcecấu trúc với dữ liệu hình ảnh của mình bằng cách sử dụng createImagevà hiển thị như được đề xuất trong một trong các câu trả lời.
Alden

Kiểm tra câu trả lời của tôi stackoverflow.com/questions/43861991/ từ
tommybee

Câu trả lời:


252

Đây là cách tôi thực hiện (có thêm một chút thông tin về cách tải hình ảnh):

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 cho việc triển khai paintComponent không hợp lệ (@Dogmatixed rất có thể đó là lý do tại sao bạn gặp phải các vấn đề vẽ lại đó) - nó phải đảm bảo che phủ toàn bộ khu vực của nó nếu nó báo cáo là mờ (là mặc định), dễ dàng đạt được nhất bằng cách gọi super.paintComponent
kleopatra

5
@kleopatra, Cảm ơn, tôi đã không nhận ra rằng ... theo javadoc: "Hơn nữa, nếu bạn không mời người thực hiện siêu năng lực, bạn phải tôn trọng tài sản mờ đục, đó là nếu thành phần này mờ đục, bạn phải điền hoàn toàn vào nền trong một màu không mờ. Nếu bạn không tôn vinh tài sản mờ đục, bạn có thể sẽ thấy các tạo tác trực quan. " Tôi sẽ cập nhật câu trả lời ngay bây giờ.
Brendan

8
Vui lòng luôn tôn trọng các Principle of Encapsulationphương thức trong khi ghi đè của Super Class, Công cụ xác định truy cập của paintComponent(...)phương thức protectedvà không public:-)
nIcE cOw

1
Điều này là không tốt, nó không làm bất cứ điều gì về kích thước bảng điều khiển cho hình ảnh. Nhiều mã sẽ phải được thêm vào sau để giải quyết vấn đề này. Đơn giản hơn nhiều để sử dụng câu trả lời JLabel.
Keilly

2
@Jonathan: Tôi thực sự xin lỗi, liên quan đến một số nguồn cho Principle of Encapsulation. Chỉ cần nhớ trong khi lập trình, những gì cần được ẩn khỏi phần còn lại của mã, cần được duy trì theo cách đó, thay vì hiển thị mọi thứ trong mã. Có vẻ như đó là một điều như vậy đi kèm với kinh nghiệm, khi một người học hỏi mỗi ngày. Bất kỳ cuốn sách nào cũng có thể đưa ra một ý tưởng cơ bản về đóng gói là gì, nhưng đó là cách chúng tôi thực hiện mã của chúng tôi quyết định mức độ chúng tôi tuân thủ khái niệm này.
ncc cOw

520

Nếu bạn đang sử dụng JPanels, thì có lẽ đang làm việc với Swing. Thử cái này:

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

Hình ảnh bây giờ là một thành phần swing. Nó trở thành đối tượng của điều kiện bố trí như bất kỳ thành phần nào khác.


16
Làm thế nào để chia tỷ lệ hình ảnh theo kích thước của JLabel?
mã hóa_idiot

1
Mã đẹp! Tôi không có nhiều kinh nghiệm với Swing nhưng tôi không thể làm cho nó hoạt động được. Có ai đã thử nó trong jdk 1.6.0_16 chưa?
ATorras

3
@ATorras Tôi biết bạn đã hỏi điều này một thời gian trước nhưng nếu bất kỳ người mới nào khác có vấn đề của tôi, hãy nhớ picLabel.setBound ();
kyle

Tôi có phải tạo đối tượng JLabel mới mỗi lần khi ảnh được tải lại không?
0x6B6F77616C74

2
@coding_idiot JLabel mới (ImageIcon mới (backgroundImage.getScaledInstance (chiều rộng, chiều cao, Image.SCALE_FAST));
Davide

37

Cách của Fred Haslam hoạt động tốt. Mặc dù vậy, tôi đã gặp rắc rối với filepath, vì tôi muốn tham chiếu một hình ảnh trong bình của mình. Để làm điều này, tôi đã sử dụng:

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

Vì tôi chỉ có một số lượng hữu hạn (khoảng 10) hình ảnh mà tôi cần tải bằng phương pháp này, nên nó hoạt động khá tốt. Nó nhận được tập tin mà không cần phải có filepath tương đối chính xác.


1
Tôi đã thay thế dòng đầu tiên BufferedImage previewImage = ImageIO.read(new URL(imageURL));để có được hình ảnh của tôi từ một máy chủ.
intcreator

Để tham khảo hình ảnh từ jar, hãy thử stackoverflow.com/a/44844922/3211801
Nandha

33

Tôi nghĩ rằng không cần phải phân lớp của bất cứ điều gì. Chỉ cần sử dụng một nhãn hiệu. Bạn có thể thiết lập một hình ảnh thành một nhãn hiệu. Vì vậy, thay đổi kích thước của nhãn hiệu sau đó điền vào hình ảnh. Ổn mà. Đây là cách tôi làm.


20

Bạn có thể tránh hoàn toàn cuộn lớp con Thành phần của riêng mình bằng cách sử dụng lớp JXImagePanel từ các thư viện SwingX miễn phí .

Tải xuống


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

8

Bạn có thể phân lớp JPanel - đây là một trích xuất từ ​​ImagePanel của tôi, nó đặt một hình ảnh ở bất kỳ một trong 5 vị trí, trên / trái, trên / phải, giữa / giữa, dưới / trái hoặc dưới / phải:

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. Không nên có bất kỳ vấn đề nào (ngoài bất kỳ vấn đề chung nào bạn có thể gặp phải với hình ảnh rất lớn).
  2. Nếu bạn đang nói về việc thêm nhiều hình ảnh vào một bảng điều khiển, tôi sẽ sử dụng ImageIcons. Đối với một hình ảnh duy nhất, tôi sẽ nghĩ về việc tạo một lớp con tùy chỉnh JPanelvà ghi đè paintComponentphương thức của nó để vẽ hình ảnh.
  3. (xem 2)

6

JPanelhầu như luôn luôn là lớp sai cho lớp con. Tại sao bạn không phân lớp JComponent?

Có một vấn đề nhỏ ImageIcontrong đó là các khối xây dựng đọc hình ảnh. Không thực sự là một vấn đề khi tải từ ứng dụng, nhưng có lẽ nếu bạn có khả năng đọc qua kết nối mạng. Có rất nhiều ví dụ AWT-kỷ nguyên của việc sử dụng MediaTracker, ImageObservervà bạn bè, ngay cả trong bản demo JDK.


6

Tôi đang làm một cái gì đó rất giống trong một dự án tư nhân mà tôi đang làm. Cho đến nay tôi đã tạo ra hình ảnh lên tới 1024x1024 mà không gặp vấn đề gì (ngoại trừ bộ nhớ) và có thể hiển thị chúng rất nhanh và không có bất kỳ vấn đề nào về hiệu suất.

Ghi đè phương thức sơn của lớp con JPanel là quá mức cần thiết và đòi hỏi nhiều công việc hơn bạn cần làm.

Cách tôi làm là:

Class MapIcon implements Icon {...}

HOẶC LÀ

Class MapIcon extends ImageIcon {...}

Mã bạn sử dụng để tạo hình ảnh sẽ nằm trong lớp này. Tôi sử dụng BufferedImage để vẽ lên sau đó khi paintIcon () được gọi, sử dụng g.drawImvge (bufferedImage); Điều này làm giảm lượng flash được thực hiện trong khi bạn tạo hình ảnh của mình và bạn có thể xâu chuỗi nó.

Tiếp theo tôi mở rộng JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Điều này là do tôi muốn đặt hình ảnh của mình trên một khung cuộn, tức là hiển thị một phần của hình ảnh và để người dùng cuộn xung quanh khi cần thiết.

Vì vậy, sau đó tôi sử dụng một JScrollPane để giữ MapLabel, chỉ chứa MapIcon.

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

scrollPane.getViewport ().add (mapLabel);

Nhưng đối với kịch bản của bạn (chỉ hiển thị toàn bộ hình ảnh mỗi lần). Bạn cần thêm MapLabel vào JPanel hàng đầu và đảm bảo kích thước tất cả chúng thành kích thước đầy đủ của hình ảnh (bằng cách ghi đè GetPreferredSize ()).


5

Tạo một thư mục nguồn trong thư mục dự án của bạn, trong trường hợp này tôi gọi nó là Hình ảnh.

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

Bạn có thể tránh sử dụng Componentthư viện và ImageIOlớp của thư viện SwingX :

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

4

Câu trả lời này là phần bổ sung cho câu trả lời của @ Shawalli ...

Tôi cũng muốn tham chiếu một hình ảnh trong bình của mình, nhưng thay vì có BufferedImage, tôi đơn giản làm điều này:

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

1

Tôi có thể thấy nhiều câu trả lời, không thực sự giải quyết ba câu hỏi của OP.

1) Một từ về hiệu suất: mảng byte có thể không hiệu quả trừ khi bạn có thể sử dụng thứ tự byte pixel chính xác phù hợp với độ phân giải hiện tại của bộ điều hợp hiển thị và độ sâu màu.

Để đạt được hiệu suất vẽ tốt nhất, chỉ cần chuyển đổi hình ảnh của bạn thành BufferedImage được tạo với loại tương ứng với cấu hình đồ họa hiện tại của bạn. Xem createdCompiverseImage tại https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

Những hình ảnh này sẽ được lưu tự động vào bộ nhớ của thẻ hiển thị sau khi vẽ một vài lần mà không cần bất kỳ nỗ lực lập trình nào (đây là tiêu chuẩn trong Swing kể từ Java 6), và do đó bản vẽ thực tế sẽ mất thời gian không đáng kể - nếu bạn không thay đổi hình ảnh .

Việc thay đổi hình ảnh sẽ đi kèm với việc chuyển bộ nhớ bổ sung giữa bộ nhớ chính và bộ nhớ GPU - tốc độ chậm. Do đó, tránh "vẽ lại" hình ảnh thành BufferedImage, tránh thực hiện getPixel và setPixel bằng mọi cách.

Ví dụ: nếu bạn đang phát triển một trò chơi, thay vì vẽ tất cả các diễn viên trò chơi vào BufferedImage và sau đó đến JPanel, thì việc tải tất cả các diễn viên dưới dạng BufferedImages nhỏ hơn và rút từng người một trong mã JPanel của bạn vị trí thích hợp của chúng - theo cách này không có truyền dữ liệu bổ sung giữa bộ nhớ chính và bộ nhớ GPU ngoại trừ việc chuyển hình ảnh ban đầu để lưu vào bộ đệm.

ImageIcon sẽ sử dụng BufferedImage dưới mui xe - nhưng về cơ bản phân bổ BufferedImage với chế độ đồ họa phù hợp là chìa khóa và không có nỗ lực nào để thực hiện đúng.

2) Cách thông thường để thực hiện việc này là vẽ BufferedImage theo phương thức PaintComponent được ghi đè của JPanel. Mặc dù Java hỗ trợ một số lượng tốt các tính năng bổ sung khác như chuỗi bộ đệm kiểm soát VoliverseImages được lưu trong bộ nhớ GPU, nhưng không cần sử dụng bất kỳ tính năng nào trong số này kể từ Java 6, hoạt động khá tốt mà không cần tiết lộ tất cả các chi tiết về tăng tốc GPU này.

Lưu ý rằng khả năng tăng tốc GPU có thể không hoạt động đối với các hoạt động nhất định, chẳng hạn như kéo dài hình ảnh mờ.

3) Không thêm. Chỉ cần vẽ nó như đã đề cập ở trên:

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

"Thêm" có ý nghĩa nếu hình ảnh là một phần của bố cục. Nếu bạn cần điều này làm hình nền hoặc hình nền trước lấp đầy JPanel, chỉ cần vẽ trong paintComponent. Nếu bạn thích pha một thành phần Swing chung có thể hiển thị hình ảnh của bạn, thì đó là câu chuyện tương tự (bạn có thể sử dụng JComponent và ghi đè phương thức paintComponent của nó) - sau đó thêm vào bố cục các thành phần GUI của bạn.

4) Cách chuyển đổi mảng thành Bufferedimage

Chuyển đổi mảng byte của bạn thành PNG, sau đó tải nó khá tốn tài nguyên. Một cách tốt hơn là chuyển đổi mảng byte hiện tại của bạn thành BufferedImage.

Cho rằng: không sử dụng cho các vòng lặp và sao chép pixel. Đó là rất rất chậm. Thay thế:

  • tìm hiểu cấu trúc byte ưa thích của BufferedImage (ngày nay có thể giả định RGB hoặc RGBA là 4 byte cho mỗi pixel)
  • tìm hiểu đường quét và quét kích thước đang sử dụng (ví dụ: bạn có thể có hình ảnh rộng 142 pixel - nhưng trong thực tế, nó sẽ được lưu trữ dưới dạng một mảng byte rộng 256 pixel do xử lý nhanh hơn và che dấu các pix không được sử dụng bởi phần cứng GPU )
  • sau đó khi bạn có một bản dựng mảng theo các nguyên tắc này, phương thức mảng setRGB của BufferedImage có thể sao chép mảng của bạn vào BufferedImage.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.