I Android-applikationer er brugergrænsefladen en central komponent, som kan forbedres betydeligt med funktioner, der skjuler eller viser systemgrænseflader som navigations- og statusbjælker. For at skabe en mere engagerende og fuldskærmsoplevelse kan udviklere bruge gestusdetektering til at håndtere, hvordan system UI vises og skjules. Dette kapitel vil beskrive, hvordan du opnår dette ved at implementere gestusdetektering og manipulere system UI.

For at skjule systemgrænsefladerne, som inkluderer status- og navigationsbjælkerne, kan du bruge setSystemUiVisibility() metoden, som giver dig kontrol over, hvordan systemet viser sin UI. Dette gøres ved at tilføje de relevante flag som SYSTEM_UI_FLAG_IMMERSIVE, SYSTEM_UI_FLAG_FULLSCREEN, og SYSTEM_UI_FLAG_HIDE_NAVIGATION. Når disse flags er sat, vil systemgrænsefladerne blive skjult, hvilket gør det muligt for applikationen at bruge hele skærmens plads.

java
private void hideSystemUi() {
getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION ); }

Omvendt, når du ønsker at vise system UI igen, kan du bruge en lignende metode uden SYSTEM_UI_FLAG_IMMERSIVE flaget, hvilket giver mulighed for, at system UI viser sig igen, men uden at aktivere den fuldskærmsoplevelse, der blev opnået tidligere.

java
private void showSystemUi() { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ); }

For at kunne reagere på brugerens interaktioner, som f.eks. berøringer og gestures, skal du oprette en GestureDetectorCompat instans. Denne detektor bruges til at registrere gestusbevægelser som taps og swipe-bevægelser. Når en tap detekteres, kan applikationen skifte mellem at vise og skjule system UI.

java
private GestureDetectorCompat mGestureDetector;

Her tilføjes en GestureListener klasse, som håndterer forskellige gestusmetoder. Den vigtigste metode, som vi fokuserer på, er onSingleTapUp(), som registrerer, når brugeren tapper på skærmen. Hvis system UI er synligt, skjules det, ellers vises det.

java
private class GestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent event) { return true; } @Override
public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
return true; } @Override public boolean onSingleTapUp(MotionEvent e) { if (getSupportActionBar() != null && getSupportActionBar().isShowing()) { hideSystemUi(); } else { showSystemUI(); } return true; } }

Denne metode giver brugeren mulighed for at ændre system UI med en simpel berøring. Men for at sikre, at dette fungerer på en effektiv måde, skal onTouchEvent() metoden også overskrives, så gestusdetektoren kan registrere berøringer og reagere på dem korrekt.

java
public boolean onTouchEvent(MotionEvent event) { mGestureDetector.onTouchEvent(event); return super.onTouchEvent(event); }

Ved at initialisere mGestureDetector i onCreate() metoden og tilføje hideSystemUi() kaldet, vil applikationen starte med skjult system UI og vente på brugerens interaktioner.

java
mGestureDetector = new GestureDetectorCompat(this, new GestureListener());
hideSystemUi();

Med disse metoder på plads vil applikationen reagere på brugerens input og vise eller skjule system UI afhængigt af gestusens natur.

Der er flere andre funktioner, som kan forbedre brugeroplevelsen. Hvis du ønsker, at system UI skal forblive skjult, kan du bruge SYSTEM_UI_FLAG_IMMERSIVE_STICKY, som sørger for, at UI forbliver skjult, selvom brugeren interagerer med skærmen. Hvis du kun ønsker at dæmpe system UI i stedet for at skjule det helt, kan du bruge SYSTEM_UI_FLAG_LOW_PROFILE, som reducerer synligheden af navigationsbjælken.

java
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);

For en mere avanceret kontrol over UI kan du også bruge temaindstillinger som Theme.Holo.NoActionBar.TranslucentDecor for at gøre systembjælkerne gennemsigtige eller overlejrede.

