Solved

Swing components repaint when added after init

Posted on 1998-06-17
10
582 Views
Last Modified: 2013-11-23
Hi everyone,

I have encountered a problem with the repaint method of Swing components.

I'm trying to add a JPanel subclass, with lots of swing components on it, to a JApplet.

When the JPanel is created and added to the applet in its init method, the JPanel is painted as it should.
But, when I tried to create the same JPanel subclass using a second thread and add it to the JApplet (after the JApplet was initiated and started) most components were not painted.
Minimizing the window and maximizing it back refreshs the JPanel subclass but some components (JSplitPane for example) are seen only in the upper left corner of their position in the screen (2 pixels * 2 pixels) and are repainted properly only after you click these tiny (2 pixels * 2 pixels) components.

I solved the first problem of the components that were not painted at all by creating & adding the JPanel subclass in an invisible state and was setting it back to visible afterwords and most of the components appeared just fine.

Still, the problem that bothers me is the fact that the JSplitPane for example is painted in a tiny size of (2 pixels * 2 pixels).

I would most appriciate it if anyone knows how I may tell the JSplitPane to be repainted properly (as happens when it is clicked). I already tried calling repaint, invalidate ... etc, but nothing helped.

Thanks in advance,
Guy Loewy.
0
Comment
Question by:optimal
  • 6
  • 3
10 Comments
 
LVL 4

Expert Comment

by:evijay
ID: 1223308
Can you give sample code

0
 
LVL 5

Expert Comment

by:fontaine
ID: 1223309
It is impossible to give you a hand on this with the information you provide. We need to see
concretely what you are doing. Could you post a short, representative code sample that
we could recompile and test?
0
 

Author Comment

by:optimal
ID: 1223310
Hi,

Sorry, it took me some time to add this source because I tried to create the same affect but encountered something a bit different (this time there is no refresh at all).

First the example that works fine (when I add JPanel in the init method of the JApplet):

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;

//import JPanel1;
import JPanel2;
//import loaderThread;

public class JApplet1 extends JApplet {

//    JPanel1 jp1 = null;
    JPanel2 jp2 = null;

    public void init() {

      try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
      } catch (UnsupportedLookAndFeelException exc) {
          System.err.println("Warning: UnsupportedLookAndFeel: Windows Look & feel.");
      } catch (Exception exc) {
          System.err.println("Error loading Windows Look & feel.");
      }

        this.getContentPane().setLayout(new BorderLayout());

        jp2 = new JPanel2(this);
        this.getContentPane().add(jp2);
    }


    public void start() {
//        loaderThread lt = new loaderThread(this);
//        lt.start();
    }


    public void stop() {
    }

    public void setJPanel2(JPanel2 jp2) {
        this.jp2 = jp2;
    }
}


import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import com.sun.java.swing.tree.*;
import com.sun.java.swing.table.*;

public class JPanel2 extends JPanel {
    JApplet1 ja1;

    JSplitPane splitPane;
    JScrollPane leftPane;
    JScrollPane rightPane;

    JTree tree;
    JTable tableView;

    public JPanel2(JApplet1 ja1) {
        super();
        this.ja1 = ja1;

        this.setLayout(new BorderLayout());

//        JLabel jl = new JLabel("testing");
//        jl.setBounds(50, 50, 100, 20);
//        this.add(jl);

        // Right side: Tree
        tree = new JTree();

        JScrollPane treeScrollpane = new JScrollPane();
        treeScrollpane.getViewport().add(tree);

        // Left side: Table
        tableView = new JTable();

        JScrollPane tableScrollpane = new JScrollPane(tableView);

      leftPane = tableScrollpane;
      rightPane = treeScrollpane;
      splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPane,
                           rightPane);
      splitPane.setContinuousLayout(false);
      splitPane.getAccessibleContext().setAccessibleName("Split pane");
        splitPane.setBounds(100, 100, 200, 200);
      add(splitPane);
    }
}

And now the JApplet which starts with some other JPanel and should change it with the JPanel from the example above:

import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;

import JPanel1;
import JPanel2;
import loaderThread;

public class JApplet1 extends JApplet {

    JPanel1 jp1 = null;
    JPanel2 jp2 = null;

    public void init() {

      try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
      } catch (UnsupportedLookAndFeelException exc) {
          System.err.println("Warning: UnsupportedLookAndFeel: Windows Look & feel.");
      } catch (Exception exc) {
          System.err.println("Error loading Windows Look & feel.");
      }

        this.getContentPane().setLayout(new BorderLayout());

