Kurs Android (5)

Autor: Damian Chodorek • Opublikowany: 12 lutego 2015 • Ostatnia aktualizacja: 5 marca 2015 • Kategoria: android, kursy

Wstęp do intencji (intents). Komunikacja między aktywnościami.

W poprzedniej części poznałeś aktywności, dowiedziałeś się jak ważne są oraz jak obsługiwać ich metody cyklu życia.

W tej części płynnie przejdziemy do nowego tematu – intencji (intents). Dlaczego płynnie? Ponieważ służą m.in. do tworzenia aktywności oraz przekazywania danych pomiędzy nimi. Na tym też skupi się dzisiejsze ćwiczenie. Postaramy się z aktywności głównej wywołać inną aktywność i przekazać dane pomiędzy nimi.

Intencje (intents)

Intencja to obiekt służący do komunikacji. Przy jego pomocy możesz asynchronicznie zażądać podjęcia jakiegoś działania od innego komponentu aplikacji i przekazać mu potrzebne dane. Intencje pozwalają na interakcję różnych komponentów tej w samej lub w różnych aplikacjach.

Jak już wspomniałem dzięki nim możesz ze sobą skomunikować dwie aktywności (aktywność to jeden z możliwych komponentów w Androidzie). Możesz także wywołać aplikację aparatu, którym użytkownik zrobi zdjęcie, a informacja o nim zostanie następnie przesłana do Twojego programu.

Ogólnie intencje wykorzystujemy w 3 sytuacjach:

  • wywołanie aktywności,
  • wywołanie serwisu (serwis to komponent, który poznasz w kolejnych lekcjach),
  • dostarczenie broadcastu (system może przesłać Twojej aplikacji informację o różnych zdarzeniach, np. że podłączono ładowarkę).

Zajmiemy się jedynie pierwszym przypadkiem użycia, ponieważ nie znamy jeszcze serwisów i broadcastów. Jeżeli chcesz doczytać o tych komponentach, odwiedź stronę: http://developer.android.com/guide/components/intents-filters.html.