Endvidere er det vigtigt at bemærke, at håndtering af system UI ikke kun afhænger af den gestus, brugeren udfører, men også af, hvordan applikationen interagerer med den bagvedliggende systemtilstand og hvordan ressourcer håndteres. Hvis systemet er ressourcebegrænset eller har specielle krav, kan der være behov for at justere adfærden af system UI for at sikre en stabil og effektiv oplevelse.

Hvordan man arbejder med ressourcer og SQLite-databaser i Android

Når man udvikler Android-applikationer, er det vigtigt at kunne arbejde effektivt med ressourcer som tekstfiler og databaser. I denne sammenhæng vil vi kigge på, hvordan man håndterer tekstfiler, der er gemt som rå ressourcer eller i assets-mappen, samt hvordan man arbejder med en SQLite-database til at gemme og hente data.

For at demonstrere forskellen i tilgange, vil vi vise, hvordan man læser indhold fra både rå tekstfiler og asset-filer i en Android-applikation. Disse to metoder adskiller sig i, hvordan man får adgang til filerne, men konceptet forbliver det samme.

Arbejde med rå ressourcer og assets

I Android-projekter er der mulighed for at gemme filer i forskellige mapper i projektstrukturen, hvor res/raw og assets er de mest anvendte. Begge mapper kan bruges til at opbevare ikke-kompilerede filer, som f.eks. tekstfiler, billeder eller andre ressourcer, der skal anvendes af applikationen.

For at arbejde med rå ressourcer i Android skal du placere dine filer i mappen res/raw, mens filer, der skal være tilgængelige i appens assets-mappe, skal placeres i assets-mappen. En vigtig forskel mellem disse to er, at filer i res/raw bliver indekseret ved kompileringstidspunktet, hvilket betyder, at de bliver behandlet som en del af applikationen og dermed kan få adgang til via en kompileret reference. Omvendt er filer i assets ikke indekseret på samme måde, og du skal bruge en strengreferenceret tilgang til at få adgang til dem.

Hvordan man læser en rå tekstfil

For at læse indholdet af en rå tekstfil i Android, skal du bruge følgende tilgang:

  1. Opret en ny projekt i Android Studio og kald det "ReadingResourceFiles".

  2. I projektet, åbn activity_main.xml og erstatt indholdet med et layout, der passer til din applikation.

  3. Opret en ny mappe kaldet raw i res-mappen.

  4. Opret en ny tekstfil i raw mappen, f.eks. raw_text.txt, og skriv noget tekst i denne fil.

  5. Derudover skal du oprette en assets mappe, som kan oprettes nemt ved at vælge New | Folder | Assets Folder i Android Studio.

  6. Opret en tekstfil i assets mappen, kald den f.eks. asset_text.txt.

Når disse filer er oprettet, skal du i MainActivity.java implementere metoden getText(), der tager en InputStream og læser filens indhold:

