2009-11-04 39 views
8

Trong hai ngày qua, tôi đã cố gắng hiểu cách Java xử lý đồ họa, nhưng đã thất bại thảm hại ở mức đó. Vấn đề chính của tôi là hiểu chính xác cách thức và khi nào thì paint() (hoặc mới hơn paintComponent()) là/nên được gọi.Tại sao sơn()/paintComponent() không bao giờ được gọi?

Trong mã sau tôi đã thực hiện để xem khi nào mọi thứ được tạo, paintComponent() không bao giờ được gọi, trừ khi tôi tự thêm cuộc gọi vào đó hoặc gọi tới JFrame.paintAll()/JFrame.paintComponents().

Tôi đổi tên phương thức paint() thành paintComponent() với hy vọng rằng sẽ khắc phục được sự cố của tôi về nó chưa bao giờ được gọi (ngay cả ở repaint()), nhưng không may mắn.

package jpanelpaint; 

import java.awt.*; 
import javax.imageio.*; 
import javax.swing.*; 
import java.io.*; 
import java.util.ArrayList; 

public class ImageLoadTest extends JComponent { 
ArrayList<Image> list; 

public ImageLoadTest() { 
    list = new ArrayList<Image>(); 

    try { //create the images (a deck of 4 cards) 
    for(String name : createImageFileNames(4)){ 
    System.err.println(name); 
    list.add(ImageIO.read(new File(name))); 
    } 
    } catch (IOException e) { } 
} 

    protected void paintComponent(Graphics g) { 
    int yOffset=0; 
    System.err.println("ImageLoadTest.paintComponent()"); 
    for(Image img : list) { 
     g.drawImage(img, 0, yOffset, null); 
     yOffset+=20; 
    } 
    } 

public static void main(String args[]) throws InterruptedException { 
    JFrame frame = new JFrame("Empty JFrame"); 
    frame.setSize(new Dimension(1000, 500)); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

    frame.setVisible(true); 

    Thread.sleep(1000); 
    frame.setTitle("Loading images"); 
    ImageLoadTest ilt = new ImageLoadTest(); 
    frame.add(ilt); 
    //update the screen 
    //DOESN'T WORK. only works if I call frame.paintAll(frame.getGraphics()) 
    ilt.repaint(); 
    frame.repaint(); 

    Thread.sleep(1000); 
    frame.setTitle("Setting background"); 
    ilt.setBackground(Color.BLACK); 
    //update the screen - DOESN'T WORK even if I call paintAll .. 
    ilt.repaint(); 
    frame.repaint(); 

      //have to call one of these to get anything to display 
// ilt.paintComponent(frame.getGraphics()); //works 
    frame.paintComponents(frame.getGraphics()); //works 
} 

//PRIVATE HELPER FUNCTIONS 

private String[] createImageFileNames(int count){ 
    String[] fileNames = new String[count]; 
    for(int i=0; i < count; i++) 
    fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp"; 
    return fileNames; 
} 
} 

Trả lời

3

Đây là những vấn đề chính với mã gốc gây ra nó không làm việc:

  1. không c alling validate() sau khi thao tác add()
  2. không đặt kích thước ưa thích của thành phần.
  3. không gọi super.paintComponent() khi ghi đè nó (điều này thực hiện lệnh gọi setBackground() không hoạt động)
  4. Tôi cần kế thừa từ JPanel để được sơn. Cả Component và JComponent đều không đủ để gọi setBackground() hoạt động, ngay cả khi sửa chữa điểm 3.

Thực sự không quan trọng nếu gọi phương thức paintComponent hoặc paint, cả hai dường như hoạt động miễn là tôi nhớ gọi siêu khởi tạo ngay từ đầu.

Thông tin này được lắp ráp từ những thứ @jitter, @tackline và @camickr đã viết, kudo lớn!

P.S. Không có ý kiến ​​nếu trả lời câu hỏi của bạn được coi là xấu, nhưng vì thông tin tôi cần được tập hợp từ nhiều câu trả lời, tôi nghĩ cách tốt nhất là upmodding các câu trả lời khác và viết một tổng hợp như thế này.

