• No results found

Android Development 2

N/A
N/A
Protected

Academic year: 2021

Share "Android Development 2"

Copied!
248
0
0

Loading.... (view fulltext now)

Full text

(1)

Android Development 2

Lesso n 1: Fragm e nt s

The Sandbo x Enviro nment Abo ut Eclipse

Perspectives and the Red Leaf Ico n Wo rking Sets

Andro id Fragments

Using Fragments Pro gramatically Wrapping Up

Quiz 1 Pro ject 1 Lesso n 2: Lo ade rs

Why Use a Lo ader?

Perfo rming Tasks in a Lo ader Wrapping Up

Quiz 1 Pro ject 1 Lesso n 3: Advance d Layo ut s

Suppo rting Orientatio n Changes Persisting Data o n Ro tatio n Suppo rting Multiple Screen Sizes Wrapping Up

Quiz 1 Pro ject 1

Lesso n 4: Cust o m Vie w Co m po ne nt s Defining a Custo m Co mpo nent

Implementing View Attributes in a Custo m Co mpo nent Wrapping Up

Quiz 1 Pro ject 1 Lesso n 5: Basic Se rvice s

Creating, Declaring, and Starting a Service Wrapping Up

Quiz 1 Pro ject 1 Lesso n 6 : No t if icat io ns

Creat and Update a No tificatio n

Respo nding To User Taps On A No tificatio n Updating A No tificatio n

Wrapping Up

Quiz 1 Pro ject 1 Pro ject 2 Lesso n 7: Co nt e nt Pro vide rs

Creating and Using a Co ntent Pro vider Examining the Co de

Wrapping Up

Quiz 1 Pro ject 1

Lesso n 8 : Cam e ra Basics: Using t he Built -in Cam e ra Applicat io n Starting the Built-in Camera Using an Intent

(2)

Wrapping Up

Quiz 1 Pro ject 1

Lesso n 9 : Cam e ra Advance d: Building a Cust o m Cam e ra Applicat io n Using the Camera API

Camera Parameters

Checking fo r a Camera and Handling Multiple Cameras Camera Features and the Andro id Manifest

Wrapping Up

Quiz 1 Pro ject 1

Lesso n 10 : Bro adcast Re ce ive rs

Creating a Bro adcastReceiver fo r System Events Creating a Bro adcastReceiver fo r Service Events Using the Lo calBro adcastManager

Wrapping Up

Quiz 1 Pro ject 1 Lesso n 11: Me dia: Audio

Creating a MediaPlayer and Playing an Audio File Handling MediaPlayer State and the Activity Lifecycle Handling MediaPlayer Events and UI Updates Wrapping Up Audio

Quiz 1 Pro ject 1 Lesso n 12: Me dia: Vide o

Video Playback with a Video View

Adding a MediaCo ntro ller to a Video View Video View Events and Metho ds

Wrapping UP

Quiz 1 Pro ject 1 Lesso n 13: We bVie w

WebView Basics Using WebSettings

Using a WebChro meClient Using WebViewClient Using WebView Metho ds Enabling JavaScript WebView Wrap-up

Quiz 1 Quiz 2 Pro ject 1 Lesso n 14: Andro id 2 Final Pro je ct

Final Pro ject Pro ject 1

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

(3)

Fragments

Welco me to the O'Reilly Scho o l o f Techno lo gy Andro id 2 co urse!

Course Objectives

When yo u co mplete this co urse, yo u will be able to :

create applicatio ns o ptimized fo r bo th pho nes and tablets. suppo rt o ld and new devices using the Andro id suppo rt library.

utilize vario us Andro id systems fo r sharing with and receiving data fro m o ther Andro id applicatio ns. create media rich applicatio ns with audio and video .

Note

If yo u're new to Andro id, we highly reco mmend that yo u co ntact us to co mplete the first Andro id co urse befo re taking this o ne. The lesso ns in this Andro id 2 co urse will all assume yo u have a firm grasp o n Object Oriented Pro gramming, the Java pro gramming language, and the basics o f Andro id applicatio n develo pment with the Andro id SDK.

If yo u've already co mpleted the first Andro id co urse in this series o r are already familiar with using Eclipse under the remo te develo pment pro cess fo r O'Reilly Scho o l o f Techno lo gy, yo u can skip ahead to the Andro id

Fragments sectio n.

Lesson Objectives

When yo u co mplete this lesso n, yo u will be able to : learn abo ut the UserActive metho d o f learning. read Abo ut the Learning Sandbo x Enviro nment. set Up Eclipse fo r Wo rking with Andro id Applicatio ns. create A Simple Website.

add Web Co ntro ls to Yo ur Website.

Learning with O'Reilly School of Technology Courses

As with every O'Reilly Scho o l o f Techno lo gy co urse, we'll take a user-active appro ach to learning. This means that yo u (the user) will be active! Yo u'll learn by do ing, building live pro grams, testing them and experimenting with them— hands-o n!

To learn a new skill o r techno lo gy, yo u have to experiment. The mo re yo u experiment, the mo re yo u learn. Our system is designed to maximize experimentatio n and help yo u learn to learn a new skill.

We'll pro gram as much as po ssible to be sure that the principles sink in and stay with yo u.

Each time we discuss a new co ncept, yo u'll put it into co de and see what YOU can do with it. On o ccasio n we'll even give yo u co de that do esn't wo rk, so yo u can see co mmo n mistakes and ho w to reco ver fro m them. Making mistakes is actually ano ther go o d way to learn.

Abo ve all, we want to help yo u to learn to learn. We give yo u the to o ls to take co ntro l o f yo ur o wn learning experience. When yo u co mplete an OST co urse, yo u kno w the subject matter, and yo u kno w ho w to expand yo ur kno wledge, so yo u can handle changes like so ftware and o perating system updates.

Here are so me tips fo r using O'Reilly Scho o l o f Techno lo gy co urses effectively:

T ype t he co de . Resist the temptatio n to cut and paste the example co de we give yo u. Typing the co de actually gives yo u a feel fo r the pro gramming task. Then play aro und with the examples to find o ut what else yo u can make them do , and to check yo ur understanding. It's highly unlikely yo u'll break anything by

experimentatio n. If yo u do break so mething, that's an indicatio n to us that we need to impro ve o ur system! T ake yo ur t im e . Learning takes time. Rushing can have negative effects o n yo ur pro gress. Slo w do wn and let yo ur brain abso rb the new info rmatio n tho ro ughly. Taking yo ur time helps to maintain a relaxed, po sitive

(4)

let yo ur brain abso rb the new info rmatio n tho ro ughly. Taking yo ur time helps to maintain a relaxed, po sitive appro ach. It also gives yo u the chance to try new things and learn mo re than yo u o therwise wo uld if yo u blew thro ugh all o f the co ursewo rk to o quickly.

Expe rim e nt . Wander fro m the path o ften and explo re the po ssibilities. We can't anticipate all o f yo ur questio ns and ideas, so it's up to yo u to experiment and create o n yo ur o wn. Yo ur instructo r will help if yo u go co mpletely o ff the rails.

