Před několika měsíci jsem byl požádán, abych vytvořil malou knihovnu Java, do které bude mít přístup aplikace k vykreslení grafického uživatelského rozhraní (GUI) pro hru Checkers. Kromě vykreslení šachovnice a šachovnice musí grafické uživatelské rozhraní umožnit přetažení kontroly z jednoho čtverce na druhý. Kontrola musí být také vycentrována na čtverec a nesmí být přiřazena čtverci, který je obsazen jiným hráčem. V tomto příspěvku představuji svou knihovnu.
Návrh knihovny GUI dámy
Jaké veřejné typy by měla knihovna podporovat? V dámě každý ze dvou hráčů střídavě přesouvá jednu ze svých pravidelných (ne králových) dám po desce pouze v dopředném směru a případně přeskočí dámu druhého hráče. Když se dáma dostane na druhou stranu, je povýšen na krále, který se může pohybovat také dozadu. Z tohoto popisu můžeme odvodit následující typy:
Prkno
Kontrola
CheckerType
Hráč
A Prkno
objekt identifikuje šachovnici. Slouží jako kontejner pro Kontrola
objekty, které zabírají různé čtverce. Může se kreslit a požadovat, aby každý obsahoval Kontrola
nakreslit objekt sám.
A Kontrola
objekt identifikuje kontrolu. Má barvu a označení, zda jde o běžnou kontrolu nebo kontrolu krále. Může se kreslit a zpřístupňovat svou velikost Prkno
, jehož velikost je ovlivněna Kontrola
velikost.
CheckerType
je výčet, který identifikuje barvu a typ kontroly pomocí svých čtyř konstant: BLACK_KING
, BLACK_REGULAR
, RED_KING
, a RED_REGULAR
.
A Hráč
objekt je řadič pro pohyb dáma s volitelnými skoky. Protože jsem se rozhodl implementovat tuto hru ve Swingu, Hráč
není nutné. Místo toho jsem se otočil Prkno
do komponenty Swing, jejíž konstruktor registruje myš a posluchače pohybu myší, kteří zpracovávají pohyb kontroly jménem lidského hráče. V budoucnu bych mohl implementovat počítačový přehrávač pomocí jiného vlákna, synchronizátoru a jiného Prkno
metoda (např hýbat se()
).
Co dělají veřejné API Prkno
a Kontrola
přispět? Po chvíli přemýšlení jsem přišel s následující veřejností Prkno
API:
Prkno()
: Postavte aPrkno
objekt. Konstruktor provádí různé inicializační úlohy, jako je registrace posluchače.void add (Checker checker, int řádek, int sloupec)
: Přidatkontrola
naPrkno
v poloze označenéřádek
asloupec
. Řádek a sloupec jsou hodnoty založené na 1 na rozdíl od hodnoty 0 (viz obrázek 1). Thepřidat()
hodíjava.lang.IllegalArgumentException
když jeho argument řádku nebo sloupce je menší než 1 nebo větší než 8. Také hodí nezaškrtnutéAlreadyOccupiedException
když se pokusíte přidat aKontrola
na obsazené náměstí.Dimenze getPreferredSize ()
: VraťtePrkno
preferovaná velikost komponenty pro účely rozvržení.
Obrázek 1. Levý horní roh šachovnice je umístěn na (1, 1)
Také jsem vyvinul následující veřejnost Kontrola
API:
Kontrola (CheckerType checkerType)
: Postavte aKontrola
objekt zadanéhocheckerType
(BLACK_KING
,BLACK_REGULAR
,RED_KING
neboRED_REGULAR
).void draw (Grafika g, int cx, int cy)
: NakresliKontrola
pomocí zadaného grafického kontextuG
se středem kontroly umístěným na (cx
,cy
). Tato metoda má být volána zPrkno
pouze.boolean obsahuje (int x, int y, int cx, int cy)
: Astatický
pomocná metoda volaná zPrkno
který určuje, zda souřadnice myši (X
,y
) leží uvnitř kontroly, jejíž středové souřadnice jsou specifikovány (cx
,cy
) a jehož rozměr je uveden jinde v EUKontrola
třída.int getDimension ()
: Astatický
pomocná metoda volaná zPrkno
který určuje velikost kontroly, aby deska mohla odpovídajícím způsobem velikost svých čtverců a celkové velikosti.
To do značné míry pokrývá všechny knihovny GUI dámy z hlediska jejich typů a jejich veřejných API. Nyní se zaměříme na to, jak jsem implementoval tuto knihovnu.
Implementace knihovny GUI dáma
Knihovna GUI dámy se skládá ze čtyř veřejných typů umístěných ve zdrojových souborech se stejným názvem: AlreadyOccupiedException
, Prkno
, Kontrola
, a CheckerType
. Výpis 1 dárků AlreadyOccupiedException
zdrojový kód.
Výpis 1. AlreadyOccupiedException.java
public class AlreadyOccupiedException rozšiřuje RuntimeException {public AlreadyOccupiedException (String msg) {super (msg); }}
AlreadyOccupiedException
rozšiřuje java.lang.RuntimeException
, který dělá AlreadyOccupiedException
nezaškrtnutá výjimka (nemusí být chycena nebo deklarována v hodí
doložka). Kdybych chtěl udělat AlreadyOccupiedException
zkontrolováno, prodloužil bych java.lang.Exception
. Rozhodl jsem se, že tento typ nechám nezaškrtnutý, protože funguje podobně jako nezaškrtnutý IllegalArgumentException
.
AlreadyOccupiedException
deklaruje konstruktor, který přebírá řetězcový argument popisující důvod výjimky. Tento argument je předán RuntimeException
nadtřída.
Seznam 2 dárků Prkno
.
Výpis 2. Board.java
importovat java.awt.Color; importovat java.awt.Dimension; importovat java.awt.Graphics; import java.awt.Graphics2D; importovat java.awt.RenderingHints; import java.awt.event.MouseEvent; importovat java.awt.event.MouseAdapter; importovat java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; public class Board extends JComponent {// dimenze šachovnicového čtverce (o 25% větší než checker) private final static int SQUAREDIM = (int) (Checker.getDimension () * 1,25); // rozměr šachovnice (šířka 8 čtverců) soukromý konečný int BOARDDIM = 8 * SQUAREDIM; // preferovaná velikost komponenty Board private Dimension dimPrefSize; // příznak přetažení - nastaven na true, když uživatel stiskne tlačítko myši nad kontrolou // a vymazán na false, když uživatel uvolní tlačítko myši private boolean inDrag = false; // posun mezi souřadnicemi startu drag a souřadnicemi centra checkeru private int deltaxe, deltay; // odkaz na kontrolu polohy na začátku přetahování soukromý PosCheck posCheck; // umístění středu checkeru na začátku přetažení private int oldcx, oldcy; // seznam objektů Checker a jejich počátečních pozic private List posChecks; public Board () {posChecks = new ArrayList (); dimPrefSize = nová dimenze (BOARDDIM, BOARDDIM); addMouseListener (new MouseAdapter () {@Override public void mousePressed (MouseEvent me) {// Získat souřadnice myši v době tisku. int x = me.getX (); int y = me.getY (); // Vyhledat poziční kontrolu pod stiskem myši. pro (PosCheck posCheck: posChecks) if (Checker.contains (x, y, posCheck.cx, posCheck.cy)) {Board.this.posCheck = posCheck; oldcx = posCheck.cx; oldcy = posCheck.cy ; deltax = x - posCheck.cx; deltay = y - posCheck.cy; inDrag = true; návrat;}} @Override public void mouseReleased (MouseEvent me) {// Když myš uvolníte, vymažte inDrag (pro // žádný tah právě probíhá) pokud je inDrag // již nastaven. if (inDrag) inDrag = false; else return; // Kontrola snapu do středu čtverce. int x = me.getX (); int y = me.getY (); posCheck .cx = (x - deltaxe) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; // Neposunujte šachovnici na obsazený čtverec. pro (PosCheck posCheck : posChecks) if (posCheck! = Board.this.posCheck && posC heck.cx == Board.this.posCheck.cx && posCheck.cy == Board.this.posCheck.cy) {Board.this.posCheck.cx = oldcx; Board.this.posCheck.cy = oldcy; } posCheck = null; překreslit (); }}); // Připojte k appletu posluchač pohybu myši. Tento posluchač naslouchá // událostem přetažení myší. addMouseMotionListener (nový MouseMotionAdapter () {@Override public void mouseDragging (MouseEvent me) {if (inDrag) {// Aktualizovat umístění centra kontroly. posCheck.cx = me.getX () - deltax; posCheck.cy = me.getY ( ) - deltay; překreslit ();}}}); } public void add (Checker checker, int row, int col) {if (row 8) throw new IllegalArgumentException ("row out of range:" + row); if (col 8) throw new IllegalArgumentException ("col out of range:" + col); PosCheck posCheck = nový PosCheck (); posCheck.checker = kontrola; posCheck.cx = (sloupec - 1) * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (řádek - 1) * SQUAREDIM + SQUAREDIM / 2; for (PosCheck _posCheck: posChecks) if (posCheck.cx == _posCheck.cx && posCheck.cy == _posCheck.cy) throw new AlreadyOccupiedException ("square at (" + row + "," + col + ") is employed" ); posChecks.add (posCheck); } @Override public Dimension getPreferredSize () {return dimPrefSize; } @Override protected void paintComponent (Grafika g) {paintCheckerBoard (g); pro (PosCheck posCheck: posChecks) if (posCheck! = Board.this.posCheck) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); // Nakreslete naposledy přetaženou kontrolu, aby se zobrazila nad jakoukoli základní // kontrolou. if (posCheck! = null) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); } private void paintCheckerBoard (Graphics g) {((Graphics2D) g) .setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Malování šachovnice. for (int řádek = 0; řádek <8; řádek ++) {g.setColor (((řádek & 1)! = 0)? Color.BLACK: Color.WHITE); for (int col = 0; col <8; col ++) {g.fillRect (col * SQUAREDIM, row * SQUAREDIM, SQUAREDIM, SQUAREDIM); g.setColor ((g.getColor () == Color.BLACK)? Color.WHITE: Color.BLACK); }}} // pomocná třída umisťované kontroly checkeru soukromá třída PosCheck {public Checker checker; public int cx; veřejné int; }}
Prkno
rozšiřuje javax.swing.JComponent
, který dělá Prkno
komponenta Swing. Jako takový můžete přímo přidat a Prkno
komponenta do podokna obsahu aplikace Swing.
Prkno
prohlašuje SQUAREDIM
a BOARDDIM
konstanty, které identifikují rozměry pixelů čtverce a zaškrtávacího políčka. Při inicializaci SQUAREDIM
, Dovolávám se Checker.getDimension ()
místo přístupu k rovnocenné veřejnosti Kontrola
konstantní. Joshua Block odpovídá, proč to dělám v položce # 30 (místo toho použijte enums int
konstanty) druhého vydání jeho knihy, Efektivní Java: "Programy, které používají int
vzor výčtu je křehký. Protože int
výčty jsou konstanty v době kompilace, jsou kompilovány do klientů, kteří je používají. Pokud int
přidružené ke konstantě výčtu se změní, je nutné překompilovat klienty. Pokud tomu tak není, budou i nadále běžet, ale jejich chování bude nedefinováno. “
Kvůli obsáhlým komentářům k tomu nemám moc co říct Prkno
. Všimněte si však vnořených PosCheck
třída, která popisuje kontrolu polohy uložením a Kontrola
reference a její středové souřadnice, které jsou relativní k levému hornímu rohu Prkno
součástka. Když přidáte a Kontrola
namítat proti Prkno
, je uložen v novém PosCheck
objekt spolu se středovou pozicí kontroly, která se počítá ze zadaného řádku a sloupce.
Seznam 3 dárků Kontrola
.