2012-12-25 23 views
11

मेरे समझ: घटकों का सबसे विपरीत/JComponent.repaint() करने के लिए स्विंग कॉल में आपरेशन धागा सुरक्षित यानी हालांकि एक रीपेंट अनुरोध (यानी EDT से नहीं) एक और धागे से बना है, वास्तविक चित्र में क्या होता है केवल ईडीटी। कोड स्निपेट के नीचे यह दर्शाता है।जावा स्विंग में JComponent.paintImedimedi() काम कैसे करता है?

public class PaintingDemo { 

    public static void main(String[] args) { 
     final JFrame frame = new JFrame(); 
     final JPanel p = new MyPanel(); 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       frame.add(p, BorderLayout.CENTER); 
       frame.setSize(200, 200); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setVisible(true); 
      } 
     }); 
     new Thread("MyThread") { 
      public void run() { 
       while (true) { 
       // Below statements are important to show the difference 
        p.repaint(); 
        p.paintImmediately(p.getBounds()); 
        try { 
         Thread.sleep(1000); 
        } catch(Exception e) {} 
       } 
      } 
     }.start(); 
    } 

} 

class MyPanel extends JPanel { 
    @Override 
    public void paint(Graphics g) { 
     System.out.println("paint() called in "+ Thread.currentThread().getName()); 
     super.paint(g); 
    } 
} 

उत्पादन यह ज्ञात है कि पेंटिंग EDT में किया जाता है जब रीपेंट() की जो धागे से यह कहा जाता है, भले ही शुरू हो जाती है से - तो कोई समस्या नहीं। लेकिन, पेंट के मामले में तुरंत() - पेंटिंग उसी धागे में होती है जिससे इसे कहा जाता है।

एक ऐसे मामले पर विचार करें जहां ईडीटी एक घटक की स्थिति बदल रहा है और दूसरा धागा (जिसमें से पेंट तुरंत() लगाया जाता है) एक ही घटक को चित्रित कर रहा है।

मेरे प्रश्न: paintImmediately() के मामले में, कैसे घटना डिस्पैचर थ्रेड (EDT) और अन्य धागे के बीच तुल्यकालन नियंत्रित किया जाता है?

 Component c = this; 
     Component parent; 

     if(!isShowing()) { 
      return; 
     } 

     JComponent paintingOigin = SwingUtilities.getPaintingOrigin(this); 
     if (paintingOigin != null) { 
      Rectangle rectangle = SwingUtilities.convertRectangle(
        c, new Rectangle(x, y, w, h), paintingOigin); 
      paintingOigin.paintImmediately(rectangle.x, rectangle.y, rectangle.width, rectangle.height); 
      return; 
     } 

     while(!c.isOpaque()) { 
      parent = c.getParent(); 
      if(parent != null) { 
       x += c.getX(); 
       y += c.getY(); 
       c = parent; 
      } else { 
       break; 
      } 

      if(!(c instanceof JComponent)) { 
       break; 
      } 
    } 
    if(c instanceof JComponent) { 
     ((JComponent)c)._paintImmediately(x,y,w,h); 
    } else { 
     c.repaint(x,y,w,h); 
    } 

तो, जब तक कि यह एक JComponent नहीं है, आप _paintImmediately() जो paint(Graphics) बुला समाप्त होता है बुला अंत स्टैक ट्रेस पता चलता है के रूप में:

उत्तर

5
मेरी समझ के लिए

, जब आप paintImmediately फोन, आप निम्न कोड फोन

