Tag Archives: XML

Widgetit Androidissa

Widgetit näyttävät applikaation kaikkein oleellisimman tiedon nopeasti ja yksinkertaisesti työpöydällä. Widgettejä voi myös käyttää käynnistämään applikaatioita tai taustaprosesseja, kuten mediasoittimen.

Tässä artikkelissa toteutamme mahdollisimman yksinkertaisen widgetin, jotta saamme idean mitä kaikkea sen toimintaan saamiseen tarvitaan. Toteutamme widgetin, jossa on tekstikenttä ja nappula, nappulaa painamalla kasvatamme tekstikentässä olevaa numeroa yhdellä. Erityisesti kiinnitämme huomiota widgetin tilan päivittämiseen sekä manuaalisesti, että automaattisesti.

Esimerkki koodi: https://github.com/Androidkehitys/Androidkehitys-Widget

Widget layout

Widgetin layout toteutetaan samalla tavalla, kuin minkä tahansa aktiviteetin layout. On kuitenkin otettava huomioon, että widgetti luodaan RemoteViews-luokan ilmentymänä ja layoutissa voi näin ollen käyttää vain seuraavia layout-elementtejä:

Layoutit

Widgetit

AppWidgetProviderInfo Metadata

Tämä XML-tiedosto määrittää widgetille tarpeelliset tiedot, kuten korkeuden ja leveyden, päivitysvälin, esikatselun, layoutin, asetukset-aktiviteetin sekä widgetin koon muuttamisen vaaka ja/tai pystysuunnassa (Android 3.1).

Androidin kotinäkymä koostuu ruudukosta, jossa jokainen ruutu on kooltaan 74dp * 74dp. Widgetin tulee jättää 2dp:tä tilaa leveydessä ja korkeudessa, jotta vältetään mahdolliset virheet ajonaikaisessa koon määrityksessä kun dp:t muutetaan pikseleiksi. Käytä kaavaa (solujen määrä * 74) – 2 widgetin koon määrittelemiseen.

Päivitysväli määrittää ajan, jonka välein Android päivittää widgetin tiedot. Tämä päivitys herättää laitteen lepotilasta ja suorittaa onUpdate()-metodin AppWidgetProvider-luokassa. Ei myöskään ole takuita tapahtuuko päivitys ajallaan. Suositeltavampaa on toteuttaa widgetti niin että se päivittää vain silloin, kun sen on pakko, eikä useammin.

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  android:minWidth="146dp"
  android:minHeight="72dp"
  android:updatePeriodMillis="86400000"
  android:initialLayout="@layout/widget_layout"
  >
  </appwidget-provider>

Widget Manifestissä ja painallukseen reagointi

Manifestissä widget määritellään <receiver> elementin sisälle. Tässä elementissä määrittelet oman toteutuksesi AppWidgetProvider luokan ilmentymästä ja määrittelet ne Intent lähetykset (broadcast), jotka AppWidgetProvider hyväksyy. Nämä lähetykset suoritetaan onReceive() -metodissa.

<receiver android:name=".ExampleWidgetProvider">
  <intent-filter>
    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
  </intent-filter>
  <meta-data android:name="android.appwidget.provider"
   android:resource="@xml/appwidget_provider" />
</receiver>

AppWidgetProvideria voi myös kutsua PendingIntent:llä, kuten tässä esimerkissä napin painalluksiin reagointi on toteutettu:

Intent intent = new Intent(context, ExampleWidgetProvider.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.start_button, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);

AppWidgetProvider

Kokonaisuudessaan omaan toteutukseesi AppWidgetProvideristä tarvitset vain onUpdate()- ja onReceive()-metodit. Voit myös tehdä tämän esimerkin mukaisesti paketin sisällä näkyvän staattisen metodin, joka päivittää widgetin grafiikat esimerkiksi konfiguraatioaktiviteetistä.

Android Manifest

Android-manifest voi aluksi näyttää vaikealta ymmärtää, mutta ilman sitä ei koodia voi suorittaa. Tässä artikkelissa opetetaan Android-manifestin perus käyttö ja käydään läpi vaativammat käyttömahdollisuudet.

Manifestin rakenne Androidin dokumentaatiosta:

<?xml version="1.0" encoding="utf-8"?>

<manifest>
<uses-permission />
 <permission />
 <permission-tree />
 <permission-group />
 <instrumentation />
 <uses-sdk />
 <uses-configuration />
 <uses-feature />
 <supports-screens />
 <compatible-screens />
 <supports-gl-texture />
 <application>
 <activity>
 <intent-filter>
 <action />
 <category />
 <data />
 </intent-filter>
 <meta-data />
 </activity>
 <activity-alias>
 <intent-filter> . . . </intent-filter>
<meta-data />
 </activity-alias>
 <service>
 <intent-filter> . . . </intent-filter>
 <meta-data/>
 </service>
 <receiver>
 <intent-filter> . . . </intent-filter>
 <meta-data />
 </receiver>
 <provider>
 <grant-uri-permission />
 <meta-data />
 </provider>
 <uses-library />
 </application>