Acce pt guidance , but do n't de pe nd o n it . Try to so lve pro blems o n yo ur o wn. Go ing fro m misunderstanding to understanding is the best way to acquire a new skill. Part o f what yo u're learning is pro blem so lving. Of co urse, yo u can always co ntact yo ur instructo r fo r hints when yo u need them. Use all available re so urce s! In real-life pro blem-so lving, yo u aren't bo und by false limitatio ns; in OST co urses, yo u are free to use any reso urces at yo ur dispo sal to so lve pro blems yo u enco unter: the Internet, reference bo o ks, and o nline help are all fair game.

Have f un! Relax, keep practicing, and do n't be afraid to make mistakes! Yo ur instructo r will keep yo u at it until yo u've mastered the skill. We want yo u to get that satisfied, "I'm so co o l! I did it!" feeling. And yo u'll have so me pro jects to sho w o ff when yo u're do ne.

Lesson Format

We'll try o ut lo ts o f examples in each lesso n. We'll have yo u write co de, lo o k at co de, and edit existing co de. The co de will be presented in bo xes that will indicate what needs to be do ne to the co de inside.

Whenever yo u see white bo xes like the o ne belo w, yo u'll type the co ntents into the edito r windo w to try the example yo urself. The CODE TO TYPE bar o n to p o f the white bo x co ntains directio ns fo r yo u to fo llo w:

CODE TO TYPE:

White boxes like this contain code for you to try out (type into a file to run).

If you have already written some of the code, new code for you to add looks like this.

If we want you to remove existing code, the code to remove will look like this.

We may also include instructive comments that you don't need to type.

We may run pro grams and do so me o ther activities in a terminal sessio n in the o perating system o r o ther co mmand-line enviro nment. These will be sho wn like this:

INTERACTIVE SESSION:

The plain black text that we present in these INTERACTIVE boxes is

provided by the system (not for you to type). The commands we want you to type look lik e this.

Co de and info rmatio n presented in a gray OBSERVE bo x is fo r yo u to inspect and absorb. This info rmatio n is o ften co lo r-co ded, and fo llo wed by text explaining the co de in detail:

OBSERVE:

Gray "Observe" boxes like this contain information (usually code specifics) for you to observe.

The paragraph(s) that fo llo w may pro vide additio n details o n inf o rm at io n that was highlighted in the Observe bo x. We'll also set especially pertinent info rmatio n apart in "No te" bo xes:

Note

No tes pro vide info rmatio n that is useful, but no t abso lutely necessary fo r perfo rming the tasks at hand.

(5)

WARNING

Warnings pro vide info rmatio n that can help prevent pro gram crashes and data lo ss.

The Sandbox Environment

About Eclipse

We're using an Integrated Develo pment Enviro nment (IDE) called Eclipse. It's the pro gram filling up yo ur screen right no w. IDEs assist pro grammers by perfo rming many o f the tasks that need to be do ne repetitively. IDEs can also help to edit and debug co de, and o rganize pro jects.

Note

Yo u'll make so me changes to yo ur wo rking enviro nment during this lesso n, so when yo uco mplete the lesso n, yo u'll need to exit Eclipse to save tho se changes.

The Eclipse windo w displays lesso n co ntent, and pro vides space fo r yo u to create, manage, and run pro grams:

Perspectives and the Red Leaf Icon

The Ellipse Plug-in fo r Eclipse, develo ped by the O'Reilly Scho o l o f Techno lo gy, adds an ico n to the to o l bar in Eclipse. This ico n is yo ur "panic butto n." Since Eclipse is so versatile, yo u are allo wed to mo ve things aro und, like views, to o lbars, and such. If yo u beco me co nfused and want to return to the default perspective (windo w layo ut), clicking o n the Red Leaf ico n allo ws yo u to do that right away.

The ico n has these functio ns:

To reset the current perspective, click the ico n.

To change perspectives, click the dro p-do wn arro w beside the ico n and select a series name (Andro id, Java, Pytho n, C++, etc.). Mo st o f the perspectives lo o k similar, but subtle changes may be present "behind the scenes," so it's best to use the co rrect perspective fo r the co urse. Fo r this co urse, select Andro id.

(6)

Working Sets

All pro jects created in Eclipse exist in the wo rkspace directo ry o f yo ur acco unt o n o ur server. As yo u create multiple pro jects fo r each lesso n in each co urse, it's po ssible that yo ur wo rkspace directo ry can beco me pretty cluttered. To help alleviate the po tential clutter, in this co urse, we use working sets. A wo rking set is a lo gical view o f the wo rkspace; it behaves like a fo lder, but it's really just an asso ciatio n o f files. Wo rking sets allo w yo u to limit the detail that yo u see at any given time. The difference between a wo rking set and a fo lder is that a wo rking set do esn't actually exist in the file system. A wo rking set is a co nvenient way to gro up related items to gether. Yo u can assign a pro ject to o ne o r mo re wo rking sets. In so me cases, like with the Andro id ADT plugin to Eclipse, new pro jects are created witho ut regard fo r wo rking sets and will be placed in the wo rkspace, but no t assigned to a wo rking set (appearing in the "Other Pro jects" wo rking set). To assign o ne o f these pro jects to a wo rking set, right-click o n the pro ject name and select Assign Wo rking Se t s fro m the co ntext menu.

We've created so me wo rking sets fo r yo u already. To turn the wo rking set display o n and o ff in Eclipse, see these instructio ns.

Setting Up Your Android Emulator

The Andro id team has made an excellent Eclipse plugin fo r Andro id called ADT (Andro id Develo per To o lkit). ADT helps with Andro id develo pment in Eclipse in many different ways, so it's impo rtant that we get the Eclipse enviro nment and ADT set up co rrectly fro m the start, so we can build and test o ur Andro id applicatio ns.

Note

The Andro id Develo per To o lkit plugin fo r Eclipse changes extremely frequently. The develo pers behind the to o lkit are do ing amazing wo rk and co nstantly updating and impro ving the plugin. Ho wever, this means the mo st recent versio n may differ fro m what yo u see here and what the instructio ns detail. Do n't wo rry if what yo u see slightly differs fro m the instructio ns. While the lo o k, feel, and features may have changed (likely fo r the better), the co re decisio ns and o ptio ns such as applicatio n and package names will generaly still be reco gnizable. We perio dically update the to o lkit o n o ur systems.

Point ADT to the Android SDK

The ADT plugin is installed o n the instance o f Eclipse that yo u are using right no w. To o pen ADT, yo u can either click the Andro id Virtual Device Manager ico n in the butto n bar at the to p, o r select Windo w | AVD Manage r:

(7)

Go ahead and try that no w. Yo u'll pro bably get an erro r message info rming yo u that the Andro id SDK co uld no t be fo und:

To fix this erro r, o pen the Eclipse preferences fro m the to o lbar menu by clicking Windo w | Pre f e re nce s. The Eclipse preferences windo w will appear. Then click the Andro id sectio n o n the left. (Yo u may be asked if yo u want to send usage data to Go o gle. Click "No .") Then, in the SDK Lo catio n field, type C:\Pro gram File s (x86 )\Andro id\andro id-sdk and click OK.

Note

So metimes when reo pening a remo te Eclipse sessio n, ADT will fo rget that it already has the lo catio n o f the SDK, and will po p-up the erro r again. If that happens, just o pen the Eclipse Preferences windo w again (Windo w | Pre f e re nce s) and it sho uld sho w that the path is in there already. Click OK and everything sho uld wo rk fine again.

(8)

