September discount week
Tento týden až 80% sleva na e-learning týkající se jazyka C
50 % bodů zdarma na online výuku díky naší Slevové akci!

Lekce 3 - Vlastní Android komponenta - Kreslený graf

V minulé lekci, Vlastní Android komponenta - Dokončení textového pole, jsme použili vlastních parametrů pro nastavení a dokončili vlastní vylepšené pole pro zadávání textu pro Android.

V dnešním Android tutoriálu si ukážeme jak vytvořit speciální komponentu, kterou nemáme ve standardní nabídce a kterou si tedy budeme muset od základu "nakreslit". Řekněme, že potřebujeme takovýto graf:

Vlastní Android komponenta – Graf

Protože budeme celý View od nuly vykreslovat a nikoli skládat z existujících komponent, odpadá nutnost vytváření XML návrhu tak, jak jsme to dělali v předchozím příkladu s vlastním textovým polem.

Parametry pro nastavení komponenty

Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Nadefinujeme tedy parametry, kterými budeme moci grafu v XML nastavit vzhled a nějaké ty vlastnosti. Opět vytvoříme soubor attrs.xml a opět ho umístíme do res\values\attrs.xml. Jeho obsah je následující:

<resources>
    <declare-styleable name="DrawView">

        <attr name="dwDirection" format="enum">
            <enum name="CW" value="1"/>
            <enum name="CCW" value="2"/>
        </attr>

        <attr name="dwStartAngle" format="integer" />
        <attr name="dwMaxValue" format="integer" />
        <attr name="dwValue" format="integer" />
        <attr name="dwDisableText" format="boolean" />
        <attr name="dwDisableAnimation" format="boolean" />
        <attr name="dwColorResGraph" format="color" />
        <attr name="dwColorResGraphBackground" format="color" />
        <attr name="dwTextColor" format="color" />
        <attr name="dwDisableQuerterLines" format="boolean" />
        <attr name="dwQuerterLinesColor" format="color" />

        <attr name="dwTextValueFormat" format="enum">
            <enum name="percentages" value="1"/>
            <enum name="value" value="2"/>
        </attr>
    </declare-styleable>
</resources>

Vysvětlíme si, k čemu jednotlivé parametry slouží:

  • dwDirection - Směr, kterým hodnota grafu narůstá. Ve směru hodinových ručiček (CW) nebo opačně (CCW). Hodnota CW je defaultní.
  • dwStartAngle - Pokud nebude nastaveno, nachází se počátek grafu vpravo, vztaženo k hodinám, v místě trojky. O velikost tohoto parametru (velikost úhlu ve stupních) se posune počátek grafu ve směru hodinových ručiček.
  • dwMaxValue - Maximální hodnota, kterou graf zobrazí a při které bude celý vyplněný. Při nenastavení tohoto parametru bude maximální hodnota defaultně na hodnotě 100.
  • dwValue - Aktuální hodnota grafu.
  • dwDisableText - Skrytí číselné hodnoty uvnitř grafu.
  • dwColorResGraph - Barva kruhové výseče grafu.
  • dwColorResGraphBackground - Barva pozadí kruhové výseče grafu.
  • dwTextColor - Barva číselné hodnoty uvnitř grafu.
  • dwDisableQuerterLines - Skrýt pomocné čáry označující čtvrtiny grafu.
  • dwQuerterLinesColor - Barva pomocných čar, označujících čtvrtiny grafu.
  • dwTextValueFormat - Formát číselné hodnoty ve středu grafu. Výběr z dvou možností - přímo zadaná hodnota nebo přepočet na procenta. Procentuální zobrazení je defaultní.

Tvorba třídy dědící od View

Pokračujeme vytvořením nové třídy DrawView dědící od třídy View:

public class DrawView extends View {

    public DrawView(Context context) {
        super(context);
        init();
    }

    public DrawView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        applyAttributeSet(context, attrs);
        init();
    }

    public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        applyAttributeSet(context, attrs);
        init();
    }

    public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        applyAttributeSet(context, attrs);
        init();
    }
}

Třída obsahuje nám již známé čtyři konstruktory a i zde napíšeme metody applyAttributeSet() a init():

// Směr pohybu výseče grafu
// CW - Ve směru hodinových ručiček
// CWW - Proti směru hodinových ručiček
public enum Direction {CW, CCW}

// Způsob zobrazení zadané hodnoty grafu
// PERCENTAGES - přepočítá na procenta
// VALUE - zobrazí přímo zadanou hodnotu
public enum ValueFormat {PERCENTAGES, VALUE}