Zanim zaczniemy omawiać konkretne metody, warto wiedzieć, że intencje dzielimy na dwa rodzaje:

  • jawne (explicit) – komponent do którego kierujemy komunikat jest nazwany (korzystamy z nazwy jego klasy),
  • niejawne (implicit) – deklarujemy, że chcemy wywołać konkretną akcję (np. otworzyć stronę WWW), ale nie wiemy jaki konkretny program ma to zrobić.
    • Przeważnie jawnych intencji używamy w odniesieniu do komponentów naszej aplikacji, ponieważ znamy nazwy swoich klas.

      Przypadek użycia intencji niejawnej może wyglądać tak: przy pomocy intencji mówimy systemowi, że chcemy otworzyć stronę damianchodorek.com przy pomocy zewnętrznej aplikacji. Jeśli w systemie jest jeden program, który potrafi to zrobić, to zostaje uruchomiony z naszym adresem strony (który przekazaliśmy w intencji). Jeżeli natomiast programów jest więcej, to system zapyta użytkownika, którego powinien użyć.

      1. Wywołujemy aktywność

      Założenia są takie:

      • tworzymy projekt z główną aktywnością,
      • definiujemy drugą aktywność,
      • definiujemy przycisk w pierwszej aktywności, po jego wciśnięciu zostanie uruchomiona druga aktywność,
      • w drugiej aktywności także będzie przycisk, który posłuży do jej zamknięcia.

      Kiedy opanujemy ten prosty przykład, zmodyfikujemy nasz kod tak, aby intencja przekazywała dane pomiędzy aktywnościami.

      1.1. Tworzymy aktywności

      Stwórz w Eclipse nowy projekt Androida o nazwie com.damianchodorek.kurs.android5 i nazwij główną aktywność (w kreatorze) MainActivity.

      W tym samym pakiecie co główna aktywność utwórz klasę SecondActivity, a następnie skopiuj do niej kod z klasy MainActivity. Po skopiowaniu zmień nazwę klasy na właściwą. Chodzi o to, aby mieć dwie klasy z takim samym ciałem

      Aktywność jest komponentem aplikacji. Każdy komponent należy jawnie zdefiniować w pliku AndroidManifest.xml. Poniżej przedstawiam kod swojego pliku po dokonaniu zmian.

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.com.damianchodorek.kurs.android5"
          android:versionCode="1"
          android:versionName="1.0" >
      
          <uses-sdk
              android:minSdkVersion="17"
              android:targetSdkVersion="21" />
      
          <application
              android:allowBackup="true"
              android:icon="@drawable/ic_launcher"
              android:label="@string/app_name"
              android:theme="@style/AppTheme" >
              <activity
                  android:name=".MainActivity"
                  android:label="@string/app_name" >
                  <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
      
                      <category android:name="android.intent.category.LAUNCHER" />
                  </intent-filter>
              </activity>
              
              <!-- ten fragment należy dodać -->
              <activity
                  android:name=".SecondActivity"
                  android:label="@string/app_name" >
              </activity>
              
          </application>
      
      </manifest>
      

      1.2. Definiujemy przyciski oraz UI aktywności

      Kolejnym krokiem jest zdefiniowanie odpowiedniego wyglądu naszych aktywności. Zajmijmy się najpierw plikiem res/layout/activity_main.xml. Usuwamy element TextView z napisem Hello World, a następnie definiujemy przycisk.

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical" >
      
          <Button
              android:id="@+id/main_button"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_margin="20dp"
              android:onClick="openSecondActivity"
              android:text="Otwórz SecondActivty" />
      
          <TextView
              android:id="@+id/message"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_margin="20dp" />
      
      </LinearLayout>

      W przycisku zdefiniowałem:

      • id – identyfikator, który pozwoli odnieść się do elementu w kodzie Javy. + oznacza, żeby id zostało automatycznie dodane w przypadku gdyby go nie było.
      • layout_width, layout_height – wysokość i szerokość. Zastosowane prze zemnie ustawienia są dosyć oczywiste.
      • text – treść wewnątrz przycisku. Wartość tekstową powinno się umieścić w pliku strings.xml, a następnie zrobić odniesienie do niej. Ja wpisałem bezpośrednio w elemencie żeby nie komplikować spraw i skrócić lekcję.
      • onClick – nazwa metody, która będzie wywołana po wciśnięciu przycisku. Jak zapewne się domyślasz będziemy musieli ją dodać w klasie.

      Ponadto utworzyłem pole tekstowe, które jest puste. Posłuży nam ono w przyszłości do wyświetlania przekazanych danych. Na razie jest nieważne.

      Teraz musimy utworzyć plik z layoutem dla drugiej aktywności. Stwórz plik res/layout/activity_second.xml i uzupełnij jego treść zgodnie z kodem poniżej.

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical" >
      
          <Button
              android:id="@+id/main_button"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_margin="20dp"
              android:onClick="closeActivity"
              android:text="Zamknij" />
      
          <TextView
              android:id="@+id/message"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_margin="20dp" />
      
      </LinearLayout>

      Jak widzisz obydwie aktywności mają podobny layout. Najważniejszą zmianą jest nazwa metody, która będzie wywołana po wciśnięciu przycisku.

      W klasie SecondActivity musimy jeszcze dokonać małej zmiany. Przyjrzyj się metodzie onCreate(). Ponieważ skopiowałeś ciało z klasy MainActivity to we wspomnianej metodzie znajduje się linijka setContentView(R.layout.activity_main);

      Metoda setContentView jako parametr przyjmuje id elementu (z pliku XML) i na jego podstawie tworzy UI danej aktywności.

      Klasa R jest domyślnie generowana przez środowisko. Przechowuje ona wszystkie identyfikatory zdefiniowane w plikach XML oraz odniesienia do plików samych w sobie. Konstrukcja R.layout.activity_main oznacza: plik activity_main.xml z folderu layout.

      Jak więc widzisz nasza klasa SecondActivity korzysta z layoutu, który został przeznaczony dla klasy MainActivity. Jeśli chcemy, aby zaczęła korzystać z pliku, który właśnie zdefiniowaliśmy (activity_second.xml) powinniśmy zmienić to odniesienie na R.layout.activity_second. Ciało metody powinno wyglądać jak poniżej.

      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_second);
      }

      Aby wszystko działało musimy jeszcze zdefiniować metody, które mają zostać wywołane po wciśnięciu przycisków. Dodaj w klasie MainActivity poniższą metodę.

      public void openSecondActivity(View view) {
      }

      W klasie SecondActivity również dodaj metodę.

      public void closeActivity(View view) {	
      }

      Gotowe. Aplikacja jeszcze nic nie robi, ale powinna się poprawie uruchomić. Przetestuj czy wszystko działa.

      1.3. Uruchamiamy aktywność

      Mamy już wszystko przygotowane. Najwyższy czas wprawić maszynerię w ruch. Co musimy zrobić? Trzeba uzupełnić ciała metod, które dodaliśmy przed chwilą.

      public void openSecondActivity(View view) {
          // tworzymy obiekt intencji, drugim parametrem jest klasa (aktywność),
          // którą chcemy wywołać
          Intent intent = new Intent(this, SecondActivity.class);
      
          // ta linijka uruchomi aktywność
          startActivity(intent);
      }

      Najważniejsze rzeczy opisałem w komentarzach. Dodam jeszcze tylko, że pierwszym parametrem konstruktora Intent() jest kontekst uruchomienia. W naszym przypadku będzie to główna aktywność. Dlaczego? Ponieważ to ona tworzy intencję i wywołuje inną aktywność. Czas na ciało drugiej metody.

      public void closeActivity(View view) {
          // poniższa metoda kończy działanie aktywności
          finish();
      }

      Uruchom program i przetestuj działanie przycisków. Pierwszy powinien otworzyć nową aktywność, a drugi powinien ją zamknąć.

      1.4. Przekazujemy wiadomości

      Zanim uruchomimy aktywność wpakujemy do intencji jakieś dane i skorzystamy z metody startActivityForResult(). W aktywności docelowej odbierzemy te dane i wyświetlimy je na ekranie, po czym utworzymy nową intencję, wsadzimy do niej trochę danych i przekażemy je do aktywności początkowej.

      W aktywności początkowej odbierzemy dane i również wyświetlimy na ekranie.

      Zmodyfikuj metodę openSecondActivity() oraz dodaj pole REQUEST_CODE jak poniżej.

      private final int REQUEST_CODE = 1;
      
      public void openSecondActivity(View view) {
          Intent intent = new Intent(this, SecondActivity.class);
          intent.putExtra("val1", "wiadomość 1");
          intent.putExtra("val2", "inna wiadomość");
          startActivityForResult(intent, REQUEST_CODE);
      }

      Metoda startActivityForResult() różni się od metody startActivity() tym, że spodziewamy się, iż aktywność docelowa zwróci nam jakiś wynik i po jej zakończeniu zostanie wywołana metoda onActivityResult(), która pozwoli odebrać dane.

      REQUEST_CODE to kod żądania. Jest potrzebny, aby rozróżnić od którego żądania dostaniemy odpowiedź. Przecież może być ich wiele, ale za każdym razem zostanie wywołana tylko jedna metoda – onActivityResult().

      Skoro wysłaliśmy wiadomość, wypada ją odebrać. Dokonamy tego w metodzie onCreate() klasy SecondActivity. Zmodyfikuj kod jak poniżej.

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_second);
      		
          // uzyskujemy intencję, która wywołała tę aktywność
          Intent intent = getIntent();
      		
          // uzyskujemy dane wg. klucza
          String val1 = intent.getStringExtra("val1");
          String val2 = intent.getStringExtra("val2");
      		
          // uzyskujemy odniesienie do pola tekstowego w layoucie
          // jest to element z activity_second.xml
          TextView textView=(TextView) findViewById(R.id.message);
          // zmieniamy treść pola tekstowego
          textView.setText(val1+"\n"+val2);
      }

      Powyższy kod powinien być jasny. Odbieramy dane przy pomocy getStringExtra() podając nazwy kluczy, które zdefiniowaliśmy. Oczywiście możemy odebrać więcej typów niż String. Przykładowe metody to getBooleanExtra() czy getIntExtra().

      Metoda findViewById() na podstawie przekazanego id pozwala nam zdobyć referencję do obiektu, który zdefiniowaliśmy w pliku XML. To bardzo ważna metoda. Będziemy korzystać z niej dosyć często.

      Zwrócony obiekt należy poddać rzutowaniu – typ obiektu ma taką samą nazwę jak element w pliku XML (TextView). Następnie ustawiamy tekst w tym elemencie.

      Aby odesłać dane podczas zamykania aktywności SecondActivity, należy przesłonić metodę finish()

      @Override
      public void finish() {
          Intent intent = new Intent();
          intent.putExtra("msg", "Wiadomość powrotna");
          setResult(RESULT_OK, intent);
          super.finish();
      }

      W momencie gdy aktywność zakończy swoje działanie, zostanie uruchomiona metoda onActivityResult() w aktywności, która spodziewa się wyniku. Wstaw więc poniższą metodę do klasy MainActivity.

      @Override
      protected void onActivityResult(int requestCode, int resultCode,
          Intent intent) {
      		
          // sprawdzamy czy przyszło odpowiednie żądanie
          if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
               // sprawdzamy czy intencja przyniosła odpowiednią wiadomość
              if (intent.hasExtra("msg")) {
                  // tutaj uzyskujemy obiekt o tym samym id
                  // ale dotyczy pliku activity_main.xml
                  TextView textView = (TextView) findViewById(R.id.message);
                  textView.setText( intent.getStringExtra("msg") );
              }
          }
      }

      Gotowe. Teraz wiesz jak uruchamiać nowe aktywności oraz jak wymieniać dane pomiędzy nimi.

      Następnym krokiem jest użycie intencji niejawnej.

      2. Intencja niejawna (implicit intent)

      Chcemy dodać przycisk do głównej aktywności, którego wciśnięcie będzie powodować uruchomienie innej aplikacji. Spróbujemy uruchomić przeglądarkę.

      Do pliku activity_main.xml dodaj poniższy kod.

      <Button
          android:id="@+id/link_button"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_margin="20dp"
          android:onClick="openBrowser"
          android:text="Otwórz przegląradkę" />

      W klasie MainActivity dodaj poniższą metodę, która będzie wywołana po wciśnięciu przycisku.

      public void openBrowser(View view) {
          String url = "http://www.damianchodorek.com";
          Intent i = new Intent(Intent.ACTION_VIEW);
          i.setData(Uri.parse(url));
          startActivity(i);
      }

      Zwróć uwagę, że nie podaliśmy nigdzie klasy, która ma zostać wywołana. ACTION_VIEW to stała, która mówi:

      chcę wywołać dowolną aktywność, która potrafi pokazać użytkownikowi dane, które zamieściłem w intencji.

      Tej stałej używamy jeżeli chcemy coś pokazać użytkownikowi: stronę WWW, jakieś koordynaty na mapie, zdjęcie z galerii. Są także stałe, z których korzystamy w innych sytuacjach, np. gdy chcemy zrobić zdjęcie, uruchomić kalendarz itd. Więcej na ten temat przeczytasz na stronie: https://developer.android.com/guide/components/intents-common.html.

      Wróćmy na chwilę do naszej aplikacji. Prawdopodobnie po wciśnięciu przycisku od razu uruchamia się przeglądarka z odpowiednią stroną. Gdyby w systemie było więcej przeglądarek, to użytkownik dostałby pytanie, której ma użyć.

      Na dziś to wszystko. Nauczyłeś się całkiem sporo. W razie gdybyś chciał zgłębić temat intencji, zajrzyj do linków, które umieściłem w artykule. Uruchamianie aktywność to jedna z kilku możliwości. Przy pomocy intencji możemy robić inne rzeczy, ale o tym w kolejnych artykułach.

      część 6