Paint foreign component (i.e. belonging to a diffe

2019-04-16 01:53发布

问题:

Hallo all. I want to paint a foreign component (i.e. belonging to a different frame's content pane), lets call it frame B, inside a component in frame A.

The problem is that when I paint the component it is painted also in the content pane of the frame A, also it flickers or all gets ugly when the frame is re-sized (i.e. painted few times inside the component, some blue squares are appearing, etc.). The issue becomes more visible if I try to, for example, scale or translate the foreign component before painting.

After a while I sorted it, I think. But I do not feel good with this solution, for some reason I believe there might be a better one, a more appropriate one. Here I need you. :)

This question is more a call for an explanation why the foreign component is painted incorrectly without manipulating it's double buffering feature before and after the paint inside the component. For example with use of either pair of setDoubleBuffered(false) and setDoubleBuffered(true) or disableDoubleBuffering(jP) and enableDoubleBuffering(jP)

respectively before and after the call to the foreign component's paint method.

Thank You in advance. The SSCCE showing the problem is presented below.


import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.RepaintManager;

public class PaintForeignComponentSSCCE extends JFrame
{
    public static void main(String[] args) throws IOException
    {
        //foreign panel
        JPanel fp = new JPanel();
        fp.setBackground(Color.PINK);
        fp.setPreferredSize(new Dimension(200, 300));
        //component in which the foreign panel is painted
        ForeignComponentPainter fcp = new ForeignComponentPainter(fp);
        fcp.setPreferredSize(new Dimension(600, 600));
        //main frame's content
        JPanel contentPane = new JPanel();
        contentPane.setBackground(Color.BLUE);
        contentPane.add(fcp);
        //main frame
        JFrame f = new PaintForeignComponentSSCCE();
        f.setContentPane(contentPane);
        f.setSize(700, 500);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);             
        //foreign panel frame
        JFrame fpf = new JFrame();
        JPanel panelFrameContent = new JPanel();
        panelFrameContent.add(fp);
        fpf.setContentPane(panelFrameContent);
        fpf.setSize(400, 400);
        fpf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        fpf.setVisible(true);
    }
}

class ForeignComponentPainter extends JButton
{
    private static final long serialVersionUID = 1L;
    private JPanel jP;

    public ForeignComponentPainter(JPanel jP)
    {
        super();
        this.jP = jP;
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.drawString("OIOI", 50, 50);
        //g2.translate(100, 50);
        //g2.scale(.5, .5);
//      jP.setDoubleBuffered(false);
//      disableDoubleBuffering(jP);
        jP.paint(g2);
//      jP.setDoubleBuffered(true);
//      enableDoubleBuffering(jP);
        //g2.scale(1/.5, 1/.5);
    }   
    public static void disableDoubleBuffering(Component c)
    {
        RepaintManager currentManager = RepaintManager.currentManager(c);
        currentManager.setDoubleBufferingEnabled(false);
    }
    public static void enableDoubleBuffering(Component c)
    {
        RepaintManager currentManager = RepaintManager.currentManager(c);
        currentManager.setDoubleBufferingEnabled(true);
    }
}

Not related to the SSCCE example. The below is unrelated to the problem itself. This piece of code serves the purpose of presentation of how I am implementing Printable in the component which I also want to render in a print preview fashion. The print calls paint of the component (as shown below).


public int print(Graphics g, PageFormat pageFormat, int pageIndex)
{ 
   if(pageIndex >= pageHeights.size()) 
      return NO_SUCH_PAGE; 
   int savedPage = currentPageIndex; 
   currentPageIndex = pageIndex; 
   Graphics2D g2 = (Graphics2D) g; 
   paint(g2); 
   currentPageIndex = savedPage; 
   return PAGE_EXISTS; 
} 

回答1:

Sorry, I can't address your question directly, but it may be possible to avoid it by having two different views of a common model. As shown in How to Write a Document Listener, it is possible to update more than one view of a Document. Your model and view(s) might be different, but the concept would still apply.

Addendum:

I think that is what I am doing at the moment, isn't it? I have one model i.e. the 'foreign' component, and two views one doing default paint and second custom paint.

No, you have one view updating another; you need two views responding to one model. This related example may offer some insight.

I am still interested in suggestions as to the reasons of this erroneous painting.

The attempt to interleave the updates of two components is, IMO, fundamentally flawed; it subverts the normal process of Painting in Swing. On my platform, I see only a very brief flicker and no adventitious painting. Although it may be possible to obtain satisfactory results on one system, the arrangement would be unreliable across implementations.

Is a simple call to repaint of the label, placed in the ModelObserver's update() method the appropriate solution?

Yes, repaint(), but it should be done in the ButtonHandler for View:

private class ButtonHandler implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        PieceButton pb = (PieceButton) e.getSource();
        icon.color = pb.piece.color;
        label.repaint();
        model.check(pb.piece);
    }
}