</manifest>

Nämä on osattava

Käytetään esimerkkinä SQL-tietokanta artikkelissa annettua koodia ja sen AndroidManifest.xml:ää.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="androidkehitys.fi.database"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="7" />

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name="DatabaseActivity" android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<activity android:name="EnterProductActivity"></activity>

</application>
</manifest>

Manifestissä ei elementtien sisään tule muuta, kuin toisia elementtejä. Arvon kirjottaminen elementin sisään ei toimi ja aiheuttaa virheen. Kaikki arvot mitä manifestissä annetaan tulee antaa elementtien parametreissä.

<manifest>-elementin attribuuteiksi tulee applikaation oletuspaketti, versionumeroja nimi. Elementin sisälle asetetaan minimi versio Android käyttöjärjestelmästä. Esimerkiksi tässä tapauksessa käytetty API versio 7 on “Android 2.1 – update 1”.

Manifest-elementin sisälle tulee myös <application>-elementti, jossa määritellään applikaation ikoni ja nimi.

Applikaatio elementtien sisällä tehdään jokaiselle aktiviteetille, jonka käyttäjä voi käynnistää, <activity>-elementti. Mikäli aktiviteetti on samassa paketissa, kuin manifest-elementissä määritelty oletuspaketti, riittää asettaa aktiviteetin nimeksi kyseisen luokan nimi. Mikäli aktiviteetti on jossain toisessa paketissa tulee sinun määrittää koko paketin polku.

Aktiviteetti-elementin sisälle voi määritellä <intent-filter>-elementin, joka määrää erityistilanteita, missä aktiviteetti käynnistetään. Ensimmäinen aktiviteetti, mikä applikaatiossasi käynnistetään täytyy määrittää oikeilla intent-filtereillä:

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

Aktiviteetit, jotka eivät saa intenttejä applikaation ulkopuolelta ei tarvitse muuta, kuin nimetä manifestissä. Mikäli aktiviteettiä ei julisteta manifestissa sen käynnistäminen kaataa applikaation ja aiheuttaa ActivityNotFoundExceptionin;

Vaativampaa käyttöä varten hyvä tietää

Tässä käydään nopeasti läpi manifestin vaikeammin käytettäviä ja ymmärrettäviä ominaisuuksia. Näitä ei välttämättä tarvitse tietää yksinkertaisia applikaatioita tehtäessä, mutta ennemmin tai myöhemmin ne tulevat vastaan androidkehityksessä.

Tietyt osat Android-järjestelmästä vaativat käyttäjän lupaa ja ne täytyy määritellä manifestissä. Esimerkkinä tällaisista luvista on esimerkiksi internet-yhteyden käyttö:

<uses-permission android:name="android.permission.INTERNET" />

Intenttien vastaanottamisen määrittely manifestissä taas mahdollistaa reagoinnin tapahtumiin, vaikka applikaatio ei olisikaan käynnissä. Tällaisia tapauksia voi esimerkiksi olla NFC-sirujen lukeminen tai push-notificaatioiden vastaanottaminen.

<service>-elementti määrittelee applikaation tarjoaman palvelun muille laitteen applikaatioille. Näin määritellyt palvelut pysyvät päällä, vaikka käyttäjä vaihtaisi toiseen applikaatioon. Tällaisten palveluiden tulisi suorittaa yksi tehtävä ja sitten sulkea itsensä, sillä muuten näitä palveluita ei tapeta koskaan.

Jos haluat applikaatiosi reagoivan järjestelmän tai muiden applikaatioiden lähettämiin intentteihin, kuten push-notifikaatioihin on sinun määriteltävä <receiver>-elementti ja sen sisältö manifestissä applikaatio-elementin sisällä.

<provider>-elementin sisällä määritellään applikaation tarjoama sisältö. ContentProvider tarjoaa pääsyn applikaation hallinoimaan tietoon, myös muista applikaatioista.

Manifestin vaativampaan käyttöön palataan, kun sitä tarvitaan käytännön esimerkeissä.

Animaatiot Androidissa

Helppo tapa tehdä applikaatiostasi hienompi on lisätä animaatioita. Käymme tässä läpi yksinkertaisia animaatioita ja niiden käyttämistä applikaatiossasi. Tässä esimerkissä keskitymme elementtien sijainnin muuttamiseen animaatioilla.

Animaatioita voi tehdä kahdella tavalla. Joko suoraan koodissa, tai määritellä erikseen xml:ssä. Jos animaatiossa ei tarvitse laskea suoritusaikana mitään tulisi ne tehdä xml:ssä, että koodi on selkeämpää.

Voit ladata esimerkin lähdekoodin täältä.

Animaatio koodissa

Animaation luominen koodissa on hyvin suoraviivaista. Uusi animaatio olio luodaan halutunlaisesta animaatiotyypistä. Tässä tapauksessa:

TranslateAnimation (int fromXType, float fromXValue,
                    int toXType, float toXValue,
                    int fromYType, float fromYValue,
                    int toYType, float toYValue)

