Android
fragment
Sök…
Introduktion
Introduktion om fragment och deras interkommunikationsmekanism
Syntax
void onActivityCreated (Bundle sparadInstanceState) // Kallas när fragmentets aktivitet har skapats och detta fragments visningshierarki instanserades.
void onActivityResult (int requestCode, int resultCode, Intent data) // Få resultatet från ett tidigare samtal till startActivityForResult (Intent, int).
void onAttach (Aktivitetsaktivitet) // Den här metoden avskrivs på API-nivå 23. Använd onAttach (Context) istället.
void onAttach (Context context) // Anropas när ett fragment först knyts till sitt sammanhang.
void onAttachFragment (Fragment childFragment) // Kallas när ett fragment är fäst som ett barn av detta fragment.
void onConfigurationChanged (Configuration newConfig) // Anropas av systemet när enhetskonfigurationen ändras medan din komponent körs.
void onCreate (Bundle sparadInstanceState) // Kallas för att skapa ett första fragment av ett fragment.
Visa påCreateView (LayoutInflater-uppblåsare, ViewGroup-behållare, Bundle sparadInstanceState) // Kallas för att fragmentet ska instansera användargränssnittsvyen.
void onDestroy () // Kallas när fragmentet inte längre används.
void onDestroyView () // Anropas när vyn som tidigare skapats av onCreateView (LayoutInflater, ViewGroup, Bundle) har tagits bort från fragmentet.
void onDetach () // Anropas när fragmentet inte längre är kopplat till sin aktivitet.
void onInflate (Aktivitetsaktivitet, AttributeSet attrs, Bundle SavedInstanceState) // Den här metoden avskrivs på API-nivå 23. Använd onInflate (Context, AttributeSet, Bundle) istället.
void onInflate (Context context, AttributeSet attrs, Bundle SavedInstanceState) // Anropas när ett fragment skapas som en del av en visningslayout-inflation, vanligtvis från att ställa in innehållsvyen för en aktivitet.
void onPause () // Ringas när fragmentet inte längre återupptas.
void onResume () // Anropas när fragmentet är synligt för användaren och aktivt körs.
void onSaveInstanceState (Bundle outState) // Kallas för att be fragmentet att spara det nuvarande dynamiska tillståndet, så att det senare kan rekonstrueras i en ny instans av sin process startas om.
void onStart () // Anropas när fragmentet är synligt för användaren.
void onStop () // Ringas när fragmentet inte längre startas.
void onViewStateRestored (Bundle sparadInstanceState) // Anropas när allt sparat tillstånd har återställts i fragmentets visningshierarki.
Anmärkningar
Ett fragment representerar ett beteende eller en del av användargränssnittet i en aktivitet. Du kan kombinera flera fragment i en enda aktivitet för att bygga ett gränssnitt för flera fönster och återanvända ett fragment i flera aktiviteter. Du kan tänka på ett fragment som ett modulärt avsnitt av en aktivitet, som har sin egen livscykel, får sina egna inmatningshändelser, och som du kan lägga till eller ta bort medan aktiviteten körs (liksom en "subaktivitet" som du kan återanvändning i olika aktiviteter).
Konstruktör
Varje fragment måste ha en tom konstruktör , så det kan instanseras när man återställer sin aktivitets tillstånd. Det rekommenderas starkt att underklasser inte har andra konstruktörer med parametrar, eftersom dessa konstruktörer inte kommer att anropas när fragmentet återinstanseras; istället kan argument levereras av den som ringer med setArguments (Bundle) och senare hämtas av Fragmentet med getArguments ().Mönstret newInstance ()
Även om det är möjligt att skapa en fragmentkonstruktör med parametrar, anropar Android internt nollargumentkonstruktören när man återskapar fragment (till exempel om de återställs efter att ha dödats av Android: s egna skäl). Av detta skäl är det inte tillrådligt att lita på en konstruktör som har parametrar.
För att säkerställa att dina förväntade fragmentargument alltid finns närvarande kan du använda en statisk newInstance()
metod för att skapa fragmentet och lägga till de parametrar du vill ha i ett bunt som kommer att finnas tillgängligt när du skapar en ny instans.
import android.os.Bundle;
import android.support.v4.app.Fragment;
public class MyFragment extends Fragment
{
// Our identifier for obtaining the name from arguments
private static final String NAME_ARG = "name";
private String mName;
// Required
public MyFragment(){}
// The static constructor. This is the only way that you should instantiate
// the fragment yourself
public static MyFragment newInstance(final String name) {
final MyFragment myFragment = new MyFragment();
// The 1 below is an optimization, being the number of arguments that will
// be added to this bundle. If you know the number of arguments you will add
// to the bundle it stops additional allocations of the backing map. If
// unsure, you can construct Bundle without any arguments
final Bundle args = new Bundle(1);
// This stores the argument as an argument in the bundle. Note that even if
// the 'name' parameter is NULL then this will work, so you should consider
// at this point if the parameter is mandatory and if so check for NULL and
// throw an appropriate error if so
args.putString(NAME_ARG, name);
myFragment.setArguments(args);
return myFragment;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle arguments = getArguments();
if (arguments == null || !arguments.containsKey(NAME_ARG)) {
// Set a default or error as you see fit
} else {
mName = arguments.getString(NAME_ARG);
}
}
}
Nu i aktiviteten:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();
Detta mönster är en bästa praxis för att säkerställa att alla nödvändiga argument kommer att överföras till fragment om skapandet. Observera att när systemet förstör fragmentet och skapar det senare kommer det automatiskt att återställa dess tillstånd - men du måste förse det med en onSaveInstanceState(Bundle)
-implementering.
Navigering mellan fragment med backstack och statiskt tygmönster
Först och främst måste vi lägga till vårt första Fragment
i början, vi bör göra det i onCreate()
i vår aktivitet:
if (null == savedInstanceState) {
getSupportFragmentManager().beginTransaction()
.addToBackStack("fragmentA")
.replace(R.id.container, FragmentA.newInstance(), "fragmentA")
.commit();
}
Därefter måste vi hantera vår backstack. Det enklaste sättet är att använda en funktion som läggs till i vår aktivitet som används för alla FragmentTransactions.
public void replaceFragment(Fragment fragment, String tag) {
//Get current fragment placed in container
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
//Prevent adding same fragment on top
if (currentFragment.getClass() == fragment.getClass()) {
return;
}
//If fragment is already on stack, we can pop back stack to prevent stack infinite growth
if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
getSupportFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
//Otherwise, just replace fragment
getSupportFragmentManager()
.beginTransaction()
.addToBackStack(tag)
.replace(R.id.container, fragment, tag)
.commit();
}
Slutligen bör vi åsidosätta onBackPressed()
att avsluta applikationen när vi går tillbaka från det sista fragmentet som finns tillgängligt i backstacken.
@Override
public void onBackPressed() {
int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
getSupportFragmentManager().popBackStack();
} else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to prevent leaving empty screen
finish();
} else {
super.onBackPressed();
}
}
Utförande i aktivitet:
replaceFragment(FragmentB.newInstance(), "fragmentB");
Utförande utanför aktivitet (förutsatt att MainActivity
är vår aktivitet):
((MainActivity) getActivity()).replaceFragment(FragmentB.newInstance(), "fragmentB");
Skicka data från aktivitet till fragment med hjälp av bunt
Alla fragment bör ha en tom konstruktör (dvs. en konstruktormetod som saknar inmatningsargument). Därför bör du använda setArguments()
för att överföra dina data till det fragment som skapas. Denna metod får ett paket som du lagrar dina data i och lagrar paketet i argumenten. Därefter kan detta paket hämtas i onCreate()
och onCreateView()
återuppringningar av Fragmentet.
Aktivitet:
Bundle bundle = new Bundle();
String myMessage = "Stack Overflow is cool!";
bundle.putString("message", myMessage );
FragmentClass fragInfo = new FragmentClass();
fragInfo.setArguments(bundle);
transaction.replace(R.id.fragment_single, fragInfo);
transaction.commit();
Fragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
String myValue = this.getArguments().getString("message");
...
}
Skicka händelser tillbaka till en aktivitet med återuppringningsgränssnitt
Om du behöver skicka händelser från fragment till aktivitet är en av de möjliga lösningarna att definiera återuppringningsgränssnittet och kräva att värdaktiviteten implementerar det.
Exempel
Skicka återuppringning till en aktivitet när fragmentets knapp klickade
Definiera först återuppringningsgränssnittet:public interface SampleCallback {
void onButtonClicked();
}
Nästa steg är att tilldela detta återuppringning i fragment:
public final class SampleFragment extends Fragment {
private SampleCallback callback;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SampleCallback) {
callback = (SampleCallback) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement SampleCallback");
}
}
@Override
public void onDetach() {
super.onDetach();
callback = null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.sample, container, false);
// Add button's click listener
view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
callback.onButtonClicked(); // Invoke callback here
}
});
return view;
}
}
Och slutligen implementera återuppringning i aktivitet:
public final class SampleActivity extends Activity implements SampleCallback {
// ... Skipped code with settings content view and presenting the fragment
@Override
public void onButtonClicked() {
// Invoked when fragment's button has been clicked
}
}
Animera övergången mellan fragment
För att animera övergången mellan fragment, eller för att animera processen att visa eller dölja ett fragment använder du FragmentManager
att skapa en FragmentTransaction
.
För en enda FragmentTransaction
finns det två olika sätt att utföra animationer: du kan använda en vanlig animation eller du kan leverera dina egna animerade animationer.
Standardanimationer specificeras genom att kalla FragmentTransaction.setTransition(int transit)
och använda en av de fördefinierade konstanterna som finns tillgängliga i klassen FragmentTransaction
. I skrivande stund är dessa konstanter:
FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE
Den kompletta transaktionen kan se ut så här:
getSupportFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
Anpassade animationer specificeras genom att anropa antingen FragmentTransaction.setCustomAnimations(int enter, int exit)
eller FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit)
.
enter
och exit
animationerna kommer att spelas upp för FragmentTransaction
s som inte involverar att du spricker fragment från bakstacken. popEnter
och popExit
animationerna kommer att spelas när du spricker ett fragment från bakbunten.
Följande kod visar hur du skulle ersätta ett fragment genom att skjuta ut ett fragment och skjuta det andra på sin plats.
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
.replace(R.id.contents, new MyFragment(), "MyFragmentTag")
.commit();
XML-animationsdefinitionerna använder taggen objectAnimator
. Ett exempel på slide_in_left.xml kan se ut så här:
<?xml version="1.0" encoding="utf-8"?>
<set>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="x"
android:valueType="floatType"
android:valueFrom="-1280"
android:valueTo="0"
android:duration="500"/>
</set>
Kommunikation mellan fragment
All kommunikation mellan fragment måste gå via en aktivitet. Fragment KAN INTE kommunicera med varandra utan någon aktivitet.
Ytterligare resurser
I det här exemplet har vi en MainActivity
som är värd för två fragment, SenderFragment
och ReceiverFragment
, för att skicka och ta emot ett message
(en enkel sträng i detta fall).
En knapp i SenderFragment
initierar processen att skicka meddelandet. En TextView i ReceiverFragment
uppdateras när meddelandet tas emot av det.
Följande är utdraget för MainActivity med kommentarer som förklarar de viktiga kodraderna:
// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements SenderFragment.SendMessageListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* This method is called when we click on the button in the SenderFragment
* @param message The message sent by the SenderFragment
*/
@Override
public void onSendMessage(String message) {
// Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
ReceiverFragment receiverFragment = (ReceiverFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);
// Make sure that such a fragment exists
if (receiverFragment != null) {
// Send this message to the ReceiverFragment by calling its public method
receiverFragment.showMessage(message);
}
}
}
Layoutfilen för MainActivity
värd för två fragment i en LinearLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.naru.fragmentcommunication.MainActivity">
<fragment
android:id="@+id/fragment_sender"
android:name="com.naru.fragmentcommunication.SenderFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_sender" />
<fragment
android:id="@+id/fragment_receiver"
android:name="com.naru.fragmentcommunication.ReceiverFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
tools:layout="@layout/fragment_receiver" />
</LinearLayout>
SenderFragment
visar ett gränssnitt SendMessageListener
som hjälper MainActivity
veta när knappen i SenderFragment
klickades.
Följande är kodavsnittet för SenderFragment
förklarar de viktiga kodraderna:
public class SenderFragment extends Fragment {
private SendMessageListener commander;
/**
* This interface is created to communicate between the activity and the fragment. Any activity
* which implements this interface will be able to receive the message that is sent by this
* fragment.
*/
public interface SendMessageListener {
void onSendMessage(String message);
}
/**
* API LEVEL >= 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
* @param context
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ "must implement the SendMessageListener interface");
}
}
/**
* API LEVEL < 23
* <p>
* This method is called when the fragment is attached to the activity. This method here will
* help us to initialize our reference variable, 'commander' , for our interface
* 'SendMessageListener'
*
* @param activity
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Try to cast the context to our interface SendMessageListener i.e. check whether the
// activity implements the SendMessageListener. If not a ClassCastException is thrown.
try {
commander = (SendMessageListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ "must implement the SendMessageListener interface");
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
// Initialize button and a click listener on it
Button send = (Button) view.findViewById(R.id.bSend);
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Sanity check whether we were able to properly initialize our interface reference
if (commander != null) {
// Call our interface method. This enables us to call the implemented method
// in the activity, from where we can send the message to the ReceiverFragment.
commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
}
}
});
return view;
}
}
Layoutfilen för SenderFragment
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/bSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SEND"
android:layout_gravity="center_horizontal" />
</LinearLayout>
ReceiverFragment
är enkelt och visar en enkel offentlig metod för att uppdatera sin TextView. När MainActivity
tar emot meddelandet från SenderFragment
kallar det denna offentliga metod för ReceiverFragment
Följande är kodavsnittet för ReceiverFragment
med kommentarer som förklarar de viktiga kodraderna:
public class ReceiverFragment extends Fragment {
TextView tvMessage;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// Inflate view for the sender fragment.
View view = inflater.inflate(R.layout.fragment_receiver, container, false);
// Initialize the TextView
tvMessage = (TextView) view.findViewById(R.id.tvReceivedMessage);
return view;
}
/**
* Method that is called by the MainActivity when it receives a message from the SenderFragment.
* This method helps update the text in the TextView to the message sent by the SenderFragment.
* @param message Message sent by the SenderFragment via the MainActivity.
*/
public void showMessage(String message) {
tvMessage.setText(message);
}
}
Layoutfilen för ReceiverFragment
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tvReceivedMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Waiting for message!" />
</LinearLayout>