No w ADT is ready to go ! To test to make sure it's wo rking, o pen the ADT windo w by clicking the butto n o r selecting Windo w | AVD Manage r. The ADT dialo g windo w will o pen. Feel free to lo o k aro und in the windo w to get an idea o f what go es o n there befo re yo u co ntinue o n to the next sectio n, where we'll create an emulato r using the AVD Manager.

Note

Yo ur AVD Manager pro bably wo n't be empty like the screensho t abo ve. Due to the nature o f the remo te develo pment enviro nment we're using and the way the AVD Manager handles

emulato rs, yo u'll pro bably see many o ther users' emulato rs. Co nversely, any changes yo u make in the AVD Manager will be visible to o ther users as well. Please be respectful o f the o ther users and do not mo dify o r delete any emulato rs o ther than tho se yo u've created fo r yo urself.

Create an Emulator

If yo u clo sed it, o pen yo ur ADT windo w again. This is the windo w that allo ws yo u to create and co nfigure as many Andro id emulato rs as yo u like so yo u can test yo ur applicatio n o n vario us different hardware and so ftware co nfiguratio ns. Fo r no w, we'll create a single emulato r.

On the right side o f the ADT windo w, click Ne w.... The "Create new Andro id Virtual Device (AVD)" wizard appears.

Fo r the Name, enter your-ost-username-andro id2.2.3 (fo r example, if yo ur username is jjam iso n, yo ur emulato r name wo uld be jjam iso n-andro id2.2.3).

(9)

in the Target dro pdo wn, select Andro id 2.2.3 - API Le ve l 10 . Fo r the SD card, select the Size radio butto n and enter 20 MiB.

When yo u're ready, click Cre at e AVD at the bo tto m. Then, select yo ur new emulato r in the Virtual Devices list, and click St art ... o n the right:

A Launch Optio ns windo w appears. The emulato r is actually a little to o big fo r o ur remo te Eclipse sessio n, so we'll scale it do wn a little. Check the Scale display t o re al size bo x, enter 8.0 in the Screen Size (in.) field, and then click Launch:

The emulato r will take a while to lo ad. No w might be a go o d time to po ur yo urself ano ther cup o f co ffee o r let the do g o ut. When the emulato r is finally lo aded, yo u'll see it in ano ther windo w o n to p o f Eclipse.

At this po int, yo u can clo se the Virtual Device Manager windo w, but try no t to clo se the emulato r when develo ping yo ur applicatio n. Yo u'll save a lo t o f time if yo u do n't have to sit thro ugh the bo o t-up pro cess o f the emulato r. Alternatively, yo u might use the Snapshot feature in the Launch Optio ns windo w (abo ve). In Snapsho t mo de, whenever the emulato r is clo sed, AVD saves a snapsho t o f the current state o f the emulato r, which allo ws it to bo o t up faster. Ho wever, if yo ur emulato r ends up in a weird o r bro ken state, yo u'll need to check the Wipe use r dat a bo x in the Launch Optio ns windo w when yo u restart it, in o rder to reset the snapsho t state o f the emulato r.

To switch between this lesso n co ntent and the emulato r, use the tabs at the bo tto m o f the screen:

Note

Yo u can set up o ther emulato rs to match different devices, if yo u like. Always begin the emulato rname with yo ur OST user name, so yo u can differentiate them fro m emulato rs created by o ther users.

In the next sectio n, we'll finally dig into so me co de and run o ur first Andro id applicatio n!

Android Fragments

What are Andro id Fragments? The Andro id develo per do cumentatio n describes a fragment as "a piece o f an

applicatio n's user interface o r behavio r that can be placed in an Activity." I like to think o f fragments as an extensio n o f Andro id's Activity pattern to better encapsulate yo ur view lo gic away fro m each specific Activity. This makes it easier to reuse yo ur view lo gic and better suppo rt multiple screen sizes fro m pho nes to tablets. If yo u to o k the first co urse, yo u'll remember using Fragments briefly in the Dialo gs lesso n. In this co urse, we'll use Fragments much mo re; in fact, we'll use them in every single applicatio n we build.

Note

Do n't co nfuse Andro id Fragments with the fragmentatio n o f the Andro id platfo rm. Andro id fragmentatio nthat yo u may hear abo ut in vario us news so urces refers to the "fragmentatio n" o f the vario us different platfo rm versio ns o f the Andro id OS installed o n each Andro id pho ne.

Let's get go ing and start a pro ject using Fragments. Create a new Andro id Pro ject. Select File | Ne w | Ot he r, and select Andro id Applicat io n Pro je ct . Name the pro ject Fragm e nt s, enter the package name

(10)

Uncheck the Cre at e cust o m launche r ico n bo x, check the Add pro je ct t o wo rking se t s bo x and click Se le ct to cho o se the Andro id2_Le sso ns wo rking set:

(11)
(12)
(13)

Click Finish to create the pro ject. Next, we need to add the suppo rt library to the pro ject. ADT makes this pro cess pretty straightfo rward. Right-click the Fragm e nt s ro o t pro ject fo lder, cho o se Andro id T o o ls | Add Suppo rt Library. Andro id auto matically do wnlo ads the latest versio n o f the suppo rt library and includes it in yo ur pro ject. When it's finished, verify that the pro cess wo rked by expanding the Fragm e nt s/libs fo lder to find the andro id-suppo rt -v4 .jar file.

No w, in yo ur new pro ject, in the /src fo lder, co m .o st .andro id.f ragm e nt s package, o pen the MainAct ivit y.java file and make these changes:

(14)

CODE TO TYPE: MainActivity.java package com.ost.android.fragments; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main); }

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu);

return true; }

}

This co de will co mpile as is; if yo u see any red squiggles in the text, go back and make sure yo u've included the Suppo rt Library pro perly. No w that o ur Activity suppo rts fragments, let's create a fragment. Create a new class named Ho m e Fragm e nt , change the package name to co m .o st .andro id.f ragm e nt s and make sure it extends fro m andro id.suppo rt .v4 .app.Fragm e nt . Yo ur New Java Class wizard lo o ks like this:

(15)

We'll co me back to this file in a bit, but first we'll ho o k this Fragment up to o ur Activity. Open the act ivit y_m ain.xm l layo ut file in the /re s/layo ut fo lder and make these changes:

(16)

/res/layo ut/activity_main.xml

<RelativeLinearLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/homefragment" class="com.ost.android.fragments.HomeFragment"/> </RelativeLinearLayout>

No w the Activity will lo ad the new fragment auto matically as so o n as the view is lo aded. Let's run it. Right-click the Fragm e nt s ro o t pro ject name and select Run As | Andro id Applicat io n. The applicatio n will crash. Check Lo gCat fo r the erro r (yo u may have to do uble-click o n the Lo gCat windo w tab to expand the windo w and see the erro r message clearly):

There's a lo t o f red text here so it co uld be to ugh to find the exact info rmatio n we need. We'll lo o k fo r references to files we've actually created in the applicatio n, which in this case is Ho meFragment. The erro r tells us "Fragment

co m.o st.andro id.fragments.Ho meFragment did no t create a view." We are o n the right track. Our Fragment is being lo aded, but hasn't created a view yet so it's crashing the applicatio n right away. Let's fix that. Create a new Andro id XML Layo ut file named ho m e _f ragm e nt .xm l and then mo dify it as sho wn:

(17)

CODE TO TYPE: /res/layo ut/ho me_fragment.xml <?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:orientation="vertical" > <Button android:id="@+id/home_fragment_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Home Fragment Button"/> </LinearLayout>

