Opprette et Android Wear Watch Face

En av funksjonene som gjør Android så spesiell, er evnen til å tilpasse alle aspekter av brukeropplevelsen. Når Android Wear ble lansert første gang i Google I / O 2014, fant mange utviklere og brukere at dette ikke var helt sant for smarte klokker, da den offisielle API for å lage watch faces manglet merkbart. Gitt at evnen til å lage egendefinerte watch-ansikter var en av de viktigste ønsker fra brukere, er det ikke overraskende at utviklere oppdaget en måte å skape sine egne klokke ansikter med et uokumentert hack i Android Wear.

Heldigvis lar Google raskt alle få vite at en offisiell API var på vei, og i desember 2014 ble API endelig gitt ut til utviklingssamfunnet. I denne artikkelen skal du lære om den offisielle Watch Faces API for Android Wear og implementere et enkelt digitalt klokke ansikt som du vil kunne utvide på for dine egne behov. Implementering av watch-ansikter kan være litt verbose, men du kan finne eksempelprogrammet for denne artikkelen på GitHub.

1. Sette opp din IDE

Det første du må gjøre for å lage ditt eget klokke ansikt, er at prosjektet ditt blir satt opp i Android Studio. Når du oppretter prosjektet, velger du Telefon og Tablet med en Minimum SDK av API 18 som Android 4.3 er den laveste versjonen av operativsystemet for å støtte kombinert Android Wear-applikasjoner. Du må også sjekke Ha på boks med a Minimum SDK av API 21 valgt. Du kan se et eksempel på hva din Mål Android-enheter skjermen skal se ut.

Når du kommer til de to Legg til en aktivitet skjermbilder, velg Legg til ingen aktivitet for begge skjermene.

Når du klikker Bli ferdig, prosjektmiljøet ditt skal bygge og ha en modul for mobil og en annen for ha på.

2. Bygg Wear Watch Service

Android Wear implementerer se på ansikter ved bruk av WatchFaceService. I denne artikkelen skal du lage en utvidelse av CanvasWatchFaceService klassen, som er en implementering av WatchFaceService som også gir en Lerret for å trekke ut ditt ansikt. Begynn med å opprette en ny Java-klasse under ha på modul i Android Studio som strekker seg CanvasWatchFaceService.

offentlig klasse WatchFaceService utvider CanvasWatchFaceService

Når du har din klasse, trenger du å lage en indre klasse, WatchFaceEngine i kildefilene i denne artikkelen, som strekker seg ut Motor. Dette er klokkeovervåkingsmotoren som håndterer systemhendelser, for eksempel skjermen slår av eller går inn i omgivende modus.

Private klasse WatchFaceEngine utvider motoren

Når stubkoden din er kode for WatchFaceEngine er inne, gå tilbake til ytre klasse og overstyr den onCreateEngine metode for å returnere din nye indre klasse. Dette vil knytte din ansiktstjeneste med koden som kjører skjermen.

@Override public Engine onCreateEngine () returner nytt WatchFaceEngine (); 

Når du har den bare bein-tjenesten satt sammen, kan du gå videre til de generelle husholdningsoppgaver for å oppdatere din AndroidManifest filer slik at tjenesten din blir oppdaget av Android Wear. Husk at din nåværende kode ikke vil gjøre noe enda. Vi kommer tilbake til denne klassen og kjøler ut motoren etter å ha gjort noe prosjektrengjøring.

3. Oppdatering av AndroidManifest-filene

Åpne AndroidManifest.xml fil i ha på modul. Nær toppen bør du allerede se en linje som sier:

Under denne linjen må vi legge til de to nødvendige tillatelsene for et klokkeansikt. Disse kravene er:

 

Når dine tillatelser er angitt, må du legge til en node for tjenesten din i applikasjon nod med tillatelse til BIND_WALLPAPER, noen få sett med meta-dataene inneholder referansebilder av uret ditt for valgskjermen (i dette eksemplet bruker vi bare lanseringsikonet), og en intent-filteret for å la systemet vite at tjenesten din er ment for å vise et klokkeansikt.

        

