Ucrtajte svoj put do prilagođenih komponenti grafa

Naše prilagođene komponente grafikona zahtijevaju ručno crtanje, pa ćemo morati podrazred Canvas, što je standardna komponenta predviđena za izravno manipuliranje grafikom. Tehnika koju ćemo koristiti bit će nadjačavanje paintmetode Canvasprilagođenim crtežom koji nam treba. Mi ćemo koristiti Graphicsobjekt, koji automatski se prenosi u paintnačinu sve komponente, za pristup bojama i metode za crtanje.

Stvorit ćemo dvije prilagođene komponente grafičkog prikaza: trakasti grafikon i linijski grafikon. Počet ćemo s izgradnjom opće klase okvira za dva grafa koja dijele neke osnovne elemente.

Izgradnja generičkog okvira grafikona

Linijski grafikon i trakasti grafikon koji ćemo izgraditi dovoljno su slični da možemo stvoriti generički

Graph

razreda izvesti neke zamorne radove na rasporedu. Nakon što to završimo, možemo produžiti razred za određenu vrstu grafa koji nam treba.

Prva stvar koju trebate učiniti prilikom dizajniranja prilagođenih grafičkih komponenata je stavljanje olovke na papir i crtanje slike onoga što vam treba. Budući da računamo piksele, lako se miješamo oko postavljanja elemenata. Ako malo razmislite o imenovanju i pozicioniranju elemenata, pomoći će vam da kôd bude čišći i lakši za čitanje kasnije.

Linijski grafikon i trakasti grafikon koriste isti raspored za naslov i crte, pa ćemo započeti stvaranjem generičkog grafa koji sadrži ove dvije značajke. Izgled koji ćemo stvoriti prikazan je na donjoj slici.

Da bismo stvorili generičku Graphklasu, podrazredit ćemo Canvas. Središnje područje je mjesto gdje će se prikazati stvarni podaci grafa; ostavit ćemo ovo produženju Graphza provedbu. Ostale elemente - naslovnu traku, okomitu crtu lijevo, vodoravnu crtu na dnu i vrijednosti raspona implementirat ćemo u osnovnu klasu. Mogli bismo odrediti font i tvrdo kodirati mjerenja piksela, ali korisnik ne bi mogao promijeniti veličinu grafikona. Bolji pristup je mjerenje elemenata prema trenutnoj veličini komponente, tako da će promjena veličine aplikacije rezultirati ispravnom promjenom veličine grafa.

Evo našeg plana: Uzet ćemo Stringnaslov, intminimalnu vrijednost i intmaksimalnu vrijednost u konstruktoru. Oni nam daju sve informacije potrebne za postavljanje okvira. Zadržat ćemo četiri varijable koje se koriste u podrazreda - na top, bottom, lefti rightvrijednosti za granice graf crtanje regiji. Te ćemo varijable koristiti za kasnije izračunavanje pozicioniranja stavki grafikona. Počnimo s brzim Graphpregledom deklaracije klase.

