2015-03-11 3 views
0

У меня есть JPanel внутри JScrollPane. В моей программе JPanel показывает изображение карты и показывает текущее местоположение транспортного средства. Моя программа получает местоположение и текущее направление транспортного средства. Мне нужно показать это на карте.Рисование стрелки в верхней левой части scrollpane

У меня возникла проблема с указанием направления движения автомобиля. Я хочу показать стрелку в левом верхнем углу видимой области карты, чтобы показать направление. Ниже приведен код, который я попробовал. Но он бросает исключение, когда я прокручиваю вниз.

Exception in thread "AWT-EventQueue-0" java.awt.image.RasterFormatException: Transformed height (-287) is less than or equal to 0. 
    at java.awt.image.AffineTransformOp.createCompatibleDestImage(Unknown Source) 
    at java.awt.image.AffineTransformOp.filter(Unknown Source) 
    at Test.paintComponent(Test.java:62) 
    at javax.swing.JComponent.paint(Unknown Source) 
    at javax.swing.JComponent.paintToOffscreen(Unknown Source) 


import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Rectangle; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.geom.AffineTransform; 
import java.awt.image.AffineTransformOp; 
import java.awt.image.BufferedImage; 
import java.io.IOException; 
import java.net.URL; 
import java.util.Random; 

import javax.imageio.ImageIO; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.Timer; 

public class Test extends JPanel implements ActionListener { 
    public static void main(String[] args) { 
     JFrame f = new JFrame(); 
     f.setSize(300, 400); 
     Test view = new Test(); 
     Dimension minimumSize = new Dimension(1000, 1000); 

     view.setBorder(BorderFactory.createLineBorder(Color.BLUE)); 
     JScrollPane comp = new JScrollPane(view); 
     comp.setBorder(BorderFactory.createLineBorder(Color.RED)); 
     f.getContentPane().add(comp, BorderLayout.CENTER); 
     f.setVisible(true); 
     view.setPreferredSize(minimumSize); 
    } 

    private BufferedImage i; 
    private Random r = new Random(); 

    public Test() { 
     try { 
      i = ImageIO 
        .read(new URL("https://cdn4.iconfinder.com/data/icons/cc_mono_icon_set/blacks/48x48/br_next.png")); 
      new Timer(1000, this).start(); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     Graphics2D g2d = (Graphics2D) g.create(); 
     AffineTransform transform = g2d.getTransform(); 

     transform.rotate(r.nextFloat() * Math.PI * 2, i.getWidth()/2, i.getHeight()/2); 
     AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR); 

     Rectangle cb = g.getClipBounds(); 
     g2d.drawImage(op.filter(i, null), (int) cb.getX(), (int) cb.getY(), null); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     repaint(); 
    } 

} 

Как я могу сделать стрелку всегда видимой в верхнем левом углу, даже если пользователь прокручивает с помощью полос прокрутки?

+0

Параметр 'JScrollPane' и' JViewPort' оптимизированы. Когда вы нарисованы, границы клипов компонента будут включать только те области, которые были изменены и нуждаются в обновлении в окне просмотра – MadProgrammer

+0

@MadProgrammer, используя то, как вы думаете, что я могу достичь этого? –

+0

Создайте свой собственный 'JViewPort' и нарисуйте стрелку над верхней частью содержимого. – MadProgrammer

ответ

2

JScrollPane и JViewport оба сильно оптимизированы, когда нарисованы, отсекающая граница устанавливаются только в области, которая на самом деле должна быть обновлена ​​(область, которая была введена)

Лучшей идея может быть, чтобы создать свой собственный JViewport, на которую вы можете закрасить содержимое окна просмотра

Viewport

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.geom.AffineTransform; 
import java.awt.image.BufferedImage; 
import java.io.IOException; 
import java.net.URL; 
import java.util.Random; 
import javax.imageio.ImageIO; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import javax.swing.JViewport; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 
import javax.swing.event.ChangeEvent; 
import javax.swing.event.ChangeListener; 

public class Test extends JPanel { 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
        ex.printStackTrace(); 
       } 

       JFrame f = new JFrame(); 
       f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       f.setSize(300, 400); 
       Test view = new Test(); 

       view.setBorder(BorderFactory.createLineBorder(Color.BLUE)); 
       JScrollPane comp = new JScrollPane(); 
       ViewPortDirection vp = new ViewPortDirection(); 
       comp.setViewport(vp); 
       comp.setViewportView(view); 
       comp.setBorder(BorderFactory.createLineBorder(Color.RED)); 
       f.getContentPane().add(comp, BorderLayout.CENTER); 
       f.setVisible(true); 
      } 
     }); 
    } 

    public static class ViewPortDirection extends JViewport { 

     private BufferedImage i; 
     private Random r = new Random(); 

     public ViewPortDirection() { 
      try { 
       i = ImageIO.read(new URL("https://cdn4.iconfinder.com/data/icons/cc_mono_icon_set/blacks/48x48/br_next.png")); 
       new Timer(1000, new ActionListener() { 
        @Override 
        public void actionPerformed(ActionEvent e) { 
         repaint(); 
        } 
       }).start(); 
      } catch (IOException e) { 
       throw new RuntimeException(e); 
      } 

      addChangeListener(new ChangeListener() { 
       @Override 
       public void stateChanged(ChangeEvent e) { 
        repaint(0, 0, i.getWidth(), i.getHeight()); 
       } 
      }); 
     } 

     @Override 
     public void paint(Graphics g) { 
      super.paint(g); 

      Graphics2D g2d = (Graphics2D) g.create(); 
      AffineTransform transform = AffineTransform.getRotateInstance(r.nextFloat() * Math.PI * 2, i.getWidth()/2, i.getHeight()/2); 

      int x = 0; 
      int y = 0; 
      g2d.setTransform(transform); 
      g2d.drawImage(i, x, y, this); 
      g2d.dispose(); 
     } 

    } 

    public Test() { 
     setBackground(Color.BLUE); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     return new Dimension(1000, 1000); 
    } 

} 
+0

Спасибо за ответ. Можете ли вы сказать мне, почему вы перегрузили метод 'paint' вместо' paintComponent'? И почему вы добавили «это» в качестве наблюдателя изображения? –

+0

1- Это один из тех редких случаев, когда вы должны переопределить краску, почему? Поскольку у JViewport на самом деле есть дочерний компонент, который окрашивается особым образом, переопределение paintComponent нарисовало бы стрелку под видом; 2- Почему нет? Вы всегда должны передавать «это» (где «this» является экземпляром JComponent) для drawImage, поскольку он позволяет компоненту реагировать на изменения, которые может вызвать изображение. Конечно, в этом случае вы знаете, что это BufferedImage, но вы не можете этого гарантировать;) – MadProgrammer

Смежные вопросы