2012-08-05 128 views
2

我试图通过制作一个谜题程序来提高我对Java的理解,特别是Java GUI。目前,用户选择一张图像,该图像被分割成指定数量的图像。这些作品是随机抽取到屏幕上的,但它们似乎被其他作品的空白部分覆盖,并不是所有作品都显示出来,但我可以打印出所有的坐标。我正在使用绝对定位,因为LayoutManager似乎不工作。我简单地尝试了layeredPanes,但他们困惑了我,似乎没有解决问题。我真的很感谢一些帮助。
这里有2相关的类:绝对定位图形JPanel里面的JFrame被空白部分阻塞

import javax.swing.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 

public class PuzzlePieceDriver extends JFrame 
{ 
    private static Dimension SCREENSIZE = Toolkit.getDefaultToolkit().getScreenSize(); 
    private static final int HEIGHT = SCREENSIZE.height; 
    private static final int WIDTH = SCREENSIZE.width; 

    public static int MY_WIDTH; 
    public static int MY_HEIGHT; 

    private static BufferedImage image; 


    private int xPieces = PuzzleMagicDriver.getXPieces(); 
    private int yPieces = PuzzleMagicDriver.getYPieces(); 

    private PuzzlePiece[] puzzle = new PuzzlePiece[xPieces*yPieces]; 

    public Container pane = this.getContentPane(); 
    private JLayeredPane layeredPane = new JLayeredPane(); 


    public PuzzlePieceDriver(ImageIcon myPuzzleImage) 
    { 
    MY_WIDTH = myPuzzleImage.getIconWidth()+(int)myPuzzleImage.getIconHeight()/2; 
    MY_HEIGHT = myPuzzleImage.getIconHeight()+(int)myPuzzleImage.getIconHeight()/2; 
    setTitle("Hot Puzz"); 
setSize(MY_WIDTH,MY_HEIGHT); 
setLocationByPlatform(true); 

pane.setLayout(null); 


image = iconToImage(myPuzzleImage); //pass image into bufferedImage form 

puzzle = createClip(image); 

//pane.add(layeredPane); 


setVisible(true); 
    }//end constructor 



    public static BufferedImage iconToImage(ImageIcon icon) 
    { 
    Image img = icon.getImage(); 
int w = img.getWidth(null); 
int h = img.getHeight(null); 
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 
Graphics g = image.createGraphics(); 
    // Paint the image onto the buffered image 
    g.drawImage(img, 0, 0, null); 
    g.dispose(); 

    return image; 
    }//end BufferedImage 

    protected int randomNumber(int min, int max) 
    { 
    int temp = 
    min + (int)(Math.random() * ((max - min) + 1)); 

return temp; 
    }//end randomNumber 


    private PuzzlePiece[] createClip(BufferedImage passedImage) 
    { 

int cw, ch; 
int w,h; 
w = image.getWidth(null); 
h = image.getHeight(null); 
cw = w/xPieces; 
    ch = h/yPieces; 

int[] cells=new int[xPieces*yPieces]; 

int dx, dy; 

BufferedImage clip = passedImage; 

//layeredPane.setPreferredSize(new Dimension(w,h)); 

    for (int x=0; x<xPieces; x++) 
     { 
     int sx = x*cw; 
     for (int y=0; y<yPieces; y++) 
      { 
      int sy = y*ch; 
      int cell = cells[x*xPieces+y]; 
      dx = (cell/xPieces) * cw; 
      dy = (cell % yPieces) * ch; 

      clip= passedImage.getSubimage(sx, sy, cw, ch); 
    int myX = randomNumber(0,(int)w); 
    int myY = randomNumber(0,(int)h); 

    PuzzlePiece piece=new PuzzlePiece(clip,myX,myY); 
    puzzle[x*xPieces+y]=piece; 
    piece.setBounds(myX,myY,w,h); 
    //layeredPane.setBounds(myX,myY,w,h); 
    //layeredPane.add(piece,new Integer(x*xPieces+y)); 
    pane.add(piece); 
    piece.repaint(); 

     }//end nested for 
}//end for 
return puzzle; 
    }//end createClip 

}//end class 

很抱歉,如果间距有点搞砸了!

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 
import java.awt.image.*; 

