Skærmbillede: YouTube Android-appen
ExoPlayer er en medieafspiller på app-niveau, der er bygget oven på medie-API’er på lavt niveau i Android. ExoPlayer har en række fordele i forhold til den indbyggede MediaPlayer i Android. Den understøtter mange af de samme medieformater som MediaPlayer samt adaptive formater, såsom DASH og SmoothStreaming. ExoPlayer kan i høj grad tilpasses og udvides, hvilket gør den i stand til mange avancerede brugssituationer. Det er et open source-projekt, der bruges af Google-apps, herunder YouTube og Google Play Movies & TV.
- Forudsætninger
- Hvad du skal gøre
- Det skal du bruge
- Hent koden
- Katalogstruktur
- Importer det oprindelige projekt
- app/build.gradle
- Føj ExoPlayer-afhængighed til
- player-lib/build.gradle
- Føj PlayerView-elementet
- activity_player.xml
- PlayerActivity.java
- Opret en ExoPlayer
- PlayerActivity.java
- Opret et medieelement
- PlayerActivity.java
- Spil med Activity-livscyklus
- PlayerActivity.java
- PlayerActivity.java
- PlayerActivity.java
- PlayerActivity.java
- Slutforberedelse
- PlayerActivity.java
- Afspilning af lyd
- Test aktivitetslivscyklus
- Afspil video
- PlayerActivity.java
- PlayerActivity.java
- Adaptivt sporvalg
- PlayerActivity.java
- Byg et adaptivt MediaItem
- PlayerActivity.java
- Andre adaptive streamingformater
- Lyt op
- PlayerActivity.java
- PlayerActivity.java
- PlayerActivity.java
- Registrer din lytter
- PlayerActivity.java
- PlayerActivity.java
- Gå dybere
- activity_player.xml
- activity_player.xml
- Apas adfærden
- activity_player.xml
- Anpas udseendet
- activity_player.xml
- custom_player_control_view.xml
- custom_player_control_view.xml
- activity_player.xml
- exo_player**_control_view.xml**
- Lær mere
Forudsætninger
- Moderat kendskab til Android-udvikling og Android Studio
Hvad du skal gøre
- Opret en
SimpleExoPlayer
instans, som forbereder og afspiller medier fra en række forskellige kilder. - Integrer ExoPlayer med appens aktivitetslivscyklus for at understøtte baggrunds-, forgrunds- og genoptagelse af afspilning i et enkelt- eller flervinduemiljø.
- Brug
MediaItem
s til at oprette en afspilningsliste. - Afspil adaptive videostreams, som tilpasser mediekvaliteten til den tilgængelige båndbredde.
- Registrér hændelseslyttere til at overvåge afspilningstilstanden, og vis, hvordan lyttere kan bruges til at måle kvaliteten af afspilningen.
- Brug standard ExoPlayer UI-komponenter, og tilpas dem derefter til din app’s stil.
Det skal du bruge
- Android Studio version 3.5 eller højere
- En Android-enhed med JellyBean (4.1) eller højere, ideelt set med Nougat (7.1) eller højere, da den understøtter flere vinduer.
Hent koden
For at komme i gang skal du downloade Android Studio-projektet:
Download zip
Alternativt kan du klone GitHub-repositoriet:
git clone https://github.com/googlecodelabs/exoplayer-intro.git
Katalogstruktur
Kloning eller udpakning giver dig en rodmappe (exoplayer-intro
), som indeholder en mappe for hvert trin i dette codelab, sammen med alle de ressourcer, du har brug for:
/PATH/TO/YOUR/FOLDER/exoplayer-intro/exoplayer-codelab-00/PATH/TO/YOUR/FOLDER/exoplayer-intro/exoplayer-codelab-01/PATH/TO/YOUR/FOLDER/exoplayer-intro/exoplayer-codelab-02/PATH/TO/YOUR/FOLDER/exoplayer-intro/exoplayer-codelab-03/PATH/TO/YOUR/FOLDER/exoplayer-intro/exoplayer-codelab-04
Mapperne exoplayer-codelab-N
(hvor N
er 00
til 04
) indeholder den ønskede sluttilstand for hvert trin i dette codelab. Det er selvstændige Android Studio-projekter, som hver især kan importeres.
Importer det oprindelige projekt
- Start Android Studio.
- Vælg fil > Ny > Importer projekt*.*
- Importer det oprindelige projekt fra
exoplayer-codelab-00
.
Skærmbillede: Projektstruktur ved import
Når opbygningen er afsluttet, vises to moduler: app
-modulet (af typen program) og player-lib
-modulet (af typen bibliotek). Modulet app
er faktisk tomt, idet det kun har et manifest. Alt fra player-lib
-modulet bliver slået sammen, når appen bygges ved hjælp af en gradle-afhængighed i app/build.gradle
.
app/build.gradle
dependencies { implementation project(":player-lib")}
Din medieafspilleraktivitet bliver gemt i player-lib
-modulet. Grunden til at holde den i et separat biblioteksmodul er, at du kan dele den mellem APK’er, der er rettet mod forskellige platforme, f.eks. mobil og Android TV. Det giver dig også mulighed for at drage fordel af funktioner som f.eks. dynamisk levering, som gør det muligt at installere din medieafspilningsfunktion kun, når brugeren har brug for den.
- Deploy og kør appen for at kontrollere, at alt er i orden. Appen skal fylde skærmen med en sort baggrund.
Skærmbillede: Blank app kører
Føj ExoPlayer-afhængighed til
ExoPlayer er et open source-projekt, der er hostet på GitHub. Hver udgivelse distribueres gennem jCenter, som er et af de standardpakkeopbevaringssteder, der bruges af Android Studio og Gradle. Hver udgivelse er entydigt identificeret ved en streng med følgende format:
com.google.android.exoplayer:exoplayer:rX.X.X
Du kan tilføje ExoPlayer til dit projekt ved blot at importere dets klasser og brugergrænsefladekomponenter. Det er ret lille, idet det har et skrumpet fodaftryk på omkring 70-300 kB afhængigt af de inkluderede funktioner og understøttede formater. ExoPlayer-biblioteket er opdelt i moduler for at give udviklere mulighed for kun at importere den funktionalitet, de har brug for. Du kan finde flere oplysninger om ExoPlayers modulære struktur under Tilføj ExoPlayer-moduler.
- Åbn
build.gradle
-filen forplayer-lib
-modulet. - Føj følgende linjer til
dependencies
-afsnittet, og synkroniser projektet.
player-lib/build.gradle
dependencies { implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0'implementation 'com.google.android.exoplayer:exoplayer-dash:2.12.0'implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0'}
Føj PlayerView-elementet
- Åbn layoutressourcefilen
activity_player.xml
fraplayer-lib
modulet. - Placér markøren inde i
FrameLayout
-elementet. - Begynd at skrive
<PlayerView
, og lad Android Studio autokompletterePlayerView
-elementet. - Brug
match_parent
tilwidth
ogheight
. - Deklarér id’et som
video_view
.
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
Fremadrettet henviser du til dette brugergrænsefladeelement som videovisningen.
- I
PlayerActivity
skal du nu finde videovisningen, så du kan opsætte den korrekt ionCreate
-metoden i aktiviteten.
PlayerActivity.java
@Overrideprotected void onCreate(Bundle savedInstanceState) { playerView = findViewById(R.id.video_view);}
- Føj medlemsfeltet
playerView
til dinPlayerActivity
-klasse. Sørg for, at visningstypen erPlayerView
.
Bemærk: Brug Quick Fix-funktionen i Android Studio til at tilføje et medlemsfelt automatisk. Husk at indstille typen til
PlayerView
i stedet for standard
View
Screenshot: Hurtigrettelsesmenu til oprettelse af et medlemsfelt
Opret en ExoPlayer
For at afspille streamingmedier skal du bruge et ExoPlayer
-objekt. Den enkleste måde at oprette et på er at bruge SimpleExoPlayer.Builder
-klassen. Som navnet antyder, bruger den builder-mønsteret til at oprette en SimpleExoPlayer
-instans.
SimpleExoPlayer
er en praktisk, universel implementering af ExoPlayer
-grænsefladen.
Føj en privat metode initializePlayer
til at oprette din SimpleExoPlayer
.
PlayerActivity.java
private SimpleExoPlayer player;private void initializePlayer() { player = new SimpleExoPlayer.Builder(this).build(); playerView.setPlayer(player);}
Opret en SimpleExoPlayer.Builder
ved hjælp af din kontekst, og kald derefter build
for at oprette dit SimpleExoPlayer
-objekt. Dette tildeles derefter til player
, som du skal deklarere som et medlemsfelt. Du bruger derefter playerView.setPlayer
til at binde player
til den tilsvarende visning.
Opret et medieelement
Din player
skal nu have noget indhold til afspilning. Til dette opretter du et MediaItem
. Der findes mange forskellige typer MediaItem
, men du starter med at oprette en til en MP3-fil på internettet.
Den enkleste måde at oprette en MediaItem
på er ved at bruge MediaItem.fromUri
, som accepterer URI’en for en mediefil. Tilføj MediaItem
til player
ved hjælp af player.setMediaItem
.
- Føj følgende kode til
initializePlayer
:
PlayerActivity.java
private void initializePlayer() { MediaItem mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3)); player.setMediaItem(mediaItem);}
Bemærk, at R.string.media_url_mp3
er defineret som https://storage.googleapis.com/exoplayer-test-media-0/play.mp3 i strings.xml
.
Spil med Activity-livscyklus
Vores player
kan beslaglægge en masse ressourcer, herunder hukommelse, CPU, netværksforbindelser og hardwarecodecs. Mange af disse ressourcer er en mangelvare, især for hardware-codecs, hvor der måske kun er ét. Det er vigtigt, at du frigiver disse ressourcer til andre apps, så de kan bruge dem, når du ikke bruger dem, f.eks. når din app sættes i baggrunden.
Sagt på en anden måde bør din afspillers livscyklus være bundet til din app’ livscyklus. For at implementere dette skal du overskrive de fire metoder i PlayerActivity
: onStart
, onResume
, onPause
og onStop
.
- Med
PlayerActivity
åben skal du klikke på Code menu > Override methods…. - Vælg
onStart
,onResume
,onPause
ogonStop
. - Initialisér afspilleren i
onStart
elleronResume
callback afhængigt af API-niveauet.
PlayerActivity.java
@Overridepublic void onStart() { super.onStart(); if (Util.SDK_INT >= 24) { initializePlayer(); }}@Overridepublic void onResume() { super.onResume(); hideSystemUi(); if ((Util.SDK_INT < 24 || player == null)) { initializePlayer(); }}
Android API-niveau 24 og højere understøtter flere vinduer. Da din app kan være synlig, men ikke aktiv i split window-tilstand, skal du initialisere afspilleren i onStart
. Android API-niveau 24 og lavere kræver, at du venter så længe som muligt, indtil du får fat i ressourcer, så du venter til onResume
, før du initialiserer afspilleren.
- Føj metoden
hideSystemUi
til.
PlayerActivity.java
@SuppressLint("InlinedApi")private void hideSystemUi() { playerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);}
hideSystemUi
er en hjælpemetode, der kaldes i onResume
, og som giver dig mulighed for at få en fuldskærmsoplevelse.
- Frikøb ressourcer med
releasePlayer
(som du opretter om kort tid) ionPause
ogonStop
.
PlayerActivity.java
@Overridepublic void onPause() { super.onPause(); if (Util.SDK_INT < 24) { releasePlayer(); }}@Overridepublic void onStop() { super.onStop(); if (Util.SDK_INT >= 24) { releasePlayer(); }}
Med API-niveau 24 og lavere er der ingen garanti for, at onStop
bliver kaldt, så du skal frigive spilleren så tidligt som muligt i onPause
. Med API-niveau 24 og højere (som bragte multi- og split-window-tilstand) er det garanteret, at onStop
bliver kaldt. I den pausede tilstand er din aktivitet stadig synlig, så du venter med at frigive spilleren til onStop
.
Du skal nu oprette en releasePlayer
-metode, som frigør spillerens ressourcer og destruerer den.
- Føj følgende kode til aktiviteten:
PlayerActivity.java
private boolean playWhenReady = true;private int currentWindow = 0;private long playbackPosition = 0;private void releasePlayer() { if (player != null) { playWhenReady = player.getPlayWhenReady(); playbackPosition = player.getCurrentPosition(); currentWindow = player.getCurrentWindowIndex(); player.release(); player = null; }}
Hvor du frigiver og ødelægger afspilleren, skal du gemme følgende oplysninger:
- Play/pause-status ved hjælp af
getPlayWhenReady
. - Aktuel afspilningsposition ved hjælp af
getCurrentPosition
. - Aktuel vinduesindeks ved hjælp af
getCurrentWindowIndex
. Du kan finde flere oplysninger om vinduer under Tidslinje.
Dette giver dig mulighed for at genoptage afspilningen fra det sted, hvor brugeren slap. Det eneste, du skal gøre, er at levere disse tilstandsoplysninger, når du initialiserer din afspiller.
Slutforberedelse
Det eneste, du skal gøre nu, er at levere de tilstandsoplysninger, du gemte i releasePlayer
, til din afspiller under initialiseringen.
- Føj følgende til
initializePlayer
:
PlayerActivity.java
private void initializePlayer() { player.setPlayWhenReady(playWhenReady); player.seekTo(currentWindow, playbackPosition); player.prepare();}
Her er, hvad der sker:
-
setPlayWhenReady
fortæller afspilleren, om den skal starte afspilningen, så snart alle ressourcer til afspilning er blevet erhvervet. DaplayWhenReady
oprindeligt ertrue
, starter afspilningen automatisk, første gang appen køres. -
seekTo
fortæller afspilleren, at den skal søge til en bestemt position i et bestemt vindue. BådecurrentWindow
ogplaybackPosition
er initialiseret til nul, så afspilningen starter helt fra starten, første gang appen køres. -
prepare
fortæller afspilleren, at den skal erhverve alle de ressourcer, der er nødvendige for afspilningen.
Afspilning af lyd
Endeligt er du færdig! Start appen for at afspille MP3-filen og se det indlejrede kunstværk.
Skærmbillede: Appen afspiller et enkelt nummer.
Test aktivitetslivscyklus
Test, om appen fungerer i alle de forskellige tilstande i aktivitetslivscyklusen.
- Start en anden app, og sæt din app i forgrunden igen. Genoptages den i den korrekte position?
- Pause appen, og flyt den til baggrunden og derefter til forgrunden igen. Holder den fast i en pauseret tilstand, når den er sat i baggrunden i pauseret tilstand?
- Rotér appen. Hvordan opfører den sig, hvis du ændrer orienteringen fra portræt til landskab og tilbage?
Afspil video
Hvis du ønsker at afspille video, er det lige så nemt som at ændre medieelementets URI til en MP4-fil.
- Ændre URI’en i
initializePlayer
tilR.string.media_url_mp4
. - Start appen igen, og test adfærden efter at være blevet afspillet i baggrunden med videoafspilning også.
PlayerActivity.java
private void initializePlayer() { MediaItem mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3)); }
Den PlayerView
gør det hele. I stedet for kunstværket gengives videoen i fuld skærm.
Skærmbillede: Appen afspiller video.
Du rocker! Du har lige oprettet en app til streaming af medier i fuld skærm på Android, komplet med livscyklusstyring, gemt tilstand og brugergrænsefladekontroller!
Din nuværende app afspiller en enkelt mediefil, men hvad hvis du ønsker at afspille flere mediefiler efter hinanden? Til det har du brug for en afspilningsliste.
Playlister kan oprettes ved at tilføje flere MediaItem
s til din player
ved hjælp af addMediaItem
. Dette giver mulighed for problemfri afspilning, og buffering håndteres i baggrunden, så brugeren ikke ser en bufferingsspinner, når der skiftes medieelementer.
- Føj følgende kode til
initializePlayer
:
PlayerActivity.java
private void initializePlayer() { player.setMediaItem(mediaItem); // Existing code MediaItem secondMediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3)); player.addMediaItem(secondMediaItem); }
Kontroller, hvordan afspillerkontrollerne opfører sig. Du kan bruge og til at navigere i sekvensen af medieelementer.
Skærmbillede: Afspilningskontroller viser en næste og forrige knap
Det er ret praktisk! Du kan finde flere oplysninger i udviklerdokumentationen om medieelementer og afspilningslister og i denne artikel om API’et for afspilningslister.
Adaptiv streaming er en teknik til streaming af medier ved at variere streamens kvalitet på baggrund af den tilgængelige netværksbåndbredde. Dette giver brugeren mulighed for at opleve medierne i den bedste kvalitet, som deres båndbredde tillader.
Typisk er det samme medieindhold opdelt i flere spor med forskellige kvaliteter (bitfrekvenser og opløsninger). Afspilleren vælger et spor ud fra den tilgængelige netværksbåndbredde.
Hvert spor er opdelt i bidder af en given varighed, typisk mellem 2 og 10 sekunder. Dette giver afspilleren mulighed for hurtigt at skifte mellem sporene, efterhånden som den tilgængelige båndbredde ændrer sig. Afspilleren er ansvarlig for at sætte disse stykker sammen for at sikre en problemfri afspilning.
Adaptivt sporvalg
Det centrale i adaptiv streaming er at vælge det mest passende spor til det aktuelle miljø. Opdater din app til afspilning af adaptive streamingmedier ved hjælp af adaptivt sporvalg.
- opdatér
initializePlayer
med følgende kode:
PlayerActivity.java
private void initializePlayer() { if (player == null) { DefaultTrackSelector trackSelector = new DefaultTrackSelector(this); trackSelector.setParameters( trackSelector.buildUponParameters().setMaxVideoSizeSd()); player = new SimpleExoPlayer.Builder(this) .setTrackSelector(trackSelector) .build(); } // Remove or comment out. // player = new SimpleExoPlayer.Builder(this).build(); }
Opret først en DefaultTrackSelector
, som er ansvarlig for at vælge spor i medieelementet. Fortæl derefter din trackSelector
, at den kun skal vælge spor med standardopløsning eller lavere – en god måde at spare på brugerens data på bekostning af kvaliteten. Til sidst skal du videregive din trackSelector
til din builder, så den bruges, når du bygger SimpleExoPlayer
-instansen.
Byg et adaptivt MediaItem
DASH er et udbredt adaptivt streamingformat. Hvis du vil streame DASH-indhold, skal du oprette en MediaItem
som før. Denne gang skal vi dog bruge en MediaItem.Builder
i stedet for fromUri
.
Dette skyldes, at fromUri
bruger filudvidelsen til at bestemme det underliggende medieformat, men vores DASH-URI har ikke en filudvidelse, så vi skal angive en MIME-type på APPLICATION_MPD
, når vi konstruerer MediaItem
.
- Opdatér
initializePlayer
på følgende måde:
PlayerActivity.java
- Start appen igen, og se adaptiv videostreaming med DASH i aktion. Det er ret nemt med ExoPlayer!
Andre adaptive streamingformater
HLS (MimeTypes.APPLICATION_M3U8
) og SmoothStreaming (MimeTypes.APPLICATION_SS
) er andre almindeligt anvendte adaptive streamingformater, som begge understøttes af ExoPlayer. Du kan finde flere oplysninger om opbygning af andre adaptive mediekilder i ExoPlayer-demo-appen.
I de foregående trin lærte du, hvordan du streamer progressive og adaptive mediestrømme. ExoPlayer udfører en masse arbejde for dig bag kulisserne, herunder følgende:
- Allokering af hukommelse
- Hentning af containerfiler
- Udtrækning af metadata fra containeren
- Dekodning af data
- Gengivelse af video, lyd og tekst til skærmen og højttalerne
Sommetider er det nyttigt at vide, hvad ExoPlayer gør ved kørselstid for at forstå og forbedre afspilningsoplevelsen for dine brugere.
Du kan f.eks. ønske at afspejle ændringer i afspilningstilstanden i brugergrænsefladen ved at gøre følgende:
- Vise en loading spinner, når afspilleren går i en bufferingtilstand
- Vise et overlay med “se næste” muligheder, når sporet er slut
ExoPlayer tilbyder flere lyttergrænseflader, der leverer callbacks for nyttige hændelser. Du bruger en lytter til at logge, hvilken tilstand afspilleren befinder sig i.
Lyt op
- Deklarér et privat medlem af typen
PlaybackStateListener
iPlayerActivity
. - Opret en
TAG
-konstant, som du bruger til logning senere.
PlayerActivity.java
private PlaybackStateListener playbackStateListener;private static final String TAG = PlayerActivity.class.getName();
- Instantiér
playbackStateListener
i begyndelsen afonCreate
(det kompileres ikke endnu, men du retter det om kort tid).
PlayerActivity.java
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_player); playbackStateListener = new PlaybackStateListener();
- Brug Quick Fix til automatisk at oprette den indre klasse
PlaybackStateListener
.
Screenshot: Quick Fix-menuen til oprettelse af en manglende klasse
- Implementer
Player.EventListener
grænsefladen. Dette bruges til at informere dig om vigtige afspillerbegivenheder, herunder fejl og ændringer i afspilningstilstanden. - Override
onPlaybackStateChanged
ved at tilføje følgende kode: - Registrer lytteren, før afspilningen forberedes.
- Fjern lytteren i
releasePlayer
: - Åbn logcat, og kør appen.
- Brug brugergrænsefladekontrollerne til at søge, sætte afspilningen på pause og genoptage den. Du bør kunne se, at afspilningstilstanden ændres i logfilerne.
PlayerActivity.java
private class PlaybackStateListener implements Player.EventListener { @Override public void onPlaybackStateChanged(int playbackState) { String stateString; switch (playbackState) { case ExoPlayer.STATE_IDLE: stateString = "ExoPlayer.STATE_IDLE -"; break; case ExoPlayer.STATE_BUFFERING: stateString = "ExoPlayer.STATE_BUFFERING -"; break; case ExoPlayer.STATE_READY: stateString = "ExoPlayer.STATE_READY -"; break; case ExoPlayer.STATE_ENDED: stateString = "ExoPlayer.STATE_ENDED -"; break; default: stateString = "UNKNOWN_STATE -"; break; } Log.d(TAG, "changed state to " + stateString); }}
onPlaybackStateChanged
kaldes, når afspilningstilstanden ændres. Den nye tilstand angives af parameteren playbackState
.
Afspilleren kan være i en af følgende fire tilstande:
State |
Description |
|
Afspilleren er blevet instantieret, men er endnu ikke blevet forberedt. |
|
Spilleren er ikke i stand til at spille fra den aktuelle position, fordi der ikke er blevet gemt nok data i buffer. |
|
Spilleren er i stand til straks at spille fra den aktuelle position. Det betyder, at afspilleren automatisk begynder at afspille medierne, hvis afspillerens playWhenReady-egenskab er |
|
Afspilleren er færdig med at afspille mediet. |
Registrer din lytter
For at få dine callbacks kaldt, skal du registrere din playbackStateListener
hos afspilleren. Det skal du gøre i initializePlayer
.
PlayerActivity.java
private void initializePlayer() { if (player == null) { player.addListener(playbackStateListener); player.prepare();}
Også her skal du rydde op for at undgå dinglende referencer fra afspilleren, som kan forårsage en hukommelseslækage.
PlayerActivity.java
private void releasePlayer() { if (player != null) { player.removeListener(playbackStateListener); player.release(); player = null; }}
Gå dybere
ExoPlayer tilbyder en række andre lyttere, som er nyttige til at forstå brugerens afspilningsoplevelse. Der er lyttere til lyd og video samt en AnalyticsListener
, som indeholder callbacks fra alle lytterne. Nogle af de vigtigste metoder er følgende:
-
onRenderedFirstFrame
kaldes, når den første frame i en video gengives. Hermed kan du beregne, hvor lang tid brugeren måtte vente på at se meningsfuldt indhold på skærmen. -
onDroppedVideoFrames
kaldes, når videoframes er blevet droppet. Droppede frames er tegn på, at afspilningen er skramlet, og at brugeroplevelsen sandsynligvis vil være dårlig. -
onAudioUnderrun
kaldes, når der er sket en lydunderkørsel. Underrunner forårsager hørbare glitches i lyden og er mere mærkbare end tabte videoframes.
AnalyticsListener
kan tilføjes til player
med addAnalyticsListener
. Der er også tilsvarende metoder til lyd- og videolytterne.
Tænk over, hvilke hændelser der er vigtige for din app og dine brugere. Du kan finde flere oplysninger under Lytte til afspillerbegivenheder. Det var det med hændelseslytterne!
Så langt har du brugt ExoPlayer’s PlayerControlView
til at vise en afspilningscontroller til brugeren.
Screenshot: Standard afspilningscontroller
Hvad sker der, hvis du ønsker at ændre funktionaliteten eller udseendet af disse kontroller? Heldigvis kan disse kontroller i høj grad tilpasses.
Den første enkle tilpasning er, at du slet ikke bruger controlleren. Dette kan nemt gøres ved at bruge attributten use_controller
på PlayerView
-elementet inde i activity_player.xml
.
- Sæt
use_controller
tilfalse
, og kontrolelementet vises ikke længere:
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView app:use_controller="false"/>
- Føj følgende namespace til din
FrameLayout
:
activity_player.xml
<FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto">
Prøv det nu.
Apas adfærden
PlayerControlView
har flere attributter, der påvirker dens adfærd. Brug show_timeout
, fastforward_increment
og rewind_increment
til at tilpasse controllerens adfærd.
- Fjern
app:use_controller="false"
. - Ændre spillervisningen, så den bruger
show_timeout
,fastforward_increment
ogrewind_increment
:
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent" app:show_timeout="10000" app:fastforward_increment="30000" app:rewind_increment="30000"/>
- Værdien
show_timeout
fortællerPlayerView
forsinkelsen i millisekunder, før kontrollen skjules, efter at brugeren sidst har interageret med den. - Værdierne
fastforward_increment
ogrewind_increment
fortæller afspilleren, hvor lang tid i millisekunder der skal springes fremad eller tilbage, når brugeren trykker på knapperne til at spole fremad eller tilbage.
Den PlayerControlView
s attributter kan også indstilles programmatisk.
Anpas udseendet
Det er en god start. Men hvad nu, hvis du vil have PlayerControlView
til at se anderledes ud eller ændre, hvilke knapper der vises? Implementeringen af PlayerControlView
forudsætter ikke, at der findes nogen knapper, så det er nemt at fjerne dem og tilføje nye.
Se på, hvordan du kan tilpasse PlayerControlView
.
- Opret en ny layoutfil
custom_player_control_view.xml
i mappenplayer-lib/res/layout/
. - I kontekstmenuen i layoutmappen skal du vælge Ny – Layoutressourcefil og navngive den
custom_player_control_view.xml
.
Skærmbillede: Layoutfilen til afspillerkontrolvisningen er blevet oprettet.
- Kopier den oprindelige layoutfil herfra til
custom_player_control_view.xml
. - Fjern
ImageButton
-elementerne med id@id/exo_prev
og@id/exo_next
.
For at bruge dit brugerdefinerede layout skal du indstille attributten app:controller_layout_id
for PlayerView
-elementet i activity_player.xml
-filen.
- Brug layout-id’et for din brugerdefinerede fil som i følgende kodestump:
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent" app:controller_layout_id="@layout/custom_player_control_view"/>
- Start appen igen. Kontrolvisningen for spilleren har ikke længere knapperne forrige og næste.
Skærmbillede: Brugerdefineret afspillerkontrolvisning uden knapperne forrige og næste
Du kan anvende alle de ændringer, du ønsker, i layoutfilen. Som standard er farverne i Android-temaet valgt. Du kan tilsidesætte dette, så det passer til designet i din app.
- Føj en
android:tint
-attribut til hvertImageButton
-element:
custom_player_control_view.xml
<ImageButton android:id="@id/exo_rew" android:tint="#FF00A6FF" style="@style/ExoMediaButton.Rewind"/>
- Opnå alle
android:textColor
-attributter, du finder i din brugerdefinerede fil, til den samme farve:#FF00A6FF
.
custom_player_control_view.xml
<TextView android:id="@id/exo_position" android:textColor="#FF00A6FF"/><TextView android:id="@id/exo_duration" android:textColor="#FF00A6FF"/>
- Kør appen. Nu har du smukke, farvede UI-komponenter!
Skærmbillede: Du har lige oprettet en brugerdefineret layoutfil og refereret til den ved hjælp af controller_layout_id
i activity_player.xml
.
En anden fremgangsmåde er at tilsidesætte standardlayoutfilen, som PlayerControlView
bruger. Kildekoden for PlayerControlView
fortæller os, at den bruger R.layout.exo_player_control_view
til layout. Hvis du opretter vores egen layoutfil med samme filnavn, bruger PlayerControlView
din fil i stedet.
- Fjern den
controller_layout_id
-attribut, du lige har tilføjet. - Slet filen
custom_player_control_view.xml
.
Den PlayerView
i activity_player.xml
skal nu se sådan ud:
activity_player.xml
<com.google.android.exoplayer2.ui.PlayerView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
- Opret en fil med navnet
exo_player_control_view.xml
ires/layout
-mappen i dit biblioteksmodulplayer-lib
. - Indsæt følgende kode i
exo_player_control_view.xml
for at tilføje en afspilningsknap, en pauseknap og enImageView
med et logo:
exo_player**_control_view.xml**
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layoutDirection="ltr" android:background="#CC000000" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:paddingTop="4dp" android:orientation="horizontal"> <ImageButton android:id="@id/exo_play" style="@style/ExoMediaButton.Play"/> <ImageButton android:id="@id/exo_pause" style="@style/ExoMediaButton.Pause"/> </LinearLayout> <ImageView android:contentDescription="@string/logo" android:src="@drawable/google_logo" android:layout_width="match_parent" android:layout_height="wrap_content"/></LinearLayout>
Dette demonstrerer, hvordan du kan tilføje dine egne elementer her og blande dem med standardkontrolelementer. ExoPlayerView
bruger nu din brugerdefinerede kontrol, og al logik til at skjule og vise, når du interagerer med kontrollen, er bevaret.
Godt tillykke! Du har lært en masse om integration af ExoPlayer i din app.