En gang din ha på manifest er fullført, må du åpne AndroidManifest.xml fil i mobil modul og legg til i de to tillatelsene vi brukte i ha på modul for PROVIDE_BACKGROUND og WAKE_LOCK, fordi Android Wear krever at begge ha på og mobil moduler krever samme tillatelser for slitasje APK som skal installeres på brukerens klokke. Når begge manifestfilene er fylt ut, kan du gå tilbake til CustomWatchFaceService.java å begynne å implementere motoren.

4. Start motoren

De Motor objekt som er knyttet til tjenesten din, er det som driver visningsansikten. Den håndterer timere, viser brukergrensesnittet, beveger seg inn og ut av omgivelsesmodus, og får informasjon om fysisk klokkevisning. Kort sagt, dette er hvor magien skjer.

Trinn 1: Definere nødvendige verdier og variabler

Det første du vil gjøre, er å implementere et sett med medlemsvariabler i motoren din for å holde oversikt over enhetsstatus, tidsintervaller og attributter for skjermen..

// Medlemsvariabler privat skrifttype WATCH_TEXT_TYPEFACE = Typeface.create (Typeface.SERIF, Typeface.NORMAL); privat statisk endelig int MSG_UPDATE_TIME_ID = 42; privat lang mUpdateRateMs = 1000; privat tid mDisplayTime; privat maling mBackgroundColorPaint; privat maling mTextColorPaint; privat boolsk mHasTimeZoneReceiverBeenRegistered = false; privat booleansk mIsInMuteMode; privat booleansk mIsLowBitAmbient; privat float mXOffset; privat float mYOffset; privat int mBackgroundColor = Color.parseColor ("svart"); privat int mTextColor = Color.parseColor ("rød");

Som du kan se definerer vi skriftsnitt som vi vil bruke til vår digitale seekst, så vel som klokkeslettets bakgrunnsfarge og tekstfarge. De Tid Objektet brukes til, du gjettet det, holder oversikt over gjeldende enhetstid. mUpdateRateMs brukes til å styre en tidtaker som vi må implementere for å oppdatere vaktovervåket hvert sekund (dermed 1000 millisekunder verdi for mUpdateRateMs), fordi standarden WatchFaceService holder kun tid i løpet av ett minutt. mXOffset og mYOffset defineres når motoren kjenner urets fysiske form, slik at vekkoverflaten kan trekkes uten å være for nær toppen eller til venstre på skjermen, eller avskjæres med et avrundet hjørne. De tre boolske verdiene brukes til å holde oversikt over ulike enheter og applikasjonsstatus.

Det neste objektet du må definere er en kringkastingsmottaker som håndterer situasjonen der en bruker kan reise og endre tidssoner. Denne mottakeren sletter bare den lagrede tidssonen og nullstiller visningstiden.

Endelig BroadcastReceiver mTimeZoneBroadcastReceiver = Ny BroadcastReceiver () @Overtrid offentlig tomgang onReceive (Kontekst kontekst, Intent intent) mDisplayTime.clear (intent.getStringExtra ("tidssone")); mDisplayTime.setToNow (); ;

Etter at mottakeren din er definert, er det endelige objektet du må opprette øverst på motoren din a handler å ta vare på å oppdatere ditt ansikt hvert sekund. Dette er nødvendig på grunn av begrensningene til WatchFaceService diskutert ovenfor. Hvis ditt eget klokke ansikt bare må oppdateres hvert minutt, så kan du sikkert ignorere denne delen.

Privat Endelig Handler mTimeHandler = Ny Handler () @Override Offentlig tomgangshåndtakMessage (Meldingsmelding) bytt (msg.what) tilfelle MSG_UPDATE_TIME_ID: invalidate (); hvis (isVisible () &&! isInAmbientMode ()) long currentTimeMillis = System.currentTimeMillis (); lang forsinkelse = mUpdateRateMs - (currentTimeMillis% mUpdateRateMs); mTimeHandler.sendEmptyMessageDelayed (MSG_UPDATE_TIME_ID, forsinkelse);  gå i stykker; ;