public class PuzzlePiece extends JPanel 
{ 
private Point imageCorner;  //the image's top-left corner location 
private Point prevPt;    //mouse location for previous event 
private Boolean insideImage =false; 

private BufferedImage image; 

public PuzzlePiece(BufferedImage clip, int x, int y) 
{ 
image = clip; 
imageCorner = new Point(x,y); 
//repaint(); 
}//end constructor 

public void paintComponent(Graphics g) 
{ 
    super.paintComponent(g); 
g.drawImage(image, (int)getImageCornerX(),(int)getImageCornerY(), this); 
    System.out.println("paint "+getImageCornerX()+" "+getImageCornerY()); 
    //repaint(); 
//g.dispose(); 
}//end paintComponent 

public Point getImageCorner() 
{ 
    return imageCorner; 
}//end getImageCorner 
public double getImageCornerY() 
{ 
    return imageCorner.getY(); 
}//end getImageCornerY 
public double getImageCornerX() 
{ 
    return imageCorner.getX(); 
}//end getPoint 


}//end class PuzzlePiece 

任何帮助将不胜感激,我真的被卡住了!谢谢!!

+0

确保您更新父容器以及与重绘。我个人会创建自己的LayoutManager来执行所需的操作,但是我会先尝试GridBagLayout;) – MadProgrammer 2012-08-05 20:24:17

+0

MigLayout比GridBadLayout工作得更好 - 它也非常易于使用。 – rtheunissen 2012-08-05 22:38:18

回答

3

在你的拼图构造函数中尝试使用setBorder(new LineBorder(Color.RED))来查看你的拼图的边界在哪里。如果他们在你期望的地方,那很可能你的定位是错误的。如果您要延伸JPanel,也可以将拼图延伸至JComponent,或使用setOpaque(false)

+0

谢谢!问题是我没有意识到getSubimage保留原始图像的边界,而不是子图像 – foolsgoldfish 2012-08-06 15:44:51

+0

边界可以是一个方便的调试工具。 :) – rtheunissen 2012-08-06 23:55:51

3

有很多的建议,我想打,但首先

你选择一个随机位置是关闭的方式......

int myX = randomNumber(0,(int)w); 
int myY = randomNumber(0,(int)h); 

这允许重复位置的是产生(并覆盖细胞)

UPDATES(利用布局管理)

好了,所以这是一个slig转向范式。而是制作一个剪辑并将其传递给作品,我允许作品选择关于如何渲染作品。相反,我将它负责的Rectangle传递给它。

这意味着,你可以简单地使用类似setCell(Rectangle)做一件改变(除非你一意孤行上拖放;))

我结束了使用Board面板由于一些有趣的行为Java 7的下,但这是另一个问题;)

package puzzel; 

import java.awt.BorderLayout; 
import java.awt.EventQueue; 
import javax.swing.*; 

public class PuzzlePieceDriver extends JFrame { 


    public PuzzlePieceDriver(ImageIcon myPuzzleImage) { 

     setTitle("Hot Puzz"); 

     setDefaultCloseOperation(EXIT_ON_CLOSE); 

     setLayout(new BorderLayout()); 
     add(new Board(myPuzzleImage)); 

     pack(); 

     setVisible(true); 


    }//end constructor 

    public static void main(String[] args) { 

     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 

       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException ex) { 
       } catch (InstantiationException ex) { 
       } catch (IllegalAccessException ex) { 
       } catch (UnsupportedLookAndFeelException ex) { 
       } 

       ImageIcon image = new ImageIcon(PuzzlePieceDriver.class.getResource("/issue459.jpg")); 

       PuzzlePieceDriver driver = new PuzzlePieceDriver(image); 
       driver.setLocationRelativeTo(null); 
       driver.setVisible(true); 

      } 
     }); 

    } 
}//end class 

一块面板... 面板覆盖preferredminimum大小的方法......而它的工作原理在这个例子中,它可能会更好使用setPreferredSizesetMiniumumSize代替;)

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package puzzel; 

import javax.swing.*; 
import java.awt.*; 
import java.awt.image.*; 

public class PuzzlePiece extends JPanel { 

    private BufferedImage masterImage; 
    private Rectangle pieceBounds; 
    private BufferedImage clip; 

    public PuzzlePiece(BufferedImage image, Rectangle bounds) { 

     masterImage = image; 
     pieceBounds = bounds; 

     // Make sure the rectangle fits the image 
     int width = Math.min(pieceBounds.x + pieceBounds.width, image.getWidth() - pieceBounds.x); 
     int height = Math.min(pieceBounds.y + pieceBounds.height, image.getHeight() - pieceBounds.y); 

     clip = image.getSubimage(pieceBounds.x, pieceBounds.y, width, height); 

    }//end constructor 

    @Override 
    public Dimension getPreferredSize() { 

     return pieceBounds.getSize(); 

    } 

    @Override 
    public Dimension getMinimumSize() { 

     return getPreferredSize(); 

    } 

    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     int x = 0; 
     int y = 0; 

     g.drawImage(clip, x, y, this); 

     g.setColor(Color.RED); 
     g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); 

    }//end paintComponent 

}//end class PuzzlePiece 

