Naše vlastní komponenty grafu vyžadují ruční kreslení, takže budeme muset podtřídu Plátno
, což je standardní součást poskytovaná pro přímou manipulaci s grafikou. Technika, kterou použijeme, bude přepsat malovat
metoda Plátno
s vlastní kresbou, kterou potřebujeme. Budeme používat Grafika
objekt, který je automaticky předán do malovat
metoda všech komponent, přístup k barvám a metodám kreslení.
Vytvoříme dvě vlastní komponenty grafů: sloupcový graf a spojnicový graf. Začneme vytvořením třídy obecného rámce pro dva grafy, které sdílejí některé základní prvky.
Vytváření rámce obecného grafu
Čárový graf a sloupcový graf, které budeme vytvářet, jsou dostatečně podobné, abychom mohli vytvořit obecný
Graf
třídy, abyste provedli některé zdlouhavé práce s uspořádáním. Jakmile to bude hotové, můžeme rozšířit třídu pro konkrétní druh grafu, který potřebujeme.
První věcí, kterou musíte udělat, když navrhujete vlastní grafické komponenty, je přiložit pero k papíru a nakreslit obrázek toho, co potřebujete. Protože počítáme pixely, je snadné se pomýlit s umístěním prvků. Když se zamyslíte nad pojmenováním a umístěním prvků, pomůže vám to udržet kód čistší a snáze čitelný později.
Čárový graf a sloupcový graf používají stejné rozložení pro nadpis a řádky, takže začneme vytvořením obecného grafu obsahujícího tyto dvě funkce. Rozložení, které vytvoříme, je znázorněno na obrázku níže.