Gjennomføringen av handler er ganske grei. Det kontrollerer først meldings-ID-en. Hvis matcher MSG_UPDATE_TIME_ID, Det fortsetter å ugyldiggjøre den nåværende visningen for omformulering. Etter at visningen er ugyldiggjort, vil handler sjekker for å se om skjermen er synlig og ikke i omgivende modus. Hvis den er synlig, sender den en gjentakelsesforespørsel et sekund senere. Grunnen til at vi bare gjentar handlingen i handler når klokkeflaten er synlig og ikke i omgivelsesmodus, er det at det kan være lite batteriintensivt å holde oppdateringen hvert sekund. Hvis brukeren ikke ser på skjermen, faller vi bare tilbake på WatchFaceService implementering som oppdateres hvert minutt.

Trinn 2: Initialisering av motoren

Nå som variablene og objektene er erklært, er det på tide å begynne å initialisere klokkeansiktet. Motor har en onCreate Metode som skal brukes til å lage objekter og andre oppgaver som kan ta betydelig tid og batteri. Du vil også ønske å sette noen få flagg for WatchFaceStyle her for å kontrollere hvordan systemet samhandler med brukeren når klokkeansiktet ditt er aktivt.

@Override public void onCreate (SurfaceHolder holder) super.onCreate (holder); setWatchFaceStyle (ny WatchFaceStyle.Builder (CustomWatchFaceService.this) .setBackgroundVisibility (WatchFaceStyle.BACKGROUND_VISIBILITY_INTERRUPTIVE) .setCardPeekMode (WatchFaceStyle.PEEK_MODE_VARIABLE) .setShowSystemUiTime (false) .build ()); mDisplayTime = ny tid (); initBackground (); initDisplayText (); 

For eksempelappen bruker du setWatchFaceStyle for å angi bakgrunnen til varselkortene dine for å vise kort om korttypen er avbrutt. Du vil også sette tittelmodus slik at meldingskort bare tar opp så mye plass som nødvendig.

Til slutt vil du fortelle at systemet ikke viser standardtiden siden du vil vise det selv. Mens disse bare er noen av de tilgjengelige alternativene, kan du finne enda mer informasjon i den offisielle dokumentasjonen for WatchFaceStyle.Builder gjenstand.

Etter din WatchFaceStyle har blitt satt, kan du initialisere mDisplayTime som en ny Tid gjenstand.

initBackground og initDisplayText fordel de to Maling Objekter du definerte øverst på motoren. Bakgrunnen og teksten har så sitt farger og teksten har skrifttype og skriftstørrelse, samtidig som den slår på anti-aliasing.

privat tomt initBackground () mBackgroundColorPaint = ny Maling (); mBackgroundColorPaint.setColor (mBackgroundColor);  private void initDisplayText () mTextColorPaint = ny Paint (); mTextColorPaint.setColor (mTextColor); mTextColorPaint.setTypeface (WATCH_TEXT_TYPEFACE); mTextColorPaint.setAntiAlias ​​(true); mTextColorPaint.setTextSize (getResources (). getDimension (R.dimen.text_size)); 

Trinn 3: Håndtering av enheten

Deretter må du implementere ulike metoder fra Motor klasse som utløses av endringer i enhetens tilstand. Vi begynner med å gå over onVisibilityChanged metode, som kalles når brukeren skjuler eller viser klokkeansiktet.

@Override public void onVisibilityChanged (boolean synlig) super.onVisibilityChanged (synlig); hvis (synlig) hvis (! mHasTimeZoneReceiverBeenRegistered) IntentFilter filter = ny IntentFilter (Intent.ACTION_TIMEZONE_CHANGED); CustomWatchFaceService.this.registerReceiver (mTimeZoneBroadcastReceiver, filter); mHasTimeZoneReceiverBeenRegistered = true;  mDisplayTime.clear (TimeZone.getDefault (). getID ()); mDisplayTime.setToNow ();  annet hvis (mHasTimeZoneReceiverBeenRegistered) CustomWatchFaceService.this.unregisterReceiver (mTimeZoneBroadcastReceiver); mHasTimeZoneReceiverBeenRegistered = false;  updateTimer (); 

