Tharwen
Tharwen

Reputation: 3107

Swing components drawing inside each other

This is my fourth question here in about two weeks... I think I have a lot to learn.

Anyway, I'm hoping the sample I've prepared below will explain the problem better than I can, but since I changed all of my components to Swing rather than AWT, they've all been drawing every component in the panel inside themselves whenever I call repaint().

This code is essentially my program stripped down as much as I can without destroying the problem completely. I've stuck a couple of my custom listbox components in there to demonstrate the issue, and made the border red to make it slightly easier to see what's drawing where.

Test.java:

package test;

import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;

public class Test extends JFrame
{
    public static void main(String[] args)
    {           
        Test test1 = new Test();
    }

    public Test()
    {
        super("Test Frame");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                     
        setVisible(true);
        setSize(800, 480);
        setLocationRelativeTo(null);

        loadTestScreen();
    }   

    public void loadTestScreen()
    {
        TestScreen newTestScreen = new TestScreen();
        newTestScreen.setSize(new Dimension(getWidth() - getInsets().left - getInsets().right, getHeight() - getInsets().top - getInsets().bottom));
        setContentPane(newTestScreen);
    }

}

TestScreen.java:

package test;

import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;
import javax.imageio.*;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.util.*;
import java.net.URI;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

public class TestScreen extends JPanel implements ComponentListener, MouseListener
{
    int border, LT;

    public TestScreen()
    {
        setLayout(null);
        setOpaque(true);
        setBackground(Color.WHITE);
        addComponentListener(this);

        border = 8;
        LT = 4;

        ///////////////////////////////////////////////////////////

        ArrayList<String> testList = new ArrayList<String>();
        testList.add("1");
        testList.add("2");
        testList.add("3");
        testList.add("4");
        testList.add("5");
        testList.add("6");

        add(new ListBox(testList), 0);
        add(new ListBox(testList), 1);
    }

    public void paintComponent(Graphics g)
    {
        Graphics2D g2d = (Graphics2D)g;
        g2d.setStroke(new BasicStroke((float)(LT)));

        g2d.setColor(new Color(255, 0, 0));
        g2d.drawRoundRect(border, border, getWidth() - border - border, getHeight() - border - border, border + border, border + border);
    }


    public void componentHidden(ComponentEvent e){}
    public void componentShown(ComponentEvent e){}
    public void componentMoved(ComponentEvent e)
    {
        componentResized(e);
    }
    public void componentResized(ComponentEvent e)
    {       
        getComponent(0).setLocation(20, 20);
        getComponent(0).setSize(100, 100);

        getComponent(1).setLocation(200, 200);
        getComponent(1).setSize(150, 150);

        repaint();
    }

    public void mouseEntered(MouseEvent e)
    {
            repaint();
    }
    public void mouseExited(MouseEvent e)
    {
            repaint();  
    }

    public void mouseReleased(MouseEvent e){}  public void mouseDragged(MouseEvent e){} public void mouseClicked(MouseEvent e){}

    public void mousePressed(MouseEvent e)
    {
        repaint();
    }
}

ListBox.java:

package test;

import java.beans.*;
import java.util.*;
import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.ComponentListener;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeSupport;
import javax.swing.*;

public class ListBox extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener, ComponentListener
{
    public ArrayList<String> data;
    int border, LT;
    int selectedIndex = 0;
    int mousedIndex = -1;
    public int viewedHeight = 0;
    int itemHeight;
    int numberOfDisplayedItems;
    Font miniFont;
    FontMetrics miniMetrics;
    private final PropertyChangeSupport pcs = new PropertyChangeSupport( this );

    public ListBox(ArrayList<String> list)
    {
        setVisible(true);
        setOpaque(true);

        border = 8;
        LT = 4;

        addMouseListener(this);
        addMouseMotionListener(this);
        addComponentListener(this);
        addMouseWheelListener(this);

        data = list;
        miniFont = new Font("Calibri", Font.PLAIN, 15);
        miniMetrics = getFontMetrics(miniFont);

        itemHeight = miniMetrics.getAscent() + miniMetrics.getDescent() + border + border;
    }

