Fragmenty (statyczne)
---------------------
Źródła:
Damian Chodorek: http://damianchodorek.com/kurs-android-fragment-selektor-6/ (główne źródło!!!)
Damian Chodorek: http://damianchodorek.com/kurs-android-fragment-dynamiczne-zarzadzanie-headless-7/
Huyen Tue Dao: https://www.raywenderlich.com/149112/android-fragments-tutorial-introduction
0. Tworzymy projekt z Empty Activity (Min SDK 4.0.3, API 15)
1. Do katalogu res/layout dodajemy pliki fragment1.xml i fragment2.xml opisujące zawartość fragmentów z LinearLayout jako "korzeniem":
Plik fragment1.xml
Plik fragment2.xml
2. Do każdego pliku xml tworzymy klasy dziedziczące z klasy android.app.Fragment
(analogicznie jak MainActivity.java dla activity_main.xml):
Do katalogu java/.../fragmenty dodajemy pliki Fragment1.java i Fragment2.java
Plik Fragment1.java
package pl.umk.fizyka.fragmenty;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class Fragment1 extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)
{
//return super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.fragment1, container, false);
}
public void zmieńZawartość(String tekst)
{
TextView textView = (TextView)getView().findViewById(R.id.zawartosc);
textView.setText(tekst);
}
}
Plik Fragment2.java
package pl.umk.fizyka.fragmenty;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
public class Fragment2 extends Fragment
{
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)
{
//return super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment2, container, false);
//nasłuchiwacze dla przycisków
View.OnClickListener nasłuchiwacz = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.button1:
//zmiana zawartości we Fragment1
break;
case R.id.button2:
//zmiana zawartości we Fragment1
break;
}
}
};
view.findViewById(R.id.button1).setOnClickListener(nasłuchiwacz);
view.findViewById(R.id.button2).setOnClickListener(nasłuchiwacz);
return view;
}
}
Uwaga!
Zostawiliśmy miejsce na kod zmieniający Fragment1 z klasy Fragment2.
Nie powinno być bezpośrednio przekazania instancji, bo warto zachować luźne
relacje -> dynamika UI będzie tego wymagać. Kontakt przez aktywność-pojemnik.
3. Przechodzimy do aktywności (plik activity_main.xml):
4. W klasie aktywności (plik MainActivity.java) na razie nic nie zmieniamy.
KOMUNIKACJA
-----------
5. W klasie Fragment2 definiujemy interfejs nasłuchiwacza i deklarujemy referencję na jego instancję:
//interfejs
public interface InterfejsDoKomunikacji
{
void klikniętyPrzycisk(String tekst);
}
//referencja
private InterfejsDoKomunikacji aktywność;
6. Do klasy Fragment2 dodajemy metodę uruchamianą przy przyłączeniu fragmentu do aktywności
@Override
public void onAttach(Context context) {
super.onAttach(context);
Activity activity = getActivity();
if (activity instanceof InterfejsDoKomunikacji)
{
aktywność = (InterfejsDoKomunikacji) activity;
}
else
{
throw new ClassCastException("Klasa aktywności musi implementować interfejs 'InterfejsDoKomunikacji'");
}
}
7. Implementujemy interfeks w klasie aktywności
package pl.umk.fizyka.fragmenty;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity implements Fragment2.InterfejsDoKomunikacji
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void klikniętyPrzycisk(String tekst)
{
//pobranie referencji do aktywności
Fragment1 fragment = (Fragment1)getFragmentManager().findFragmentById(R.id.fragment1);
//sprawdzamy czy fragment istnieje w tej aktywności i jeżeli tak, zmieniamy w nim tekst
if (fragment != null && fragment.isInLayout()) fragment.zmieńZawartość(tekst);
}
}
8. Wracamy do Fragment2 i wykorzystujemy możliwość połączenia via aktywność:
View.OnClickListener nasłuchiwacz = new View.OnClickListener()
{
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.button1:
//zmiana zawartości we Fragment1
aktywność.klikniętyPrzycisk("button1");
break;
case R.id.button2:
//zmiana zawartości we Fragment1
aktywność.klikniętyPrzycisk("button2");
break;
}
}
};
9. Obracamy emulator lub urządzenie poziomo i testujemy aplikację - klikanie przycisków we Fragmencie 2 zmienia tekst we Fragmencie 1
ALTERNATYWNY LAYOUT DLA PORTRAIT
--------------------------------
10. W katalogu res tworzymy nowy katalog layout-port (nie będzie widoczny w widoku Android, ale jest w Project)
11. W tym katalogu tworzymy alternatywny plik activity_main.xml:
Uwaga!
Po utworzeniu widoczny jest w widoku Android
Uwaga!
W tej chwili początkowe ułożenie zależy od ustawienia urządzenia w momencie uruchomienia aplikacji.
Ale zmienia się po zmianie orientacji urządzenia.
Inne możliwości katalogów: layout-large, layout-xlarge, layout-port, layout-land
ALTERNATYWNY LAYOUT Z JEDNYM FRAGMENTEM I DRUGA AKTYWNOŚĆ
---------------------------------------------------------
12. W widoku layout-port/activity_main.xml usuwamy pierwszy fragment:
13. Pierwszy fragment umieścimy w osobnej aktywności.
Do projektu dodajemy drugą aktywność (menu kontekstowe dla app, New->Activity...->Empty Activity, używamy nazw "Druga")
W manifeście automatycznie dodana zostanie deklaracja.
Kod XML kopiujemy z pierwszej aktywności, ale zmieniamy fragment
14. Klasa nowej aktywności:
package pl.umk.fizyka.fragmenty;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class Druga extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
// jeżeli użytkownik będzie w orientacji landscape, należy zamknąć aktywność
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
finish();
return;
}
*/
setContentView(R.layout.activity_druga);
Bundle extras = getIntent().getExtras();
if (extras != null) {
String tekst = extras.getString("tekst");
Fragment1 fragment = (Fragment1)getFragmentManager().findFragmentById(R.id.fragment1);
fragment.zmieńZawartość(tekst);
}
}
}
15. Testujemy
KONTENER FRAGMENTÓW
-------------------
Zamiast dwóch aktywności dla orientacji portret chcemy tylko jednej,
ale zawierającej kontener fragmentów, w którym będziemy przełączać fragmenty
16. Zmieniamy res/layout-port/activity_main.xml
17. Zmieniamy kod klasy MainActivity:
package pl.umk.fizyka.fragmenty;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.res.Configuration;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements Fragment2.InterfejsDoKomunikacji
{
Boolean isLandscape = false;
private final FragmentManager fragmentManager = getFragmentManager();
private Fragment fragment = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
isLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
if(!isLandscape)
{
FragmentTransaction ft = fragmentManager.beginTransaction();
fragment = new Fragment2();
ft.replace(R.id.pojemnikNaFragmenty, fragment);
ft.commit();
}
}
/*
@Override
public void klikniętyPrzycisk(String tekst)
{
//pobranie referencji do aktywności
Fragment1 fragment = (Fragment1)getFragmentManager().findFragmentById(R.id.fragment1);
//sprawdzamy czy fragment istnieje w tej aktywności i jeżeli tak, zmieniamy w nim tekst
if (fragment != null && fragment.isInLayout()) fragment.zmieńZawartość(tekst);
}
*/
public void klikniętyPrzycisk(String tekst)
{
//pobranie referencji do aktywności
Fragment1 fragment = (Fragment1)getFragmentManager().findFragmentById(R.id.fragment1);
//sprawdzamy czy fragment istnieje w tej aktywności i jeżeli tak, zmieniamy w nim tekst
if (fragment != null && fragment.isInLayout()) fragment.zmieńZawartość(tekst);
else
{
try
{
/*
Intent intent = new Intent(getApplicationContext(), Druga.class);
intent.putExtra("tekst", tekst);
startActivity(intent);
*/
FragmentTransaction ft = fragmentManager.beginTransaction();
fragment = new Fragment1();
ft.replace(R.id.pojemnikNaFragmenty, fragment);
ft.addToBackStack(null); //fragment na stos, powrót przyciskiem Back
ft.commit();
fragmentManager.executePendingTransactions(); //dopiero po tym we fragmencie widoczna będzie aktywność
fragment.zmieńZawartość(tekst);
}
catch(Exception exc)
{
Toast.makeText(getApplicationContext(), exc.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
}
}
}
18. Usuwamy pliki Druga.java i activity_druga.xml
Fragmenty nie wymagały zmiany kodu - zaleta separacji i luźnych relacji