Programmering

Kortlæg din vej til brugerdefinerede grafkomponenter

Vores brugerdefinerede grafkomponenter kræver manuel tegning, så vi bliver nødt til at underklasse Lærred, som er standardkomponenten til direkte grafikmanipulation. Den teknik, vi skal bruge, er at tilsidesætte maling metode til Lærred med den brugerdefinerede tegning, som vi har brug for. Vi bruger Grafik objekt, der automatisk overføres til maling metode til alle komponenter, for at få adgang til farver og tegningsmetoder.

Vi opretter to brugerdefinerede grafkomponenter: et søjlediagram og en linjediagram. Vi starter med at opbygge en generel rammeklasse til de to grafer, der deler nogle basiselementer.

Opbygning af en generisk graframme

Linjegrafen og søjlediagrammet, vi skal bygge, er ens nok til, at vi kan oprette en generisk

Kurve

klasse for at udføre noget af det kedelige layoutarbejde. Når det er gjort, kan vi derefter udvide klassen til den bestemte slags graf, vi har brug for.

Den første ting, du skal gøre, når du designer brugerdefinerede grafikkomponenter, er at sætte pen på papir og tegne et billede af, hvad du har brug for. Fordi vi tæller pixels, er det let at blande sig om placeringen af ​​elementerne. At lægge en tanke i navngivning og placering af elementer hjælper dig med at holde koden renere og lettere at læse senere.

Linjediagrammet og søjlediagrammet bruger det samme layout til titlen og linjerne, så vi begynder med at oprette en generisk graf, der indeholder disse to funktioner. Det layout, vi skal oprette, vises i nedenstående figur.

At skabe det generiske Kurve klasse, vi underklasse Lærred. Midteregionen er hvor faktiske grafdata vises; vi overlader dette til en udvidelse af Kurve at implementere. Vi implementerer de andre elementer - en titellinje, en lodret linje til venstre, en vandret linje i bunden og værdier for området - i basisklassen. Vi kunne angive en skrifttype og hårdkode pixelmålingerne i, men brugeren kunne ikke ændre størrelsen på grafen. En bedre tilgang er at måle elementerne mod nuværende komponentens størrelse, så at størrelsen af ​​applikationen resulterer i en korrekt størrelse på grafen.

Her er vores plan: Vi tager en Snor titel, en int minimumsværdi og en int maksimal værdi i konstruktøren. Disse giver os al den information, vi har brug for til at lægge rammen. Vi opbevarer fire variabler til brug i underklasser - top, bund, venstreog ret værdier for grænserne for graftegningsområdet. Vi bruger disse variabler til at beregne positionering af grafelementer senere. Lad os begynde med et hurtigt kig på Kurve klassedeklaration.