Type attribuutit kertovat onko animoitava matka absoluuttinen vai suhteellinen. Suhteellinen matka voi riippua joko itse animoitavasta näkymästä, tai sen vanhemmasta:

Animation.ABSOLUTE
Animation.RELATIVE_TO_SELF
Animation.RELATIVE_TO_PARENT

Koodissa olen käyttänyt absoluuttista siirtymätyyppiä, sillä haluan animoida valikon liikkumisen vain piiloon jäävältä osiolta, jonka olen asettanut 100dp:ksi. Huomaa että koodissa täytyy ottaa huomioon dp:t, eli näytön tiheydestä riippumattomat pikselit. Näytön tiheyden saat:

float scale = getResources().getDisplayMetrics().density;

Koska absoluuttisia siirtoja ei voi ilmoittaa dp:nä on annetut dp:t kerrottava näytön tiheydellä, jotta saat dp:n arvon pikseleinä.

Animaatiolle tulee asettaa kesto, minkä aikana animaatio suoriutuu loppuun. Kesto annetaan millisekuntteina.

animation.setDuration(500);

Animaation loputtua liikutettu näkymä ei suoraan jää sille asetettuun paikkaan, vaan sen paikka täytyy asettaa koodissa ja vasta, kun animaatio on päättynyt. Muuten kuva hyppää asettamaasi paikkaan suoraan.

Huomaa:

Huomaa, että animaatio lähtee aina animoidun näkymän sen hetkisestä asemasta. Aseman pystyy muuttamaan myös automaattisesti:

animation.setFillEnabled(true);
animation.setFillAfter(true);

Tätä tapaa voi käyttää sekä koodissa, että xml:ssä, mutta jos kuvasi reagoi painalluksiin sen oikea paikka on edelleen siellä, missä se on xml layoutissa asetettu. Eli kuvan tai napin paikka on oikeasti eri, kuin missä se näyttäisi olevan. Tästä syystä tätä tapaa ei voi käyttää tässä esimerkissä. Jos esimerkiksi valikossa olisi nappula se kyllä tulisi näkyviin, mutta sen painalluksen tunnistava osuus olisi edelleen näytön ulkopuolella, eikä nappulaa näin voisi painaa.

Lopuksi animaatio käynnistetään sille näkymälle, mitä halutaan liikuttaa:

slider.startAnimation(animation);

Animaatio XML:ssä

Animaatioiden määrittäminen XML:ssä on melko suoraviivaista. Tarvittavat ohjeet ja listat mahdollisista elementeistä löytyvät Googlen Android developer-sivustolta.

Animaatiot tallennetaan applikaatiosi resurssi kansioon res/anim/animaation_nimi.xml. Animaatio voi sisältää useita animaatioita, kuten liikkumista ja koon muutosta. Nämä animaatiot tulee asettaa <set>-elementin sisälle. Jos animaatiosi koostuu vain yhdestä osasta ei <set>-elementin käyttäminen ole pakollista. Yllä olevasta linkistä löytyy mahdolliset animaatiotyypit ja niiden parametrit.

Tähän esimerkkiin tein yksinkertaisen siirtymisanimaation, missä kuva tulee näytön vasemmasta laidasta sisään. Animaatiolle on asetettu kesto, lähtöpaikka ja lopullinen paikka. Tässä tapauksessa kuva on koko näytön kokoinen ja aloittaa -100%:sta ja siirtyy normaaliin paikkaansa. X-akselin negatiiviset arvot ovat ruudun vasemmalla puolella ja positiiviset oikealla puolella.

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
   android:interpolator="@android:anim/decelerate_interpolator"
   android:duration="500"
   android:fromXDelta="-100%"
   android:toXDelta="0"
   android:fillEnabled="true"
   android:fillAfter="true"
   />

Animaatio ladataan koodissa:

Animation slideFromLeft = AnimationUtils.loadAnimation(this, R.anim.slide_in_from_left);

Muista asettaa animoitu näkymä näkyväksi, ennen kuin aloitat animaation, jos näkymän näkyvyys on poissa. Tai jos viet näkymää pois ruudulta aseta näkymä pois animaation jälkeen. Muuten animaatio aloitetaan samalla tavalla, kuin koodiesimerkissä.

Vinkki

Mukava tapa sulkea valikoita, jotka eivät peitä koko näyttöä on tehdä tyhjän osan peittävä näkymä, jota painamalla valikko menee kiinni. Tässä esimerkissä teemme slidecontainer:ssä FrameLayoutin nimeltä hider, joka tekee saman asian, kuin välilehden painaminen. Kun valikko on ylhäällä piilotamme hiderin, niin ettei sitä pysty painamaan. Tämä lisää valikon käytettävyyttä.

Valikot olisi myös hyvä toteuttaa omina aktiviteetteinään, mutta tässä esimerkissä näin ei voi tehdä, sillä valikon alareuna on osittain näkyvissä koko ajan. Aktiviteetti voi olla vain osittain toisen päällä, mutta alempaa aktiviteettiä ei voi käyttää.