IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

4. diel - Vlastné Android komponent - Meranie a kreslenie

V minulej lekcii, Vlastné Android komponent - Kreslený graf , sme si pripravili základ triedy grafu a metódy pre obsluhu jeho atribútov.

V dnešnom Android tutoriálu sa budeme venovať merania a konečne aj kreslenie:)

Meranie

Začneme prepísaním metódy onMeasure().

onMeasure()

Túto metódu prepíšeme, aby sme získali rozmery priestoru, v ktorom sa bude graf nachádzať. Vďaka tomu potom môžeme aj určiť veľkosť grafu. Kód metódy bude nasledujúci:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    width = getMeasuredWidth();
    height = getMeasuredWidth();
    initParams(width, height);
    setMeasuredDimension(width, height);
}

Metódy getMeasuredWidth() a getMeasuredHeight() sú metódy triedy View, ktoré vracia zmeranú šírku a výšku priestoru určeného pre komponent. Možnosť získať tieto "namerané" hodnoty máme vďaka volanie super.onMeasure(widthMeasureSpec, heightMeasureSpec) v prvom riadku metódy onMeasure().

V predchádzajúcej ukážke je dvakrát použitá metóda getMeasuredWidth() pre získanie šírky aj výšky priestoru pre graf. To pretože komponenta s grafom má štvorcový tvar a výška komponenty bude rovná jej šírke. Ak by sa jednalo o obdĺžnikový tvar, bola by pre výšku použitá metóda getMeasuredHeight().

Získanú šírku a výšku priestoru potom odovzdávame metóde initParams() (tú si uvedieme nižšie) a tam vypočítame rozmery všetkých častí grafu. Rozmery, s ktorými sa pracuje v onMeasure(), sú v pixeloch.

Prepisujeme Ak onMeasure(), je nutné na jej konci volať setMeasuredDimension(). Do parametrov vložíme zistené rozmery. Ak tak neurobíme, aplikácia bude ukončená s výnimkou IllegalStateException.

V metóde onMeasure() ide o zladenie priestorového požiadavke grafu s tým, aké priestorové možnosti mu rodičovský element poskytuje.

Výpočet rozmerov častí grafu

Poďme sa pozrieť na deklaráciu konštánt, ktoré slúžia na tento výpočet a na deklaráciu metódy initParams(), ktorú voláme v metóde onMeasure() po zistení rozmerov priestoru:

// Šířka výseče grafu (% z poloměru grafu)
final int ARC_WIDTH_PERCENTAGES = 40;

// Přesah pozadí grafu přes výseč
// 1 = žádný přesah
final int ARC_BACKGROUND_WIDTH_PERCENTAGES = 3;

// Přepočet procent na úhly (1% => 3,6°)
final float PERCENTAGES_TO_ANGLE_CONSTANT = 3.6f;

// Poměr výšky textu k poloměru kruhové výseče grafu
final float MAX_TEXT_SIZE_RATIO = 0.60f;

// Vzdálenost grafu od okolních hran
final float PADDING_RATIO = 0.05f;

int width;
int height;

// Maximální poloměr grafu
float maxRadius;

// Souřadnice středu
int centerOfGraphX;
int centerOfGraphY;

// Šířka výseče
int arcWidth;

// Přesah pozadí přes výseč grafu
int backgroundOverlap = 5;


private void initParams(int width, int height) {
    graphPadding = (int) (height * PADDING_RATIO);
    maxRadius = (width / 2) - graphPadding;
    centerOfGraphX = width/2;
    centerOfGraphY = height/2;
    arcWidth = (int) (maxRadius / 100 * ARC_WIDTH_PERCENTAGES);
    backgroundOverlap = (int) (maxRadius / 100 * ARC_BACKGROUND_WIDTH_PERCENTAGES);
    textSize = (int) (maxRadius * MAX_TEXT_SIZE_RATIO);
}

Metóda initParams() na základe zadanej výšky a šírky nastaví spomínané premenné.

Ideme kresliť

Graf nie je vykresľovaný "jedným ťahom" a skladá sa z viacerých častí:

  • Inicializácia farieb a štýlov
  • textu grafu
  • pozadie grafu
  • výseč grafu
  • čiary štvrtí

Inicializácia farieb a štýlov

Farby sme získali v minulej časti z XML metódou applyAttributeSet() a uložili sme ich do premenných, tiež deklarovaných v predchádzajúcej časti kurzu. Teraz teda môžeme založiť jednotlivé časti grafu a nastaviť im tieto farby:

Paint paint = new Paint();             // Výseč
Paint paintBackground = new Paint();   // Pozadí
Paint paintText = new Paint();         // Text
Paint paintLines = new Paint();        // Pomocné čtvrtinové čáry

private void setPaints() {
    paint.setColor(colorResGraph);
    paint.setAntiAlias(true);

    paintBackground.setColor(colorResGraphBackground);
    paintBackground.setAntiAlias(true);

    paintText.setColor(colorResText);
    paintText.setAntiAlias(true);

    paintLines.setColor(colorQuarterLines);
    paintLines.setAntiAlias(true);
}

Text grafu

V nasledujúcej ukážke je zaujímavý spôsob, akým text vycentrujeme vertikálne. Pre horizontálne vycentrovanie máme parameter Paint.Align.CENTER. Pre vertikálne zarovnanie podpora nie je, preto si budeme musieť poradiť sami.