No w go back to Ho meFragment.java and make these changes:

CODE TO TYPE: Ho meFragment.java package com.ost.android.fragments; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;

public class HomeFragment extends Fragment { @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedIn stanceState) {

return inflater.inflate(R.layout.home_fragment, container, false); }

}

No w o ur applicatio n wo rks and sho ws the fragment lo ading successfully. Save the mo dified files and run the applicatio n o nce mo re. Once it lo ads, yo ur emulato r lo o ks like this:

(18)

Great! So what's go ing o n here? Well, first we changed o ur usual starting Activity to extend fro m Fragm e nt Act ivit y. We used the Andro id Suppo rt library to get access to the FragmentActivity class. If we were writing an applicatio n fo r the Andro id 3 (Ho neyco mb) versio n o r later, we wo uldn't need the suppo rt library. As yo u might have guessed, a

FragmentActivity class is required to lo ad a Fragment.

OBSERVE: activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ...

<fragment

android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/homefragment"

class="com.ost.android.fragments.HomeFragment" />

(19)

We used the MainActivity's view to lo ad the fragment. In activity_main.xml, we added the f ragm e nt xml no de to o ur layo ut. The fragment no de tells the Activity to lo ad a Fragment and place the Fragment's view into the layo ut in its place. The layo ut widt h and he ight pro perties are applied to the Fragment's view:

OBSERVE: Ho meFragment.java package com.ost.android.fragments; import com.ost.android.fragments.R; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.ost.android.fragments.R;

public class HomeFragment extends Fragment { @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedIn stanceState) {

return inflater.inflate(R.layout.home_fragment, container, false); }

}

In o ur newly created Ho meFragment class (that extends the Fragm e nt class fro m the Suppo rt Library) we

implemented the o nCre at e Vie w metho d in o rder to lo ad a view fo r the Fragment pro perly. The metho d receives a reference to a Layo ut Inf lat e r o bject, so we use that to inflate a view we defined in XML and return that inflated view. The seco nd parameter sent to the inflate metho d is the Vie wGro up that will eventually co ntain this view. By sending the ViewGro up co nt aine r, o ur new view will inherit the layo ut parameters fro m this ViewGro up. The last param e t e r defines whether we want to attach this view auto matically to the co ntainer ViewGro up fro m the seco nd parameter. We do n't want to do that tho ugh because it's already go ing to be handled auto matically in the framewo rk classes, so we pass in f alse here.

So , Fragments are lo aded into Activities and have their o wn views. Yo u can learn mo re in the Andro id do cumentatio n fo r the Fragment class. If yo u take a lo o k at the lifecycle, yo u see it has a similar lifecycle to that o f the Activity class. Just like Activity, Fragment has o nCre at e , o nSt art , and o nRe sum e metho ds, as well as their co rrespo nding deco nstructio n metho ds o nPause , o nSt o p, and o nDe st ro y. There are also so me o ther lifecycle metho ds that distinguish Fragment fro m the Activity class.

Perhaps the mo st impo rtant difference between a Fragment and an Activity is that the Fragment class is no t an extensio n o f Co nt e xt . Fragments get their co ntext fro m the Activity that creates them, so they canno t exist witho ut an Activity. A Fragment can always get a reference to its parent Activity, and thus a Co ntext reference, by calling the ge t Act ivit y() metho d. Ho wever, when implementing a Fragment yo u must make sure that the parent Activity hasn't been destro yed. This is where the new Fragment lifecycle metho d o nAct ivit yCre at e d co mes in handy. If a Fragment must perfo rm lo gic requiring a co ntext when it is lo aded, then yo u place that lo gic in the o nAct ivit yCre at e d metho d where yo u can guarantee that the parent Activity has already finished its creatio n lifecycle and is ready to be used as a Co ntext.

Using Fragments Programatically

In additio n to lo ading fragments thro ugh XML, we can lo ad them dynamically in o ur Activity. Often yo u do n't even need a Layo ut XML fo r yo ur activity at all when lo ading Fragments pro grammatically, but we're go ing to co ntinue using o ur previo us view here. Let's start by creating a new Fragment; name the class

Se co ndFragm e nt and o f co urse have it extend the andro id.suppo rt .v4 .app.Fragm e nt class. Also , make sure the file is in the co m .o st .andro id.f ragm e nt s package. No w make these changes:

(20)

CODE TO TYPE: Seco ndFragment.java package com.ost.android.fragments; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.ost.android.fragments.R;

public class SecondFragment extends Fragment { @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

return inflater.inflate(R.layout.second_fragment, container, false); }

}

No w let's create the XML layo ut view file fo r this fragment. As yo u might have guessed, we'll name this file se co nd_f ragm e nt .xm l. Make sure that the file is in the /re s/layo ut / fo lder and then make these changes:

CODE TO TYPE: /res/layo ut/seco nd_fragment.xml <?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:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Second Fragment Loaded!" /> </LinearLayout>

No w we can clo se these new files, go back to o ur previo us co de, and update it to lo ad the new Fragment. Open the act ivit y_m ain.xm l layo ut file and make these changes:

(21)

CODE TO TYPE: /res/layo ut/activity_main.xml <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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <fragment android:layout_height="match_parentwrap_content" android:layout_width="match_parent" android:id="@+id/homefragment" class="com.ost.android.fragments.HomeFragment" /> <LinearLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent" /> </LinearLayout>

Next, o pen MainAct ivit y.java and make these changes:

CODE TO TYPE: MainActivity.java

package com.ost.android.fragments; import android.os.Bundle;

import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; public class MainActivity extends FragmentActivity { /** Called when the activity is first created. */ @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main); }

public void loadSecondFragment() {

FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); SecondFragment sf = new SecondFragment(); ft.add(R.id.fragment_container, sf); ft.commit();

} }

(22)

CODE TO TYPE: Ho meFragment.java package com.ost.android.fragments; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;

public class HomeFragment extends Fragment { @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

return inflater.inflate(R.layout.home_fragment, container, false); View view = inflater.inflate(R.layout.home_fragment, container, false); view.findViewById(R.id.home_fragment_button).setOnClickListener(buttonClickL istener);

return view; }

public View.OnClickListener buttonClickListener = new View.OnClickListener() { @Override

public void onClick(View v) {

MainActivity activity = (MainActivity) getActivity(); activity.loadSecondFragment();

} }; }

Save the changed files and run the applicatio n. Yo ur emulato r lo ads and lo o ks the same as befo re, but no w when yo u click the Ho m e Fragm e nt But t o n, it lo ads the Se co ndFragm e nt :

(23)

Let's go back o ver so me key areas no w and discuss o ur co de in detail. First, let's lo o k at the changes we made to MainAct ivit yFragm e nt .java:

OBSERVE:

public void loadSecondFragment() {

FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); SecondFragment sf = new SecondFragment(); ft.add(R.id.fragment_container, sf); ft.commit();

}

We start by getting a reference to the Fragm e nt Manage r class. This is accessed by using the

ge t Suppo rt Fragm e nt Manage r class inherited fro m FragmentActivity. Just like befo re, this is the Suppo rt Library versio n o f the FragmentManager. If this applicatio n was targeting Ho neyco mb o r later, we'd just call getFragmentManager to get o ur reference. This is o ne o f the few instances using Fragments where the API name differs in the Suppo rt Library fro m the latest SDK.