Når denne metoden blir kalt, kontrollerer den å se om klokken er synlig eller ikke. Hvis klokken er synlig, ser det ut til å se om BroadcastReceiver som du definerte øverst på Motor er registrert. Hvis ikke, oppretter metoden en IntentFilter for ACTION_TIMEZONE_CHANGED handling og registrerer BroadcastReceiver å lytte etter det.

Hvis klokkeoverflaten ikke er synlig, kontrollerer denne metoden for å se om BroadcastReceiver kan være uregistrert. Først når BroadcastReceiver har blitt håndtert, updateTimer kalles for å utløse ugyldigelse av klokkeoverflaten og omklare klokkeoverflaten. updateTimer stopper noen handler handlinger som er ventet og kontrollerer for å se om en annen skal sendes.

privat ugyldig oppdateringTimer () mTimeHandler.removeMessages (MSG_UPDATE_TIME_ID); hvis (isVisible () &&! isInAmbientMode ()) mTimeHandler.sendEmptyMessage (MSG_UPDATE_TIME_ID); 

Trinn 4: Samarbeid med den bærbare maskinvaren

Når tjenesten din er knyttet til Android Wear, onApplyWindowInsets er kalt. Dette brukes til å avgjøre om enheten din klokke ansiktet kjører på er avrundet eller kvadret. Dette gjør at du kan endre uret ditt for å matche maskinvaren.

Når denne metoden kalles i prøveapplikasjonen, kontrollerer denne metoden bare enhetens form og endrer x-forskyvningen som brukes til å tegne klokkeansiktet for å sikre at klokkeansiktet ditt er synlig på enheten.

@Override public void onApplyWindowInsets (WindowInsets-innsettinger) super.onApplyWindowInsets (innlegg); mYOffset = getResources (). getDimension (R.dimen.y_offset); hvis (insets.isRound ()) mXOffset = getResources (). getDimension (R.dimen.x_offset_round);  ellers mXOffset = getResources (). getDimension (R.dimen.x_offset_square); 

Den neste metoden du må overstyre er onPropertiesChanged. Denne metoden kalles når maskinvareegenskapene for Wear-enheten er bestemt, for eksempel hvis enheten støtter innbruddsbeskyttelse eller lav bit omgivelsesmodus.

I denne metoden kontrollerer du om disse attributter gjelder for enheten som kjører uret ditt, og lagrer dem i en medlemsvariabel som er definert øverst på din Motor.

@Override public void onPropertiesChanged (Bundle egenskaper) super.onPropertiesChanged (egenskaper); hvis (properties.getBoolean (PROPERTY_BURN_IN_PROTECTION, false)) mIsLowBitAmbient = properties.getBoolean (PROPERTY_LOW_BIT_AMBIENT, falsk); 

Trinn 5: Behold batteriet i omgivende og nedtonede modus

Etter at du har håndtert de første enhetstilstandene, vil du ønsker å implementere onAmbientModeChanged og onInterruptionFilterChanged. Som navnet tilsier, onAmbientModeChanged kalles når enheten beveger seg inn i eller ut av omgivelsesmodus.

Hvis enheten er i omgivende modus, vil du endre fargen på uret ditt for å være svart og hvitt for å være oppmerksom på brukerens batteri. Når enheten vender tilbake fra omgivelsesmodus, kan du tilbakestille urets ansikts farger. Du vil også være oppmerksom på anti-aliasing for enheter som ber om lite bit ambientstøtte. Etter at alle flaggvariablene er angitt, kan du få uret til å ugyldiggjøre og omlegge, og deretter sjekke om en annen timer skal starte.

@Override public void onAmbientModeChanged (boolean inAmbientMode) super.onAmbientModeChanged (inAmbientMode); hvis (inAmbientMode) mTextColorPaint.setColor (Color.parseColor ("white"));  ellers mTextColorPaint.setColor (Color.parseColor ("rød"));  hvis (mIsLowBitAmbient) mTextColorPaint.setAntiAlias ​​(! inAmbientMode);  ugyldig (); updateTimer (); 