java
private String getText(InputStream inputStream) {
StringBuilder stringBuilder = new StringBuilder();
try { if (inputStream != null) { InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String newLine;
while ((newLine = bufferedReader.readLine()) != null) { stringBuilder.append(newLine + "\n"); } inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } return stringBuilder.toString(); }

Denne metode vil læse indholdet af de tekstfiler, som vi har oprettet i både raw og assets mappen. I onCreate()-metoden i MainActivity.java, kan du derefter bruge følgende kode for at vise indholdet af tekstfilerne i TextView-elementerne i brugergrænsefladen:

java
TextView textViewRaw = (TextView) findViewById(R.id.textViewRaw);
textViewRaw.setText(getText(this.getResources().openRawResource(R.raw.raw_text))); TextView textViewAsset = (TextView) findViewById(R.id.textViewAsset); try { textViewAsset.setText(getText(this.getAssets().open("asset_text.txt"))); } catch (IOException e) { e.printStackTrace(); }

Denne kode viser, hvordan man nemt kan få adgang til og vise indholdet af begge typer filer i applikationen. Den store forskel ligger i metoden for at åbne filerne: openRawResource() bruges til rå ressourcer, mens getAssets().open() bruges til at få adgang til asset-filer.

Arbejde med SQLite-databaser

SQLite er en letvægtsdatabase, der er indbygget i Android, og som gør det muligt at gemme strukturerede data lokalt på en enhed. For at arbejde med SQLite i Android er den typiske fremgangsmåde at oprette en klasse, der udvider SQLiteOpenHelper, som er ansvarlig for at oprette, opdatere og administrere databasen.

I et simpelt eksempel kan vi oprette en ordbogsdatabase, hvor vi gemmer ord og deres definitioner. Denne database vil have grundlæggende CRUD-operationer (Create, Read, Update, Delete), som er nødvendige for at manipulere data i databasen. Her er, hvordan man opretter en simpel SQLite-database til dette formål:

  1. Opret et nyt Android-projekt og kald det "SQLiteDatabase".

  2. Opret en ny Java-klasse, f.eks. DictionaryDatabase, der udvider SQLiteOpenHelper.

  3. Tilføj nødvendige metoder som insert(), query(), update(), og delete() til at manipulere dataene.

For at oprette ordbogsdatabasen og håndtere CRUD-operationer kan du bruge følgende metode:

java
public class DictionaryDatabase extends SQLiteOpenHelper {
public DictionaryDatabase(Context context) { super(context, "DictionaryDB", null, 1); } @Override public void onCreate(SQLiteDatabase db) {
String createTableQuery = "CREATE TABLE Words (id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT, definition TEXT)";
db.execSQL(createTableQuery); }
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS Words"); onCreate(db); }
public void insertWord(String word, String definition) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(
"word", word); values.put("definition", definition); db.insert("Words", null, values); } public Cursor getWords() { SQLiteDatabase db = this.getReadableDatabase(); return db.rawQuery("SELECT * FROM Words", null); } }

Når databasen er oprettet og du kan indsætte data, kan du bruge en Cursor til at hente ord og deres definitioner. Dette kan vises i en ListView, og du kan håndtere brugerinteraktion ved at oprette funktioner til at vise definitioner eller slette ord ved et langt tryk.

Vigtige bemærkninger

Når du arbejder med SQLite og ressourcer i Android, er det essentielt at forstå forskellen mellem de to typer filopbevaring og deres respektive anvendelser. Rå ressourcer er velegnede til filer, der skal behandles som en del af applikationens APK og derfor kan optimeres til effektiv adgang. Assets, på den anden side, kan bruges til mere fleksible filbehandlinger, som f.eks. at hente opdateret indhold uden at ændre selve APK’en.

I arbejdet med SQLite-databaser er det vigtigt at huske på, at det er nødvendigt at håndtere databasens versionering korrekt. Dette sikrer, at opgraderinger af databasen ikke forårsager tab af data. Det anbefales at benytte sig af de indbyggede funktioner som onUpgrade() for at håndtere ændringer i databasen, især når applikationen udvikles videre.

Hvordan oprette en kortflip-animation i Android

For at implementere en kortflip-animation i en Android-applikation kræves der flere forskellige ressourcer og en velorganiseret struktur. Her beskrives de nødvendige trin for at skabe en sådan animation og hvordan man håndterer fragemterne korrekt.

Start med at forberede billederne til forsiden og bagsiden af kortene. Disse billeder skal gemmes i mappen res/drawable som card_front.jpg og card_back.jpg (behold den originale filtype, hvis billederne har en anden filudvidelse).

Næste skridt er at oprette et animator-ressourcekatalog i projektstrukturen. I Android Studio gør du dette ved at vælge File | New | Android resource directory. Når dialogboksen vises, vælg "animator" under "Resource Type" dropdown-menuen. I dette katalog opretter du fire XML-filer for at definere animationen af kortflip. Hver af disse filer beskriver en bestemt animering for kortet: én for, hvordan kortet vender om til venstre og højre, og én for, hvordan det vender tilbage.