(24)

We initiate a Fragm e nt T ransact io n that will define the Fragment changes that are abo ut to o ccur. Every time a change is made to an Activity's Fragments, a Fragm e nt T ransact io n must be used.

Then we create an instance o f o ur Se co ndFragm e nt, and update the Fragm e nt T ransact io n, telling it to add o ur fragment to the ViewGro up in this Activity's view with the co rrespo nding id

R.id.f ragm e nt _co nt aine r. Finally, we call co m m it o n the transactio n to finalize o ur changes.

OBSERVE: Ho meFragment.java @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.home_fragment, container, false); view.findViewById(R.id.home_fragment_button).setOnClickListener(buttonClickL istener);

return view; }

public View.OnClickListener buttonClickListener = new View.OnClickListener() { @Override

public void onClick(View v) {

MainActivity activity = (MainActivity) getActivity(); activity.loadSecondFragment();

} };

We also made so me interesting changes to o ur Ho m e Fragm e nt .java. First, we mo dified the

o nCre at e Vie w metho d in o rder to set a click listener o n o ur Butto n. Yo u might be used to implementing click handlers fo r Butto ns in XML Layo uts using the o nClick attribute co nventio n. Unfo rtunately, a Fragment canno t use that co nventio n, so click listeners must be set using the se t OnClickList e ne r metho d o n the Butto n directly. If an o nClick attribute metho d is defined in the View, Andro id will still attempt to call a metho d with that name o n the o wning Activity (that is, the activity class that's created when yo u create the pro ject, MainActivity.java), even if the View was defined in a Fragment; ho wever, we are writing o ur co de so that o ur Activities do n't need to manage the co ntents o f the Fragment's views, so we keep this lo gic co ntained in the Fragment itself.

In o ur click listener, we used the ge t Act ivit y metho d (inherited fro m the Fragment class) to get a reference to o ur FragmentActivity. We kno w that this Fragment will belo ng to a MainAct ivit y class, so we can safely cast o ur reference to that class. Finally, we call the lo adSe co ndFragm e nt metho d o n o ur activity to start the lo ading pro cess.

Wrapping Up

We've co vered the basics o f Fragments in Andro id in this lesso n, but there's still mo re functio nality to explo re. Check the Andro id Develo per Do cumentatio n Site fo r mo re detailed info rmatio n regarding the entire Fragment pro cess. We'll be using Fragments, o r at the very least FragmentActivity, in every lesso n fo r this co urse, so make sure yo u feel co mfo rtable with the basics we've learned here befo re yo u go o n.

Practice what yo u've learned in the ho mewo rk. See yo u in the next lesso n! Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

(25)

Loaders

Lesson Objectives

In this lesso n, yo u will:

write and implement a Lo ader. replace an AsyncTask with a Lo ader.

implement Lo aderCallbacks to handle Lo ader results.

register a Lo ader and Lo aderCallbacks with the Lo aderManager.

Welco me back! In this lesso n we'll co ver Lo aders, a great new feature in Andro id that helps to lo ad data asynchro no usly. Lo aders are managed o utside o f the sco pe o f an activity, which allo ws us to retrieve data fro m a Lo ader even if the activity has been destro yed and recreated (like when the user ro tates the screen). Like Fragments, Lo aders first became available in API 11 (Ho neyco mb), and are available to applicatio ns targeting earlier APIs thro ugh the suppo rt library.

Why Use a Loader?

At first glance, Lo aders might no t seem vital. After all, we already have AsyncTasks to perfo rm lo ng-running pro cesses. Ho wever, AsyncTasks do n't exactly co o perate with Andro id's life-cycle fo r Views and Fragments. The example will help illustrate the need fo r Lo aders.

Let's get started. Create a new Andro id pro ject using these criteria: Name the pro ject Lo ade rs.

Use the package name co m .o st .andro id.lo ade rs. Uncheck the Cre at e cust o m launche r ico n bo x.

Assign the Andro id2_Le sso ns wo rking set to the pro ject.

(26)

CODE TO TYPE: MainActivity.java package com.oreillyschool.android2.loaders; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.text.format.DateUtils; import android.widget.TextView; import android.view.Menu;

public class MainActivity extends Activity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

AsyncTask<Void, Void, String> myTask = new AsyncTask<Void, Void, String>() { @Override

protected String doInBackground(Void... params) { try {

Thread.sleep(DateUtils.SECOND_IN_MILLIS * 5); } catch (InterruptedException e) {

}

return "AsyncTask Complete!"; }

@Override

protected void onPostExecute(String result) { super.onPostExecute(result);

TextView tv = (TextView) findViewById(R.id.text); tv.setText(result); } }; myTask.execute(); } @Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu);

return true; }

}

(27)

CODE TO TYPE: /res/layo ut/activity_main.xml <RelativeLayout 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: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=".MainActivity" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@string/hello_world" /> </RelativeLayout>

(28)

Then, after abo ut five seco nds (depending o n ho w fast the emulato r is running thro ugh the Virtual Deskto p), the screen updates:

(29)

OBSERVE: MainActivity.java .

. .

public class MainActivity extends Activity { @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

AsyncTask<Void, Void, String> myTask = new AsyncTask<Void, Void, String>() { @Override

protected String doInBackground(Void... params) { try {

Thread.sleep(DateUtils.SECOND_IN_MILLIS * 5); } catch (InterruptedException e) {

}

return "AsyncTask Complete!"; }

@Override

protected void onPostExecute(String result) { super.onPostExecute(result);

TextView tv = (TextView) findViewById(R.id.text); tv.setText(result); } }; myTask.execute(); } }

This little applicatio n demo nstrates running an AsyncT ask pro cess that takes abo ut five seco nds to finish. When the task is started, it calls T hre ad.sle e p() in its do InBackgro und() metho d. This causes the executio n in that Thread to pause o n this line fo r five seco nds. After waiting five seco nds, the thread co ntinues and finishes the

do InBackgro und() metho d, returning the St ring value. In the o nPo st Exe cut e () metho d, the resulting St ring value is finally applied to the T e xt Vie w.

Note

Yo u sho uld never actually use T hre ad.sle e p() in yo ur Andro id applicatio ns. We're just using it here to simulate so mething that takes five seco nds to co mplete. If yo u need to schedule so mething to o ccur after a sho rt perio d o f time in yo ur Applicatio ns, co nsider using the Timer and TimerTask classes instead. Yo u might also co nsider using a Se rvice , which we'll co ver in a bit.

No w, ro tate the emulato r. Fo cus o n the emulato r windo w and press [Ct rl+F12] o n yo ur keybo ard. The emulato r ro tates to landscape mo de. Also , the TextView in the middle o f the screen go es back to displaying the previo us message, "Hello Wo rld, MainActivity!" Then, after ano ther five seco nds, the message "AsyncTask Co mplete!" displays o nce mo re. This will happen each time yo u ro tate the emulato r. Go ahead and try it a co uple o f times.

Note

[Ct rl+F11] and [Ct rl+F12] will bo th ro tate the emulato r. To find o ut abo ut mo re emulato r keybo ardsho rtcuts, see the do cumentatio n site.