// Proměnné k uživatelskému nastavení grafu
Direction direction = Direction.CW;
ValueFormat valueFormat = ValueFormat.PERCENTAGES;
int startAngle = 0; // Posunutí nulové hodnoty grafu
int maxValue = 100;
int value = 0;
int colorResGraph;
int colorResGraphBackground;
int colorResText;
boolean disableText;
boolean disableQuarterLines;
int colorQuarterLines;

int graphPadding = 50;       // Odsazení grafu od okrajů jeho okolního prostoru
int valueInPercentages = 0;  // Hodnota grafu přepočtená na procenta
int valueInAngle = 0;        // Hodnota grafu přepočítaná na úhel, na který je graf naplněn vůči počátku grafu
int textSize = 0;            // Proměnná pro vypočítanou velikost textu ve středu grafu


private void applyAttributeSet(Context context, AttributeSet attrs) {
    if (attrs == null) return;

    TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DrawView, 0, 0);

    if (ta == null) return;

    int tempDirection = ta.getInt(R.styleable.DrawView_dwDirection, 1);

    if (tempDirection == 1) {
        direction = Direction.CW;
    } else {
        direction = Direction.CCW;
    }

    int tempValueFormat = ta.getInt(R.styleable.DrawView_dwTextValueFormat, 1);

    if (tempValueFormat == 1) {
        valueFormat = ValueFormat.PERCENTAGES;
    } else {
        valueFormat = ValueFormat.VALUE;
    }

    try {
        startAngle = ta.getInt(R.styleable.DrawView_dwStartAngle, 0);
        maxValue = ta.getInt(R.styleable.DrawView_dwMaxValue, 100);
        value = valueToDraw = ta.getInt(R.styleable.DrawView_dwValue, 0);
        colorResGraph = ta.getColor(R.styleable.DrawView_dwColorResGraph, 0);
        colorResGraphBackground = ta.getColor(R.styleable.DrawView_dwColorResGraphBackground, 0);
        colorResText = ta.getColor(R.styleable.DrawView_dwTextColor, 0);
        disableText = ta.getBoolean(R.styleable.DrawView_dwDisableText, false);
        animationDisabled = ta.getBoolean(R.styleable.DrawView_dwDisableAnimation, false);
        disableQuarterLines = ta.getBoolean(R.styleable.DrawView_dwDisableQuerterLines, false);
        colorQuarterLines = ta.getColor(R.styleable.DrawView_dwQuerterLinesColor, 0);
    } finally {
        ta.recycle();
    }

    // Nastavení defaultních barev v případě, že nejsou nastaveny z venčí
    if (colorResGraph == 0) {
        colorResGraph = context.getResources().getColor(R.color.colorGraph);
    }

    if (colorResGraphBackground == 0) {
        colorResGraphBackground = context.getResources().getColor(R.color.colorGraphBackground);
    }

    if (colorResText == 0) {
        colorResText = colorResGraph;
    }

    if (colorQuarterLines == 0) {
        colorQuarterLines = colorResGraph;
    }
}

V metodě init() máme volání metod, které si napíšeme pro provedení několika výpočtů:

private void init() {
    computePercentages();   // Přepočet hodnoty grafu na procenta
    convertValueToAngle();  // Přepočet hodnoty na úhel, na který se graf natočí

    // Požadavek, aby došlo k novému vykreslení grafu na displeji,
    // protože byl změněn vzhled objektu. Metoda třídy View.
    invalidate();
}

private void computePercentages() {
    if (maxValue == 0) return;
    if (value == 0) valueInPercentages = 0;
    valueInPercentages = (int) (100 * valueToDraw / maxValue);
}

private void convertValueToAngle() {
    if (value < 0 || maxValue == 0) {
        valueInAngle = 0;
        return;
    }

    if (valueFormat == ValueFormat.PERCENTAGES) {
        valueInAngle = (int) (valueInPercentages * PERCENTAGES_TO_ANGLE_CONSTANT);
    } else {
        valueInAngle = (int) ((float)valueToDraw/(float)maxValue*360f);
    }
}

V metodě init() si všimněte volání invalidate(). Jde o metodu třídy View, která zajistí obnovení View na obrazovce. Doslova dle dokumentace: Zruší platnost celého View. Je-li View viditelný, bude v budoucnu zavolána metoda onDraw(). Díky tomu objekt bude znovu vykreslen. Toto volání je nutné vždy, kdykoliv provedeme nějaké změny mající vliv na vzhled objektu.

Dalším krokem bude přepsání metod onMeasure() a onDraw(), což jsou metody třídy View. Ale to až v následující lekci našeho seriálu, Vlastní Android komponenta - Měření a kreslení ;-)


 

Předchozí článek
Vlastní Android komponenta - Dokončení textového pole
Všechny články v sekci
Vlastní View pro Android aplikace
Článek pro vás napsal lupa.lupa
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Aktivity (5)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!