        jp1 = new JPanel1(this);
        this.getContentPane().add(jp1);
    }


    public void start() {
        loaderThread lt = new loaderThread(this);
        lt.start();
    }


    public void stop() {
    }

    public void setJPanel2(JPanel2 jp2) {
        this.jp2 = jp2;
    }
}



import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;

public class JPanel1 extends JPanel
{
    JApplet1 ja1 = null;
    JButton nextBtn = null;

    public JPanel1(JApplet1 ja1) {
      super();

        this.ja1 = ja1;

      setLayout(null);

        nextBtn = new JButton("Next >");
        nextBtn.setBounds(300, 300, 100, 20);
        nextBtn.setEnabled(false);
        this.add(nextBtn);
        nextBtn.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    nextBtnClicked();
                }
            });      
    }

    public void enableNextBtn() {
        nextBtn.setEnabled(true);
    }

    private void nextBtnClicked() {
        this.setVisible(false);
        ja1.jp2.setVisible(true);
    }
}


import java.lang.Thread;

public class loaderThread extends Thread {

    private JApplet1 ja1 = null;

    loaderThread(JApplet1 ja1) {
        super("loaderThread");

        this.ja1 = ja1;
    }

    public void run() {
        JPanel2 jp2 = new JPanel2(ja1);
        jp2.setVisible(false);

        ja1.setJPanel2(jp2);
        ja1.getContentPane().add(jp2);
        ja1.jp1.enableNextBtn();
       
    }
}


I hope this code helps to understand the problem.
I'm using Swing1.0.2 from the new activator.

Thanks Guy.
0
 
LVL 5

Expert Comment

by:fontaine
ID: 1223311
Optimal, I have a problem with the activator of my browser. However, there is something
that seems not correct in the code of JApplet1: if you are using a BorderLayout, you
should, when you add the componenet, indicate the location: North, etc.

Please try:

this.getContentPane().add("Center",jp2);

instead of:

this.getContentPane().add(jp2);



0
 
LVL 5

Expert Comment

by:fontaine
ID: 1223312
Am I understanding correctly? You would like to have an applet with a disabled button
at the beginning. Another panel is loaded in the background. When available, it is
automatically added to the applet and the button is disabled. I suppose that for some
reason, you are not using a JAR (in this case, the thread would not server to any purpose).
Can I propose you an easy to use framework to do this, to use instead of your current code,
which seems me quite complicated?
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 

Author Comment

by:optimal
ID: 1223313
Hi Fontaine,

About the first comment ("this.getContentPane().add("Center",jp2)"):
I tried it but since the default state is "Center" it didn't change anything.

About the second comment:
You understood right, but there is a reason.
One of the main problems with the Java activator is that the initialization process takes lot of time (On my computer, P166 64MB, with nothing else running the initialization takes 17 seconds).
My applet should work in a LAN, therefor, the problem is not only the network's bandwidth, but also CPU power.
The second panel my applet has cause the initialization time to get to 47 seconds which is much more than I would like my clients to wait. Therefor, instead of creating the second panel in the init method of the applet, I initiate a thread that creates the panel and sets the "Next >" button in the "light" first panel to enabled after the second panel was created and may be seen.
I also didn't want to create the second panel only as the user clicks the button, because that again takes too much time.
If you have any idea how I might do these thing in a better technic I'd appriciate it.

And third thing, I'm happy to say that I found the problem with the refresh of the JSplitPane component.
It seems that for some reason, when a JSplitPane was not created in the init method, where the look&feel was set, it had problems to repaint itself. Therfor, I called the "updateUI()" for the JSplitPane component and it was painted as it should have.

Thanks,
Guy.
0
 
LVL 5

Expert Comment

by:fontaine
ID: 1223314
Here is some code for you with a concrete example of use. I prefer to explain the example.
We are going:

- to display 3 panels: BluePanel, RedPanel and GreenPanel
- download them ASAP, using each time a separate thread
- ensure with enabled/disabled buttons that the order of use (B, R, G) is respected.

This is basically what you would like to do. You will see that the applet can serve you as a
template for your own problem and that the classes developed are already quite generic.
For the example, the blue panel will be available after 5 sec., the red one afer 10 and the
last after 15. You will have to remove these artificial delays from the code. Feel free to
use this code, even for commercial use. I post as a comment. Tell me if you can consider
this as an answer. I don't have that much time to comment the code but if you have
questions, don't hesitate.

import java.util.*;
import java.awt.*;
import java.awt.event.*;

import com.sun.java.swing.*;

public class MyApplet extends JApplet {

