Programování

Nakreslete si cestu k vlastním komponentám grafu

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.

$config[zx-auto] not found$config[zx-overlay] not found