Link to home
Start Free TrialLog in
Avatar of optimal
optimal

asked on

Swing components repaint when added after init

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.
Avatar of evijay
evijay

Can you give sample code

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?
Avatar of optimal

ASKER

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.
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);



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?
Avatar of optimal

ASKER

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.
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.
Avatar of optimal

ASKER

Thanks fontaine,

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

Thanks again,
Guy.
ASKER CERTIFIED SOLUTION
Avatar of fontaine
fontaine

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial