001 /** 002 * @author Václav Mikolášek 003 * nicklaus@students.zcu.cz 004 */ 005 006 package animace.label; 007 008 import animace.*; 009 import javax.swing.*; 010 import javax.imageio.*; 011 import java.awt.*; 012 import java.awt.image.*; 013 import java.awt.event.*; 014 import java.io.*; 015 import java.util.*; 016 017 018 /** 019 * Třída MoveablePicture poskytuje ukázku, jak by měli vypadat 020 * grafické komponenty pro třídu animace.label.PictureLabel. 021 * Tedy komponenty pro systém, kde se programátor stará o vykreslování sám. 022 * Podstatný je princip vykreslování. Objekt pri nějaké změně (pohyb) nastaví 023 * proměnou needUpdate na true a volá repaint(). Metoda repaint() je volána s parametry 024 * označující tzv. dirty region. Takto by měla být tato metoda volána pokaždé, jinak dochází často zbytečně 025 * k překreslováni celé obrazovky (panelu). 026 * I když není potřeba objekt překreslit, protože na něm nedošlo ke změně (needUpdate nastaveno na false), 027 * musíme ještě otestovat, zda jej jiný objekt "nepřejel", 028 * to se zjistí z Clip oblasti nastavené v Graphics g. Viz níže metoda paint(Graphics g). 029 * Dále stojí za povšimnuti, že je implementováno přidávání posluchačů udalostí stejně, jako jsme zvyklí 030 * u klasických komponent, tedy metodou add"Něco"Listener, v tomto případě addActionListener. To je proto, 031 * že lze očekávat, že se na objekty bude klikat. V komponentě, na které tyto objekty visí, je nutné ještě 032 * zajistit, že bude volat metodu processEvent(...) nad tou komponentou, na kterou uživatel kliknul. 033 * Je toho dost co musíme napsat, abychom zajistili základní funkčnost. 034 */ 035 public class MoveablePicture implements Moveable, PaintControled { 036 private Component owner; 037 private BufferedImage img; 038 private PaintControl pc = null; 039 private ActionListener actionListener = null; 040 041 /** 042 * Indikuje, že na objetku nastaly změny, a že je nutné jej znovu překreslit. 043 */ 044 public boolean needUpdate = true; 045 046 private int x = 0; // location 047 private int y = 0; // location 048 private int imgWidth = 0; 049 private int imgHeight = 0; 050 051 /** 052 * Konstruktor vyžaduje jako parametr grafického vlastnika (rodiče) objektu, 053 * tedy nejakou Componentu na ktere spočívá. 054 * @param owner Componenta, na které je MoveablePicture nakreslen 055 * @param img vlastní obrázek, který se bude pohybovat 056 */ 057 public MoveablePicture(Component owner,BufferedImage img) { 058 this.owner = owner; 059 this.img = img; 060 imgWidth = img.getWidth(); 061 imgHeight = img.getHeight(); 062 } 063 064 /** 065 * Nakreslí tento objekt do grafického kontextu předaného parametrem. 066 * Algortimus je následující. Pokud se s objektem hýbalo a je třeba jej překreslit, 067 * needUpdate == true, pak nic nezmůžeme a musíme jej kreslit. Ale pokud needUpdate == false a 068 * tento objekt není v "dirty regionu" (g.getClipBounds()) kreslit jej nemusíme a ušetříme čas. 069 */ 070 public void paint(Graphics g) { 071 if (!needUpdate) { 072 Rectangle toDraw = g.getClipBounds().intersection(new Rectangle(x,y,imgWidth,imgHeight)); 073 if (!toDraw.isEmpty()) { 074 g.drawImage(img,x,y,imgWidth,imgHeight,null); 075 } 076 } 077 else { 078 g.drawImage(img,x,y,imgWidth,imgHeight,null); 079 needUpdate = false; 080 } 081 } 082 083 /** 084 * Vrací obrázek 085 */ 086 public BufferedImage getImage() { 087 return img; 088 } 089 090 /** 091 * Vrací preferovanou velikost - rozměry obrázku. 092 */ 093 public Dimension getPrefferedSize(){ 094 return new Dimension(img.getWidth(),img.getHeight()); 095 } 096 097 /** 098 * Vrací šířku obrázku 099 */ 100 public int getWidth() { 101 return img.getWidth(); 102 } 103 104 /** 105 * Vrací víšku obrázku 106 */ 107 public int getHeight() 108 { 109 return img.getHeight(); 110 } 111 112 /** 113 * Metoda z rozhraní Moveable. Nastaví novou pozici objektu a hodnotu needUpdate na true. 114 * Poté zavolá owner.repaint(int x, int y, int width, int height), která 115 * zaregistruje požadavek na překreslení pouze té části obrazovky, 116 * kde došlo ke změnám na grafice. 117 * Při zavolani této metody, nedochází k žádným kontrolám, 118 * zda jsou údaje správné 119 */ 120 public synchronized void setLocation(int x,int y) { 121 int left = (this.x < x ? this.x : x) - 1; 122 int up = (this.y < y ? this.y : y) - 1; 123 int dx = Math.abs(this.x - x) + 2; 124 int dy = Math.abs(this.y - y) + 2; 125 this.x = x; 126 this.y = y; 127 needUpdate = true; 128 owner.repaint(left,up,imgWidth+dx,imgHeight+dy); 129 } 130 131 /** 132 * Metoda z rozhrani Moveable, Funguje standardně, vrací pozici objektu 133 */ 134 public Point getLocation(){ 135 return new Point(x,y); 136 } 137 138 /** 139 * Metoda z rozhraní PaintControled 140 */ 141 public void setPaintControl(PaintControl pc) { 142 this.pc = pc; 143 } 144 145 /** 146 * Metoda z rozhraní PaintControled 147 */ 148 public PaintControl getPaintControl() { 149 return pc; 150 } 151 152 /** 153 * Metoda addActionListener, jak jsme zvyklí z java.AWT.Component 154 * Zde implementována pomocí třídy AWTEventMulticaster. 155 */ 156 public void addActionListener(ActionListener newActionListener) { 157 actionListener = AWTEventMulticaster.add(actionListener, newActionListener); 158 } 159 160 /** 161 * Metoda processEvent, kterou bude volat zejména předek objektu, 162 * v našem případě PictureLabel, provede zavolání metody actionPerformed(...) nad všemi 163 * zaregistrovanými posluchači. 164 */ 165 public void processEvent(AWTEvent e) { 166 if (actionListener != null) { 167 actionListener.actionPerformed(new ActionEvent(this,0,"")); 168 } 169 } 170 171 /** 172 * Vrací true, jestliže objekt obsahuje určitý bod, hodi se pri testu na kliknuti 173 * @param point bod, který je testován, zda leží na obrazku 174 */ 175 public boolean contains(Point point) { 176 return (new Rectangle(x,y,imgWidth,imgHeight)).contains(point); 177 } 178 179 } 180