import java.awt. *; uvoz java.util. *; javna klasa Graph proširuje Canvas {// varijable potrebne public int top; javni int dno; javni int lijevo; javno int pravo; int titleHeight; int labelWidth; FontMetrics fm; int padding = 4; Naslov niza; int min; int max; Predmeti vektora;

Da bismo izračunali ispravan smještaj elemenata grafikona, prvo moramo izračunati regije u našem generičkom rasporedu grafova koje čine okvir. Da bismo poboljšali izgled komponente, na vanjske rubove dodajemo oblogu od 4 piksela. Dodati ćemo naslov usredotočen na vrh, uzimajući u obzir područje dodavanja. Da bismo bili sigurni da graf nije nacrtan na vrhu teksta, moramo oduzeti visinu teksta od područja naslova. Moramo učiniti isto za oznake mini maxraspona vrijednosti. Širina ovog teksta pohranjena je u varijabli labelWidth. Moramo zadržati referencu na metriku fonta kako bismo mogli mjeriti. Theitemsvector koristi se za praćenje svih pojedinačnih stavki koje su dodane u komponentu Graph. Klasa koja se koristi za zadržavanje varijabli povezanih sa stavkama grafikona uključena je (i objašnjena) nakon Graphklase, što je prikazano sljedeće.

javni grafikon (naslov niza, int min, int max) {this.title = naslov; ovo.min = min; this.max = max; stavke = novi vektor (); } // krajnji konstruktor

Konstruktor uzima naslov grafa i raspon vrijednosti, a mi stvaramo prazan vektor za pojedinačne stavke grafa.

javno preoblikovanje praznine (int x, int y, int širina, int visina) {super.reshape (x, y, width, height); fm = getFontMetrics (getFont ()); titleHeight = fm.getHeight (); labelWidth = Math.max (fm.stringWidth (novi Integer (min) .toString ()), fm.stringWidth (novi Integer (max) .toString ())) + 2; vrh = podmetač + titleHeight; dno = veličina (). visina - podstava; lijevo = podmetač + labelWidth; desno = veličina (). širina - podmetač; } // kraj preoblikovanja

Napomena: U JDK 1.1 reshapemetoda je zamijenjena sa public void setBounds(Rectangle r). Za detalje pogledajte API dokumentaciju.

Nadjačavamo reshapemetodu koja se nizom nasljeđuje od Componentklase. reshapeMetoda se zove kad se mijenja veličina je komponenta i kada je izložena prvi put. Ovu metodu koristimo za prikupljanje mjerenja, tako da će se uvijek ažurirati ako se komponenti promijeni veličina. Dobivamo metriku fonta za trenutni font i titleHeightvarijabli dodjeljujemo maksimalnu visinu tog fonta. Dobivamo maksimalnu širinu naljepnica, testiramo kako bismo vidjeli koja je veća, a zatim koristimo tu. top, bottom, leftI rightvarijable izračunate su iz drugih varijabli i predstavljaju granice za crtanje regije centar graf. Te ćemo varijable koristiti u podrazredima Graph. Imajte na umu da sva mjerenja uzimaju u obzir strujuveličini komponente tako da će crtanje biti ispravno u bilo kojoj veličini ili aspektu. Ako bismo koristili čvrsto kodirane vrijednosti, komponenti se ne bi moglo promijeniti veličina.

Zatim ćemo nacrtati okvir za grafikon.

javna void boja (grafika g) {// crtanje naslova fm = getFontMetrics (getFont ()); g.drawString (naslov, (veličina (). širina - fm.stringWidth (naslov)) / 2, vrh); // crtanje maks. i min. vrijednosti g.drawString (novi Integer (min) .toString (), padding, dno); g.drawString (novo Integer (max) .toString (), podmetanje, vrh + titleHeight); // crtanje okomitih i vodoravnih linija g.drawLine (lijevo, gore, lijevo, dolje); g.drawLine (lijevo, dolje, desno, dno); } // // krajnja boja

Okvir je nacrtan u paintmetodi. Naslov i naljepnice crtamo na odgovarajućim mjestima. Na lijevoj granici područja crtanja grafa crtamo okomitu crtu, a na donjoj granici vodoravnu crtu.

U ovom sljedećem isječku postavljamo željenu veličinu komponente nadjačavanjem preferredSizemetode. preferredSizeMetoda također je naslijedio od Componentklase. Komponente mogu odrediti željenu veličinu i minimalnu veličinu. Odabrao sam poželjnu širinu od 300 i željenu visinu od 200. Upravitelj izgleda pozvat će ovu metodu kada položi komponentu.

javna dimenzija preferSize () {return (nova dimenzija (300, 200)); }} // kraj grafikona

Napomena: U JDK 1.1 preferredSizemetoda je zamijenjena sa public Dimension getPreferredSize().

Dalje, trebamo objekt za dodavanje i uklanjanje predmeta za grafanje.

public void addItem (ime niza, vrijednost int, boja col) {items.addElement (novi GraphItem (ime, vrijednost, kol)); } // kraj addItem javne void addItem (ime niza, vrijednost int) {items.addElement (novi GraphItem (ime, vrijednost, boja.crna)); } // // end addItem javna praznina removeItem (naziv niza) {for (int i = 0; i <items.size (); i ++) {if ((((GraphItem) items.elementAt (i)). title.equals (name )) items.removeElementAt (i); }} // kraj removeItem} // kraj Graph

Modelirao sam metode addItemi removeItemmetode prema sličnim metodama u Choiceklasi, tako da će kôd imati poznati osjećaj. Primijetite da ovdje koristimo dvije addItemmetode; trebamo način dodavanja predmeta s bojom ili bez nje. Kada se doda stavka, stvara se novi GraphItemobjekt koji se dodaje u vektor stavki. Kad se stavka ukloni, uklonit će se prva u vektoru s tim imenom. Predavanje GraphItemje vrlo jednostavno; ovdje je kod:

import java.awt. *; klasa GraphItem {Naslov niza; int vrijednost; Boja boje; javni GraphItem (naslov niza, vrijednost int, boja u boji) {this.title = naslov; this.value = vrijednost; this.color = boja; } // krajnji konstruktor} // kraj GraphItem

GraphItemKlasa djeluje kao držač za varijable koje se odnose na graf stavke. Ovdje sam uvrstio Colorslučaj da će se koristiti u podrazredu Graph.

S tim okvirom na mjestu možemo stvoriti proširenja za obradu svake vrste grafa. Ova je strategija prilično prikladna; ne moramo se ponovno mučiti s mjerenjem piksela za okvir, a možemo stvoriti potklase kako bismo se usredotočili na popunjavanje područja crtanja grafikona.

Izgradnja trakasti grafikona

Sada kad imamo grafički okvir, možemo ga prilagoditi proširivanjem

Graph

i provođenje prilagođenog crtanja. Započet ćemo s jednostavnim trakasti grafikon koji možemo koristiti kao i bilo koja druga komponenta. Tipični trakasti grafikon prikazan je u nastavku. Ispunit ćemo regiju za crtanje grafa nadjačavanjem

paint

metoda za pozivanje superklase

paint

method (to draw the framework), then we'll perform the custom drawing needed for this type of graph.

import java.awt.*; public class BarChart extends Graph { int position; int increment; public BarChart(String title, int min, int max) { super(title, min, max); } // end constructor 

To space the items evenly, we keep an increment variable to indicate the amount we will shift to the right for each item. The position variable is the current position, and the increment value is added to it each time. The constructor simply takes in values for the super constructor (Graph), which we call explicitly.

Now we can get down to some actual drawing.

 public void paint(Graphics g) { super.paint(g); increment = (right - left)/(items.size()); position = left; Color temp = g.getColor(); for (int i = 0; i < items.size(); i++) { GraphItem item = (GraphItem)items.elementAt(i); int adjustedValue = bottom - (((item.value - min)*(bottom - top)) /(max - min)); g.drawString(item.title, position + (increment - fm.stringWidth(item.title))/2, adjustedValue - 2); g.setColor(item.color); g.fillRect(position, adjustedValue, increment, bottom - adjustedValue); position+=increment; g.setColor(temp); } } // end paint } // end BarChart 

Let's take a close look at what's happening here. In the paint method, we call the superclass paint method to draw the graph framework. We then find the increment by subtracting the right edge from the left edge, and then dividing the result by the number of items. This value is the distance between the left edges of the graph items. Because we want the graph to be resizable, we base these values on the current value of the left and right variables inherited from Graph. Recall that the left, right, top, and bottom values are the current actual pixel measurements of the graph drawing region taken in the reshape method of Graph, and therefore available for our use. If we did not base our measurements on these values, the graph would not be resizable.

We'll draw the rectangles in the color specified by the GraphItem. To allow us to go back to the original color, we set a temporary color variable to hold the current value before we change it. We cycle through the vector of graph items, calculating an adjusted vertical value for each one, drawing the title of the item and a filled rectangle representing its value. The increment is added to the x position variable each time through the loop.

The adjusted vertical value ensures that if the component is stretched vertically, the graph will still remain true to its plotted values. To do this properly, we need to take the percentage of the range the item represents and multiply that value by the actual pixel range of the graph drawing region. We then subtract the result from the bottom value to correctly plot the point.

As you can see from the following diagram, the total horizontal pixel size is represented by right - left and the total vertical size is represented by bottom - top.

We take care of the horizontal stretching by initializing the position variable to the left edge and increasing it by the increment variable for each item. Because the position and increment variables are dependent on the actual current pixel values, the component is always resized correctly in the horizontal direction.

Da bismo osigurali da je vertikalno crtanje uvijek točno, moramo mapirati vrijednosti stavki grafikona sa stvarnim položajima piksela. Postoji jedna komplikacija: Vrijednosti maxi i mintrebale bi biti značajne za položaj vrijednosti stavke grafikona. Drugim riječima, ako grafikon započinje s 150, a ide na 200, stavka s vrijednošću 175 trebala bi se pojaviti na pola vertikalne osi. Da bismo to postigli, pronalazimo postotak raspona grafikona koji stavka predstavlja i pomnožimo ga sa stvarnim rasponom piksela. Budući da je naš graf naopako od koordinatnog sustava grafičkog konteksta, oduzimamo taj broj da bottombismo pronašli ispravnu točku grafikona. Zapamtite, ishodište (0,0) je u gornjem lijevom kutu koda, ali donji lijevi kut za stil grafikona koji stvaramo.