板面板...使用,主要是因为我是用Java 7中有一些有趣的问题......实现一个MouseListener,当你运行该程序,请单击板,它的乐趣; )

package puzzel; 

import java.awt.Graphics; 
import java.awt.GridBagConstraints; 
import java.awt.GridBagLayout; 
import java.awt.Image; 
import java.awt.Rectangle; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import java.awt.image.BufferedImage; 
import java.util.ArrayList; 
import java.util.List; 
import javax.swing.ImageIcon; 
import javax.swing.JPanel; 

/** 
* 
* @author shane 
*/ 
public class Board extends JPanel { 

    public static final int X_PIECES = 4; 
    public static final int Y_PIECES = 4; 
    private PuzzlePiece[] puzzle = new PuzzlePiece[X_PIECES * Y_PIECES]; 

    private static BufferedImage image; 

    public Board(ImageIcon myPuzzleImage) { 

     image = iconToImage(myPuzzleImage); //pass image into bufferedImage form 

     puzzle = createClip(); 

     addMouseListener(new MouseAdapter() { 
      @Override 
      public void mouseClicked(MouseEvent e) { 

       removeAll(); 

       invalidate(); 

       createClip(); 

//    doLayout(); 

       invalidate(); 
       revalidate(); 
       repaint(); 

      } 
     }); 

    } 

    public static BufferedImage iconToImage(ImageIcon icon) { 
     Image img = icon.getImage(); 
     int w = img.getWidth(null); 
     int h = img.getHeight(null); 
     BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 
     Graphics g = image.createGraphics(); 
     // Paint the image onto the buffered image 
     g.drawImage(img, 0, 0, null); 
     g.dispose(); 

     return image; 
    }//end BufferedImage 

    protected int randomNumber(int min, int max) { 
     int temp = min + (int) (Math.random() * ((max - min) + 1)); 

     return temp; 
    }//end randomNumber 

    private PuzzlePiece[] createClip() { 

     int cw, ch; 
     int w, h; 
     w = image.getWidth(null); 
     h = image.getHeight(null); 
     cw = w/X_PIECES; 
     ch = h/Y_PIECES; 

     // Generate a list of cell bounds 
     List<Rectangle> lstBounds = new ArrayList<>(25); 

     for (int y = 0; y < h; y += ch) { 

      for (int x = 0; x < w; x += cw) { 

       lstBounds.add(new Rectangle(x, y, cw, ch)); 

      } 

     } 

     BufferedImage clip = image; 

     setLayout(new GridBagLayout()); 

     for (int x = 0; x < X_PIECES; x++) { 
      for (int y = 0; y < Y_PIECES; y++) { 

       // Get a random index 
       int index = randomNumber(0, lstBounds.size() - 1); 

       // Remove the bounds so we don't duplicate any positions 
       Rectangle bounds = lstBounds.remove(index); 

       PuzzlePiece piece = new PuzzlePiece(clip, bounds); 
       puzzle[x * X_PIECES + y] = piece; 

       GridBagConstraints gbc = new GridBagConstraints(); 
       gbc.gridx = x; 
       gbc.gridy = y; 
       gbc.fill = GridBagConstraints.BOTH; 

       add(piece, gbc); 
       piece.invalidate(); 
       piece.repaint(); 

      }//end nested for 
     }//end for 

     invalidate(); 
     repaint(); 

     return puzzle; 
    }//end createClip 

} 

现在我知道你最终要问有关如何移动一块,GridBagLayout有这个叫做getConstraints精彩的方法,它允许您检索使用的布局有问题的组件的约束。然后,您可以修改gridxgridy值,并使用setConstraints来更新它(不要忘记调用invalidaterepaint;))

我建议具有How to Use GridBagLayout读取更多的信息)

最终,你最终会喜欢的东西:

Puzzle 1Puzzle 2

+0

你真的是一个疯狂的程序员,先生。 :) – rtheunissen 2012-08-06 07:37:11

+0

爱好良好的图形挑战 – MadProgrammer 2012-08-06 08:08:59

+0

+1不错:) :) – 2013-01-26 12:06:01

4

我真的被这个想法很感兴趣,所以我做了另一个例子,使用自定义布局管理河

public class MyPuzzelBoard extends JPanel { 

    public static final int GRID_X = 4; 
    public static final int GRID_Y = 4; 
    private BufferedImage image; 