Thread [pool-1-thread-1] (Suspended)  
    TestPaint$1.paint(Graphics) line: 23  
    TestPaint$1(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5221 
    RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482 
    RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413 
    RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206 
    TestPaint$1(JComponent)._paintImmediately(int, int, int, int) line: 5169  
    TestPaint$1(JComponent).paintImmediately(int, int, int, int) line: 4980 
    TestPaint$1(JComponent).paintImmediately(Rectangle) line: 4992 
    TestPaint$3.run() line: 50 
    ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1110 
    ThreadPoolExecutor$Worker.run() line: 603 
    Thread.run() line: 722 

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

बस मेरे बिंदु को वर्णन, मैं paint विधि के भीतर एक चर के राज्य को संशोधित करने, 2 धागे (EDT और अन्य) के बीच टकराव के खतरे को बढ़ा करने के लिए एक sleep जोड़ने की कोशिश की और यह obvisouly लगता है कि वहाँ कोई दो थ्रेड के बीच सिंक्रनाइज़ेशन (System.err.println() समय-समय पर null आउटपुट)।

अब मुझे आश्चर्य होगा कि आपको तुरंत एक पेंट करने की आवश्यकता क्यों है। जब तक आप ईडीटी को अवरुद्ध नहीं कर रहे हैं, तब तक ऐसी चीज करने के लिए बहुत सारे वैध कारण नहीं हैं।

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

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.util.Random; 
import java.util.concurrent.Executors; 

import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 
import javax.swing.Timer; 

public class TestPaint { 

    protected void initUI() { 
     JFrame frame = new JFrame(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setTitle(TestPaint.class.getSimpleName()); 
     final Random rand = new Random(); 
     final JPanel comp = new JPanel() { 
      private String value; 

      @Override 
      public void paint(Graphics g) { 
       value = "hello"; 
       super.paint(g); 
       try { 
        Thread.sleep(20); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       g.setColor(new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256))); 
       g.fillRect(0, 0, getWidth(), getHeight()); 
       if (SwingUtilities.isEventDispatchThread()) { 
        System.err.println("Painting in the EDT " + getValue()); 
       } else { 
        System.err.println("Not painting in EDT " + getValue()); 
       } 
       value = null; 
      } 

      public String getValue() { 
       return value; 
      } 
     }; 
     frame.add(comp); 
     frame.setSize(400, 400); 
     frame.setLocationRelativeTo(null); 
     frame.setVisible(true); 
     Timer t = new Timer(1, new ActionListener() { 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       comp.repaint(); 
      } 
     }); 
     t.start(); 
     Executors.newSingleThreadExecutor().execute(new Runnable() { 

      @Override 
      public void run() { 
       while (true) { 
        comp.paintImmediately(comp.getBounds()); 
       } 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       new TestPaint().initUI(); 
      } 
     }); 
    } 

} 
+0

+1 आपने इसे खींचा। मुझे आश्चर्य है कि क्यों 'JComponent' दस्तावेज़ संदर्भ' पेंट तुरंत() 'और 'repaint (..)' जहां स्रोत अलग है। मुझे आपके स्निपेट में हालांकि पूछना चाहिए कि हम कभी भी 'जेकंपोनेंट' के 'पेंट (..)' को ओवरराइड नहीं करेंगे, तो आपने 'पेंट कॉम्पोनेंट' का उपयोग क्यों नहीं किया (यह अभी भी परिणाम पुन: पेश करता है)? और एक और सवाल क्यों 'नींद (int milis) बढ़ाना क्यों टक्कर में वृद्धि नहीं करेगा? और आप ईडीटी के अलावा किसी थ्रेड से 'पेंट तुरंत' क्यों कहते हैं, (क्योंकि मुझे एक बार ईडीटी पर कॉल नहीं किया जाता है, कोई शून्य नहीं होगा)? –

+0

धन्यवाद @Guillaume इसे अपने आप को आजमाने और अपने शोध को साझा करने के लिए।मैं मानता हूं कि पेंट का उपयोग करने के कई कारण नहीं हैं() लेकिन मैं बस यह समझने की कोशिश कर रहा था कि "यह कैसे काम करता है?" चूंकि जावाडोक कहता है ... "यह विधि तब उपयोगी होती है जब किसी को वर्तमान घटना प्रेषित होने पर डिस्प्ले अपडेट करने की आवश्यकता होती है।" साथ ही, यह विधि सार्वजनिक है और इसकी चेतावनी के बारे में कहीं भी कोई चेतावनी नहीं दी गई है; कम से कम जावाडोक भ्रामक है। अब मैं पेंट को तुरंत समझता हूं() को ईडीटी के भीतर बुलाया जाना चाहिए और यह रीडेंट() के विपरीत थ्रेड-सुरक्षित नहीं है। – Learner

+0

@DavidKroukamp बिल्कुल 'पेंट कॉम्पोनेंट' की बजाय 'पेंट' को ओवरराइड करने का कोई अच्छा कारण नहीं है, मैंने सोचा कि यह देखने के प्रयास में कि' पेंट तुरंत 'क्यों' पेंट 'कहता है, स्टैक कॉल में' पेंट कॉम्पोनेंट 'को अतिरिक्त कॉल जोड़ना अनावश्यक था और मनाए गए परिणाम वही बने रहेंगे। तो जानकारी से किसी भी "शोर" को हटाने के लिए और अधिक था, और जैसा कि मैंने अपने स्निपेट से पहले कहा था, यह स्विंग का उपयोग करने के लिए उचित तरीका नहीं दिखाता है। चीयर्स ;-) –