diff --git a/build/fragments.html b/build/fragments.html index a82fed1..5c2d67f 100644 --- a/build/fragments.html +++ b/build/fragments.html @@ -333,7 +333,7 @@
In order to illustrate how to make a Fragment, we will refactor the MainActivity to use Fragments for displaying the list of movies. This will help to illustrate the relationship between Activities and Fragments.
To create a Fragment, you subclass the Fragment class. Let’s make one called MovieListFragment (in a MovieListFragment.java file). You can use Android Studio to do this work: via the File > New > Fragment > Fragment (blank) menu option. (DO NOT select any of the other options for in the wizard for now; they provide template code that can distract from the core principles).
There are two versions of the Fragment class: one in the framework’s android.app package and one in the android.support.v4 package. The later package refers to the Support Library. As discussed in the previous chapter, these are libraries of classes designed to make Android applications backwards compatible: for example, Fragment and its related classes came out in API 11 so aren’t in the android.app package for earlier devices. By including the support library, we can include those classes on older devices as well!
There are two versions of the Fragment class: one in the framework’s android.app package and one in the android.support.v4 package. The latter package refers to the Support Library. As discussed in the previous chapter, these are libraries of classes designed to make Android applications backwards compatible: for example, Fragment and its related classes came out in API 11 so aren’t in the android.app package for earlier devices. By including the support library, we can include those classes on older devices as well!
Support libraries also include additional convenience and helper classes that are not part of the core Android package. These include interface elements (e.g., ConstraintLayout, RecyclerView, or ViewPager) and accessibility classes. See the features list for details. Thus it is often useful to include and utilize support library versions of classes even when targeting later devies so that you don’t need to “roll your own” versions of these convenience classes.
The main disadvantage to using support libraries is that they need to be included in your application, so will make the final .apk file larger (and may potentially require workarounds for method count limitations). You will also run into problems if you try and mix and match versions of the classes (e.g., from different versions of the support library). But as always, you should avoid premature optimization. Thus in this course you should default to using the support library version of a class when given a choice!
//MovieListFragment example
MovieListFragment fragment = (MovieListFragment)getSupportFragmentManager().findFragmentById(R.id.fragment);FragmentManager. The Activity class (API level 15+) is able to work with both the platform and support FragmentManager classes. But because these classes don’t have a shared interface, the Activity needs to provide different Java methods which can return the correct type.FragmentManager. The Activity class (API level 15+) is able to work with both the platform and support FragmentManager classes. But because these classes don’t have a shared interface, the Activity needs to provide different Java methods which can return the correct type.Once you have a reference to the Fragment, this acts just like any other object—you can call any public methods it has! For example, if you give the Fragment a public method (e.g., searchMovies()), then this method can be called from the Activity:
//called from Activity on the referenced fragment
@@ -393,8 +393,8 @@ 6.2 Dynamic Fragments
In this section, we will continue to refine the Movie app so that when the user clicks on a Movie in the list, the app shows a screen (Fragment) with details about the selected movie.
Instantiating Fragments
-To do this, we will need to instantiate the desired Fragment dynamically (in Java code), rather than statically in the XML using the <fragment> element. This is because we need to be able to dynamically change which Fragment is currently being shown, which is not possibly for Fragments that are “hard-coded” in the XML.
-Unlike Activities, Fragments (such as MovieListFragment) do have constructor methods that can be called. In fact, Android requires that every Fragment include a default (no-argument) constructor that is called when Fragments are created by the system! While we are have access to the constructor, it is considered best practice to not call this constructor directly when you want to instantiate a Fragment, and to in fact leave the method empty. This is because we do not have full control over when the constructor is executed: the Android system may call the no-argument constructor whenever it needs to recreate the Activity (or just the Fragment), which can happen at arbitrary times. Since only this default constructor is called, we can’t add an additional constructor with any arguments we may want the Fragment to have (e.g., the searchTerm)… and thus it’s best to not use it at all.
+To do this, we will need to instantiate the desired Fragment dynamically (in Java code), rather than statically in the XML using the <fragment> element. This is because we need to be able to dynamically change which Fragment is currently being shown, which is not possible for Fragments that are “hard-coded” in the XML.
+Unlike Activities, Fragments (such as MovieListFragment) do have constructor methods that can be called. In fact, Android requires that every Fragment include a default (no-argument) constructor that is called when Fragments are created by the system! While we have access to the constructor, it is considered best practice to not call this constructor directly when you want to instantiate a Fragment, and to in fact leave the method empty. This is because we do not have full control over when the constructor is executed: the Android system may call the no-argument constructor whenever it needs to recreate the Activity (or just the Fragment), which can happen at arbitrary times. Since only this default constructor is called, we can’t add an additional constructor with any arguments we may want the Fragment to have (e.g., the searchTerm)… and thus it’s best to not use it at all.
Instead, we specify a simple factory method (by convention called newInstance()) which is able to “create” an instance of the Fragment for us. This factory method can take as many arguments as we want, and then does the work of passing these arguments into the Fragment instantiated with the default constructor:
public static MyFragment newInstance(String argument) {
MyFragment fragment = new MyFragment(); //instantiate the Fragment
@@ -403,7 +403,7 @@ Instantiating Fragments
fragment.setArguments(args); //add the Bundle to the Fragment
return fragment; //return the Fragment
}
-In order to pass the arguments into the new Fragment, we wrap them up in a Bundle (an object containing basic key-value pairs). Values can be added to a Bundle using an appropriate putType() method; note that these do need to be primitive types (int, String, etc.). The Bundle of arguments can then be assignment to the Fragment by calling the setArguments() method.
+In order to pass the arguments into the new Fragment, we wrap them up in a Bundle (an object containing basic key-value pairs). Values can be added to a Bundle using an appropriate putType() method; note that these do need to be primitive types (e.g. int), String objects, or Parcelable objects. The Bundle of arguments can then be assignment to the Fragment by calling the setArguments() method.
We will be able to access this Bundle from inside the Fragment (e.g., in the onCreateView() callback) by using the getArguments() method (and getType() to retrieve the values from it). This allows us to dynamically adjust the content of the Fragment’s Views! For example, we can run the downloadMovieData() function using this argument, fetching movie results as soon as the Fragment is created (e.g., on a button press)… allowing the downloadMovieData() function to again be made private, for example.
Since the Bundle is a set of key-value pairs, each value needs to have a particular key. These keys are usually defined as private constants (e.g., ARG_PARAM_KEY in the above example) to make storage and retrieval easier.
@@ -433,7 +433,7 @@ Transactions
Inter-Fragment Communication
-We can this transaction-based structure to instantiate and load a second Fragment (e.g., a “detail” view for a selected Movie). We can add functionality (e.g., in the onClick() handler) so that when the user clicks on a movie in the list, we replace() the currently displayed Fragment with this new details Fragment.
+We can use this transaction-based structure to instantiate and load a second Fragment (e.g., a “detail” view for a selected Movie). We can add functionality (e.g., in the onClick() handler) so that when the user clicks on a movie in the list, we replace() the currently displayed Fragment with this new details Fragment.
However, remember that Fragments are supposed to be modular—each Fragment should be self-contained, and not know about any other Fragments that may exist (after all, what if we wanted the master/detail views to be side-by-side on a large screen?)
Using getActivity() to reference the Activity and getSupportFragmentManager() to access the manager is a violation of the Law of Demeter—don’t do it!
@@ -463,7 +463,8 @@
Inter-Fragment Communication
Parcelable
-It’s not uncommon to want to pass multiple or complex data values as arguments to a new Fragment, such as a Movie object. However, the arguments for a new Fragment must be passed in through a Bundle, which are only able to store primitive values (e.g., int, float, String)… and a special data type called Parcelable. An object that implements the Parcelable interface includes methods that allow it to be serialized into a value that is simple enough to place in a Bundle—in effect, it’s values are converted into a formatted String (similar in concept to JSON.stringify() in JavaScript).
+It’s not uncommon to want to pass multiple or complex data values as arguments to a new Fragment, such as a Movie object. However, the arguments for a new Fragment must be passed in through a Bundle, which are only able to store primitive values (e.g., int, float), String objects, and a special data type called Parcelable. An object that implements the Parcelable interface includes methods that allow it to be serialized into a value that is simple enough to place in a Bundle—in effect, it’s values are converted into a formatted String (similar in concept to JSON.stringify() in JavaScript).
+
Implementing the Parcelable interface involves the following steps: 1. provide a writeToParcel() method that adds the object’s fields to a given Parcel object 2. provide a constructor that can re-create the object from a Parcel by reading the values from it (in the EXACT SAME order they were added!) 3. provide a describeContents() that returns whether the parcel includes a file descriptor (usually 0, meaning not) 4. provide a Parcelable.Creator constant that can be used to create the Parcelable.
These steps seem complex but are fairly rote: as such, it’s possible to “automate” making a class into a Parcelable. My favorite strategy is to use http://www.parcelabler.com/, a website that allows you to copy and paste a class and provides you a version of that class with the Parcelable interface implemented!