    public MyPuzzelBoard(BufferedImage image) { 
     setLayout(new VirtualLayoutManager()); 
     setImage(image); 

     addMouseListener(new MouseAdapter() { 
      @Override 
      public void mouseClicked(MouseEvent e) { 
       if (e.getClickCount() == 2) { 
        removeAll(); 
        generatePuzzel(); 
       } else { 
        Component comp = getComponentAt(e.getPoint()); 
        if (comp != null && comp != MyPuzzelBoard.this) { 
         setComponentZOrder(comp, 0); 
         invalidate(); 
         revalidate(); 
         repaint(); 
        } 
       } 
      } 
     }); 
    } 

    public void setImage(BufferedImage value) { 
     if (value != image) { 
      image = value; 
      removeAll(); 
      generatePuzzel(); 
     } 
    } 

    public BufferedImage getImage() { 
     return image; 
    } 

    protected float generateRandomNumber() { 
     return (float) Math.random(); 
    } 

    protected void generatePuzzel() { 
     BufferedImage image = getImage(); 

     if (image != null) { 
      int imageWidth = image.getWidth(); 
      int imageHeight = image.getHeight(); 

      int clipWidth = imageWidth/GRID_X; 
      int clipHeight = imageHeight/GRID_Y; 
      for (int x = 0; x < GRID_X; x++) { 
       for (int y = 0; y < GRID_Y; y++) { 

        float xPos = generateRandomNumber(); 
        float yPos = generateRandomNumber(); 
        Rectangle bounds = new Rectangle((x * clipWidth), (y * clipHeight), clipWidth, clipHeight); 
        MyPiece piece = new MyPiece(image, bounds); 
        add(piece, new VirtualPoint(xPos, yPos)); 

       } 
      } 
     } 

     invalidate(); 
     revalidate(); 
     repaint(); 
    } 

    public class VirtualPoint { 

     private float x; 
     private float y; 

     public VirtualPoint(float x, float y) { 
      this.x = x; 
      this.y = y; 
     } 

     public float getX() { 
      return x; 
     } 

     public float getY() { 
      return y; 
     } 

     public void setX(float x) { 
      this.x = x; 
     } 

     public void setY(float y) { 
      this.y = y; 
     } 
    } 

    public class VirtualLayoutManager implements LayoutManager2 { 

     private Map<Component, VirtualPoint> mapConstraints; 

     public VirtualLayoutManager() { 
      mapConstraints = new WeakHashMap<>(25); 
     } 

     @Override 
     public void addLayoutComponent(Component comp, Object constraints) { 
      if (constraints instanceof VirtualPoint) { 
       mapConstraints.put(comp, (VirtualPoint) constraints); 
      } 
     } 

     @Override 
     public Dimension maximumLayoutSize(Container target) { 
      return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 
     } 

     @Override 
     public float getLayoutAlignmentX(Container target) { 
      return 0.5f; 
     } 

     @Override 
     public float getLayoutAlignmentY(Container target) { 
      return 0.5f; 
     } 

     @Override 
     public void invalidateLayout(Container target) { 
     } 

     @Override 
     public void addLayoutComponent(String name, Component comp) { 
     } 

     @Override 
     public void removeLayoutComponent(Component comp) { 
      mapConstraints.remove(comp); 
     } 

     @Override 
     public Dimension preferredLayoutSize(Container parent) { 
      return new Dimension(400, 400); 
     } 

     @Override 
     public Dimension minimumLayoutSize(Container parent) { 
      return preferredLayoutSize(parent); 
     } 

     @Override 
     public void layoutContainer(Container parent) { 
      int width = parent.getWidth(); 
      int height = parent.getHeight(); 

      for (Component comp : parent.getComponents()) { 

       VirtualPoint p = mapConstraints.get(comp); 
       if (p != null) { 

        int x = Math.round(width * p.getX()); 
        int y = Math.round(height * p.getY()); 

        Dimension size = comp.getPreferredSize(); 

        x = Math.min(x, width - size.width); 
        y = Math.min(y, height - size.height); 

        comp.setBounds(x, y, size.width, size.height); 

       } 
      } 
     } 
    } 
} 

基本上,这使用了“虚拟”坐标系中,其中通过而不是在像素提供绝对X/Y位置,则提供它们作为父容器的百分比。现在,说实话,将其转换回绝对定位并不需要太多,就这样,您也可以获得布局缩放。

该实施例还证明Z-reording(以防万一)和双点击简单的重新随机化puzzel

啊,我也取得了一块透明的(opaque = false

Randomized layout

哦,我应该提到的一点是,在通过这个例子的时候,我发现可以将部分片段放置在屏幕外(完全和部分)。

您可能要检查你的定位代码,以确保当他们奠定了图像没有被移动关闭屏幕;)

+0

谢谢你的帖子是如此非常有帮助!这正是我解决空白面板问题后需要继续努力的地方:) – foolsgoldfish 2012-08-06 15:46:21