2010-07-15 13 views
9

सूत्रण क्या कभी किसी ने शीर्ष जिनमें से स्विंग उपयोगकर्ता इंटरफ़ेस तत्वों जोड़ा जा सकता है पर एक उचित बहु बफ़र प्रतिपादन पर्यावरण निर्माण करने के लिए स्विंग का उपयोग करने की कोशिश की है?JTextFields, समस्याओं

इस मामले में मेरे पास एक पृष्ठभूमि पर एक एनिमेटिंग लाल आयत है। पृष्ठभूमि को हर फ्रेम को अपडेट करने की आवश्यकता नहीं है, इसलिए मैं इसे बुफर्ड इमेज पर प्रस्तुत करता हूं और आयताकार के पिछले स्थान को साफ़ करने के लिए केवल आवश्यक भाग को दोबारा हटा देता हूं। नीचे पूरा कोड देखें, यह पिछले थ्रेड में @trashgod द्वारा दिए गए उदाहरण को बढ़ाता है, here

अब तक इतना अच्छा है; चिकनी एनीमेशन, कम सीपीयू उपयोग, कोई झिलमिलाहट।

फिर मैं जेपीएलएल में एक जेटीक्स्टफिल्ड जोड़ता हूं (स्क्रीन पर किसी भी स्थिति पर क्लिक करके), और टेक्स्ट बॉक्स के अंदर क्लिक करके उस पर ध्यान केंद्रित करें। आयताकार के पिछले स्थान को साफ़ करना अब प्रत्येक कर्सर झपकी पर विफल रहता है, नीचे दी गई छवि देखें।

मुझे उत्सुकता है कि अगर किसी को यह पता चल जाए कि ऐसा क्यों हो सकता है (स्विंग थ्रेड-सुरक्षित नहीं है? छवि को अतुल्यकालिक रूप से चित्रित किया जा रहा है?) और संभावित समाधानों को किस दिशा में देखना है।

यह, जावा 1,6

JPanel redraw fail http://www.arttech.nl/javaredrawerror.png

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.Insets; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.image.BufferedImage; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

public class NewTest extends JPanel implements 
    MouseListener, 
    ActionListener, 
    ComponentListener, 
    Runnable 
{ 

JFrame f; 
Insets insets; 
private Timer t = new Timer(20, this); 
BufferedImage buffer1; 
boolean repaintBuffer1 = true; 
int initWidth = 640; 
int initHeight = 480; 
Rectangle rect; 

public static void main(String[] args) { 
    EventQueue.invokeLater(new NewTest()); 
} 

@Override 
public void run() { 
    f = new JFrame("NewTest"); 
    f.addComponentListener(this); 
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    f.add(this); 
    f.pack(); 
    f.setLocationRelativeTo(null); 
    f.setVisible(true); 
    createBuffers(); 
    insets = f.getInsets(); 
    t.start(); 
} 

public NewTest() { 
    super(true); 
    this.setPreferredSize(new Dimension(initWidth, initHeight)); 
    this.setLayout(null); 
    this.addMouseListener(this); 
} 

void createBuffers() { 
    int width = this.getWidth(); 
    int height = this.getHeight(); 

    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
    GraphicsDevice gs = ge.getDefaultScreenDevice(); 
    GraphicsConfiguration gc = gs.getDefaultConfiguration(); 

    buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);   

    repaintBuffer1 = true; 
} 

@Override 
protected void paintComponent(Graphics g) { 
    int width = this.getWidth(); 
    int height = this.getHeight(); 

    if (repaintBuffer1) { 
     Graphics g1 = buffer1.getGraphics(); 
     g1.clearRect(0, 0, width, height); 
     g1.setColor(Color.green); 
     g1.drawRect(0, 0, width - 1, height - 1); 
     g.drawImage(buffer1, 0, 0, null); 
     repaintBuffer1 = false; 
    } 

    double time = 2* Math.PI * (System.currentTimeMillis() % 5000)/5000.; 
    g.setColor(Color.RED); 
    if (rect != null) { 
     g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this); 
    } 
    rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40); 
    g.fillRect(rect.x, rect.y, rect.width, rect.height); 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    this.repaint(); 
} 

