czwartek, 4 marca 2021
czwartek, 20 sierpnia 2020
Android Basics - User Interface
Tego kursu wcześniej nie słuchałam i jak teraz usłyszałam, że to dla kogoś, kto nigdy nie napisał ani jednej linijki kodu, to zaczęłam wątpić...
Ale dobrze, pomyślałam, przekonaj się, że to umiesz, a przy okazji może usłyszysz jakąś informację, o której nie wiesz. I tak dowiedziałam się o:
- atrybucie android:textAppearance="?android:textAppearanceSmallInverse" , którego mogę użyć, gdy nie będę wiedzieć jaki rozmiar czcionki wybrać
- atrybucie layout_weight, który może wystąpić w LinearLayout przy layout_width="0dp", a jego wartość określa jaką częścią całości jest dany element (sumuje się wartości tego atrybutu u wszystkich dzieci rodzica i proporcjonalnie rozdysponowuje się miejsce)
- RelativeLayout - gdy robiłam kurs mniej podstawowy, ten layout był pominięty, zastąpiony ConstraintLayout, okazuje się, że są bardzo podobne a atrybutem typu android:layout_toLeftOf lub android:layout_below można ustawiać elementy obok siebie
- da się sensownie podzielić ekran bez wartości procentowych Guideline w ConstraintLayout
- to, co ma być wyświetlane na ekranie nie musi być od razu w całoścu widoczne - mogę użyć ScrollView i wewnątrz niego zaprojektować ciekawy układ elementów
środa, 19 sierpnia 2020
Jak dopasować style tekstu - wnioski z Material Design
Jak się okazuje, AndroidStudio ma zaimplementowane style wg Material Design, tylko nigdy nie dałam sobie czasu na to, żeby się w tym zorientować. To teraz już wiem :)
Planowałam te style przenieść do mojego projektu wpisując wszystkie dane, zgodnie z informacjami, a już nie muszę, bo tam wszystko jest. Tylko kroje czcionek zmieniłam.
I teraz wskazówki z tamtej strony (są tam też ciekawe, dobrze ilustrujące zagadnienia przykłady):
- Nagłówki
- to krótkie, ważne teksty,
- wyraźne, ciekawe kroje czcionek,
- mniejsze nagłówki będą lepiej wyglądać w spokojniejszym kroju czcionki.
- Podtytuły
- mniejsze od nagłówków,
- krókie teksty,
- wyraźne kroje czcionek.
- Treść
- długa forma,
- wyraźne kroje czcionek.
- Podpisy
- mogą być pod lub nad,
- dotyczą tekstu, tytułu lub obrazu,
- najmniejszy rozmiar czcionki,
- proste, wyraźne kroje czcionek.
- Przycisk
- często allCaps,
- często sans serif,
- powinien być wyraźny, widoczny, dawać jasno do zrozumienia co robi.
- Wyrównanie tekstu
- Najczęściej do lewej
- Cytaty wyśrodkowane, nie bardzo długie
O typografii z Material Design jest tutaj.
czwartek, 13 sierpnia 2020
Zmiana myślenia
Choć pozornie nie ma to nic wspólnego z programowaniem, od 4 tygodni realizuję program Droga Artysty. Bardzo polecam, to chyba najlepszy program rozwojowy, w jakim brałam udział. A możę pierwszy, za który wzięłam się na poważnie?
Tak czy inaczej - on oraz mój zespół Mastermind pomogli mi odkryć moją największą blokadę.
Ja NIE MOGĘ być programistą
Taka myśl, za każdym razem w mojej głowie. I nie chodzi o to, że nie będę dobrym programistą, czy idealnym, że nie w jakomś języku. Wcale. Nie mogę i już. Nie moja ścieżka, nie moje miejsce. NIE. Wstęp wzbroniony, możesz sobie siedzieć IT, zajmować się czymkolwiek, ale tego nie wolno dotknąć. Jak Drzewo Poznania Dobra i Zła. A ja się słuchałam, nie moje, to nie. Czy kusiło? Nie wiem. Lubiłam inne rzeczy, lubiłam to, co robiłam.
Aż do środy, 3 tygodnie temu, kiedy uświadomiłam sobie, skąd to się wzięło i dlaczego to jest takie złe.
A zatem się z tego uwalniam, wróciłam do kursu, zaplanowałam kolejne. Wymyśliłam jak wykorzystam wiedzę w praktyce.
Jest lepiej, serio. Jeśli Cię coś blokuje i nie masz z kim pogadać - napisz do mnie, zrozumiem Cię :)
Zapiszę sobie jeszcze co sobie przypomniałam powtarzając pierwszy moduł kursu (już to umiałam, tylko zapomniałam):
- Używaj najprostrzego layoutu w kolejności:
- Frame
- Linear
- Constrain
- Ustawianie tekstu w TextView:
- setText - wpisuje nową wartość
- append - dodaje wartość do tego co już było
czwartek, 6 lutego 2020
Dobre praktyki - Lekcja 2
2. Weryfikuj wywołania funkcji - czy na pewno tylko raz (rozsądne logi).
3. Porównuj Stringi przez .equals() a nie przez "=="
wtorek, 7 sierpnia 2018
Exception - org.json.JSON.typeMismatch
Myślałam, że parsowany string to JSONObject a to był JSONArray.
Plik .json (a właściwie String) zrobiłam sama, więc wystarczyło usunąć [ ] z początku i końca.
sobota, 28 lipca 2018
Android - testy wydania
Testy wydania aplikacji - android (ogólne)
- Aplikacja aktualizuje się z poprzedniej wersji do nowej (update)
- Aplikacja w aktualnej wersji może być zainstalowana na "czystym" urządzeniu (na takim, na którym nie jest zainstalowana) (instal)
- Aplikacja po wybraniu ikony włącza się i pokazuje ekran startowy (start)
- Aplikacja po wybraniu przycisku cofinij wyłącza się (end)
- Aplikacja wykonuje podstawową funkcję (work)
- Aplikacja aktualizuje się do poprzedniej wersji (downgrade)
Testy wydania aplikacji - android (dodatkowe)
- Aplikacja działa bez internetu (internet permission)
- brak wyjątków
- informacja o braku połączenia
- niezakłócone działanie
- Aplikacja działa przy obracaniu ekranu (orientation)
- dźwięk
- obraz
- funkcje
- ...
poniedziałek, 23 lipca 2018
Jak dodać odtwarzacz audio?
- Dodaj do pliku build.gradle -> dependencies
compile 'com.google.android.exoplayer:exoplayer:r2.2.0' - Dodaj do swojej aktywności
implements View.OnClickListener, ExoPlayer.EventListener - Dodaj do swojej aktywności obsługującej audio zmienne ExoPlayer
private SimpleExoPlayer mExoPlayer;
private SimpleExoPlayerView mPlayerView;
private static MediaSessionCompat mMediaSession;
private PlaybackStateCompat.Builder mStateBuilder;
private NotificationManager mNotificationManager; - Zainicjalizuj wstępnie zmienne
mPlayerView = (SimpleExoPlayerView) findViewById(R.id.playerView);
mPlayerView.setPlayer(mExoPlayer);
initializeMediaSession();
initializePlayer(Uri.parse(audioPath)); - Zaimplementuj inicjalizację MediaSession
mMediaSession = new MediaSessionCompat(this, this.getClass().getSimpleName());
mMediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setMediaButtonReceiver(null);
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY |
PlaybackStateCompat.ACTION_PAUSE |
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS |
PlaybackStateCompat.ACTION_PLAY_PAUSE);
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setCallback(new MySessionCallback());
mMediaSession.setActive(true); - Zaimplementuj podklasę MySessionCallback
@Override
public void onPlay() {
mExoPlayer.setPlayWhenReady(true);
}
@Override
public void onPause() {
mExoPlayer.setPlayWhenReady(false);
}
@Override
public void onSkipToPrevious() {
mExoPlayer.seekTo(0);
} - Zaimplementuj inicjalizację playera
if (mExoPlayer == null) {
// Create an instance of the ExoPlayer.
TrackSelector trackSelector = new DefaultTrackSelector();
LoadControl loadControl = new DefaultLoadControl();
mExoPlayer = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl);
mPlayerView.setPlayer(mExoPlayer);
// Set the ExoPlayer.EventListener to this activity.
mExoPlayer.addListener(this);
// Prepare the MediaSource.
String userAgent = Util.getUserAgent(this, "ClassicalMusicQuiz");
MediaSource mediaSource = new ExtractorMediaSource(mediaUri, new DefaultDataSourceFactory(
this, userAgent), new DefaultExtractorsFactory(), null, null);
mExoPlayer.prepare(mediaSource);
mExoPlayer.setPlayWhenReady(true);
} - Zaimplementuj funkcję zamykania playera
mNotificationManager.cancelAll();
mExoPlayer.stop();
mExoPlayer.release();
mExoPlayer = null; - Dodaj funkcję zamykania playera do odDestroy()
super.onDestroy();
releasePlayer();
mMediaSession.setActive(false); - Zaimplementuj funkcję onPlayerStateChanged()
if((playbackState == ExoPlayer.STATE_READY) && playWhenReady){
mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
mExoPlayer.getCurrentPosition(), 1f);
} else if((playbackState == ExoPlayer.STATE_READY)){
mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
mExoPlayer.getCurrentPosition(), 1f);
}
mMediaSession.setPlaybackState(mStateBuilder.build());
showNotification(mStateBuilder.build()); - Zaimplementuj MediaReceiver
public static class MediaReceiver extends BroadcastReceiver {
public MediaReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
MediaButtonReceiver.handleIntent(mMediaSession, intent);
}
} - Dodaj do manifestu aktywność obsługującą odtwarzacz
<activity android:name=".QuizActivity"
android:launchMode="singleTop"/>
<receiver android:name=".QuizActivity$MediaReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver> - Przygotuj layout uwzględniający SimpleExoPlayerView
<com.google.android.exoplayer2.ui.SimpleExoPlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:layout_marginRight="0dp"
android:layout_marginBottom="0dp"
android:layout_marginLeft="0dp"/>
niedziela, 22 lipca 2018
Jak przechowywać dane - json
Cechy w porównaniu z XML i JAML
- dostęp do danych w formacie JSON jest bardziej naturalny z poziomu języka JavaScript niż dostęp do tych samych danych w formacie XML
- nie jest już jednak tak naturalny dla Javy i wymaga stosowania specjalnych bibliotek
- w nowszych wersjach standardów dopuszczana jest wymiana danych za pomocą JSON-a (np. w REST)
- w praktyce zajmuje znacząco mniej miejsca niż analogiczny obiekt przesyłany za pomocą formatu XLM.
- JSON jest łatwiejszy do analizowania składni niż jego nadzbiór YAML
| [ | ||||||||||||||
| { | ||||||||||||||
| "name": "Toccata and Fugue in D minor", | ||||||||||||||
| "id": 0, | ||||||||||||||
| "uri": "asset:///toccata_fugue.mp3", | ||||||||||||||
| "composer": "Johann Sebastian Bach", | ||||||||||||||
| "albumArtID": "bach" | ||||||||||||||
| }, | ||||||||||||||
| { | ||||||||||||||
| "name": "Fur Elise", | ||||||||||||||
| "id": 1, | ||||||||||||||
| "uri": "asset:///fur_elise.mp3", | ||||||||||||||
| "composer": "Ludwig van Beethoven", | ||||||||||||||
| "albumArtID": "beethoven" | ||||||||||||||
| } ] |
Jak zastosować?
Stworzyć obiekt .java, który będzie odczytywał dane, np.:
private Sample(int sampleID, String composer, String title, String uri, String albumArtID) {
mSampleID = sampleID;
mComposer = composer;
mTitle = title;
mUri = uri;
mAlbumArtID = albumArtID;
}
środa, 11 lipca 2018
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
Próbowałam inicjalizować zmienne String jeszcze przed zaczęciem funkcji, tam gdzie powinna być tylko deklaracja.
Rozwiązanie - na górze tylko geklaracje, inicjalizacja w kodzie funkcji.
wtorek, 10 lipca 2018
Jak dodać menu do aplikacji?
- Nadaj nazwy swoim opcjom - np. wprowadź je jako wartości w strings.xml
- Utwórz katalog zasobów dla menu:
- Kliknij prawy przyciskiem na katalog res
- Wybierz New Resource Directory
- Zmień Recource Type z value na menu
- Dodaj w katalogu plik menu.xml:
- Kliknij prawy przyciskiem na katalog menu
- Wybierz New -> Menu Resource File
- Wprowadź ustawienia opcji menu, np.:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/menu_day1"
android:orderInCategory="1"
app:showAsAction="never"
android:title="@string/menu_d1" />
</menu> - Zaimplementuj w aktywności, która udostępnia menu funkcje onCreateOptionsMenu i onOptionsItemSelected, np.:
@Overridepublic boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu, menu); return true;} @Overridepublic boolean onOptionsItemSelected(MenuItem item) { return selectItem(item);} public boolean selectItem(MenuItem item) { switch (item.getItemId()) { case R.id.menu_day1: return true; case R.id.menu_day2: return true; case R.id.menu_day3: return true; case R.id.menu_day4: return true; case R.id.menu_day5: return true; default: return super.onOptionsItemSelected(item); } }
piątek, 22 czerwca 2018
Jak działa AppWidgetProvider czyli jakie widget otrzymuje sygnały i co się wtedy dzieje
- onUpdate()
- wywoływana automatycznie zgodnie z parametrem updatePeriodMillis
- wywoływana automatycznie przy dodawaniu widgetu, żeby np. domyślnie skofigurować widget, jeśli trzeba uruchomić serwis
- wywoływana z aktywności przy dodawaniu widgetu tylko wtedy, jeśli zostało tak zaprogramowane - programista musi zapewnić, że widget będzie w zupełności skonfigurowany, jeśli decyduje się sosać aktywność umożliwiającą konfigurację
- onAppWidgetOptionsChanged()
- wywoływana przy dodawaniu widgetu
- wywoływana przy zmianie rozmiaru widgetu
- umożliwia pokazanie lub schowanie elementów widgetu w zależności od jego rozmiaru
- wprowadzona w Android 4.1 (API 16)
- onDeleted(Context, int[])
- wywoływana gdy App Widget host zamyka widget - konkretną instancję, tylko jedną
- onEnabled(Context)
- wywoływana gdy dodawany jest pierwszy obiekt danego widgetu
- jeśli widget może mieć więcej niż jedną instancję, przy dodawaniu drugiej metoda nie jest wywoływana
- metoda odpowiednia do implementacji wszystkich funkcji, które mają się wykonać tylko raz, np. stworzenie bazy danych, szczególna konfiguracja
- onDisabled(Context)
- wywoływana gdy z App Widget host usuwana jest ostatnia instancja widgetu
- metoda odpowiednia do implementacji funkcji, które "posprzątają" po widgecie, np. usuną czasowe bazy danych
- onReceive(Context, Intent)
- wywoływana przy każdym odebraniu sygnału
- wywoływana przed każdą ze wspomnianych powyżej metod
- nie musi być implementowana - AppWidgetProvider ma domyślną implementację, która w odpowiedni sposób radzi sobie z odbieranymi sygnałami i wywołuje odpowiednie funkcje
Wygląd widgetu - zmiana rozmiaru
Nie musi też mieć stałych elementów - można je dopasować do wybranego rozmiaru.
Podczas zmiany rozmiaru widgetu wywoływana jest metoda onAppWidgetOptionsChanged(). To w jej treści możemy określić które elementy będą widoczne a które nie. Żeby zorientować się jaki jest aktualny (ustawiony przez użytkownika) rozmiar trzeba wywołać metodę getAppWidgetOptions(), która zwróci w odpowiedzi obiekt typu Bundle, który zawiera:
- OPTION_APPWIDGET_MIN_WIDTH - dolna granica szerokości w jednostkach dp
- OPTION_APPWIDGET_MAX_WIDTH - górna granica szerokości w jednostkach dp
- OPTION_APPWIDGET_MIN_HEIGHT - dolna granica długości w jednostkach dp
- OPTION_APPWIDGET_MAX_HEIGHT - górna granica długości w jednostkach dp
Wygląd widgetu - podstawowy projekt
Tworząc układ widgetu trzeba pamiętać o:
- sprawdzeniu, czy wybrany układ (layout) i elementy są wspierane przez RemoteViews
- marginesach całego widgetu - widget nie powinien stykać się z krawędziami ekranu i z innymi aplikacjami (od wersji Android 4.0 (targetSdkVersion >= 14) te marginesy są ustawiane domyślnie, nie trzeba ich dodatkowo wprowadzać)
- marginesach elementów widgetu - powinny być od siebie trochę odsunięte, żeby dało się ich używać
czwartek, 21 czerwca 2018
Konfiguracja widgetu czyli metadane
- minWidth, minHeight - minimalna, domyślna przestrzeń zajmowana przez widget
- widgety zajmują na ekranie pewne zdefiniowane komórki (tak samo ustawiając ikonki aplikacji na ekranie też nie możemy ich mieć gdziekolwiek), jeśli podana wartość nie będzie odpowiadała wielkości pełnych komórek, zostanie zaokrąglona w górę, tak aby zająć pełną liczbę zdefiniowanych komórek
- minimalna wartość nie może być większa niż 4x4 komórki (żeby zachować przenoszalność widgetu na różne urządzenia)
- minResizeWidth, minResizeHeight - wielkość, poniżej której widget nie będzie nadawał się do użycia
- ustawienie tych parametrów oznacza, że użytkownik będzie mógł zmienić wielkość widgetu na mniejszy niż domyślny (ale nie mniejszy niż podany w tym parametrze)
- updatePeriodMillis - częstotliwość z jaką widget będzie wysyłał żądanie aktualizacji (onUpdate(), AppWidgetProvider )
- podana wartość jest tylko szacunkowa, system wykona aktualizację mniej więcej w tym czasie, jeśli będzie mógł
- trzeba ostrożnie dobrać ten parametr, żeby nie zużywać za dużo baterii
- sysem wybudzi urządzenie z uśpienia gdy nadejdzie czas aktualizacji ustawiony w tym parametrze
- żeby uniknąć budzenia urządzenia, można ustawić aktualizację opartą na alarmie "niebudzącym" urządzenia
- korzystając z AlarmManager ustaw alarm przez Intent otrzymywany przez AppWidgetProvider
- ustaw alarmowi typ ELAPSED_REALTIME lub RTC
- ustaw updatePeriodMillis = 0
- initialLayout - ścieżka do pliku określającego wygląd (layout) widgetu
- configure - aktywność, która zostanie uruchomiona po dodaniu widgetu w celu umożliwienia użytkownikowi skonfigurowania go
- previewImage - podgląd przykładowo skonfigurowanego widgetu dla użytkownika, dostępny dla niego gdy wybiera widgety
- jeśli nie ma ustwionego tego parametru, jako podgląd wyświetlana będzie ikona aplikacji
- autoAdvanceViewId - id widoku "rozszerzonego"
- dla widgetów, które mogą być rozbudowywane (advanceable)
- informuje host co powinno być rozbudowywane
- host będzie wykonywał akcję advance() gdy będzie to sensowne, czyli np. tylko gdy widget będzie widoczny
- resizeMode - określa zasady zmiany rozmiaru widgetu - horizontal, vertical, none
- widgetCategory - określa gdzie może być wyświetlany widget - home_screen, keyguard (tylko do Android 5.0), both
środa, 20 czerwca 2018
AndroidManifest - dodajemy widget
(deklarujemy obiekt, który będzie mógł odbierać informacje od aplikacji i/lub systemu) <receiver android:name="nazwa AppWidgetProvider" > (tu powyżej -"name"- mówimy aplikacji jak nazywa się klasa, która ma informacje jak będzie zachowywał się ten obiekt - widget) <intent-filter> (tu deklarujemy, na co widget będzie reagował, co będzie odbierał) <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> (jeśli widget ma być aktualizowany należy to koniecznie zadeklarować) </intent-filter> (tu mówimy gdzie są dane konfiguracyjne widgetu) <meta-data android:name="android.appwidget.provider" android:resource="@xml/example_appwidget_info" /> </receiver>więcej o atrybucie <receiver>
wtorek, 19 czerwca 2018
Z czego składa się widget?
- AppWidgetProviderInfo - plik XML zawierający metadane naszego widgetu
- metadane - dane o danych – ustrukturalizowane informacje stosowane do opisu zasobów informacji lub obiektów informacji, dostarczające szczegółowych danych, dotyczących atrybutów zasobów lub obiektów informacji, w celu ułatwienia ich znalezienia, identyfikacji, a także zarządzania tymi zasobami
- AppWidgetProvider (implementacja) - zdefiniownie tego jak widget będzie się zachowywał pod wpływem różnych działań
- View layout - plik XML opisujący wygląd widgetu
- App Widget configuration Activity (opcjonalnie) - sktywność, która otworzy się w momencie dodawania widgetu i umożliwi ustawienie konfiguracji
- zmiana w AndroidManifest.xml - deklaracja atrybutu <receiver> o takiej nazwie jaką ma dodany wcześniej AppWidgetProviderInfo
poniedziałek, 18 czerwca 2018
Co to jest widget?
Widget:
- może być osadzony w innych aplikacjach (taka aplikacja to App Widget Host i jest nią np. home screen),
- cyklicznie się aktualizuje,
- jest wyświetlany dzięki App Widget Provider.
piątek, 18 maja 2018
Dlaczego moja aplikacja nie ma tej ładnej ikonki?
Wygrywam apkę na wirtualkę a tam androidek zamiast mojej ikonki.
Sprawdzam manifest - OK.
Sprawdzam grafikę - OK.
Co się dzieje?
Jest nie w tym katalogu - znowy bezmyślnie dodałam ją do katalogu anydpi. A z tego katalogu poprawnie obsługiwane są grafiki wektorowe a nie rastowe...
Po przełożeniu mojego .png do innego katalogu wszystko zadziałało.
czwartek, 17 maja 2018
Dlaczego "R" jest czerwone?
Po raz kolejny mam nadzieję, że to kompilator ma jakiś problem - przecież dopiero go włączyłam, pewnie coś się źle załadowało. W dodatku znowu chce zrobić jakiś update. Pewnie to to.
NIE!
Zrobiłam "niewielką" modyfikację w zasobach, dodałam tylko jedną linijkę w stylach, kompilator jej nie podkreślił, uznałam, że wszystko jest OK. A jednak nie było.
Od dziś:
Jeśli zasoby nie budują się poprawnie, upewnij się, że wprowadzone zmiany są poprawne.

