2012-08-05 35 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