2013-10-05 29 views
0

作爲一名java非專家,我想知道如何更改下面的代碼以使其工作。這是我想要做的如何顯示/更新JFrame中java中的按鍵後的圖像?

  • Java代碼的時候被稱爲args包含多個圖像文件名
  • 我想看到的第一個圖像在此列表中
  • 然後當我按一個鍵,索引是改變,並顯示下一個圖像。

使用下面進行,這裏的建議是編譯,運行一段代碼:

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


public class LoadImageApp extends Component { 

    BufferedImage img; 
    private static int index = 0; 

    public void paint(Graphics g) { 
     g.drawImage(img, 0, 0, null); 
    } 

    public LoadImageApp(String filename) { 
     try { 
      img = ImageIO.read(new File(filename)); 
     } catch (IOException e) { 
      System.out.println(e.getMessage()); 
      System.exit(0); 
     } 
    } 

    public Dimension getPreferredSize() { 
     if (img == null) { 
      return new Dimension(100,100); 
     } else { 
      return new Dimension(img.getWidth(null), img.getHeight(null)); 
     } 
    } 

    static public void changeImage(JFrame frame, String filename) { 
     System.out.println("Using file "+filename); 
     frame.add(new LoadImageApp(filename)); 
     frame.pack(); 
     frame.setVisible(true); 
     frame.repaint(); 
    } 

    public static void main(final String[] args) { 

     char c=0; 
     String filename = args[0]; 
     int numberImages = args.length; 

     final JFrame f = new JFrame("Load Image Sample"); 
     BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 

     f.addKeyListener(new KeyAdapter(){ 
       public void keyPressed(KeyEvent event) { 
        int key = event.getKeyCode(); 
        if (key==81) { 
         System.exit(0); 
        } else if (key==89) { 
         System.out.println("yes"); 
        } else if (key==78) { 
         System.out.println("no"); 
        } 
        index += 1; 
        f.removeAll(); 
        changeImage(f, args[index]); 
       } 
      }); 

     // show first image here 
     changeImage(f, args[index]); 

    } 
} 

如果我用用類似的代碼

java LoadImageApp *.jpg 

它只顯示第一個圖像。我可以按鍵,但顯示的圖像不會改變。我想要改變圖像。我發現revalidate()repaint()作爲可能的解決方案。儘管frame.revalidate()完全不存在,但frame.repaint()(在changeImage之內)仍然沒有任何改變。我仍然看到顯示的第一個圖像。

無論如何這是正確的方法嗎?有沒有更優雅的方式?

+0

如果你不喜歡'最終'變量不使用匿名類。爲'KeyAdapter'創建一個常規類。 – omainegra

+0

我不知道最後的變量是什麼。或者如果在我的情況下如何使用它們,如果解決了我的問題。 – Alex

+0