    public void paintComponent(Graphics g)
    {   
        Graphics2D g2d = (Graphics2D)g;
        g2d.setColor(new Color(93, 138, 168));
        g2d.setStroke(new BasicStroke((float)LT / 2));
        g2d.setClip(-(LT / 2), -(LT / 2), getWidth() + LT, getHeight() + LT);

        int cumulativeDist = -viewedHeight;

        for (int i = 0; i < data.size(); i++)
        {
            if (selectedIndex == i)
                g2d.fillRect(0, cumulativeDist, getWidth(), itemHeight);

            cumulativeDist += itemHeight;
            g2d.drawLine(0, cumulativeDist, getWidth(), cumulativeDist);
        }
        g2d.drawRect(0, 0, getWidth(), getHeight());

        g2d.setFont(miniFont);
        g2d.setColor(new Color(42, 60, 76));

        cumulativeDist = -viewedHeight + border + miniMetrics.getAscent();

        for (int i = 0; i < data.size(); i++)
        {
            if (mousedIndex == i){
            g2d.drawString(data.get(i), border + border / 2, cumulativeDist);
            } else {
            g2d.drawString(data.get(i), border, cumulativeDist);
            }

            cumulativeDist += itemHeight;
        }

    }

    public String getSelectedItem()
    {
        return data.get(selectedIndex);
    }   
    public void mouseReleased(MouseEvent e){}  public void mouseEntered(MouseEvent e){}  public void mouseDragged(MouseEvent e){}public void mouseClicked(MouseEvent e){}

    public void mousePressed(MouseEvent e)
    {
        int old = selectedIndex;
        int mouseHeight = viewedHeight + e.getY();
        int ID = mouseHeight / itemHeight;
        System.out.println(mouseHeight / itemHeight);
        selectedIndex = ID;
        pcs.firePropertyChange("selectedIndex", old, selectedIndex);
    }

    public void componentHidden(ComponentEvent e){} public void componentShown(ComponentEvent e){} public void componentMoved(ComponentEvent e){}
    public void componentResized(ComponentEvent e)
    {
        numberOfDisplayedItems = (int)((getHeight() / itemHeight) + 0.5);
        repaint();
    }

    public void mouseWheelMoved(MouseWheelEvent e)
    {
        if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
        {
            if (e.getUnitsToScroll() > 0)
            {
            viewedHeight += itemHeight;
            }else{
            viewedHeight -= itemHeight;
            }

            if (viewedHeight > (data.size() * itemHeight) - getHeight())
                viewedHeight = (data.size() * itemHeight) - getHeight();

            if (viewedHeight < 0)
            {
                viewedHeight = 0;
            }

            mouseMoved((MouseEvent)e);

            repaint();
        }
    }

    public void mouseMoved(MouseEvent e)
    {
        int mouseHeight = viewedHeight + e.getY();
        int ID = mouseHeight / itemHeight;
        mousedIndex = ID;
        repaint();
    }

    public void mouseExited(MouseEvent e)
    {
        mousedIndex = -1;
        repaint();
    }
}

Thanks, and if you find any other glaring issues in my programming, please don't hesitate to tell me :P

Upvotes: 1

Views: 840

Answers (2)

trashgod
trashgod

Reputation: 205785

The immediate problem—painting artifacts in ListBox—is caused by not honoring the opaque property. Using true promises to paint all of the bits, but your paintComponent() fails to do so.

Here are a few more suggestions:

  1. Don't neglect the event dispatch thread.

  2. Do use event listeners, but also consider the available adapters, which have convenient null implementations.

  3. Don't reinvent existing components, and override paintComponent() only as a last resort.

  4. Do learn to use layout managers and borders.

Yes there is much to learn, but this kind of experimentation is invaluable.

Upvotes: 3

I&#241;igo Beitia
I&#241;igo Beitia

Reputation: 6353

You should call setVisible(true); only once you've set up the component, at the end of your constructor.

Upvotes: 1

Related Questions