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
poniedziałek, 4 maja 2020
Dlaczego nie piszemy wymagań i testów w Wordzie (albo jakimś typowym edytorze tekstu)?
Warto mieć powiązania między wymaganiami, testami, planami i wykonaniami testów, ponieważ:
- łatwiej jest wszystko edytować w przypadku zmian
- łatwiej zauważyć błędy i braki w dokumentacji
- łatwiej przygotować podsumowanie, zobaczyć jak długo znajmuje testowanie, które testy są dla aplikacji problemowe.
wtorek, 24 marca 2020
Czego nauczył mnie kompilator cz.1
- Gdy łączę tekst w pętli (np. z listy z bazy danych) to zamiast String (+) stosować StringBuilder (.append)
- Mogę zwrócić w funkcji wartość pętli if - nie muszę tam zapisywać true i false.
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 "=="
środa, 1 stycznia 2020
Jak zrobić aplikację bez górnego paska tytułowego?
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
Plik styles.xml może wtedy wyglądać tak:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:fontFamily">@font/italianno</item>
<item name="android:textColor">@color/black</item>
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
</resources>
wtorek, 5 listopada 2019
Szablon Planu Jakości
Przemyśleć, sprawdzić, ustalić - to najważniejszy cel wypełniania Planu Jakości.
Manifest Agile (który bardzo cenię) stawia dokumentację i przestrzeganie planu na drugim miejscu - zgadzam się z tym całkowicie - nie wypełniajmy planu dla smego wypełniania i generowania niepotrzebnej dokumentacji. Wypełniamy tyle, ile trzeba, a myślimy o wszystkim bo od nas zależy Jakość w projekcie.
- Identyfikator
- Wstęp
- autoryzacja projektu
- plan projektu
- plan QA
- plan zarządzania konfiguracją
- polityka testów
- standardy testów
- Przedmiot testów
- specyfikacja wymagań
- specyfikacja projektowania
- instrukcja obsługi/instalacji/funkcji
- Testowane funkcje
- Nietestowane funkcje
- Podejście do testów
- Kryteria przejścia/niezaliczenia testów
- Kryteria zawieszenia/wznowienia testów
- Rezultaty testów
- plan testów
- specyfikacja projektów testowych
- specyfikacja przypadków testowych
- przekazanie przedmiotu testów
- logi z testów
- raport incydentu testowego
- podsumowanie raportu testów
- Testowane zadania
- Niezbędne środowisko testowe
- Odpowiedzialność
- Zapotrzebowanie na personel i szkolenia
- Harmonogram
- Ryzyka i nieprzewidziane wydatki
- Pozwolenia/zatwierdzenia
wtorek, 9 kwietnia 2019
Screen size - czyli jaki rozmiar powinna mieć grafika tła
Pracowałam nad nią długo, nadszedł dzień premiery i... nie dało się jej otworzyć na niektórych urządzeniach.
Okazało się, że dodane przeze mnie grafiki były za duże - urządzenia nie radziły sobie z ich dopasowaniem.
Podstawa, tak, jednak jakoś przeoczyłam.
Poczytałam trochę, między innymi:
https://vinsol.com/blog/2014/11/20/tips-for-designers-from-a-developer/
https://blog.prototypr.io/designing-for-multiple-screen-densities-on-android-5fba8afe7ead
https://medium.com/@sashaserg/a-mysterious-density-independent-pixel-a-quick-introduction-to-android-design-111d68be7cf5
I od tamtej pory wielkość grafik tła w moich projektach to około:
- mdpi - 360x480 px
- hdpi - 480x800 px
- xhdpi - 720x1280 px
- xxhdpi - 1080x1920 px
- xxxhdpi - 1440x2560 px
czwartek, 28 lutego 2019
Dobre praktyki - Lekcja 1
- Testuj małe elementy - im mniej, tym lepiej, nawet jeśli to tylko zmiana koloru - upewnij się, że się zmienił tam gdzie powinien.
- Testuj małe elementy - nawet jeśli nie powodują widocznej zmiany wyglądu lub zachowania - sprawdzaj, czy nic nie popsuły.
- Czytaj materiały branżowe regularnie - warto się uczyć od innych.
- Porządnie planuj - najpierw zgrubnie, później coraz dokładniej, im mniejsze zadania tym łatwiej je wykonać nawet przy zmęczeniu.
- Usuwaj logi, których nie potrzebujesz.
- Upewnij się, że element, którego używasz !=null
czwartek, 9 sierpnia 2018
Jak ustawić własne czcionki w aplikacji
- Pobierz pliki czcionek, których potrzebujesz
- Przekonwertuj czcionki do formatu .ttf (jeśli są w innym)
- Utwórz katalog czcionek w swoim projekcie (jeśli go nie ma)
- Wybierz menu konekstowe z katalogu res
- New -> Android Resource Directory
- Recource type: font
- Dodaj do katalogu font swoje czcionki
- Wprowadź swoje czcionki do projektu, np.
- Dodaj je do swojego stylu: <item name="android:fontFamily">@font/pt_sans</item> (API >=16)
- Dodaj je do swojego obiektu tekstowego w layout: android:fontFamily="@font/pt_sans"
- Dodaj je w kodzie swojej aplikacji do obiektu tekstowego
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.
niedziela, 5 sierpnia 2018
Room - kilka słów o przechowywaniu danych
- Dodaj odpowiednie zależności do build.gradle(Module: app)
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
annotationProcessor "android.arch.persistence.room:compiler:$room_version"
testImplementation "android.arch.persistence.room:testing:$room_version" - Zsynchronizuj build.gradle
- kliknij prawym przyciskiem na build.gradle
- wybierz Synchronize build.gradle
- Dodaj klasę obiektu, który będzie reprezentował tabelę
- Daj jej annotację @Entity
- Dodaj kolumnę @PrimaryKey
- Dodaj pozostałe kolumny
- Dodaj gettery i settery elementów obiektu (na potrzeby Room)
@Entity
public class StoryObject {
@PrimaryKey
private int storyId;
@ColumnInfo(name = "main_text")
private String mainText; - Stwórz interfejs z metodami dostępu do bazy - DAO
- Daj jej annotację @Dao
- Dodaj metody dostępu (napisane w SQL)
@Dao
public interface StoryDao {
@Query("SELECT * FROM story")
List<StoryObject> getAll();
@Insert
void insertAll(StoryObject... objects);
@Delete
void delete(StoryObject object);
} - Stwórz klasę (abstract) bazy danych
- Dodaj jej annotację @Database i przypisz klasy, które ją reprezentują (entities)
- Dodaj metodę (abstract), która zwróci odpowiedni obiekt Dao
@Database(entities = {StoryObject.class}, version = 1)
public abstract class StoryDatabase extends RoomDatabase {
public abstract StoryDao getStoryDao();
} - W kodzie aplikacji wywołaj budowanie bazy danych
StoryDatabase db = Room.databaseBuilder(getApplicationContext(),
StoryDatabase.class, "story_database").build();
poniedziałek, 30 lipca 2018
Release – checklista
- Change log - powiązanie z zadaniami (historyjkami) - scope of work vs lista commitów
- Kiedy wypuszczono produkt
- Jaki jest tag w repozytorium
- Jakie były testy i gdzie są raporty
- Gdzie znajduje się backup
- Dokumentacja i konfiguracja - aktualne wersje
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); } }