爲了儘快提供更好的幫助,請發佈[SSCCE](http://sscce.org/)。 –

回答

2

我寫了一個程序來證明你問什麼,這裏是代碼:

import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.event.KeyAdapter; 
import java.awt.event.KeyEvent; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import java.util.LinkedList; 
import javax.imageio.ImageIO; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class ImageShow { 

    /** Inner class: JPanel that displays images. **/ 
    static class JImagePanel extends JPanel { 

    protected final LinkedList<BufferedImage> images; 
    protected BufferedImage currentImage; 
    protected int currentIndex; 

    public JImagePanel(final LinkedList<BufferedImage> images) { 
     super(true); 
     this.setFocusable(false); 
     this.images = images; 
     this.setIndex(0); 
    } 

    /** Has to be private to not cause issues when used in the constructor. **/ 
    private void setIndex(final int index) { 
     if (index >= this.images.size()) { 
     this.currentIndex = 0; 
     } else if (index < 0) { 
     this.currentIndex = this.images.size() - 1; 
     } else { 
     this.currentIndex = index; 
     } 

     this.currentImage = this.images.get(this.currentIndex); 
     this.setPreferredSize(new Dimension(this.currentImage.getWidth(), this.currentImage.getHeight())); 
     this.repaint(); 
    } 

    public void shiftIndex(final int amount) { 
     this.setIndex(this.currentIndex + amount); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     g.drawImage(this.currentImage, 0, 0, null); 
    } 
    } 

    public static void main(final String[] args) { 
    final LinkedList<BufferedImage> images = loadImages(args); 
    if (images.size() > 0) { 
     final JFrame window = new JFrame("Image Show"); 
     window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     final JImagePanel imagePanel = new JImagePanel(images); 
     window.add(imagePanel); 

     window.addKeyListener(new KeyAdapter() { 
     private void shiftIndex(final int amount) { 
      imagePanel.shiftIndex(amount); 
      window.pack(); 
      window.setLocationRelativeTo(null); 
     } 

     @Override 
     public void keyReleased(KeyEvent e) { 
      switch (e.getKeyCode()) { 
      case KeyEvent.VK_ESCAPE: 
       window.dispose(); 
       e.consume(); 
       break; 
      case KeyEvent.VK_LEFT: 
      case KeyEvent.VK_NUMPAD4: 
       shiftIndex(-1); 
       e.consume(); 
       break; 
      case KeyEvent.VK_RIGHT: 
      case KeyEvent.VK_NUMPAD6: 
       shiftIndex(+1); 
       e.consume(); 
       break; 
      } 
     } 
     }); 

     window.pack(); 
     window.setLocationRelativeTo(null); 
     window.setVisible(true); 
    } else { 
     System.err.println("No image could be loaded.\nPlease provide a list of image files as parameters."); 
    } 
    } 

    private static LinkedList<BufferedImage> loadImages(final String[] filenames) { 
    final LinkedList<BufferedImage> result = new LinkedList<>(); 
    for (String filename : filenames) { 
     try { 
     final File file = new File(filename); 
     final BufferedImage image = ImageIO.read(file); 
     if (image == null) { 
      throw new IOException("Unknown image format"); 
     } 
     result.add(image); 
     } catch (IOException e) { 
     System.err.println("Unable to load image \"" + filename + "\": " + e.getMessage()); 
     } 
    } 
    return result; 
    } 
} 

請注意,這不是寫這個工具的最美麗的方式,但它的工作原理。

你通常應該做的:

  • 每類應該在自己的java文件。這個想法是有一個結構,即使你3年後重新訪問這個代碼也很容易閱讀。

  • 你不應該使用其他範圍的變量,就像我在這裏在main函數中使用window和imagePanel一樣。相反,使用存儲局部變量的構造函數,可以使用給定的值或值的副本(取決於您的需要),就像我在JImagePanel構造函數中那樣。
    您是否需要價值副本取決於您所做的事情以及您願意承擔多少風險。在這個例子中,在創建JImagePanel後更改圖像列表可能會造成混亂。

  • 你不應該在你的版本的關鍵偵聽器中使用像你一樣的數字。你永遠不知道哪個鍵碼對應哪個鍵!只要可用,使用提供的常量或函數來獲得這樣一個「魔術」數字。

  • 在處理錯誤時總是期待最糟糕的。一次嘗試捕捉並處理所有可能的錯誤。其次,總是儘量避免潛在的問題。一個你不能製造的錯誤,是一個你不必擔心的錯誤。
    在您的版本中,每次按下按鈕時都會從磁盤加載映像文件。如果圖像在那一刻不再存在,會發生什麼?在我的版本中,所有事情都會在手前進行檢查,一旦完成,程序就不會再失敗(至少在嘗試切換圖像時不會)。 ;)

  • 一般來說:嘗試爲初學者找到一本關於Java的好書或在線教程。如果你只是謬以千里,你會錯過所有這些美好的東西的Java已經爲您準備好,不僅會加快發展多,這會以及防止否則你可能會在代碼中的所有錯誤。

+0

看到您的代碼存在問題: ImageShow。java:100:非法啓動類型 final LinkedList result = new LinkedList <>(); – Alex

+0

這是對Java 7有效的代碼。如果使用舊版本,則需要更改「new LinkedList <>();」到「新的LinkedList ();」 – TwoThe

+0

終於!非常感謝。我將嘗試獲得一本涵蓋所有java特定基礎知識,信號,事件,圖形,搖擺的書籍/教程。我擔心沒有這樣的書/教程... – Alex

0

這或多或少是你想要的。我建議你從頭開始學習Java,填補你的空白。

public class LoadImageApp extends JPanel { 

     //Current image the Canvas shows 
     private Image currentImage; 