For at definere animationerne kan du bruge følgende XML-koder:

  • card_flip_left_enter.xml

  • card_flip_left_exit.xml

  • card_flip_right_enter.xml

  • card_flip_right_exit.xml

En vigtig ressource, der skal oprettes, er en timingfil. Dette gøres ved at oprette en XML-fil under res/values, som definerer de nødvendige tidsintervaller for animationen. En simpel definition kunne være:

xml
<integer name="card_flip_duration">1000</integer>
<integer name="card_flip_half_duration">500</integer>

Når animationen er defineret, skal du oprette layoutfiler til både forsiden og bagsiden af kortet. Disse filer skal placeres under res/layout, hvor du henholdsvis opretter fragment_card_front.xml og fragment_card_back.xml. I disse filer defineres den visuelle repræsentation af kortets forsiden og bagsiden.

For at håndtere selve animationen skal du oprette to Java-klasser: én for forsiden af kortet og én for bagsiden. Disse klasser er baseret på Android's Fragment-klasse og bruges til at repræsentere de to forskellige visninger af kortet.

java
public class CardFrontFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_front, container, false); } }
java
public class CardBackFragment extends Fragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_card_back, container, false); } }

I din hovedaktivitet (f.eks. MainActivity.java), skal du oprette en global variabel til at holde styr på, om kortets bagside vises:

java
boolean mShowingBack = false;

Dernæst kan du definere en metode, der håndterer kortflipet. Når brugeren trykker på kortet, kaldes metoden, og kortet flippen enten til forsiden eller bagsiden afhængigt af den aktuelle tilstand.

java
private void flipCard() {
if (mShowingBack) { mShowingBack = false; getFragmentManager().popBackStack(); } else { mShowingBack = true; getFragmentManager() .beginTransaction() .setCustomAnimations( R.animator.card_flip_right_enter, R.animator.card_flip_right_exit, R.animator.card_flip_left_enter, R.animator.card_flip_left_exit) .replace(R.id.frameLayout, new CardBackFragment()) .addToBackStack(null) .commit(); } }

Når alt er sat op korrekt, er applikationen klar til at køre på en enhed eller emulator. Selve animationen styres af setCustomAnimations()-metoden, som tager de fire XML-animationer, vi har defineret. Denne metode gør det muligt at skabe en glidende og visuel kortflip-effekt.

Det er også vigtigt at bemærke, at den oprindelige Android Fragment Manager ikke understøtter ObjectAnimator, som kræves til at oprette denne type animation. Hvis du ønsker at understøtte versioner af Android før version 3.0, skal du inkludere de gamle animationsressourcer og kontrollere versionen af operativsystemet under kørslen. Alternativt kan animationerne oprettes direkte i kode, som vist i den næste opskrift i bogen.

Denne tilgang giver en enkel og effektiv måde at skabe en dynamisk og visuel animation på i en Android-applikation. Når animationen er konfigureret, håndteres alt automatisk af Android's indbyggede mekanismer.

Hvordan implementeres talegenkendelse og push-notifikationer i Android?

Android tilbyder kraftfulde funktioner som talegenkendelse og push-notifikationer, som kan forbedre brugeroplevelsen markant. Denne kapitel vil gennemgå, hvordan man implementerer talegenkendelse i en Android-applikation og forklarer grundlæggende principperne bag Google Cloud Messaging (GCM) til push-notifikationer.

Talegenkendelse i Android er en relativt enkel proces, men det kræver, at vi forstår, hvordan systemet fungerer og interagerer med de nødvendige komponenter. I en typisk Android-applikation, der anvender talegenkendelse, starter vi med at oprette en Intent for at aktivere talegenkendelsen. Dette gøres ved at bruge RecognizerIntent.ACTION_RECOGNIZE_SPEECH. Når denne Intent startes, får vi resultatet gennem en callback-metode kaldet onActivityResult(). Her kan vi hente den liste af genkendte ord, der returneres af systemet, og vise dem i applikationen. Det er værd at bemærke, at Android automatisk håndterer de fleste af de tekniske aspekter, herunder at identificere hvilke sprogmodeller der skal bruges, samt at beregne genkendelsens nøjagtighed.