private void drawGraphText(Canvas canvas) {
    if (disableText) {
        // Zobrazení textu není povoleno
        return;
    }

    // Proměnná pro zobrazovaný text
    String text = "";

    if (valueFormat == ValueFormat.PERCENTAGES) {
        // Zobrazení v %
        text = "" + valueInPercentages + "%";
    } else {
        // Zobrazování skutečné hodnoty
        text = "" + valueToDraw;
    }

    // Inicializace stylu textu
    paintText.setTextSize(textSize);
    paintText.setAntiAlias(true);
    paintText.setTextAlign(Paint.Align.CENTER);  // Horizontální zarovnání textu na střed

    // Měření šířky textu
    float textWidth = paintText.measureText(text);

    // Výpočet šířky prostoru pro text (průměr otvoru ve středu grafu)
    int maxTextWidth = (int) ((maxRadius - arcWidth - backgroundOverlap) * 2);

    // Výpočet vertikální pozice textu - vysvětlení v textu pod touto ukázkou
    int yPos = (int) ((centerOfGraphY) - ((paintText.descent() + paintText.ascent()) / 2)) ;

    // Test, zda se text vejde do grafu, případně i úprava velikosti textu
    if (textWidth > maxTextWidth) {
        int newTextSize = textSize;

        while (textWidth > maxTextWidth) {
            // Zmenšení textu o 10%
            newTextSize = scaleDownTextSize(newTextSize);

            paintText.setTextSize(newTextSize);

            // Přeměření šířky textu
            textWidth = paintText.measureText(text);

            // Přepočítání vertikální pozice textu
            yPos = (int) ((height / 2) - ((paintText.descent() + paintText.ascent()) / 2)) ;
        }
    } else {
        paintText.setTextSize(textSize);
    }

    canvas.drawText(text, centerOfGraphX, yPos, paintText);
}

private int scaleDownTextSize(int actualSize) {
    if (actualSize <= 0) return 0;
    return (int) (actualSize * 0.90);
}

Poďme si vysvetliť výpočet vertikálnej pozície textu. Pretože graf bude vedieť plynule reagovať na zmenu zobrazenej hodnoty, je nutné, aby vedel v priebehu tejto zmeny prispôsobovať veľkosť textu v stredu grafu. Ak grafu, ako maximálnu hodnotu, nastavíme moc vysoké číslo, hodnoty pri hornej hranici rozsahu nebudú vidieť celé - budú čiastočne prekryté pozadím a výsekov grafu. Už vieme, že náš graf bude vedieť v texte zobrazovať aktuálnu hodnotu v dvoch režimoch:

  • Prepočet na percentá na základe zadanej maximálnej hodnoty grafu
  • Priame zobrazenie hodnoty

V prípade percent problém s veľkosťou nenastane, ale v tom druhom prípade veľmi ľahko. Preto sme si napísali v metóde drawGraphText() kus kódu, ktorý nám bude veľkosť textu prispôsobovať v závislosti na jeho dĺžke. V opísanom kóde si ešte bližšie vysvetlíme tento riadok:

int yPos = (int) ((centerOfGraphY) - ((paintText.descent() + paintText.ascent()) / 2)) ;

Z kódu nemusí každému byť jasné, čo sa tu odohráva. Mal by vám pomôcť nasledujúci obrázok, kde sú schematicky naznačené niektoré parametre textu:

Parametre textu na Android zariadeniach - Vlastné View pre Android aplikácie

Všetko okolo vertikálnej polohy textu začína testom, či šírka textu, ktorú zisťujeme v riadku paintText.measureText(text), nie je väčšia ako vypočítaná maximálna možná šírka textu. Maximálna šírka textu sa riadi veľkosťou otvoru v stredu grafu. Ak je šírka textu moc veľká, vstupuje do hry cyklus, v ktorom postupne po 10tich percentách veľkosť textu zmenšujeme a vždy ihneď šírku textu znova meriame. Vo chvíli, keď sa text svojou šírkou vojde do stredu grafu, cyklus končí. Ako posledný krok metódy drawGraphText() je volanie samotné metódy drawText() triedy Canvas pre vykreslenie textu.

Na nasledujúcom obrázku je vidieť text, ktorý sa musel pre svoju dĺžku zmenšiť tak, aby sa na šírku vošiel do "diery" v grafe. V tomto kroku by ešte nebolo vykreslené pozadia ani samotný graf, ale chcem aby bolo vidieť, do akého priestoru sa text vmáčkl:

Zmenšenie textu v grafe - Vlastné View pre Android aplikácie

Naše vyššie deklarovanej metódy prijímajú jeden parameter typu Canvas. Možno vás napadla otázka, kde túto inštanciu vezmeme. Všetky tieto metódy budú neskôr volánmi v prekrytej metóde onDraw(), ktorá má tento objekt v parametri. K metóde onDraw() sa čoskoro prehrýzť;-) Pre dnešok končíme.

Nabudúce, v lekcii Vlastné Android komponent - Dokončenie kreslenie grafu , budeme pokračovať kreslením ďalších častí grafu.


 

Predchádzajúci článok
Vlastné Android komponent - Kreslený graf
Všetky články v sekcii
Vlastné View pre Android aplikácie
Preskočiť článok
(neodporúčame)
Vlastné Android komponent - Dokončenie kreslenie grafu
Článok pre vás napísal Pavel
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje programování v Javě, hlavně pro Android. Mezi jeho další zájmy patří Arduino, Minecraft.
Aktivity