So , let's see what's really go ing o n here. Every time an Andro id device ro tates, the Andro id system destro ys the current Activity (and any active Fragments) and then recreates them in the new o rientatio n. Applicatio ns can explicitly prevent this fro m happening, but at the co st o f its ability to use a separate layo ut per o rientatio n auto matically. So me applicatio ns will prevent this by disabling ro tatio n, thereby fo rcing the user to use the applicatio n in their specified o rientatio n. Generally, neither o f these strategies are reco mmended. It is better to learn ho w to use the Applicatio n life-cycle to preserve the state o f the applicatio n during a ro tatio n and resto re the state when the Activity/Fragment is resto red.

(30)

each time the device ro tates. We co uld pass the necessary data to the next Activity using the Andro id life-cycle metho ds (specifically o nSave Inst ance St at e ()), but the user co uld also ro tate befo re the task actually finishes. We sho uldn't have to start o ur request o ver just because the user ro tated befo re the task returned. This is where Lo aders co me in handy.

Performing Tasks in a Loader

(31)

CODE TO TYPE: MainActivity.java package com.oreillyschool.android2.loaders; import android.app.Activity; import android.os.AsyncTask; import android.content.Context; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.format.DateUtils; import android.widget.TextView;

public class MainActivity extends Activity {

public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallb acks<String> {

@Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

AsyncTask<Void, Void, String> myTask = new AsyncTask<Void, Void, String>() { @Override

protected String doInBackground(Void... params) { try {

Thread.sleep(DateUtils.SECOND_IN_MILLIS * 5); } catch (InterruptedException e) {

}

return "AsyncTask Complete!"; }

@Override

protected void onPostExecute(String result) { super.onPostExecute(result);

TextView tv = (TextView) findViewById(R.id.text); tv.setText(result); } }; myTask.execute(); LoaderManager lm = getSupportLoaderManager();

Loader<String> loader = lm.initLoader(0, null, this); if (!loader.isStarted())

loader.forceLoad(); }

@Override

public Loader<String> onCreateLoader(int loaderId, Bundle args) { return new MyLoader(this);

}

@Override

public void onLoadFinished(Loader<String> loader, String data) { TextView tv = (TextView) findViewById(R.id.text);

tv.setText(data); }

@Override

public void onLoaderReset(Loader<String> loader) { }

private static class MyLoader extends AsyncTaskLoader<String> { public MyLoader(Context context) {

(32)

super(context); }

@Override

public String loadInBackground() { try {

Thread.sleep(DateUtils.SECOND_IN_MILLIS * 5); } catch (InterruptedException e) {

}

return "AsyncTaskLoader Complete!"; }

} }

Make sure yo u're impo rting the Suppo rt Library versio n o f the FragmentActivity, Lo aderManager, AsyncTaskLo ader, and Lo ader classes. No w, save yo ur changes and run the Applicatio n again. Test the ro tatio n. Test ro tating befo re the first five seco nds are even up. No tice anything different? The Applicatio n no w takes o nly five seco nds to tal to switch the TextView to say "AsyncTaskLo ader Co mplete!" Even if the five seco nds runs o ut during a ro tatio n, the View reflects the result immediately when recreated.

Alright, so this is pretty co o l, but what's happening? Why is this different fro m the AsyncTask? Let's walk thro ugh this co de step by step, starting with o ur additio ns to the o nCre at e () metho d.

OBSERVE: MainActivity.java - o nCreate() @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

LoaderManager lm = getSupportLoaderManager();

Loader<String> loader = lm.initLoader(0, null, this); .

. .

We start by getting an instance o f the Lo ade rManage r fro m the Activity by calling ge t Suppo rt Lo ade rManage r(). Then we get an instance o f the Lo ade r we want, using the init Lo ade r() metho d o n the Lo ade rManage r.

init Lo ade r() takes three parameters. The f irst, an Integer, is an ID that can be used to help the callbacks identify which type o f Lo ader it sho uld create. We have o nly o ne type o f Lo ader in this ro utine, so this value do esn't really matter. The ne xt param e t e r is a Bundle o bject that can be used to send so me additio nal data to the ro utine that creates the Lo ader, such as data that might be needed in the co nstructo r o f the Lo ader. We do n't have any data like this, so we simply send null. The last param e t e r is the mo st impo rtant fo r o ur co de. It requires an instance o f the Lo ade rManage r.Lo ade rCallbacks<T > interface. The interface has a generic defined, which must match the Generic used in the definitio n o f the Lo ader class.

OBSERVE: . . . if (!loader.isStarted()) loader.forceLoad();

Next, we che ck t o se e whe t he r o r no t t he Lo ade r has act ually be e n st art e d ye t, and if no t, f o rce it t o st art right away. There's ano ther metho d o n the Lo ader class called st art Lo ade r() which might seem like the metho d to call when yo u want to start the Lo ader, but in this case it isn't. Our Lo ader is an implementatio n o f the

AsyncT askLo ade r class, which requires yo u to call the f o rce Lo ad() metho d o n the Lo ade r to kick o ff its request pro cess. Next, we'll lo o k at the changes to the class definitio n.

(33)

OBSERVE: MainActivity class

public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallb acks<String>

Our regular "pre-Ho neyco mb" Activity class do esn't actually suppo rt getting an instance o f the Lo ade rManage r class, so we need to change o ur class to extend the suppo rt library versio n o f Fragm e nt Act ivit y instead. Also , in o rder to send "this" to the Lo ade rManage r.init Lo ade r() metho d, we have to im ple m e nt t he Lo ade rCallbacks interface as well. We want o ur Lo ade r to return a St ring value, so we define the generic parameter here as the St ring class. Next, we'll lo o k at the metho ds required to implement Lo ade rCallbacks:

OBSERVE: MainActivity.java - Lo aderCallbacks metho ds @Override

public Loader<String> onCreateLoader(int loaderId, Bundle args) { return new MyLoader(this);

}

@Override

public void onLoadFinished(Loader<String> loader, String data) { TextView tv = (TextView) findViewById(R.id.text);

tv.setText(data); }

@Override

public void onLoaderReset(Loader<String> data) { }

Lo aderManager.Lo aderCallbacks<T> requires three metho ds. The first metho d, o nCre at e Lo ade r(), has two

parameters. The f irst param e t e r, an Int e ge r, is the same Integer that was passed to the Lo aderManager.initLo ader earlier. We do n't need to use this parameter in o ur implementatio n. The se co nd param e t e r is a Bundle o bje ct and, o f co urse, co rrespo nds to the seco nd parameter sent to Lo ade rManage r.init Lo ade r earlier. Again, we're no t using this parameter, so we just igno re it. The mo st impo rtant requirement o f this metho d is that it returns a Lo ade r o bject that implements the Generic parameter defined fo r this implementatio n. We just create a new instance o f o ur MyLo ader class. All Lo ade rs require a Co nt e xt in the co nstructo r, and since o ur Fragm e nt Act ivit y is a Co ntext, we just pass this to the MyLo ader co nstructo r. This metho d is actually called internally by the Lo ade rManage r class the first time init Lo ad() is called o n the Lo ade rManage r. Lo ade rManage r will then cache the Lo ade r and return the cache value o n subsequent calls to init Lo ade r().

The seco nd metho d, o nLo adFinishe d(), is called after the Lo ade r finishes lo ading its data. The callback metho d receives two parameters. T he f irst is a re f e re nce t o t he Lo ade r that perfo rmed the wo rk. T he se co nd param e t e r is t he dat a re sult; in o ur case, it will co ntain the St ring value "AsyncTaskLo ader Co mplete!" This callback metho d will always be called o n the UI thread, so it is perfectly safe to mo dify UI co mpo nents here. We apply the text data to o ur T e xt Vie w here so we can see the results o n the screen.

