3. diel - Vlastné Android komponent - Kreslený graf
V minulej lekcii, Vlastné Android komponent - Dokončenie textového poľa , sme použili vlastných parametrov pre nastavenie a dokončili vlastné vylepšené pole pre zadávanie textu pre Android.
V dnešnom Android tutoriále si ukážeme ako vytvoriť špeciálny komponent, ktorú nemáme v štandardnej ponuke a ktorú si teda budeme musieť od základu "nakresliť". Povedzme, že potrebujeme takýto graf:
Pretože budeme celý View
od nuly vykresľovať a nie skladať
z existujúcich komponentov, odpadá nutnosť vytváranie XML návrhu tak, ako
sme to robili v predchádzajúcom príklade s vlastným textovým poľom.
Parametre pre nastavenie komponenty
Nadefinujeme teda parametre, ktorými budeme môcť grafu v XML nastaviť
vzhľad a nejaké tie vlastnosti. Opäť vytvoríme súbor
attrs.xml
a opäť ho umiestnime do
res\values\attrs.xml
. Jeho obsah je nasledovné:
<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>
Vysvetlíme si, k čomu jednotlivé parametre slúžia:
dwDirection
- Smer, ktorým hodnota grafu narastá. V smere hodinových ručičiek (CW
) alebo opačne (CCW
). HodnotaCW
je defaultný.dwStartAngle
- Pokiaľ nebude nastavené, nachádza sa počiatok grafu vpravo, vztiahnuté k hodinám, v mieste trojky. O veľkosť tohto parametra (veľkosť uhla v stupňoch) sa posunie počiatok grafu v smere hodinových ručičiek.dwMaxValue
- Maximálna hodnota, ktorú graf zobrazí a pri ktorej bude celý vyplnený. Pri nenastavený tohto parametra bude maximálna hodnota defaultne na hodnote100
.dwValue
- Aktuálna hodnota grafu.dwDisableText
- Skrytie číselné hodnoty vnútri grafu.dwColorResGraph
- Farba kruhového výseku grafu.dwColorResGraphBackground
- Farba pozadia kruhového výseku grafu.dwTextColor
- Farba číselné hodnoty vnútri grafu.dwDisableQuerterLines
- Skryť pomocné čiary označujúce štvrtiny grafu.dwQuerterLinesColor
- Farba pomocných čiar, označujúcich štvrtiny grafu.dwTextValueFormat
- Formát číselné hodnoty v strede grafu. Výber z dvoch možností - priamo zadaná hodnota alebo prepočet na percenta. Percentuálne zobrazenie je defaultný.
Tvorba triedy Dedič od
View
Pokračujeme vytvorením novej triedy DrawView
dedič od triedy
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(); } }
Trieda obsahuje nám už známe štyri konštruktory ai tu napíšeme metódy
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 metóde init()
máme volanie metód, ktoré si napíšeme pre
vykonanie niekoľkých výpočtov:
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 metóde init()
si všimnite volanie
invalidate()
. Ide o metódu triedy View
, ktorá
zaistí obnovenie View
na obrazovke. Doslova podľa dokumentácie:
Zruší platnosť celého View
. Ak je View
viditeľný, bude v budúcnosti zavolaná metóda onDraw()
.
Vďaka tomu objekt bude znovu vykreslený. Toto volanie je nutné vždy,
kedykoľvek vykonáme nejaké zmeny majúce vplyv na vzhľad objektu.
Ďalším krokom bude prepísanie metód onMeasure()
a
onDraw()
, čo sú metódy triedy View
. Ale to až v
nasledujúcej lekcii nášho seriálu, Vlastné Android komponent - Meranie a kreslenie;-)