【问题标题】:Getting a custom JPanel to show custom JComponents (Java Swing)获取自定义 JPanel 以显示自定义 JComponents (Java Swing)
【发布时间】:2014-10-26 01:56:53
【问题描述】:

我对这个家伙很茫然。我正在尝试创建一个自定义 LED 显示屏以显示 7 个排列为 8 的条。我有一个自定义 JComponent(条)显示在 JFrame 内部,但我无法让条显示在我的自定义面板内部创造。这是我的类中的构造方法和绘制方法的代码,以及我用来测试这些类的主要方法。

自定义 JComponent:

public class Bar extends JComponent
{
    // instance variables - replace the example below with your own
    private static boolean litUp = false;
    private static boolean vertical = false;
    private static boolean rotated = false;
    private static boolean rotClockwise = false;
    private static int positionX;
    private static int positionY;



    public Bar(boolean lit, boolean vert, int posX, int posY)
    {
        litUp = lit;
        vertical = vert;
        positionX = posX;
        positionY = posY;
        repaint();
        System.out.println("The bar is being initialized");
    }
    public void paintComponent(Graphics g)
    {
        System.out.println("BAR: Paint Component being called");

        super.paintComponent(g);
        Graphics2D g2D = (Graphics2D)g;

        if(vertical == true)
        {
            if(litUp == true)
            {
                g2D.setColor(Color.red);
            }
            else
            {
                g2D.setColor(Color.black);
            }

            g2D.drawRect(positionX , positionY, 10, 30);
            g2D.fillRect(positionX , positionY, 10, 30);
            System.out.println("BAR: fillRect is being called for a vertical bar");
            if(rotated == true)
            {
                if(rotClockwise == true)
                {
                    g2D.rotate(0.3398);
                }
                else
                {
                    g2D.rotate(-0.3398);
                }
            }

        }

        else{

            System.out.println("BAR: fillRect is being called for a horizontal bar");
            if(litUp == true)
            {
                g2D.setColor(Color.red);
            }
            else
            {
                g2D.setColor(Color.black);
            }
            g2D.drawRect(positionX,positionY, 30, 10);
            g2D.fillRect(positionX,positionY, 30, 10);
        }


    }
}

自定义 JPanel:

public class LED extends JPanel
{
    // instance variables - replace the example below with your own
    //private static Bar[] bars = new Bar[7];
    //private static int xPos;
    //private static int yPos;

    private  Bar barZero;
    private  Bar barOne;
    private  Bar barTwo;
    private  Bar barThree;
    private  Bar barFour;
    private  Bar barFive;
    private  Bar barSix;




    /**
     * Constructor for objects of class LED
     */
    public LED()
    {
        barZero = new Bar(false, false,  0, 0);
        this.add(barZero);
       // barZero.setDirection(false);
        barOne = new Bar(false, true, 0, 11);
        this.add(barOne);
        //barOne.setDirection(true);
        barTwo = new Bar(false, true, 20, 11);
        this.add(barTwo);
        //barTwo.setDirection(true);
        barThree = new Bar(false, false, 0, 42);
        this.add(barThree);
        //barThree.setDirection(false);
        barFour = new Bar(false, true, 0, 53);
        this.add(barFour);
        //barFour.setDirection(true);
        barFive = new Bar(false, true, 20, 53);
        this.add(barFive);
        //barFive.setDirection(true);
        barSix = new Bar(false, false, 0, 64);
        this.add(barSix);
        //barSix.setDirection(false);



        System.out.println("The LED class is being accessed");

        repaint();
    }

@ Override public void paintComponent(Graphics g)
    {
        System.out.println("LED: PaintComponent being called");
        //barOne.paintComponent(g);
        System.out.println("LED: barZero being painted| " + barZero.orientation() + "|  " + barZero.coordX());

        System.out.println("LED: barOne being painted| " + barOne.orientation() + "|  " + barOne.coordX());
        //barTwo.paintComponent(g);
        System.out.println("LED: barTwo being painted| " + barTwo.orientation() + "|  " + barTwo.coordX());

        //barThree.paintComponent(g);
        System.out.println("LED: barThree being painted| " + barThree.orientation() + "|  " + barThree.coordX());
        //barFour.paintComponent(g);
        System.out.println("LED: barFour being painted| " + barFour.orientation() + "|  " + barFour.coordX());
        //barFive.paintComponent(g);
        System.out.println("LED: barFive being painted| " + barFive.orientation() + "|  " + barFive.coordX());
        //barSix.paintComponent(g);
        System.out.println("LED: barSix being painted| " + barSix.orientation() + "|  " + barSix.coordX());


        super.paintComponent(g);
    }
}

以及测试方法:

public class DrawRect {
  public static void main(String[] a) {
    JFrame window = new JFrame();
    LED led = new LED()
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setBounds(30, 30, 300, 300);
    window.getContentPane().add(led);
    window.setVisible(true);
  }
}