The final callback metho d is o nLo ade rRe se t (). It takes just o ne parameter, the Lo ader that was created in init Lo ade r(). This metho d is called auto matically by the Lo ade rManage r when the Lo ade r's data is abo ut to be released and will no lo nger be available. This gives yo u the o ppo rtunity to update yo ur view to respo nd acco rdingly. Fo r example, if the lo ader is being reset and given new parameters, this metho d wo uld be called befo re the new lo ad, allo wing yo u to update yo ur view and invalidate the o ld data.

(34)

OBSERVE: MyLo ader class

private static class MyLoader extends AsyncTaskLoader<String> { public MyLoader(Context context) {

super(context); }

@Override

public String loadInBackground() { try {

Thread.sleep(DateUtils.SECOND_IN_MILLIS * 5); } catch (InterruptedException e) {

}

return "AsyncTaskLoader Complete!"; }

}

When yo u create a custo m lo ader to perfo rm backgro und data, it's usually better (and easier) to e xt e nd t he AsyncT askLo ade r class than the base Lo ader class. AsyncTaskLo ader, as the name implies, actually uses an AsyncTask o bject internally so yo u do n't have to wo rry abo ut managing any o f the threading lo gic. With

AsyncTaskLo ader, yo u o nly need to implement o ne metho d: lo adInBackgro und(). This is where yo u perfo rm the wo rk required to lo ad the data. Thanks to the underlying AsyncT ask, lo adInBackgro und() is already called o n a separate thread. As yo u can see, we just co pied o ur co de fro m the AsyncT ask into the metho d here.

Note

Time perio ds in Andro id/Java are o ften expressed in milliseco nds. The Dat e Ut ils class has so me excellent features to help manage time units. We used Dat e Ut ils in this lesso n to explicitly define a perio d o f 5 seco nds. Yo u co uld just as easily hard-co de 50 0 0 here (50 0 0 milliseco nds = 5 seco nds after all), but using Dat e Ut ils co nstants makes it easier to read so yo u (o r anyo ne else reading yo ur co de) will kno w immediately what interval is intended. There are also o ther co nstants such as

HOUR_IN_MILLIS and DAY_IN_MILLIS which help with quantities where the math is co nsiderably mo re difficult to read in an instant.

So , let's recap the flo w here step-by-step. In the o nCre at e () metho d we ask the Lo ade rManage r fo r an instance o f o ur Lo ade r. Lo ade rManage r then calls o nCre at e Lo ade r() o n the Lo ade rCallbacks implementatio n where we create o ur instance o f o ur MyLo ade r class. Lo ade rManage r caches this, and returns the reference in init Lo ade r(). Then we check to determine whether the Lo ade r is already started, and if it isn't we fo rce it to start by calling

f o rce Lo ad(). This causes o ur MyLo ade r instance to create a new thread and start its wo rk in the

lo adInBackgro und() metho d. When the wo rk is co mplete, the Lo ader returns its data, which is cached in the Lo ade rManage r. Lo ade rManage r then calls o nLo ade rFinishe d() o n the Lo ade rCallbacks, where we present the data result.

Next, whenever the device is ro tated, o ur Fragm e nt Act ivit y gets destro yed and created. o nCre at e () gets called o nce mo re, where we o nce again ask the Lo ade rManage r fo r an instance o f o ur Lo ade r. It already has a reference cached, so it returns the reference immediately. Then we check to determine whether the Lo ader is started already; it is, so we do no thing. Lo ade rManage r will also check to find o ut if the Lo ade r instance has co mpleted o r no t, and if it has, it will immediately call o nLo ade rFinishe d() o n the Lo ade rCallbacks, passing the cached data.

Wrapping Up

As yo u can see, Lo aders can help co nsiderably when yo u need to perfo rm lo ng-running tasks. In fact, yo u handle mo st backgro und tasks in yo ur applicatio ns with either a Lo ade r o r a Se rvice . There is o ne unfo rtunate do wnside to Lo aders tho ugh—a lack o f suppo rt fo r repo rting pro gress. AsyncT ask made that task relatively easy, but repo rting pro gress with a Lo ader takes co nsiderably mo re effo rt and co de to implement cleanly. One alternate so lutio n is to sho w an indeterminate pro gress bar when yo u start a lo ad. If yo ur backgro und lo ads are lo ng eno ugh that yo u need to present accurate pro gress to the user, co nsider using a Se rvice and Binde r messages to repo rt pro gress to yo ur Views. We will co ver Se rvice s and Binde rs later in the co urse.

The last type o f Lo ade r class left fo r us to explo re is the Curso rLo ade r. It's a subclass o f AsyncT askLo ade r. This type o f Lo ader is used to manage Curso r o bjects that encapsulate data results fro m a Co nt e nt Pro vide r.

Co nt e nt Pro vide rs and Curso rs will also be co vered so o n, in the upco ming Co ntent Pro viders lesso n. Practice what yo u've learned in this lesso n in yo ur ho mewo rk. See yo u next lesso n!

(35)

Copyright © 1998-2014 O'Reilly Media, Inc.

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-sa/3.0/legalcode for more information.

(36)

Advanced Layouts

Lesson Objectives

In this lesso n yo u will:

create alternate layo uts using reso urce qualifiers. reuse a fragment in bo th tablet and pho ne layo uts.

use a single activity to handle bo th pho ne and tablet layo uts using fragments.

Welco me back! In previo us lesso ns we've created layo uts fo r o ur applicatio ns using XML, but we've still o nly scratched the surface. In this lesso n we'll go o ver so me o f the mo re advanced to o ls and features available fo r making layo uts in Andro id, including alternate layo uts fo r o rientatio n and screen size (that is, fo r pho nes and tablets) as well as so me layo ut o ptimizatio n to o ls.

Let's get started. Create a new Andro id pro ject using these criteria: Name the pro ject Advance dLayo ut s.

Use the package name co m .o st .andro id.advance dlayo ut s. Uncheck the Cre at e cust o m launche r ico n bo x.

Assign the Andro id2_Le sso ns wo rking set to the pro ject.

We'll need a tablet-sized emulato r fo r this lesso n. Click the Andro id Virt ual De vice Manage r butto n ( ) at the to p o f the Eclipse windo w. In the Device Manager windo w, click Ne w. In the Create New Andro id Virtual Device (AVD) windo w, give the device an appro priate name, like username-t ab-WXGA-4 .3-18. Fo r Device, select 10 .1" WXGA (T able t ) (1280 x 80 0 : m dpi). Set the Target to Andro id 2.3.3 - API Le ve l 10 . Fo r CPU/ABI, select the ARM (arm e abi-v7 a) o ptio n. Fo r SD card, select a Size o f 20 MiB. Click OK. Back in the Andro id Virtual Device Manager windo w, make sure the new AVD is selected and click St art ... to start the emulato r. In the Launch Optio ns dialo g, select Scale display t o re al size , enter 8 fo r the screen size, and click Launch.

(37)

Supporting Orientation Changes