import java.awt. *; importer java.util. *; public class Graf udvider Canvas {// variabler nødvendige public int top; offentlige int bund; offentlig int venstre; offentlig int ret; int titelHøjde; int labelWidth; FontMetrics fm; int polstring = 4; String titel; int min; int max; Vektor poster; 

For at beregne den korrekte placering af grafelementer skal vi først beregne regionerne i vores generiske graflayout, der udgør rammen. For at forbedre komponentens udseende tilføjer vi en 4-pixel polstring til de ydre kanter. Vi tilføjer titlen centreret øverst under hensyntagen til paddding-området. For at sikre, at grafen ikke tegnes oven på teksten, skal vi trække højden af ​​teksten fra titelområdet. Vi er nødt til at gøre det samme for min og maks værdiintervaletiketter. Bredden på denne tekst er gemt i variablen labelWidth. Vi er nødt til at holde en henvisning til skrifttypemålene for at udføre målingerne. Det genstande vektor bruges til at holde styr på alle de enkelte emner, der er føjet til grafkomponenten. En klasse, der bruges til at indeholde variabler relateret til grafelementer, er inkluderet (og forklaret) efter Kurve klasse, som vises næste.

 offentlig graf (strengetitel, int min, int max) {this.title = title; this.min = min; this.max = max; genstande = ny Vector (); } // slutkonstruktør 

Konstruktøren tager graftitlen og værdiområdet, og vi opretter en tom vektor til de enkelte grafelementer.

 offentlig ugyldig omformning (int x, int y, int bredde, int højde) {super.reshape (x, y, bredde, højde); fm = getFontMetrics (getFont ()); titleHeight = fm.getHeight (); labelWidth = Math.max (fm.stringWidth (new Integer (min) .toString ()), fm.stringWidth (new Integer (max) .toString ())) + 2; top = polstring + titelHøjde; bund = størrelse (). højde - polstring venstre = polstring + labelBredde; højre = størrelse (). bredde - polstring; } // slut omformning 

Bemærk: I JDK 1.1 er omforme metoden erstattes med public void setBounds (Rektangel r). Se API-dokumentationen for detaljer.

Vi tilsidesætter omforme metode, som nedarves ned fra kæden fra Komponent klasse. Det omforme metode kaldes, når komponenten ændres, og når den lægges ud første gang. Vi bruger denne metode til at indsamle målinger, så de altid opdateres, hvis komponenten ændres. Vi får skrifttypemålene for den aktuelle skrifttype og tildeler titelHøjde variabel den maksimale højde af denne skrifttype. Vi får den maksimale bredde på etiketterne, og test for at se, hvilken der er større, og derefter bruger vi den. Det top, bund, venstreog ret variabler beregnes ud fra de andre variabler og repræsenterer grænserne for det midterste graftegningsområde. Vi bruger disse variabler i underklasser af Kurve. Bemærk, at alle målingerne tager højde for a nuværende komponentens størrelse, så tegning vil være korrekt i enhver størrelse eller ethvert aspekt. Hvis vi brugte hårdkodede værdier, kunne komponenten ikke ændres.

Dernæst tegner vi rammen for grafen.

 offentlig ugyldig maling (grafik g) {// tegne titlen fm = getFontMetrics (getFont ()); g.drawString (titel, (størrelse (). bredde - fm.stringWidth (titel)) / 2, øverst); // tegne max- og min-værdierne g.drawString (nyt heltal (min) .toString (), polstring, bund); g.drawString (nyt heltal (max) .toString (), polstring, top + titelHøjde); // tegne de lodrette og vandrette linjer g.drawLine (venstre, øverste, venstre, nederste); g.drawLine (venstre, nederste, højre, nederste); } // slutmaling 

Rammen er tegnet i maling metode. Vi tegner titlen og etiketterne på deres passende steder. Vi tegner en lodret linje ved den venstre kant af graftegningsområdet og en vandret linje ved den nederste kant.

I dette næste uddrag indstiller vi den foretrukne størrelse for komponenten ved at tilsidesætte foretrukket størrelse metode. Det foretrukket størrelse metoden er også arvet fra Komponent klasse. Komponenter kan angive en foretrukken størrelse og en minimumsstørrelse. Jeg har valgt en foretrukken bredde på 300 og en foretrukken højde på 200. Layoutmanageren kalder denne metode, når den lægger komponenten ud.

 offentlig dimension foretrukket størrelse () {retur (ny dimension (300, 200)); }} // slut graf 

Bemærk: I JDK 1.1 er foretrukket størrelse metoden erstattes med offentlig dimension getPreferredSize ().

Dernæst har vi brug for en mulighed for at tilføje og fjerne de emner, der skal tegnes.

 public void addItem (String name, int value, Color col) {items.addElement (new GraphItem (name, value, col)); } // 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); }} // slut removeItem} // slut Graf 

Jeg har modelleret addItem og Fjern vare metoder efter lignende metoder i Valg klasse, så koden får en velkendt fornemmelse. Bemærk, at vi bruger to addItem metoder her; vi har brug for en måde at tilføje varer med eller uden farve. Når et element tilføjes, et nyt Grafik objekt oprettes og føjes til elementvektoren. Når et element fjernes, fjernes det første i vektoren med det navn. Det Grafik klasse er meget enkel; her er koden:

import java.awt. *; klasse GraphItem {strengetitel; int-værdi; Farve farve; public GraphItem (String title, int value, Color color) {this.title = title; this.value = værdi; denne. farve = farve; } // slutkonstruktør} // slut GraphItem 

Det Grafik klasse fungerer som indehaver af de variabler, der vedrører grafelementer. Jeg har inkluderet Farve her, hvis det vil blive brugt i en underklasse af Kurve.

Med denne ramme på plads kan vi oprette udvidelser til at håndtere hver type graf. Denne strategi er ret praktisk; Vi behøver ikke gå i besværet med at måle pixels til rammen igen, og vi kan oprette underklasser for at fokusere på at udfylde graftegningsområdet.

Opbygning af søjlediagram

Nu hvor vi har en grafisk ramme, kan vi tilpasse den ved at udvide

Kurve

og implementering af brugerdefineret tegning. Vi begynder med et simpelt søjlediagram, som vi kan bruge ligesom enhver anden komponent. Et typisk søjlediagram er illustreret nedenfor. Vi udfylder graftegningsområdet ved at tilsidesætte

maling

metode til at kalde superklassen

maling

metode (for at tegne rammen), så udfører vi den brugerdefinerede tegning, der er nødvendig for denne type graf.

import java.awt. *; offentlig klasse BarChart udvider Graf {int position; stigning i int; public BarChart (String title, int min, int max) {super (title, min, max); } // slutkonstruktør 

For at placere varerne jævnt, holder vi en stigning variabel for at angive det beløb, vi skifter til højre for hver vare. Positionsvariablen er den aktuelle position, og stigningsværdien føjes til den hver gang. Konstruktøren tager simpelthen ind værdier for superkonstruktøren (Kurve), som vi udtrykkeligt kalder.

Nu kan vi komme ned til en faktisk tegning.

 offentlig tom maling (grafik g) {super.paint (g); inkrement = (højre - venstre) / (items.size ()); position = venstre; Farvetemp = g.getColor (); for (int i = 0; i <items.size (); i ++) {GraphItem item = (GraphItem) items.elementAt (i); int justeretVærdi = bund - (((vare.værdi - min) * (nederst - øverst)) / (max - min)); g.drawString (item.title, position + (increment - fm.stringWidth (item.title)) / 2, justeretVærdi - 2); g.setColor (item.color); g.fillRect (position, justeret værdi, stigning, bundjusteret værdi); position + = inkrement; g.setColor (temp); }} // slutmaling} // slut BarChart 

Lad os se nærmere på, hvad der sker her. I maling metode kalder vi superklassen maling metode til at tegne graframmen. Vi finder derefter stigning ved at trække højre kant fra venstre kant og derefter dividere resultatet med antallet af emner. Denne værdi er afstanden mellem de venstre kanter på grafelementerne. Da vi ønsker, at grafen skal ændres, baserer vi disse værdier på den aktuelle værdi af venstre og ret variabler arvet fra Kurve. Husk på, at venstre, ret, topog bund værdier er de aktuelle faktiske pixelmålinger af graftegningsområdet taget i omforme metode til Kurveog derfor tilgængelig til vores brug. Hvis vi ikke baserede vores målinger på disse værdier, ville grafen ikke kunne ændres.

Vi tegner rektanglerne i den farve, der er angivet af Grafik. For at give os mulighed for at gå tilbage til den originale farve, indstiller vi en midlertidig farve variabel for at holde den aktuelle værdi, før vi ændrer den. Vi cykler gennem vektoren af ​​grafelementer, beregner en justeret lodret værdi for hver enkelt, tegner artiklens titel og et fyldt rektangel, der repræsenterer dets værdi. Inkrementet føjes til x-positionsvariablen hver gang gennem sløjfen.

Den justerede lodrette værdi sikrer, at hvis komponenten strækkes lodret, vil grafen stadig være tro mod de afbildede værdier. For at gøre dette ordentligt skal vi tage den procentdel af det område, som varen repræsenterer, og multiplicere denne værdi med det faktiske pixelområde i graftegningsområdet. Vi trækker derefter resultatet fra bund værdi for at plotte punktet korrekt.

Som du kan se fra følgende diagram, er den samlede vandrette pixelstørrelse repræsenteret af højre venstre og den samlede lodrette størrelse er repræsenteret af bund - top.

Vi tager os af den vandrette strækning ved at initialisere position variabel til venstre kant og øge den med stigning variabel for hver vare. Fordi position og stigning variabler er afhængige af de aktuelle aktuelle pixelværdier, komponenten ændres altid korrekt i vandret retning.

For at sikre, at den lodrette tegning altid er korrekt, skal vi kortlægge grafens elementværdier med faktiske pixelplaceringer. Der er en komplikation: maks og min værdier skal have betydning for placeringen af ​​grafens elementværdi. Med andre ord, hvis grafen starter ved 150 og går til 200, skal et element med en værdi på 175 vises halvvejs op ad den lodrette akse. For at opnå dette finder vi procentdelen af ​​grafområdet, som varen repræsenterer, og ganger det med det faktiske pixelinterval. Fordi vores graf er på hovedet fra grafikkontekstens koordinatsystem, trækker vi dette tal fra bund for at finde det korrekte plotpunkt. Husk, oprindelsen (0,0) er i det øverste venstre hjørne for koden, men det nederste venstre hjørne for den grafstil, vi opretter.