我可以初始化一个 Bar 并让它显示在 Frame 中,但我无法让 LED(面板)在其中显示 bar。此外,这里是从测试组件中打印出来的字符串。我添加的所有条都没有设置它们的值:它们都是水平的,并且它们的 x 位置都设置为 0。我不是要放弃的人,但是这个程序让我想改变我的专业。

【问题讨论】:

  • 为了尽快获得更好的帮助,请发布MCVE(最小完整可验证示例)。这意味着一个包含导入的源文件(可能包含多个类文件),而不是三个。
  • 自定义组件应覆盖 getPreferredSize() 以向布局管理器提供有关大小的提示。
  • 一个不相关的问题,但您的 Bar 类对 static 修饰符有多种不当使用。大多数这些字段应该是实例字段,而不是静态字段。
  • 另一个建议:使用数组或列表将Bars 存储在您的自定义面板中。这将使使用 for 循环绘制它们变得非常容易。
  • 取出静态修饰符并将条添加到数组中。 Bar 类的 getPreferredSize() 方法有点模糊。该组件将有两种尺寸。您仍然建议将其覆盖吗?

标签: java swing jpanel jcomponent


【解决方案1】:

所以你已经很接近了。我认为你能做的最好的事情是降低复杂性,甚至比使用 ArrayList 还要多,并且只使用已经存在的组件。这是一个使用 Horizo​​ntalBox 的更简单的示例。

我只展示了一种方向和一种尺寸,但我认为您可以自己解决其余的问题。 paintComponent() 例程中可能有一个错误,该错误偏离了一个像素。我不能用足够的眼力盯着屏幕来确定。

也不要忘记整个多线程的事情。请在事件调度线程上。我的 IDE 中有几个宏,它们基本上可以在大约两秒内为 EDT 构建代码。没有任何借口。

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class LedBar extends JComponent
{
   private final Dimension SIZE = new Dimension( 20, 50 );
   private float value = 50f; // range 0-100

   public void setValue( float value )
   {
      this.value = value;
   }

   public float getValue()
   {
      return value;
   }

   @Override
   public void paintComponent( Graphics g ) {
      super.paintComponent( g );
      g.setColor( Color.RED );
      g.drawRect( 0, 0, SIZE.width, SIZE.height );
      g.setColor( Color.RED.darker() );
      final int top = (int)(SIZE.height*(100f-value)/100f+1);
      final int bottom = (int)(SIZE.height*(value)/100f-1);
      g.fillRect( 1, top, SIZE.width-1, bottom);
   }

   @Override
   public Dimension getPreferredSize() {
      return SIZE;
   }
}

class Test
{
   public static void main( String[] args ) {
      SwingUtilities.invokeLater( new Runnable() {
         public void run() {
            JFrame frame = new JFrame();

            Box hbox = Box.createHorizontalBox();
            for (int i = 0; i < 5; i++) {
               LedBar led = new LedBar();
               led.setValue( i * 20f );
               hbox.add( led );
            }
            frame.add( hbox );

            frame.pack();
            frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
            frame.setLocationRelativeTo( null );
            frame.setVisible( true );
         }
      } );
   }
}

【讨论】:

  • class Test { public static void main( String[] args ) {.. 呵呵。这是我第一次记得看到一个非public 类在不同public 类的源末尾粘​​贴了main(String[])。为什么不将 main 复制/粘贴到公共类中并完全丢弃 test 类?
  • 好吧,因为这样 main 方法将成为 LedBar 类的一部分。照原样,Test 类是独立的,从不向 LedBar 类文件添加字节。 (我的 IDE 会在源文件中找到任何具有 main 方法的类并运行它,因此这只是在单个源文件中创建两个类(一个类和一个测试类)的一种方便方法。)
  • @markspace 我肯定还在遇到一些多线程问题。我在 Bar 类的paintComponent 方法中有一个打印语句。测试器类是稍后用调用编写的,我正在制作一个栏并将其添加到框架中,但我仍然为一个栏获得多个打印语句。我做错了什么?
【解决方案2】:

这里有几个问题需要考虑:

  1. 每个Bar 对象都需要自己的变量来跟踪其状态。这意味着Bar 中的成员变量应该是静态的。

  2. 自定义组件需要覆盖getPreferredSize(),以告知其父容器自定义组件想要占用多少空间。

  3. 父容器负责确定它所包含的组件的位置。 (更恰当地说,这是 LayoutManager 的职责。)这意味着在自定义组件中设置 x 和 y 位置是不合适的。

  4. paintComponent(Graphics g) 中,您只能在组件拥有的区域内进行绘制。坐标相对于组件的左上角,相对于容纳它的容器的左上角。这意味着您需要绘制一个覆盖整个组件的矩形:

    g.drawRectangle(0, 0, getWidth(), getHeight());
    
  5. 您可以通过使用数组或ListBar 对象存储在自定义面板中来降低代码的一些复杂性。例如,您将能够在一个简单的 for 循环中绘制每个 Bar

【讨论】:

    猜你喜欢
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    相关资源
    最近更新 更多