    public void init() {  
        JPanel panel = new JPanel();
        panel.setLayout(new CardLayout());

        Card card1 = new Card(panel,"BluePanel"  ,"RedPanel");
        Card card2 = new Card(panel,"RedPanel"   ,"GreenPanel");
        Card card3 = new Card(panel,"GreenPanel" ,null);

        // download the BluePanel in the background
        GUILoader loader1 = new GUILoader("BluePanel");
        loader1.addObserver(card1);

        // download the RedPanel in the background
        GUILoader loader2 = new GUILoader("RedPanel");
        loader2.addObserver(card1);
        loader2.addObserver(card2);

        // download the GreenPanel in the background
        GUILoader loader3 = new GUILoader("GreenPanel");
        loader3.addObserver(card2);
        loader3.addObserver(card3);

        panel.add("one"  , card1);
          panel.add("two"  , card2);
          panel.add("three", card3);

        getContentPane().add(panel);
     }  

    public static void main(String args[]) {
        MyApplet applet = new MyApplet();
        applet.init();

        JFrame frame = new JFrame("Test");
        frame.getContentPane().add(applet);
        frame.setBounds(0,0,300,300);
        frame.show();
        return;        
    }
}

class Card extends JPanel implements Observer, ActionListener {

    private JButton   button    = null;
    private String    thisCard  = "";
    private String    nextCard  = "";
    private Container container = null;

    public Card(Container container, String thisCard, String nextCard) {    
        this.container = container;  
        this.thisCard = thisCard;
        this.nextCard = nextCard;

        setLayout(new BorderLayout());

        if (nextCard != null) {
            button = new JButton(">>");
            button.setEnabled(false);
            button.addActionListener(this);

            add("South",button);
        }
       
    }

    public void actionPerformed(ActionEvent e) {
        ((CardLayout)container.getLayout()).next(container);  
        return;
    }

    public void update(Observable o, Object arg) {
        String card = (String)arg;

        if ((nextCard != null) && card.equals(nextCard)) {
            button.setEnabled(true);
            return;
        }

        if (card.equals(thisCard)) {
            add("Center", getPanel(thisCard));
            invalidate();
            validate();
            repaint();
            return;
        }

        return;        
    }