@Override 
public void componentHidden(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void componentMoved(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void componentResized(ComponentEvent e) { 
    int width = e.getComponent().getWidth() - (insets.left + insets.right); 
    int height = e.getComponent().getHeight() - (insets.top + insets.bottom); 
    this.setSize(width, height); 
    createBuffers(); 
} 

@Override 
public void componentShown(ComponentEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseClicked(MouseEvent e) { 
    JTextField field = new JTextField("test"); 
    field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20)); 
    this.add(field); 
    repaintBuffer1 = true; 
} 

@Override 
public void mouseEntered(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseExited(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mousePressed(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 

@Override 
public void mouseReleased(MouseEvent arg0) { 
    // TODO Auto-generated method stub 

} 
} 

उत्तर

18

NewTest फैली JPanel मैक ओएस 10.5 पर है;

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    int width = this.getWidth(); 
    int height = this.getHeight(); 
    g.setColor(Color.black); 
    g.fillRect(0, 0, width, height); 
    ... 
} 

परिशिष्ट:: यदि आप ध्यान दें के रूप में, निर्माता में पृष्ठभूमि का रंग की स्थापना, लेकिन क्योंकि आप प्रत्येक कॉल पर हर पिक्सेल paintComponent() के लिए नहीं चित्रकला रहे हैं, आप सुपर वर्ग की विधि आह्वान और पुराने ड्राइंग को मिटाने के लिए की जरूरत है paintComponent() में पैनल को भरने की आवश्यकता को रोकता है, जबकि super.paintComponent() टेक्स्ट फ़ील्ड को सही ढंग से कार्य करने की अनुमति देता है। जैसा कि आप देखते हैं, प्रस्तावित कामकाज नाजुक है। इसके बजाए, कोड को सरल बनाएं और वारंट के रूप में अनुकूलित करें। उदाहरण के लिए, आपको इंससेट, अतिरिक्त बफर और एक घटक श्रोता की जटिलता की आवश्यकता नहीं हो सकती है।

अनुपूरक 2: ध्यान दें कि super.paintComponent() यूआई प्रतिनिधि की update() विधि को कॉल करता है, "जो निर्दिष्ट पृष्ठभूमि को इसके पृष्ठभूमि रंग से भरता है (यदि इसकी अपारदर्शी संपत्ति सत्य है)।" आप इसे रोकने के लिए setOpaque(false) का उपयोग कर सकते हैं।

Animation Test

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsEnvironment; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentAdapter; 
import java.awt.event.ComponentEvent; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.util.Random; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

/** @see http://stackoverflow.com/questions/3256941 */ 
public class AnimationTest extends JPanel implements ActionListener { 

    private static final int WIDE = 640; 
    private static final int HIGH = 480; 
    private static final int RADIUS = 25; 
    private static final int FRAMES = 24; 
    private final Timer timer = new Timer(20, this); 
    private final Rectangle rect = new Rectangle(); 
    private BufferedImage background; 
    private int index; 
    private long totalTime; 
    private long averageTime; 
    private int frameCount; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new AnimationTest().create(); 
      } 
     }); 
    } 

    private void create() { 
     JFrame f = new JFrame("AnimationTest"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(this); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
     timer.start(); 
    } 

    public AnimationTest() { 
     super(true); 
     this.setOpaque(false); 
     this.setPreferredSize(new Dimension(WIDE, HIGH)); 
     this.addMouseListener(new MouseHandler()); 
     this.addComponentListener(new ComponentHandler()); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     long start = System.nanoTime(); 
     super.paintComponent(g); 
     int w = this.getWidth(); 
     int h = this.getHeight(); 
     g.drawImage(background, 0, 0, this); 
     double theta = 2 * Math.PI * index++/64; 
     g.setColor(Color.blue); 
     rect.setRect(
      (int) (Math.sin(theta) * w/3 + w/2 - RADIUS), 
      (int) (Math.cos(theta) * h/3 + h/2 - RADIUS), 
      2 * RADIUS, 2 * RADIUS); 
     g.fillOval(rect.x, rect.y, rect.width, rect.height); 
     g.setColor(Color.white); 
     if (frameCount == FRAMES) { 
      averageTime = totalTime/FRAMES; 
      totalTime = 0; frameCount = 0; 
     } else { 
      totalTime += System.nanoTime() - start; 
      frameCount++; 
     } 
     String s = String.format("%1$5.3f", averageTime/1000000d); 
     g.drawString(s, 5, 16); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     this.repaint(); 
    } 

    private class MouseHandler extends MouseAdapter { 

     @Override 
     public void mousePressed(MouseEvent e) { 
      super.mousePressed(e); 
      JTextField field = new JTextField("test"); 
      Dimension d = field.getPreferredSize(); 
      field.setBounds(e.getX(), e.getY(), d.width, d.height); 
      add(field); 
     } 
    } 

    private class ComponentHandler extends ComponentAdapter { 

     private final GraphicsEnvironment ge = 
      GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     private final GraphicsConfiguration gc = 
      ge.getDefaultScreenDevice().getDefaultConfiguration(); 
     private final Random r = new Random(); 

     @Override 
     public void componentResized(ComponentEvent e) { 
      super.componentResized(e); 
      int w = getWidth(); 
      int h = getHeight(); 
      background = gc.createCompatibleImage(w, h, Transparency.OPAQUE); 
      Graphics2D g = background.createGraphics(); 
      g.clearRect(0, 0, w, h); 
      g.setColor(Color.green.darker()); 
      for (int i = 0; i < 128; i++) { 
       g.drawLine(w/2, h/2, r.nextInt(w), r.nextInt(h)); 
      } 
      g.dispose(); 
      System.out.println("Resized to " + w + " x " + h); 
     } 
    } 
} 
+0