Chcete-li vytvořit obecný Graf
třída, budeme podtřídu Plátno
. Střední oblast je místo, kde budou zobrazena aktuální data grafu; ponecháme to na rozšíření Graf
provádět. Ostatní prvky - záhlaví, svislou čáru vlevo, vodorovnou čáru dole a hodnoty rozsahu - implementujeme do základní třídy. Mohli bychom určit písmo a pevně kódovat měření pixelů, ale uživatel by nebyl schopen změnit velikost grafu. Lepším přístupem je měření prvků proti proud velikost komponenty, takže změna velikosti aplikace povede ke správné změně velikosti grafu.
Tady je náš plán: Vezmeme si Tětiva
titul, an int
minimální hodnota a int
maximální hodnota v konstruktoru. Poskytují nám všechny informace, které potřebujeme k rozvržení rámce. Ponecháme čtyři proměnné pro použití v podtřídách - horní
, dno
, vlevo, odjet
, a že jo
hodnoty pro hranice oblasti kreslení grafu. Tyto proměnné použijeme k výpočtu umístění položek grafu později. Začněme krátkým pohledem na Graf
deklarace třídy.
importovat java.awt. *; import java.util. *; public class Graph rozšiřuje Canvas {// potřebné proměnné public int top; veřejné int dno; public int left; veřejné právo; int názevVýška; int labelWidth; FontMetrics fm; int výplň = 4; Název řetězce; int min; int max; Vektorové předměty;
Chcete-li vypočítat správné umístění prvků grafu, musíme nejprve vypočítat oblasti v našem obecném rozložení grafu, které tvoří rámec. Abychom vylepšili vzhled komponenty, přidáme k vnějším okrajům výplň se 4 pixely. Přidáme nadpis na střed nahoře, s přihlédnutím k oblasti výplně. Abychom se ujistili, že graf není nakreslen v horní části textu, musíme z oblasti nadpisu odečíst výšku textu. Musíme udělat totéž pro min
a max
štítky rozsahu hodnot. Šířka tohoto textu je uložena v proměnné labelWidth
. Abychom mohli provádět měření, musíme zachovat odkaz na metriky písma. The položky
vektor se používá ke sledování všech jednotlivých položek, které byly přidány do komponenty Graf. Třída používaná k uchovávání proměnných souvisejících s položkami grafu je zahrnuta (a vysvětlena) za Graf
třída, která je zobrazena dále.
public Graph (název řetězce, int min, int max) {this.title = title; this.min = min; this.max = max; items = new Vector (); } // koncový konstruktor
Konstruktor vezme název grafu a rozsah hodnot a pro jednotlivé položky grafu vytvoříme prázdný vektor.
public void reshape (int x, int y, int width, int height) {super.reshape (x, y, width, height); fm = getFontMetrics (getFont ()); titleHeight = fm.getHeight (); labelWidth = Math.max (fm.stringWidth (nové celé číslo (min) .toString ()), fm.stringWidth (nové celé číslo (max) .toString ())) + 2; nahoře = výplň + titleHeight; bottom = size (). height - padding; left = polstrování + labelWidth; right = size (). width - polstrování; } // ukončit tvar
Poznámka: V JDK 1.1 se přetvarovat
metoda je nahrazena public void setBounds (Rectangle r)
. Podrobnosti najdete v dokumentaci API.
Přepíšeme přetvarovat
metoda, která se dědí po řetězci z Součástka
třída. The přetvarovat
metoda se volá při změně velikosti komponenty a při jejím prvním rozložení. Tuto metodu používáme ke shromažďování měření, takže se vždy aktualizují, pokud se změní velikost komponenty. Získáme metriky písma pro aktuální písmo a přiřadíme název Výška
proměnná maximální výška tohoto písma. Dostaneme maximální šířku štítků, otestujeme, abychom zjistili, který z nich je větší, a poté jej použijeme. The horní
, dno
, vlevo, odjet
, a že jo
proměnné se počítají z ostatních proměnných a představují hranice oblasti kreslení středového grafu. Tyto proměnné použijeme v podtřídách Graf
. Všimněte si, že všechna měření zohledňují a proud velikost komponenty, takže překreslení bude správné při jakékoli velikosti nebo aspektu. Pokud bychom použili pevně zakódované hodnoty, velikost komponenty nemohla být změněna.
Dále nakreslíme rámec pro graf.
public void paint (Grafika g) {// nakreslete název fm = getFontMetrics (getFont ()); g.drawString (title, (size (). width - fm.stringWidth (title)) / 2, nahoře); // nakreslete maximální a minimální hodnoty g.drawString (nové celé číslo (min). toString (), odsazení, dole); g.drawString (nové celé číslo (max). toString (), odsazení, nahoře + titleHeight); // nakreslete svislou a vodorovnou čáru g.drawLine (vlevo, nahoře, vlevo, dole); g.drawLine (vlevo, dole, vpravo, dole); } // konec barvy
Rámec je zakreslen do malovat
metoda. Nadpis a štítky nakreslíme na příslušná místa. Nakreslíme svislou čáru na levém okraji oblasti kreslení grafu a vodorovnou čáru na spodním okraji.
V tomto dalším fragmentu jsme nastavili upřednostňovanou velikost komponenty přepsáním preferovaná velikost
metoda. The preferovaná velikost
metoda je také zděděna z Součástka
třída. Komponenty mohou určit preferovanou velikost a minimální velikost. Vybral jsem preferovanou šířku 300 a preferovanou výšku 200. Správce rozložení tuto metodu zavolá, když vyloží komponentu.
public Dimension preferredSize () {return (new Dimension (300, 200)); }} // konec grafu
Poznámka: V JDK 1.1 se preferovaná velikost
metoda je nahrazena veřejná dimenze getPreferredSize ()
.
Dále potřebujeme zařízení pro přidávání a odebírání položek, které mají být grafovány.
public void addItem (název řetězce, hodnota int, barva sloupce) {items.addElement (nový GraphItem (název, hodnota, sloupec)); } // end addItem public void addItem (String name, int value) {items.addElement (new GraphItem (name, value, Color.black)); } // end addItem public void removeItem (String name) {for (int i = 0; i <items.size (); i ++) {if (((GraphItem) items.elementAt (i)). title.equals (name )) items.removeElementAt (i); }} // konec removeItem} // konec grafu
Vymodeloval jsem přidat položku
a removeItem
metody po podobných metodách v Výběr
třídy, takže kód bude mít známý pocit. Všimněte si, že používáme dva přidat položku
metody zde; potřebujeme způsob, jak přidat položky s barvou nebo bez ní. Když je přidána položka, nová GraphItem
objekt je vytvořen a přidán do vektoru položek. Když je položka odstraněna, bude odstraněna první ve vektoru s tímto názvem. The GraphItem
třída je velmi jednoduchá; zde je kód:
importovat java.awt. *; třída GraphItem {název řetězce; hodnota int; Barva barva; public GraphItem (název řetězce, hodnota int, barva barvy) {this.title = title; this.value = hodnota; this.color = color; } // end konstruktor} // end GraphItem
The GraphItem
třída funguje jako držitel proměnných vztahujících se k položkám grafu. Zahrnul jsem Barva
zde pro případ, že bude použit v podtřídě Graf
.
S tímto rámcem můžeme vytvořit rozšíření pro zpracování každého typu grafu. Tato strategie je docela pohodlná; nemusíme se znovu snažit měřit pixely pro rámec a můžeme vytvořit podtřídy, abychom se zaměřili na vyplnění oblasti kreslení grafu.
Vytvoření sloupcového grafu
Nyní, když máme grafický rámec, můžeme jej přizpůsobit rozšířením
Graf
a implementace vlastního výkresu. Začneme jednoduchým sloupcovým grafem, který můžeme použít stejně jako jakoukoli jinou komponentu. Typický sloupcový graf je znázorněn níže. Vyplníme oblast kreslení grafu přepsáním
malovat
metoda volání nadtřídy
malovat
metoda (nakreslit rámec), pak provedeme vlastní kreslení potřebné pro tento typ grafu.
importovat java.awt. *; veřejná třída BarChart rozšiřuje Graph {int pozici; přírůstek int; public BarChart (název řetězce, int min, int max) {super (název, min, max); } // koncový konstruktor
Abychom rovnoměrně rozmístili položky, ponecháme si přírůstek
proměnná k označení částky, kterou pro každou položku posuneme doprava. Proměnná pozice je aktuální poloha a hodnota přírůstku se k ní přidá pokaždé. Konstruktor jednoduše převezme hodnoty pro super konstruktor (Graf
), které výslovně nazýváme.
Nyní se můžeme dostat k nějakému skutečnému výkresu.
public void paint (Grafika g) {super.paint (g); přírůstek = (zprava doleva) / (items.size ()); pozice = vlevo; Teplota barev = g.getColor (); for (int i = 0; i <items.size (); i ++) {GraphItem item = (GraphItem) items.elementAt (i); int adjustValue = bottom - (((item.value - min) * (bottom - top)) / (max - min)); g.drawString (item.title, pozice + (přírůstek - fm.stringWidth (item.title)) / 2, adjustValue - 2); g.setColor (item.color); g.fillRect (poloha, upravená hodnota, přírůstek, spodní - upravená hodnota); pozice + = přírůstek; g.setColor (teplota); }} // konec barvy} // konec BarChart
Podívejme se blíže na to, co se tady děje. V malovat
metoda, nazýváme nadtřída malovat
metoda kreslení rámce grafu. Poté najdeme přírůstek
odečtením pravého okraje od levého okraje a následným vydělením výsledku počtem položek. Tato hodnota je vzdálenost mezi levými okraji položek grafu. Protože chceme, aby bylo možné změnit velikost grafu, zakládáme tyto hodnoty na aktuální hodnotě vlevo, odjet
a že jo
proměnné zděděné z Graf
. Připomeňme, že vlevo, odjet
, že jo
, horní
, a dno
hodnoty jsou aktuální skutečná pixelová měření oblasti kreslení grafu pořízená v přetvarovat
metoda Graf
, a proto k dispozici pro naše použití. Pokud bychom nezakládali svá měření na těchto hodnotách, nebylo by možné změnit velikost grafu.
Nakreslíme obdélníky v barvě určené znakem GraphItem
. Abychom se mohli vrátit k původní barvě, nastavili jsme dočasně barva
proměnná, která udrží aktuální hodnotu, než ji změníme. Cyklicky procházíme vektorem položek grafu, vypočítáme pro každou upravenou svislou hodnotu, nakreslíme název položky a vyplněný obdélník představující její hodnotu. Přírůstek je přidán do proměnné polohy x pokaždé ve smyčce.
Upravená svislá hodnota zajišťuje, že pokud je komponenta roztažena svisle, graf stále zůstane věrný svým vykresleným hodnotám. Abychom to udělali správně, musíme vzít procento rozsahu, který položka představuje, a vynásobit tuto hodnotu skutečným rozsahem pixelů v oblasti kreslení grafu. Poté odečteme výsledek od dno
hodnotu správně vykreslit bod.
Jak můžete vidět z následujícího diagramu, je celková horizontální velikost pixelu reprezentována symbolem vpravo vlevo a celková svislá velikost je reprezentována zdola nahoru.

Postaráme se o vodorovné natažení inicializací pozice
proměnná k levému okraji a její zvětšení o přírůstek
proměnná pro každou položku. Protože pozice
a přírůstek
proměnné jsou závislé na aktuálních aktuálních hodnotách pixelů, velikost komponenty je vždy správně změněna v horizontálním směru.
Abychom zajistili, že svislé vykreslování je vždy správné, musíme namapovat hodnoty položek grafu se skutečnými pixelovými umístěními. Existuje jedna komplikace: max
a min
hodnoty by měly být smysluplné pro pozici hodnoty položky grafu. Jinými slovy, pokud graf začíná na 150 a jde na 200, položka s hodnotou 175 by se měla objevit v polovině svislé osy. Abychom toho dosáhli, najdeme procento rozsahu grafu, které položka představuje, a vynásobíme ho skutečným rozsahem pixelů. Protože náš graf je obrácen od souřadného systému grafického kontextu, odečteme toto číslo od dno
najít správný bod vykreslení. Nezapomeňte, že počátek (0,0) je v levém horním rohu kódu, ale v levém dolním rohu pro styl grafu, který vytváříme.