onInterruptionFilterChanged kalles når brukeren manuelt endrer avbruddsinnstillingene på deres bærbare. Når dette skjer, må du sjekke om enheten er dempet og deretter endre brukergrensesnittet tilsvarende. I denne situasjonen vil du endre gjennomsiktigheten til uret ditt ansikt, angi ditt handler for å bare oppdatere hvert minutt hvis enheten er dempet, og deretter omhente klokkeansiktet ditt.

@Override public void onInruptruptionFilterChanged (int interruptFilter) super.onInterruptionFilterChanged (interruptFilter); boolsk isDeviceMuted = (interruptFilter == android.support.wearable.watchface.WatchFaceService.INTERRUPTION_FILTER_NONE); hvis (isDeviceMuted) mUpdateRateMs = TimeUnit.MINUTES.toMillis (1);  ellers mUpdateRateMs = DEFAULT_UPDATE_RATE_MS;  hvis (mIsInMuteMode! = isDeviceMuted) mIsInMuteMode = isDeviceMuted; int alpha = (isDeviceMuted)? 100: 255; mTextColorPaint.setAlpha (alfa); ugyldig (); updateTimer (); 

Når enheten er i omgivende modus, vil handler timeren vil bli deaktivert. Uret ditt kan fortsatt oppdatere med gjeldende tid hvert minutt, ved hjelp av den innebygde onTimeTick metode for å ugyldiggjøre Lerret.

@Override public void onTimeTick () super.onTimeTick (); ugyldig (); 

Trinn 6: Tegne Watch Face

Når alle dine uforutsetninger er dekket, er det på tide å endelig trekke ut ditt ansikt. CanvasWatchFaceService bruker en standard Lerret objekt, så du må legge til onDraw til din Motor og skriv ut uret ditt manuelt.

I denne opplæringen skal vi ganske enkelt tegne en tekstrepresentasjon av tiden, selv om du kan endre din onDraw for enkel å støtte et analogt ansikt. I denne metoden vil du verifisere at du viser riktig tid ved å oppdatere din Tid objekt og så kan du begynne å bruke uret ditt ansikt.

@Override public void onDraw (lerret lerret, Rect grenser) super.onDraw (lerret, grenser); mDisplayTime.setToNow (); drawBackground (lerret, grenser); drawTimeText (lerret); 

drawBackground gjelder en solid farge på bakgrunnen av wear-enheten.

privat ugyldig drawBackground (lerret lerret, Rect grenser) canvas.drawRect (0, 0, bounds.width (), bounds.height (), mBackgroundColorPaint); 

drawTimeText, men skaper tidsteksten som vil bli vist ved hjelp av et par hjelpemetoder, og bruker den deretter på lerretet på x- og y-offsetpunktene du definerte i onApplyWindowInsets.

privat ugyldig tegningstime (lerretskunst) String timeText = getHourString () + ":" + String.format ("% 02d", mDisplayTime.minute); hvis (isInAmbientMode () || mIsInMuteMode) timeText + = (mDisplayTime.hour < 12 ) ? "AM" : "PM";  else  timeText += String.format( ":%02d", mDisplayTime.second);  canvas.drawText( timeText, mXOffset, mYOffset, mTextColorPaint );  private String getHourString()  if( mDisplayTime.hour % 12 == 0 ) return "12"; else if( mDisplayTime.hour <= 12 ) return String.valueOf( mDisplayTime.hour ); else return String.valueOf( mDisplayTime.hour - 12 ); 

Konklusjon

Når du har implementert metodene for å tegne ditt visnings ansikt, bør du være helt opptatt med den grunnleggende kunnskapen som trengs for å gå ut og lage dine egne klokke ansikter. Den fine tingen med Android Wear watch-ansikter er at dette bare skraper overflaten av det som er mulig.

Du kan legge til kompanjons konfigurasjonsaktiviteter på klokken eller på telefonen, erstatt Lerret basert watch face med en OpenGL implementering, eller utlede din egen klasse fra WatchFaceService for å møte dine behov.

Legg til at du kan få tilgang til andre APIer eller informasjon fra brukerens telefon, og mulighetene virker uendelige. Bli kreativ med dine ansikts ansikter og nyt.