Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

Lekce 9 - 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 - Vlastní vzhled Android komponent

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

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í vzhled Android komponent
Přeskočit článek
(nedoporučujeme)
Vlastní Android komponenta - Měření a kreslení
Článek pro vás napsal Pavel
Avatar
Uživatelské hodnocení:
3 hlasů
Autor se věnuje programování v Javě, hlavně pro Android. Mezi jeho další zájmy patří Arduino, Minecraft.
Aktivity