आपके उत्तर के लिए धन्यवाद। यदि आप super.paintComponent (g) जोड़ते हैं और प्रदान किए गए परीक्षण एप्लिकेशन को चलाते हैं, तो आप देखेंगे कि दुर्भाग्यवश यह समाधान नहीं है। जेपीनल की डिफ़ॉल्ट पेंट कॉम्पोनेंट विधि अपनी पृष्ठभूमि को साफ़ करती है, जो पूरे पृष्ठभूमि छवि को फिर से तैयार करना आवश्यक बनाता है, जो ज्यादातर पृष्ठभूमि छवि को पूर्व-प्रस्तुत करने के उद्देश्य को हरा देती है। मेरे पास एक झुकाव है कि केवल आवश्यक क्षेत्र को फिर से निकालना संभव होना चाहिए, लेकिन ऐसा लगता है कि पृष्ठभूमि चित्र को दोबारा बनाने के बजाय टेक्स्टफील्ड को फिर से लिखने के साथ कुछ समस्या है जो इन ड्राइंग कलाकृतियों का कारण बनता है .. – Mattijs

+0

परिशिष्ट के संबंध में: इस में गोता लगाने के लिए समय लेने के लिए बहुत धन्यवाद। मुझे लगता है कि आपका मुद्दा यह है कि ऐसा कोई तरीका नहीं है कि स्वचालित रूप से पुन: प्रयास करने वाले JTextFields super.paintComponent (g) का उपयोग करके सक्रिय रीड्रॉइंग * के बिना सही ढंग से काम करेंगे, सही है। फिर भी super.paintComponent (g) का उपयोग करने का तात्पर्य है कि पूरी विंडो को प्रत्येक एनीमेशन फ्रेम पर फिर से खींचा जाना है, जिससे सीपीयू का उपयोग खिड़की के आकार पर निर्भर करता है, जो मैं रोकना चाहता हूं। तो मेरे प्रश्न का उत्तर दिया गया है, लेकिन मेरी समस्या अभी भी खड़ी है। मैं इसके बारे में एक नई पोस्ट कर दूंगा। – Mattijs

+0

@ मैटिज: सेटऑपाक (झूठा) का उपयोग भरने से बच जाएगा। मैंने पेंट टाइम दिखाने के लिए उदाहरण अपडेट किया है। अंतर काफी है, लेकिन इसे अद्यतनों को प्रबंधित करने के प्रयास के खिलाफ वजन घटाना चाहिए। – trashgod

2

मैं एक वैकल्पिक हल मिल गया।

मुझे क्या लगता है: जब भी एक जेटीक्स्टफील्ड को अद्यतन करने की आवश्यकता होती है (यानी प्रत्येक कर्सर झपकी पर), जेपीनल के ओवरराइड पेंट कॉम्पोनेंट() को बुलाया जाता है, लेकिन एक अलग ग्राफिक्स तर्क के साथ जब repaint() द्वारा बुलाया जाता है। इस प्रकार प्रत्येक कर्सर झपकी पर, मेरे आयताकार को गलत ग्राफिक्स उदाहरण पर साफ़ किया गया था और स्क्रीन पर अमान्य ग्राफिक्स को छोड़कर छोड़ दिया गया था।