4

Một vấn đề lớn ở đây là bạn không cập nhật thành phần xoay của mình trên Event Dispatch Thread (EDT). Hãy thử gói tất cả mã trong phương thức chính của bạn theo cách sau:

SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      // swing code here...    
     } 
    }); 

Ngoài ra: thêm ImageLoadTest vào khung trước khi đặt khung hiển thị. Điều này được dựa trên một đọc lướt qua nhanh chóng của mã - Tôi sẽ đọc nó hơn nữa và xem những gì khác tôi có thể tìm thấy.

EDIT:

Làm theo lời khuyên ban đầu của tôi ở trên, và đơn giản hóa phương pháp chính của bạn để trông giống như sau và paintComponent của bạn() sẽ được gọi là:

public static void main(String args[]) throws InterruptedException { 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      JFrame frame = new JFrame("Empty JFrame"); 
      frame.setSize(new Dimension(1000, 500)); 
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
      PaintComponentTest ilt = new PaintComponentTest(); 
      frame.add(ilt); 
      frame.setVisible(true); 
      ilt.setBackground(Color.BLACK); 
     } 
    }); 
} 

Ngoài ra tôi sẽ đọc lên trên sử dụng bộ tính giờ để thực hiện hoạt ảnh, cũng như điều phối sự kiện Swing chung và cách/khi ghi đè các phương thức vẽ khác nhau.

http://java.sun.com/products/jfc/tsc/articles/painting/

http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/dispatch.html

+0

Cảm ơn, sẽ thử điều đó. Nhưng tôi có thực sự sử dụng setVisible ở cuối để nó hoạt động không? Điểm có gọi nó sớm là để xem làm thế nào tôi nên xử lý thêm các yếu tố đồ họa bổ sung tại một thời gian sau đó. Nhưng toàn bộ điều Runnable là mới đối với tôi; Tôi chưa thấy điều đó trong bất kỳ hướng dẫn nào tôi đã thấy (như this one). – oligofren

+0

Sử dụng ['java.awt.EventQueue.']' invokeLater' làm cho nó hoạt động đối với tôi. –

+1

(Mặc dù đó có thể là chỉ bao gồm một lỗi. Bạn nên 'revalidate' sau khi' add' như được mô tả trong tài liệu API cho 'add'.) –

2

tôi khuyên bạn nên đọc các cặp vợ chồng đầu tiên của chương của "Filthy giàu khách hàng". Tôi đã sử dụng Swing trong nhiều năm, nhưng chỉ sau khi đọc cuốn sách này cuối cùng tôi đã hoàn toàn hiểu chính xác cơ chế vẽ của Java hoạt động như thế nào.

+0

Đó là một mẹo hay. Tôi thực sự đọc nó sau này. Cuốn sách tuyệt vời, mặc dù GUI hoạt động có vẻ là một điều của những năm cuối chín mươi trong thế giới web này. – oligofren

11

Một trong những lý do mà paintComponent() không được gọi trong mã ban đầu là vì thành phần có "kích thước bằng không" và RepaintManger đủ thông minh để không thử và vẽ thứ gì đó không có kích thước.

Lý do sắp xếp lại mã hoạt động là vì khi bạn thêm thành phần vào khung và sau đó làm cho khung hiển thị, trình quản lý bố cục được gọi để bố cục thành phần. Theo mặc định, khung sử dụng BorderLayout và theo mặc định, một thành phần được thêm vào trung tâm của BorderLayout, điều này sẽ cho tất cả không gian có sẵn cho thành phần để nó được vẽ.

Tuy nhiên, bạn thay đổi trình quản lý bố cục của ngăn nội dung thành FlowLayout, bạn vẫn gặp sự cố vì FlowLayout tôn trọng kích thước ưa thích của thành phần bằng 0.

Vì vậy, những gì bạn thực sự cần làm là chỉ định kích thước ưa thích cho bạn thành phần của bạn để người quản lý bố cục có thể thực hiện công việc của họ.

4

Để tạo Tom Hawtin - tackline vui vẻ.Tôi viết lại một lần nữa

