This document provides an introduction to design patterns, fragments, and handling different device configurations in Android app development. It discusses the model-view-controller pattern and how it separates an app into distinct model, view, and controller layers. It also introduces fragments and how they allow dividing the user interface into reusable components with their own lifecycles. The document demonstrates detecting device properties and using configuration qualifiers to provide alternative layouts for different orientations and densities. It provides examples of basic fragment and activity code.
This document provides an introduction to design patterns, fragments, and handling different device configurations in Android app development. It discusses the model-view-controller pattern and how it separates an app into distinct model, view, and controller layers. It also introduces fragments and how they allow dividing the user interface into reusable components with their own lifecycles. The document demonstrates detecting device properties and using configuration qualifiers to provide alternative layouts for different orientations and densities. It provides examples of basic fragment and activity code.
This document provides an introduction to design patterns, fragments, and handling different device configurations in Android app development. It discusses the model-view-controller pattern and how it separates an app into distinct model, view, and controller layers. It also introduces fragments and how they allow dividing the user interface into reusable components with their own lifecycles. The document demonstrates detecting device properties and using configuration qualifiers to provide alternative layouts for different orientations and densities. It provides examples of basic fragment and activity code.
This document provides an introduction to design patterns, fragments, and handling different device configurations in Android app development. It discusses the model-view-controller pattern and how it separates an app into distinct model, view, and controller layers. It also introduces fragments and how they allow dividing the user interface into reusable components with their own lifecycles. The document demonstrates detecting device properties and using configuration qualifiers to provide alternative layouts for different orientations and densities. It provides examples of basic fragment and activity code.
Download as PPTX, PDF, TXT or read online from Scribd
Download as pptx, pdf, or txt
You are on page 1of 53
Design Patterns, Fragments, and
the Real World
• You will learn about the following topics in this chapter: • Patterns and the model-view-controller pattern • Android design guidelines • Getting started with real-world designs and handling multiple different devices • An introduction to Fragments Introducing the model-view-controller pattern
• MVC refers to the separation of different
aspects of our app into distinct parts called layers. • Android apps commonly use the model-view- controller pattern. • A pattern is simply a recognized way to structure our code and other application resources, such as layout files, images, databases, and so on. The model • The model refers to the data that drives our app and any logic/code that specifically manages it and makes it available to the other layers. For example, in our Note To Self app, the Note class along with its getters, setters, and JSON code was the data and logic. The view • The view of the Note To Self app referred to all the widgets in all the different layouts. Anything the user can see or interact with on screen is typically part of the view. And as you can probably remember, the widgets actually came from the View class hierarchy of the Android API. The controller • The controller is the bit in between the view and model. It interacts with both and also keeps them separate. It contains what is known in geek language as the application logic. If a user clicks on a button, the application layer decides what to do about it. • When the user clicks on OK to add a new note, the application logic listens for the interaction on the view layer. It captures the data contained in the view and passes it to the model layer. Android design guidelines • So, what exactly do I mean by design? I am talking about where you put the widgets on the screen, which widgets, what color should they be, how big should they be, how to transition between screens, the best way to scroll a page, when and which interpolators to use, what screens should your app be divided into, and much more. The device detection mini app • To make this app, create a new project and call it Device Detection. Delete the default Hello world! widget. Drag Button onto the top of the screen and set its onClick property to detectDevice • Drag two LargeText widgets onto the layout and set their id properties to txtOrientation and txtResolution, respectively. You should now have a layout that looks something like this: • Add the following members just after the MainActivity class declaration to hold references to our two TextView widgets: • private TextView txtOrientation; • private TextView txtResolution; • Now, in the onCreate method of MainActivity, just after the call to setContentView, add this code: • // Get a reference to our TextView widgets • txtOrientation = (TextView) findViewById(R.id.txtOrientation); • txtResolution = (TextView) findViewById(R.id.txtResolution); • After onCreate, add the method that handles our button click and runs our detection code: • public void detectDevice(View v){ • // What is the orientation? • Display display = getWindowManager().getDefaultDisplay(); • txtOrientation.setText("" + display.getRotation()); • // What is the resolution? • Point xy = new Point(); • display.getSize(xy); • txtResolution.setText("x = " + xy.x + " y = " + xy.y); • } • Rotate the device to landscape (use Ctrl + F11 on PC or Ctrl + fn + F11 on Mac). Now, click on the NEW BUTTON button . • If the 0 and 1 results are less than obvious regarding the device orientation, they refer to the public static final variables of the Surface class, where Surfcae. ROTATION_0 equals 0 and Surface.ROTATION_180 equals 1. Configuration qualifiers • We have already seen configuration qualifiers such as layout-large or layout-xhdpi. • There are configuration qualifiers for size, orientation, and pixel density. • To take advantage of a configuration qualifier, we simply design a layout in the usual way that is optimized for our preferred configuration and then place that layout in a folder with a name that Android recognizes as being of that particular configuration. • So, if we want to have a different layout for landscape and portrait, we would create a folder called layout-land in the res folder and place our specially designed layout within it. • When the device is in the portrait position, the regular layout from the layout folder would be used, and when it is in landscape, the layout from the layout-land folder would be used. • If we are designing for screens with different pixel densities, we can place XML layouts into folders with names like these: • The layout-ldpi layout for low DPI devices • The layout-mdpi layout for medium DPI devices • The layout-hdpi layout for high DPI devices • The layout-xhdpi layout for extra high DPI devices • The layout-xxhdpi layout for extra, extra high DPI devices • The layout-xxxhdpi layout for extra, extra, extra high DPI devices • The layout-nodpi layout for devices with a DPI that you have not otherwise catered for • The layout-tvdpi layout for TVs Using configuration qualifiers – mini app
• Create a new project and call it Configuration
Qualifiers and follow the next steps: • Right-click on the res folder in the project explorer and navigate to New | Android resource directory. Type layout-land and click on OK. • In the layout_main.xml file, change the text of the default TextView widget from Hello world to Hello portrait!.
• Right-click on the layout-land folder and
navigate to New | Layout resource. Name the file layout_main.xml. Add a single Plain TextView widget and change the text property to Hello landscape!. • Run the app and rotate the device between landscape and portrait orientations. Note that the OS automatically uses the appropriate version of layout_main.xml. Fragments • Fragments are reusable elements of an app just like any class, but as mentioned previously, they have special features, such as the ability to load their own view as well as having their very own lifecycle methods Fragments have a lifecycle too • We can set up and control Fragments very much like we do Activities, by overriding the appropriate lifecycle methods. onCreate • In the onCreate method, we can initialize variables and do almost all the things we would typically do in the Activity onCreate method. The big exception to this is initializing our UI. onCreateView • In this method, we will, as the name suggests, get a reference to any of our UI widgets, set up anonymous classes to listen for clicks, and do more besides these tasks, as you will soon see. onAttach and onDetach • These methods are called just before the Fragment is actually put into use/taken out of use. onStart, onPause, and onStop • In these methods, we can take certain actions such as creating or deleting objects or saving data, just like we did with their counterparts that were based on Activity. Managing Fragments with FragmentManager • The FragmentManager class is part of Activity. We use it to initialize Fragment, add Fragments to the Activities layout, and to end Fragment. • The highlighted code here shows how we used FragmentManager (which is already a part of the Activity) that was passed in as an argument to create the pop-up dialog: • button.setOnClickListener(new View.OnClickListener() { • @Override • public void onClick(View v) { • // Create a new DialogShowNote called dialog • DialogShowNote dialog = new DialogShowNote(); • // Send the note via the sendNoteSelected method • dialog.sendNoteSelected(mTempNote); • // Create the dialog • dialog.show(getFragmentManager(), "123"); • } • }); • The second argument of the method call is an ID for Fragment. We will see how to use FragmentManager more extensively as well as use the Fragment ID. • FragmentManager does exactly what its name suggests. What is important here is that Activity only has one FragmentManager, but it can take care of many fragments; just what we need to have multiple behaviors and layouts within a single app. • FragmentManager also calls the various lifecycle methods of the fragments it is responsible for. This is distinct from the Activity lifecycle methods that are called by Android, yet closely related because FragmentManager calls many of the Fragment lifecycle methods in response to the Activity lifecycle methods being called Our first working Fragment mini app • Create a new project called Simple Fragment using the same default settings as always. • Switch to activity_main.xml and delete the default Hello world! widget. Now, make sure that the RelativeLayout root is selected by clicking on it in the Component Tree window. Change its id property to fragmentHolder. We will now be able to get a reference to this layout in our Java code, and as the id property implies, we will be adding a Fragment to it. • Now, we will create a layout that will define our Fragment's appearance. Right-click on the layout folder and navigate to New | Layout resource file. In the File name field, type fragment_layout and click on OK. • Add a single Button widget anywhere on the layout and make its id property button. • In the project explorer, right-click on the folder that contains the MainActivity file. From the context menu, navigate to New | Java class. In the Name field, type SimpleFragment and click on OK. • In our new SimpleFragment class, change the code to extend Fragment. As you type the code, you will be asked to choose a specific Fragment class to import, as shown in the next screenshot: • Now, add a single String variable called myString and a Button variable called myButton as members and override the onCreate method. Inside the onCreate method, initialize myString to Hello from SimpleFragment. • public class SimpleFragment extends Fragment { • // member variables accessible from anywhere in this fragment • String myString; • Button myButton; • @Override • public void onCreate(Bundle savedInstanceState){ • super.onCreate(savedInstanceState); • myString = "Hello from SimpleFragment"; • } • } • In the previous code, we created a member variable called myString, and then in the onCreate method, we initialized it. This is very much like we do for our previous apps when we only use Activity. The difference, however, is that we did not set the view or attempt to get a reference to our Button member variable, myButton • When using Fragment, we need to do this in the onCreateView method. Let's override this now and see how we set the view and get a reference to our Button. • Add this code to the SimpleFragment class after the onCreate method: • @Override • public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { • View view = inflater.inflate (R.layout.fragment_layout, container, false); • myButton = (Button) view.findViewById(R.id.button); • return view; • } • To understand the previous block of code, we must first look at the onCreateView method signature. First, note that the start of the method states that it must return an object of the type View: • public View onCreateView... • Next, we have the three arguments. Let's look at the first two now: • (LayoutInflater inflater, ViewGroup container,... • We need LayoutInflater as we cannot call setContentView because Fragment provides no such method. In the body of onCreateView, we use the inflate method of inflater to inflate our layout contained in fragment_layout.xml and initialize view (an object of the type View) with the result. • We use container that was passed in to onCreateView as an argument in the inflate method. The container variable is a reference to the layout in activity_ main.xml. • It might seem obvious that activity_main.xml is the containing layout, but as we will see later in the chapter, the ViewGroup container argument allows any Activity with any layout to be the container for our fragment. This is exceptionally flexible and makes our Fragment code reusable to a significant extent. • The third argument that we pass into inflate is false, which means that we don't want our layout to be immediately added to the containing layout. We will do this soon from another part of the code. • The third argument of onCreateView is Bundle savedInstanceState, which is there to help us maintain the data that our fragments hold. • Now that we have an inflated layout contained in view, we can use this to get a reference to Button like this: • myButton = (Button) view.findViewById(R.id.button); • And we can also use it as the return value to the calling code, as required: • return view; • Now, we can add an anonymous class to listen for clicks on our button in the usual manner. In the onClick method, we display a pop-up Toast message to demonstrate that everything is working as expected. Add this code just before the return statement in onCreateView as highlighted in this next code: • myButton = (Button) view.findViewById(R.id.button); • myButton.setOnClickListener(new View.OnClickListener() { • @Override • public void onClick(View v) { • Toast.makeText(getActivity(),myString , Toast.LENGTH_SHORT).show(); • } • }); • As a reminder, the getActivity() call that is used as an argument in makeText gets a reference to the Activity that contains Fragment. This is required to display a Toast message. We also used getActivity in our classes based on FragmentDialog in the Note To Self app. • We can't run our mini app just yet, as it will not work because there is one more step that we need to complete. We need to create an instance of SimpleFragment and initialize it appropriately. This is where FragmentManager will get introduced. • This next code creates a new FragmentManager by calling getFragmentManager. It creates a new Fragment based on our SimpleFragment class by using FragmentManager and passing in the ID of the layout (within the Activity) that will hold it. • Add this code in the onCreate method of MainActivity.java just after the call to setContentView: • FragmentManager fManager = getFragmentManager(); • // Create a new fragment using the manager • // Passing in the id of the layout to hold it • Fragment frag = fManager.findFragmentById(R.id.fragmentHolder); • // Check the fragment has not already been initialized • if(frag == null){ • // Initialize the fragment based on our SimpleFragment • frag = new SimpleFragment(); • fManager.beginTransaction() • .add(R.id.fragmentHolder, frag) • .commit(); • } Fragment reality check • So, what does this Fragment stuff really do for us? Our first Fragment mini app would have exactly the same appearance and functionality had we not bothered with Fragment at all. In fact, using Fragment has made the whole thing more complicated! Why would we want to do this? • We know that Fragment or fragments can be added to the layout of an Activity. We know that a Fragment not only contains its own layout (view), but also its very own code (controller), which although hosted by Activity, the Fragment is virtually independent. • Our quick mini app only showed one Fragment in action, but we could have an Activity that hosts two or more fragments. We can then effectively have two almost-independent controllers displayed on a single screen. • What is most useful about this, however, is that when the Activity starts, we can detect attributes of the device that our app is running on, perhaps a phone or tablet or in the portrait or landscape orientation. We can then use this information to decide to display either just one or two of our fragments simultaneously.