क्या इसका कोई अर्थ है? अगर ऐसा होता है, तो यह स्विंग में एक अजीब असुविधा नहीं है?

वैसे भी, जहां से आने वाला बूलियन (activeRedraw) रखकर, ऐसा लगता है कि मैं इस मुद्दे के आसपास काम करने में कामयाब रहा।तो ऐसा लगता है कि आखिरकार मुझे प्रत्येक फ्रेम पर पूरे स्क्रीन क्षेत्र को पुनर्स्थापित किए बिना सक्रिय ड्राइंग करने का एक तरीका मिला, जिसका अर्थ है कि कम आकार का सीपीयू उपयोग खिड़की के आकार से स्वतंत्र है!

पूरा यहाँ कोड:

import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsDevice; 
import java.awt.GraphicsEnvironment; 
import java.awt.Insets; 
import java.awt.Rectangle; 
import java.awt.Transparency; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ComponentEvent; 
import java.awt.event.ComponentListener; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.image.BufferedImage; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTextField; 
import javax.swing.Timer; 

public class NewTest extends JPanel implements 
    MouseListener, 
    ActionListener, 
    ComponentListener, 
    Runnable 
{ 

    JFrame f; 
    Insets insets; 
    private Timer t = new Timer(20, this); 
    BufferedImage buffer1; 
    boolean repaintBuffer1 = true; 
    int initWidth = 640; 
    int initHeight = 480; 
    Rectangle rect; 
    boolean activeRedraw = true; 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new NewTest()); 
    } 

    @Override 
    public void run() { 
     f = new JFrame("NewTest"); 
     f.addComponentListener(this); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(this); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
     createBuffers(); 
     insets = f.getInsets(); 
     t.start(); 
    } 

    public NewTest() { 
     super(true); 
     this.setPreferredSize(new Dimension(initWidth, initHeight)); 
     this.setLayout(null); 
     this.addMouseListener(this); 
    } 

    void createBuffers() { 
     int width = this.getWidth(); 
     int height = this.getHeight(); 

     GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     GraphicsDevice gs = ge.getDefaultScreenDevice(); 
     GraphicsConfiguration gc = gs.getDefaultConfiguration(); 

     buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);   

     repaintBuffer1 = true; 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     //super.paintComponent(g); 
     int width = this.getWidth(); 
     int height = this.getHeight(); 

     if (activeRedraw) { 
      if (repaintBuffer1) { 
       Graphics g1 = buffer1.getGraphics(); 
       g1.clearRect(0, 0, width, height); 
       g1.setColor(Color.green); 
       g1.drawRect(0, 0, width - 1, height - 1); 
       g.drawImage(buffer1, 0, 0, null); 
       repaintBuffer1 = false; 
      } 

      double time = 2* Math.PI * (System.currentTimeMillis() % 5000)/5000.; 
      g.setColor(Color.RED); 
      if (rect != null) { 
       g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this); 
      } 
      rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40); 
      g.fillRect(rect.x, rect.y, rect.width, rect.height); 

      activeRedraw = false; 
     } 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     activeRedraw = true; 
     this.repaint(); 
    } 

    @Override 
    public void componentHidden(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void componentMoved(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void componentResized(ComponentEvent e) { 
     int width = e.getComponent().getWidth() - (insets.left + insets.right); 
     int height = e.getComponent().getHeight() - (insets.top + insets.bottom); 
     this.setSize(width, height); 
     createBuffers(); 
    } 

    @Override 
    public void componentShown(ComponentEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseClicked(MouseEvent e) { 
     JTextField field = new JTextField("test"); 
     field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20)); 
     this.add(field); 
     repaintBuffer1 = true; 
    } 

    @Override 
    public void mouseEntered(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseExited(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mousePressed(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 

    @Override 
    public void mouseReleased(MouseEvent arg0) { 
     // TODO Auto-generated method stub 

    } 
} 
+0

नोट: विंडोज़ पर, व्यवहार एक बार फिर अलग है और कामकाज पर्याप्त नहीं है। – Mattijs

+0

मैंने अपने जवाब में विस्तार से बताया है। – trashgod