     //change the current image and repaint 
     public void setCurrentImage(Image currentImage) { 
       this.currentImage = currentImage;     
       repaint(); 
     } 

     @Override 
     public void paintComponent(Graphics g) { 
       g.clearRect(0, 0, getWidth(), getHeight()); 
       if (currentImage != null) 
       g.drawImage(currentImage, 0, 0, null); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
       if (currentImage == null) { 
         return new Dimension(100, 100); 
       } else { 
         return new Dimension(currentImage.getWidth(null), currentImage.getHeight(null)); 
       } 
     } 

     public static void main(final String[] args) throws IOException { 

       if (args.length > 0){ 
         final Image [] images = new Image[args.length]; 

         for (int i = 0; i < images.length; i++){ 
           images[i] = ImageIO.read(new File(args[i])); 
         } 

         //It is a goog practice to attach your code to AWT thread. 
         SwingUtilities.invokeLater(new Runnable() {         
           @Override 
           public void run() { 
             LoadImageApp app = new LoadImageApp(); 
             JFrame f = new JFrame("Load Image Sample"); 

             f.getContentPane().setLayout(new BorderLayout()); 
             f.getContentPane().add(app, BorderLayout.CENTER); 

             f.addKeyListener(new MyKeyAdapter(app, images)); 
             f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

             f.setVisible(true); 
           } 
         }); 
       } 
     }   
    } 

    // We create another class for the KeyAdapter 
    class MyKeyAdapter extends KeyAdapter{ 

     //The canvas so we can tell it that the current image has change 
     private LoadImageApp canvas; 

     //index of the current image 
     private int index; 

     //All the images 
     private Image [] images; 

     public MyKeyAdapter(LoadImageApp canvas, Image[] images) { 
       super(); 
       this.canvas = canvas; 
       this.images = images;      
       rotateRight(); 
     } 

     @Override 
     public void keyPressed(KeyEvent event) { 
       int key = event.getKeyCode();      
       if (key == 81) { 
         System.exit(0); 
       } else if (key == KeyEvent.VK_LEFT) { 
         rotateLeft(); 
       } else if (key == KeyEvent.VK_RIGHT) { 
         rotateRight(); 
       } 
     } 

     private void rotateRight() { 
       //change the image in the canvas 
       canvas.setCurrentImage(images[index]);      
       //increment index 
       index++;      
       //last element + 1, set it to 0 
       if (index >= images.length) index = 0; 
     } 

     private void rotateLeft() { 
       //change the image in the canvas 
       canvas.setCurrentImage(images[index]);      
       //decrement index 
       index--;      
       //< 0, set it to last image 
       if (index < 0) index = images.length - 1; 
     } 
} 
+0

這並不能解決我的問題。我可以聲明'f'類型的'常量',但是我仍然在引用'args'和'index'時遇到錯誤。我不能將index作爲final來聲明,因爲它正在改變。用'args'我可以想出一個解決方案。 – Alex

+0

@Alex這聽起來像是笑話,你有或沒有....,爲更好的幫助,儘快發佈SSCCE,短,可運行,可編譯,我敢打賭,你用局部變量擴展JFrame (靜態公共無效嗯,這是地獄之路,爲什麼有靜態....) – mKorbel

+0

對不起,在'java'中從來沒有做過多少。 – Alex

0

關鍵字final表示變量初始化後不會改變。爲了在匿名內部類中使用變量,即KeyAdapter,它必須被聲明爲最終的。

所以,你需要:你是在不斷變化的IT規劃

public static void main(final String[] args) { 

final JFrame f = new JFrame("Load Image Sample"); 

但這不會爲index工作。所以我建議將它聲明爲類級別的靜態變量,即在函數之外。

private static int index = 0; 
+0

謝謝,代碼現在編譯!但它只顯示一個圖像(列表中的第一個)。所以我猜想在JFrame上調用'removeAll'或'add'是不正確的。 – Alex

+0

請參閱我的問題中的可編譯代碼 – Alex

+0

我認爲您的問題是現在您正在執行'java LoadImageApp * .jpg'您的參數數組只有一個項目。這是'* .jpg'。如果你通過'java LoadImageApp img1.jpg img2.jpg img3.jpg',它可能會工作。如果您希望能夠執行'* .jpg'操作,您將需要使用Java文件API來列出代碼中的圖像。 –