En væsentlig detalje, der ofte overses, er håndteringen af tilladelser. For at bruge talegenkendelse kræver det, at applikationen har tilladelse til at optage lyd via mikrofonen. Uden denne tilladelse kan talegenkendelse ikke udføres. Det er derfor vigtigt, at udvikleren sikrer sig, at applikationen er korrekt konfigureret til at anmode om disse tilladelser, både i manifestfilen og runtime.

Desuden tilbyder Android også mulighed for at bruge en tilpasset løsning ved at benytte SpeechRecognizer-klassen direkte, hvilket giver større kontrol over funktionaliteten, men kræver mere kode og opmærksomhed på detaljer som fejlhåndtering og præstationsoptimering. Hvis du vælger at implementere dette selv, er det nødvendigt at have en RecognitionListener, der håndterer de forskellige begivenheder, der kan opstå under talegenkendelsesprocessen. Det inkluderer, men er ikke begrænset til, fejl i forbindelse med optagelse, tab af forbindelse eller andre systemfejl.

En vigtig opmærksomhed er, at genkendelsesresultaterne er afhængige af flere faktorer. En høj kvalitet af optagelsen, samt en klar og præcis tale, kan øge nøjagtigheden. Derudover skal udviklere være opmærksomme på, at resultaterne kan variere afhængigt af netværksforbindelse og enhedens hardwarekapaciteter.

For push-notifikationer er GCM (Google Cloud Messaging) en effektiv og fleksibel måde at sende beskeder til Android-enheder på. GCM gør det muligt for applikationer at modtage beskeder, selv når applikationen ikke er aktivt i brug. Når en bruger starter applikationen første gang, opretter appen forbindelse til GCM-serveren og får en enheds-token, som derefter sendes til serveren. Serveren er ansvarlig for at sende meddelelser til den specifikke enhed, når der er behov for det. Denne model gør det muligt at opretholde en effektiv og pålidelig kommunikationskanal mellem applikationen og dens server, selv når appen ikke er i brug.

Når du implementerer GCM, skal du huske på, at Google kræver, at applikationen er konfigureret korrekt i Google Developer Console, og at en passende google-services.json-fil skal inkluderes i applikationens projektstruktur. Denne fil indeholder de nødvendige oplysninger for at autentificere applikationen med Google Services og aktivere push-notifikationer.

En vigtig detalje er, at GCM kræver en aktiv Google-konto for at fungere på ældre Android-enheder, mens dette ikke længere er et krav på enheder, der kører Android 4.0.4 eller nyere. Denne ændring betyder, at nyere enheder har lettere adgang til push-notifikationer uden at skulle konfigurere en Google-konto.

Det er også vigtigt at overveje, hvordan du administrerer push-notifikationer i din applikation. Det handler ikke kun om at sende beskeder, men også om at sikre, at de relevante brugere modtager de beskeder, der er relevante for dem. For at gøre dette skal du implementere en effektiv server-side løsning, der kan håndtere enhedstokens og identificere, hvilke brugere der skal modtage hvilke meddelelser.

Når du integrerer både talegenkendelse og push-notifikationer, er det vigtigt at tage hensyn til brugeroplevelsen. Begge funktioner kræver ofte, at brugeren interagerer med appen på en aktiv måde, hvilket kan forstyrre deres oplevelse, hvis det ikke håndteres korrekt. Derfor er det vigtigt at designe din app, så både talegenkendelse og push-notifikationer er en naturlig del af appens funktionalitet, og at de præsenteres på en måde, der ikke forstyrrer brugerens aktivitet unødigt.

For udviklere er det også værd at overveje alternativer til GCM, som Firebase Cloud Messaging (FCM), som er en nyere løsning fra Google, der tilbyder lignende funktioner med forbedret ydeevne og ekstra funktioner som målretning og analytik.