The way Andro id handles ro tatio n can be a bit pro blematic at times. The Andro id OS will co mpletely destro y and recreate the fro nt Activity (and its Fragments) during ro tatio n. If the applicatio n develo per isn't prepared to handle this, it can lead to a very frustrating user experience, and po ssibly even crash the who le applicatio n. Fo rtunately, the Andro id life-cycle pro vides so me ho o ks that allo w us to persist the state o f o ur Activity thro ugh a ro tatio n. This pro cess also makes it co nvenient to change the layo ut o f o ur View depending o n the o rientatio n o f the device. Let's practice do ing that. First, we'll update o ur primary View to make it a little mo re interesting. Open the layo ut file act ivit y_m ain.xm l and make these changes:

(38)

CODE TO TYPE: /res/layo ut/activity_main.xml <RelativeLayout 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: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=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <EditText android:id="@+id/inputEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="40dp" /> <Button android:id="@+id/reverseButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/inputEditText" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:text="Reverse" /> <TextView android:id="@+id/reverseTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/reverseButton" android:layout_margin="20dp" /> </RelativeLayout>

(39)

CODE TO TYPE: MainActivity.java package com.ost.android.advancedlayouts; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.view.Menu;

public class MainActivity extends FragmentActivity { EditText inputEditText; TextView reverseTextView; Button reverseButton; @Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

inputEditText = (EditText) findViewById(R.id.inputEditText);

reverseTextView = (TextView) findViewById(R.id.reverseTextView);

reverseButton = (Button) findViewById(R.id.reverseButton);

reverseButton.setOnClickListener(new OnClickListener() { @Override

public void onClick(View v) {

String inputText = inputEditText.getText().toString();

String reversedText = new StringBuffer(inputText).reverse().toString(); reverseTextView.setText(reversedText); } }); } @Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu);

return true; }

}

Run the applicatio n, type so mething into the EditText text area, then click the Re ve rse butto n. Yo u see yo ur text reversed in the TextView area.

No w that we have a basic functio ning applicatio n, let's implement a unique layo ut fo r landscape o rientatio n. Click File | Ne w | Ot he r, cho o se Andro id XML Layo ut File fro m the list, and click Ne xt . Select the Advance dLayo ut s pro ject, name the file act ivit y_m ain.xm l (yes, the same name as o ur existing layo ut file), select Line arLayo ut as the ro o t element, and click Ne xt :

(40)

On this screen yo u'll select the reso urce qualifiers fo r this view. Select Orie nt at io n fro m the list, click the right arro w in the middle o f the windo w and, in the "Screen Orientatio n" dro p-do wn, select Landscape . Fo r the Fo lder field, enter /re s/layo ut -land:

(41)

Click Finish. ADT creates a new act ivit y_m ain.xm l file in a new fo lder named layo ut -land. Mo dify this new layo ut file as sho wn:

(42)

CODE TO TYPE: /res/layo ut-land/activity_main.xml <?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:layout_margin="20dp" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <EditText android:id="@+id/inputEditText" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" /> <Button android:id="@+id/reverseButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Reverse" /> </LinearLayout> <TextView android:id="@+id/reverseTextView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="20dp" /> </LinearLayout>

Befo re go ing any further, take a mo ment to co mpare this file and the o ther activity_main.xml file fro m the /re s/layo ut fo lder. What's different? What stayed the same? No tice that the android:id attributes we've used fo r each o f o ur views are exactly the same. We'll explain this so o n, but first, test the applicatio n.

(43)
(44)

So what happened here, and what's with the layo ut -land fo lder?

OBSERVE: MainActivity.java ...

public class MainActivity extends FragmentActivity { EditText inputEditText; TextView reverseTextView; Button reverseButton; @Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

inputEditText = (EditText) findViewById(R.id.inputEditText);

reverseTextView = (TextView) findViewById(R.id.reverseTextView);

reverseButton = (Button) findViewById(R.id.reverseButton);

reverseButton.setOnClickListener(new OnClickListener() { @Override

public void onClick(View v) {

String inputText = inputEditText.getText().toString();

String reversedText = new StringBuffer(inputText).reverse().toString(); reverseTextView.setText(reversedText); } }); } }

Just like multiple drawable fo lders with different reso lutio n qualifiers (which we co vered in the first co urse), the layo ut -land fo lder has a qualifier, but instead o f that qualifier defining an alternate device resolution, it defines a device

orientation, in this case the landscape o rientatio n. When we set the View reso urce id fo r o ur Activity, we pass the value R.layo ut .act ivit y_m ain. Andro id tries to lo ad the mo st specific type o f file matching this id first; if it do esn't find a layo ut reso urce in any fo lder with qualifiers that match the device's current co nfiguratio n, it falls back o n the default file in the fo lder with no qualifiers.

(45)

Yo u co uld also create a new act ivit y_m ain.xm l layo ut file in a /re s/layo ut -po rt fo lder. Layo ut files in this fo lder wo uld then be used fo r po rtrait o rientatio n. If yo u did this, yo u technically wo uldn't need a act ivit y_m ain.xm l in the default /re s/layo ut fo lder. Ho wever, we do n't reco mmend remo ving that default activity_main.xml. Always keep a default layo ut file fo r each view and o nly put specialized layo ut files fo r different device co nfiguratio ns in their respective reso urce qualifier fo lders as needed. If an applicatio n attempts to lo ad a layo ut file and it can't find a file in any fo lder with matching qualifiers, the applicatio n will crash.

Persisting Data on Rotation

Yo u might have no ticed a pro blem in the previo us applicatio n while ro tating the emulato r. While the text yo u typed in the EditText is still present after a ro tatio n, the TextView no lo nger co ntains the reversed text. The EditText co mpo nent has an "auto -save" feature in Andro id that allo ws it to auto matically persist its data o n ro tatio n, but the TextView co mpo nent do es no t have this feature. Many applicatio ns never have to wo rry abo ut this limitatio n o f the TextView co mpo nent. Often the TextView text will already be defined by a String reso urce co nstant, and thus will be lo aded each time the layo ut is lo aded, even o n ro tatio n. Or perhaps the o nCre at e Vie w() metho d is auto matically defining the text fo r each TextView. But, o bvio usly, there are so me situatio ns (like o urs) where the data sho uld pro bably be persisted thro ugh ro tatio n.

We co uld just "reco mpute" the reversed String after a ro tatio n, but there's a better and mo re reliable way to make the fix. Mo dify MainAct ivit y.java o nce again as sho wn:

References

Related documents

classroom practice also reflected that there are language text books in schools that talk to the activities that are to be performed by learners in order to implement both

This report has been prepared independently of the company analysed by Close Brothers Seydler Research AG and/ or its cooperation partners and the analyst(s) mentioned on the

Based on the two-pathway model of the “PE effect” and the previous positive findings about the effects of concept-based PE on PA behavior (e.g., Brynteson &amp; Adams, 1993;

• All registration data entered or updated in CTMS only • Mapping code lists for protocols sites and treatment arms Mapping code lists for protocols, sites and treatment arms •

CEN/TS 16516 contains a number of principles for making the test specimen from the sample, leaving many details open for later specification in product specific standards..

Based on our own analysis of housing price data, including housing prices, housing sales and housing futures prices as well as various market spreads, we conclude that the evidence

The work presented here investigates a new approach in the development of heat transfer empirical correlations for intermittent spray impingement, based on

parameters for newer anticoagulant medications; • Recent changes in guidelines for the management.. of high