Có một vài điều tôi đã thay đổi (đánh dấu vào dòng với //new bình luận)

Viết lại nó hoàn toàn

  • Chia thành một tập tin thành phần mới sạch (ImageLoadTest.java) và một để kiểm tra nó (Tester.java)

Cải tiến trên mã áp phích gốc

  • gọi constructor của cha mẹ trong ImageLoadTest constructor (super())
  • cung cấp constructor thứ hai để thiết lập danh sách các hình ảnh mà thành phần sẽ hiển thị
  • QUAN TRỌNG: gọi để setPreferredSize() của thành phần trong constructor. Nếu kích thước không được thiết lập swing tất nhiên sẽ không sơn thành phần của bạn. kích thước ưa thích dựa trên giá thầu CPC chiều rộng của tất cả các hình ảnh và trên tổng của tất cả chiều cao hình ảnh
  • cuộc gọi đến super.paintComponent(g) trong overriden paintComponent()
  • thay đổi paintComponent để tự động căn yOffset trên chiều cao của hình ảnh được vẽ khởi

  • GUI thực hiện trên EDT

  • như mã ban đầu dựa trên việc sử dụng sleep() để minh họa việc tải và tải hình ảnh có thể mất một thời gian dài SwingWorker 's được sử dụng
  • Sau đó,chờ đặt tiêu đề mới và sau đó tải hình ảnh
  • khi hoàn tất worker cuối cùng thêm thành phần vào JFrame và hiển thị nó. Đã thêm thành phần vào ngăn nội dung của JFrame như được mô tả trong JFrame api. Và như được mô tả trong javadoc thực hiện cuộc gọi cần thiết đến validate() trên JFrame sau khi gọi add(), vì JFrame là một vùng chứa đã hiển thị mà trẻ em đã thay đổi.

javdoc trích dẫn từ validate()

Các phương thức validate được sử dụng để gây ra một container để bố trí thành phần phụ của nó một lần nữa. Nó nên được viện dẫn khi thành phần phụ của thùng chứa được sửa đổi (được thêm vào hoặc bị xóa khỏi thùng chứa hoặc liên quan đến bố cục thay đổi) sau khi vùng chứa được hiển thị.

  • nhân thứ hai chỉ làm một số chi tiết chờ đợi sau đó đặt màu nền sang màu đen
  • sử dụng JPanel như baseclass cho ImageLoadTest để sửa chữa setBackground() mà tôi không thể có được để làm việc với JComponent.

Vì vậy, vấn đề chính của bạn mà bạn không đặt kích thước ưa thích của thành phần và bạn không gọi validate() trên JFrame sau khi thêm thứ gì đó vào vùng chứa đã hiển thị.

này nên làm việc

jpanelpaint/ImageLoadTest.java

package jpanelpaint; 

import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Image; 
import javax.swing.JPanel; 
import java.util.List; 

public class ImageLoadTest extends JPanel { 
    private List<Image> list; 

    public ImageLoadTest() { 
    super(); 
    } 

    public ImageLoadTest(List<Image> list) { 
    this(); 
    this.list = list; 
    int height = 0; 
    int width = 0; 
    for (Image img : list) { 
     height += img.getHeight(this); 
     width = img.getWidth(this) > width ? img.getWidth(this) : width; 
     setPreferredSize(new Dimension(width, height)); 
    } 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
    int yOffset=0; 
    super.paintComponent(g); 
    System.err.println("ImageLoadTest.paintComponent()"); 
    for(Image img : list) { 
     g.drawImage(img, 0, yOffset, null); 
     yOffset+=img.getHeight(this); 
    } 
    } 
} 

Tester.java

import java.awt.Dimension; 
import java.awt.Color; 
import java.awt.Image; 
import java.io.File; 
import java.io.IOException; 
import javax.imageio.ImageIO; 
import javax.swing.JFrame; 
import javax.swing.SwingWorker; 
import javax.swing.SwingUtilities; 
import java.util.List; 
import java.util.ArrayList; 
import java.util.concurrent.ExecutionException; 
import jpanelpaint.ImageLoadTest; 

public class Tester { 

    private JFrame frame; 
    private ImageLoadTest ilt; 
    private final int NUMBEROFFILES = 4; 
    private List<Image> list; 

    //will load the images 
    SwingWorker worker = new SwingWorker<List<Image>, Void>() { 
    @Override 
    public List<Image> doInBackground() throws InterruptedException { 
     //sleep at start so user is able to see empty jframe 
     Thread.sleep(1000); 
     //let Event-Dispatch-Thread (EDT) handle this 
     SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      frame.setTitle("Loading images"); 
     } 
     }); 
     //sleep again so user is able to see loading has started 
     Thread.sleep(1000); 
     //loads the images and returns list<image> 
     return loadImages(); 
    } 

    @Override 
    public void done() { 
     //this is run on the EDT anyway 
     try { 
     //get result from doInBackground 
     list = get(); 
     frame.setTitle("Done loading images"); 
     ilt = new ImageLoadTest(list); 
     frame.getContentPane().add(ilt); 
     frame.getContentPane().validate(); 
     //start second worker of background stuff 
     worker2.execute(); 
     } catch (InterruptedException ignore) {} 
     catch (ExecutionException e) { 
     String why = null; 
     Throwable cause = e.getCause(); 
     if (cause != null) { 
      why = cause.getMessage(); 
     } else { 
      why = e.getMessage(); 
     } 
     System.err.println("Error retrieving file: " + why); 
     } 
    } 
    }; 

    //just delay a little then set background 
    SwingWorker worker2 = new SwingWorker<Object, Void>() { 
    @Override 
    public List<Image> doInBackground() throws InterruptedException { 
     Thread.sleep(1000); 
     SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      frame.setTitle("Setting background"); 
     } 
     }); 
     Thread.sleep(1000); 
     return null; 
    } 

    @Override 
    public void done() { 
     ilt.setBackground(Color.BLACK); 
     frame.setTitle("Done!"); 
    } 
    }; 

    public static void main(String args[]) { 
    new Tester(); 
    } 

    public Tester() { 
    //setupGUI 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
     frame = new JFrame("Empty JFrame"); 
     frame.setSize(new Dimension(1000, 500)); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
     } 
    }); 

    //start the swingworker which loads the images 
    worker.execute(); 
    } 

    //create image names 
    private String[] createImageFileNames(int count){ 
    String[] fileNames = new String[count]; 
    for(int i=0; i < count; i++) 
     fileNames[i] = "Cards" + File.separator + (i+1) + ".bmp"; 
    return fileNames; 
    } 

    //load images 
    private List<Image> loadImages() { 
    List<Image> tmpA = new ArrayList<Image>(); 
    try { 
     for(String name : createImageFileNames(NUMBEROFFILES)){ 
     System.err.println(name); 
     tmpA.add(ImageIO.read(new File(name))); 
     } 
    } catch (IOException e) { } 

    return tmpA; 
    } 
} 
+0

Bạn vẫn đang thực hiện công cụ Swing ngoài EDT! –

+0

Cảm ơn bạn đã chỉ ra điều hiển nhiên.8 | Thay vì upvoting tôi bởi vì tôi đã giải quyết các vấn đề thực tế của poster câu hỏi (hình ảnh + nền không hiển thị) bạn xuống bỏ phiếu cho tôi cho một cái gì đó một poster đã được giải thích. Tôi nghĩ rằng ít nhất oligofren bit có thể làm/tích hợp vào giải pháp của tôi một mình – jitter

+0

Wow, jitter, đó là rất nhiều công việc để sửa chữa một mã chứng minh chỉ là khái niệm! Một chút overkill, có lẽ, nhưng một THANK YOU lớn, anyway. Tôi đã sử dụng mã của bạn để khắc phục các vấn đề chính trong mã gốc: 1) không gọi validate() sau khi thao tác add() 2) không đặt kích thước ưa thích của thành phần. 3) không gọi super.paintComponent() khi ghi đè nó (điều này làm cho lệnh gọi setBackground() không hoạt động Sau khi thực hiện điều đó, mọi thứ hoạt động như dự định. – oligofren

Các vấn đề liên quan