    public JPanel getPanel(String name) {
        try {
           Class  c = Class.forName(name);
           Object o = c.newInstance();

            return (JPanel)o;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}

// loads a class in the background
class GUILoader extends Observable implements Runnable {

    private static int i=0; // for the demo
    private String name   = "";
    private static Vector vector = new Vector(5,5);

    // name: the name of the class to load
    public GUILoader(String name) {
        this.name = name;

        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        if ((name == null) || (vector.contains(name))) {
            return;
        } else {
            vector.addElement(name);
        }

        try {
            Class.forName(name);

            i+=5000; // for the demo
            Thread.currentThread().sleep(i); // for the demo

            setChanged();
            notifyObservers(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return;
    }

}

class BluePanel extends JPanel {

    public BluePanel() {
        setBackground(Color.blue);
    }
}

class RedPanel extends JPanel {

    public RedPanel() {
        setBackground(Color.red);
    }
}

class GreenPanel extends JPanel {

    public GreenPanel() {
        setBackground(Color.green);
    }
}

0
 
LVL 5

Expert Comment

by:fontaine
ID: 1223315
The key of the design is the following. GUILoader is an Observable object. Whenever
one tries to load a new class, GUILoader takes a look at its internal database ("vector")
to see if it is not (or has) already loading (-ed) it. If not, it loads it and, when finished, it notifies
its  observers. In the present case, the observers are the Card instances, that display the
panels and the button. When a Card is notified that the panel class corresponding to what
would be displayed in the next window has been downloaded, the button is enabled. When
a Card is notified that the panel class corresponding to what it has to display has been
downloaded, it creates a panel instance and displays it.
0
 

Author Comment

by:optimal
ID: 1223316
Thanks fontaine,

This is as good an answer as I would like too recieve.
Just send it as an answer.

Thanks again,
Guy.
0
 
LVL 5

Accepted Solution

by:
fontaine earned 500 total points
ID: 1223317
Guy, I have posted hereafter the comment finally accepted as an answer for the question.

Cheers,

Fontaine

---------------------------------------------------------------------------------------------------------------------------------




Here is some code for you with a concrete example of use. I prefer to explain the example.
      We are going:

      - to display 3 panels: BluePanel, RedPanel and GreenPanel
      - download them ASAP, using each time a separate thread
      - ensure with enabled/disabled buttons that the order of use (B, R, G) is respected.

      This is basically what you would like to do. You will see that the applet can serve you as a
      template for your own problem and that the classes developed are already quite generic.
      For the example, the blue panel will be available after 5 sec., the red one afer 10 and the
      last after 15. You will have to remove these artificial delays from the code. Feel free to
      use this code, even for commercial use. I post as a comment. Tell me if you can consider
      this as an answer. I don't have that much time to comment the code but if you have
      questions, don't hesitate.

      import java.util.*;
      import java.awt.*;
      import java.awt.event.*;

      import com.sun.java.swing.*;

      public class MyApplet extends JApplet {

          public void init() {  
              JPanel panel = new JPanel();
              panel.setLayout(new CardLayout());

              Card card1 = new Card(panel,"BluePanel"  ,"RedPanel");
              Card card2 = new Card(panel,"RedPanel"   ,"GreenPanel");
              Card card3 = new Card(panel,"GreenPanel" ,null);

              // download the BluePanel in the background
              GUILoader loader1 = new GUILoader("BluePanel");
              loader1.addObserver(card1);

              // download the RedPanel in the background
              GUILoader loader2 = new GUILoader("RedPanel");
              loader2.addObserver(card1);
              loader2.addObserver(card2);

              // download the GreenPanel in the background
              GUILoader loader3 = new GUILoader("GreenPanel");
              loader3.addObserver(card2);
              loader3.addObserver(card3);

              panel.add("one"  , card1);
           panel.add("two"  , card2);
          panel.add("three", card3);

              getContentPane().add(panel);
           }  

          public static void main(String args[]) {
              MyApplet applet = new MyApplet();
              applet.init();

              JFrame frame = new JFrame("Test");
              frame.getContentPane().add(applet);
              frame.setBounds(0,0,300,300);
              frame.show();
              return;        
          }
      }

      class Card extends JPanel implements Observer, ActionListener {

          private JButton   button    = null;
          private String    thisCard  = "";
          private String    nextCard  = "";
          private Container container = null;

          public Card(Container container, String thisCard, String nextCard) {    
              this.container = container;  
              this.thisCard = thisCard;
              this.nextCard = nextCard;

              setLayout(new BorderLayout());

              if (nextCard != null) {
                  button = new JButton(">>");
                  button.setEnabled(false);
                  button.addActionListener(this);

                  add("South",button);
              }
             
          }

          public void actionPerformed(ActionEvent e) {
              ((CardLayout)container.getLayout()).next(container);  
              return;
          }

          public void update(Observable o, Object arg) {
              String card = (String)arg;

              if ((nextCard != null) && card.equals(nextCard)) {
                  button.setEnabled(true);
                  return;
              }

              if (card.equals(thisCard)) {
                  add("Center", getPanel(thisCard));
                  invalidate();
                  validate();
                  repaint();
                  return;
              }

              return;        
          }

          public JPanel getPanel(String name) {
              try {
                 Class  c = Class.forName(name);
                 Object o = c.newInstance();

                  return (JPanel)o;
              } catch (Exception e) {
                  e.printStackTrace();
              }

              return null;
          }

      }

      // loads a class in the background
      class GUILoader extends Observable implements Runnable {

          private static int i=0; // for the demo
          private String name   = "";
          private static Vector vector = new Vector(5,5);

          // name: the name of the class to load
          public GUILoader(String name) {
              this.name = name;

              Thread thread = new Thread(this);
              thread.start();
          }

          public void run() {
              if ((name == null) || (vector.contains(name))) {
                  return;
              } else {
                  vector.addElement(name);
              }

              try {
                  Class.forName(name);

                  i+=5000; // for the demo
                  Thread.currentThread().sleep(i); // for the demo

                  setChanged();
                  notifyObservers(name);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return;
          }

      }

      class BluePanel extends JPanel {

          public BluePanel() {
              setBackground(Color.blue);
          }
      }

      class RedPanel extends JPanel {

          public RedPanel() {
              setBackground(Color.red);
          }
      }

      class GreenPanel extends JPanel {

          public GreenPanel() {
              setBackground(Color.green);
          }
      }

The key of the design is the following. GUILoader is an Observable object. Whenever
      one tries to load a new class, GUILoader takes a look at its internal database ("vector")
      to see if it is not (or has) already loading (-ed) it. If not, it loads it and, when finished, it notifies
      its  observers. In the present case, the observers are the Card instances, that display the
      panels and the button. When a Card is notified that the panel class corresponding to what
      would be displayed in the next window has been downloaded, the button is enabled. When
      a Card is notified that the panel class corresponding to what it has to display has been
      downloaded, it creates a panel instance and displays it.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
The viewer will learn how to implement Singleton Design Pattern in Java.
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now