您的位置:首页 > 编程语言 > Java开发

Get Started Developing For Android With Eclipse, Reloaded

2011-05-16 22:48 477 查看
Inthefirstpart
ofthistutorialseries,webuiltasimplebrewtimerapplicationusing
AndroidandEclipse.Inthissecondpart,we’llcontinuedevelopingthe
applicationbyaddingextrafunctionality.Indoingthis,you’llbe
introducedtosomeimportantandpowerfulfeaturesoftheAndroidSDK,
includingPersistentdatastorage,ActivitiesandIntentaswellas
Shareduserpreferences.

Tofollowthistutorial,you’llneedthe
codefromthepreviousarticle.Ifyouwanttogetstartedrightaway,
grabthecodefromGitHub
andcheckoutthetutorial_part_1
tagusingthis:



1

$gitclonegit:

/

/

github.com

/

cblunt

/

BrewClock.git

2

$cdBrewClock

3

$gitcheckouttutorial_part_1

Onceyou’vecheckedoutthecodeonGitHub,you’llneedtoimporttheprojectintoEclipse:

LaunchEclipseandchooseFile→Import…

IntheImportwindow,select“ExistingProjectsintoWorkspace”andclick“Next.”

Onthenextscreen,click“Browse,”andselecttheprojectfolderthatyouclonedfromGitHub.

Click“Finish”toimportyourprojectintoEclipse.

AfterimportingtheprojectintoEclipse,youmightreceiveawarningmessage:

1

Androidrequired.

class

compatibility

set

to

5.0

.

2

Pleasefixprojectproperties.

If
thisisthecase,right-clickonthenewlyimported“BrewClock”project
inthe“ProjectExplorer,”choose“FixProjectProperties,”andthen
restartEclipse.

GettingStartedWithDataStorage

Currently,
BrewClockletsuserssetaspecifictimeforbrewingtheirfavorite
cupsoftea.Thisisgreat,butwhatiftheyregularlydrinkavariety
ofteas,eachwiththeirowndifferentbrewingtimes?Atthemoment,
usershavetorememberbrewingtimesforalltheirfavoriteteas!This
doesn’tmakeforagreatuserexperience.So,inthistutorialwe’ll
developfunctionalitytoletusersstorebrewingtimesfortheir
favoriteteasandthenchoosefromthatlistofteaswhentheymakea
brew.

Todothis,we’lltakeadvantageofAndroid’srich
data-storageAPI.Androidoffersseveralwaystostoredata,twoof
whichwe’llcoverinthisarticle.Thefirst,morepowerfuloption,uses
theSQLitedatabaseenginetostoredataforourapplication.

SQLite
isapopularandlightweightSQLdatabaseenginethatsavesdataina
singlefile.Itisoftenusedindesktopandembeddedapplications,
whererunningaclient-serverSQLengine(suchasMySQLorPostgreSQL)
isn’tfeasible.

EveryapplicationinstalledonanAndroiddevice
cansaveanduseanynumberofSQLitedatabasefiles(subjecttostorage
capacity),whichthesystemwillmanageautomatically.Anapplication’s
databasesareprivateandsocannotbeaccessedbyanyother
applications.(Datacanbesharedthroughthe
ContentProvider

class,butwewon’tcovercontentprovidersinthistutorial.)Database
filespersistwhentheapplicationisupgradedandaredeletedwhenthe
applicationisuninstalled.

We’lluseasimpleSQLitedatabasein
BrewClocktomaintainalistofteasandtheirappropriatebrewing
times.Here’sanoverviewofhowourdatabaseschemawilllook:

1

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

2

|Table:teas|

3

+

-

-

-

-

-

-

-

-

-

-

-

-

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

4

|Column|Description|

5

+

-

-

-

-

-

-

-

-

-

-

-

-

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

6

|_ID|integer,autoincrement|

7

|name|text,

not

null|

8

|brew_time|integer,

not

null|

9

+

-

-

-

-

-

-

-

-

-

-

-

-

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

Ifyou’veworkedwithSQLbefore,thisshouldlookfairlyfamiliar.Thedatabasetablehasthreecolumns:auniqueidentifier(
_ID

),
nameandbrewingtime.We’llusetheAPIsprovidedbyAndroidtocreate
thedatabasetableinourcode.Thesystemwilltakecareofcreating
thedatabasefileintherightlocationforourapplication.

AbstractingtheDatabase

To
ensurethedatabasecodeiseasytomaintain,we’llabstractallthe
codeforhandlingdatabasecreation,insertsandqueriesintoaseparate
class,
TeaData

.Thisshouldbefairlyfamiliarifyou’re
usedtothemodel-view-controllerapproach.Allthedatabasecodeis
keptinaseparateclassfromour
BrewClockActivity

.TheActivitycanthenjustinstantiateanew
TeaData

instance(whichwillconnecttothedatabase)anddowhatitneedsto
do.Workinginthiswayenablesustoeasilychangethedatabaseinone
placewithouthavingtochangeanythinginanyotherpartsofour
applicationthatdealwiththedatabase.

Createanewclasscalled
TeaData

intheBrewClockprojectbygoingtoFile→New→Class.Ensurethat
TeaData

extendsthe
android.database.sqlite.SQLiteOpenHelper

classandthatyouchecktheboxfor“Constructorsfromsuperclass.”



The
TeaData

classwillautomaticallyhandlethecreationandversioningofaSQLite
databaseforyourapplication.We’llalsoaddmethodstogiveother
partsofourcodeaninterfacetothedatabase.

Addtwoconstantsto
TeaData

tostorethenameandversionofthedatabase,thetable’snameandthe
namesofcolumnsinthattable.We’llusetheAndroid-providedconstant
BaseColumns._ID

forthetable’suniqueidcolumn:

01

//src/com/example/brewclock/TeaData.java

02

importandroid.app.Activity;

03

importandroid.content.ContentValues;

04

importandroid.content.Context;

05

importandroid.database.Cursor;

06

importandroid.database.DatabaseUtils;

07

importandroid.provider.BaseColumns;

08

09

publicclassTeaDataextendsSQLiteOpenHelper{

10


privatestaticfinalStringDATABASE_NAME=

"teas.db"

;

11


privatestaticfinalintDATABASE_VERSION=1;

12

13


publicstaticfinalStringTABLE_NAME=

"teas"

;

14

15


publicstaticfinalString_ID=BaseColumns._ID;

16


publicstaticfinalStringNAME=

"name"

;

17


publicstaticfinalStringBREW_TIME=

"brew_time"

;

18

19


//…

20

}

Addaconstructorto
TeaData

thatcallsitsparentmethod,supplyingourdatabasenameandversion.
Androidwillautomaticallyhandleopeningthedatabase(andcreatingit
ifitdoesnotexist).

1

//src/com/example/brewclock/TeaData.java

2

publicTeaData(Contextcontext){

3


super

(context,DATABASE_NAME,

null

,DATABASE_VERSION);

4

}

We’llneedtooverridethe
onCreate

methodtoexecuteastringofSQLcommandsthatcreatethedatabase
tableforourtea.Androidwillhandlethismethodforus,calling
onCreate

whenthedatabasefileisfirstcreated.

Onsubsequentlaunches,Androidcheckstheversionofthedatabaseagainstthe
DATABASE_VERSION

numberwesuppliedtotheconstructor.Iftheversionhaschanged,Androidwillcallthe
onUpgrade

method,whichiswhereyouwouldwriteanycodetomodifythedatabase
structure.Inthistutorial,we’lljustaskAndroidtodropandrecreate
thedatabase.

So,addthefollowingcodeto
onCreate

:

01

//src/com/example/brewclock/TeaData.java

02

@Override

03

publicvoidonCreate(SQLiteDatabasedb){

04


//CREATETABLEteas(idINTEGERPRIMARYKEYAUTOINCREMENT,nameTEXTNOTNULL,brew_timeINTEGER);

05


Stringsql=

06


"CREATETABLE"

+TABLE_NAME+

"("

07


+_ID+

"INTEGERPRIMARYKEYAUTOINCREMENT,"

08


+NAME+

"TEXTNOTNULL,"

09


+BREW_TIME+

"INTEGER"

10


+

");"

;

11

12


db.execSQL(sql);

13

}

14

15

@Override

16

publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){

17


db.execSQL(

"DROPTABLEIFEXISTS"

+TABLE_NAME);

18


onCreate(db);

19

}

Next,we’lladdanewmethodto
TeaData

thatletsuseasilyaddnewtearecordstothedatabase.We’llsupply
themethodwithanameandbrewingtimefortheteatobeadded.Rather
thanforcingustowriteouttherawSQLtodothis,Androidsuppliesa
setofclassesforinsertingrecordsintothedatabase.First,wecreate
asetof
ContentValues

,pushingtherelevantvaluesintothatset.

Withaninstanceof
ContentValues

,
wesimplysupplythecolumnnameandthevaluetoinsert.Androidtakes
careofcreatingandrunningtheappropriateSQL.UsingAndroid’s
databaseclassesensuresthatthewritesaresafe,andifthedata
storagemechanismchangesinafutureAndroidrelease,ourcodewill
stillwork.

Addanewmethod,
insert()

,tothe
TeaData

class:

01

//src/com/example/brewclock/TeaData.java

02

publicvoidinsert(Stringname,intbrewTime){

03


SQLiteDatabasedb=getWritableDatabase();

04

05


ContentValuesvalues=

new

ContentValues();

06


values.put(NAME,name);

07


values.put(BREW_TIME,brewTime);

08

09


db.insertOrThrow(TABLE_NAME,

null

,values);

10

}

RetrievingData

Withtheabilitytosavedataintothedatabase,we’llalsoneedawaytogetitbackout.Androidprovidesthe
Cursor

interfacefordoingjustthis.A
Cursor

representstheresultsofrunningaSQLqueryagainstthedatabase,and
itmaintainsapointertoonerowwithinthatresultset.Thispointer
canbemovedforwardsandbackwardsthroughtheresults,returningthe
valuesfromeachcolumn.Itcanhelptovisualizethis:

1

SQLQuery:SELECT

*

from

teasLIMIT

3

;

2

3

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

4

|_ID|name|brew_time|

5

+

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

-

+

6

|

1

|EarlGrey|

3

|

7

|

2

|Green|

1

|<

=

Cursor

8

|

3

|Assam|

5

|

9

+

-

-

-

-

-

-

-

+

-

-

-

-

-

-

-

-

-

-

-

-

-

+

-

-

-

-

-

-

-

-

-

-

-

-

-

+

In
thisexample,theCursorispointingatthesecondrowintheresult
set(Greentea).WecouldmovetheCursorbackarowtorepresentthe
firstrow(EarlGrey)bycalling
cursor.moveToPrevious()

,ormoveforwardtotheAssamrowwith
moveToNext()

.TofetchthenameoftheteathattheCursorispointingout,wewouldcall
cursor.getString(1)

,where
1

isthecolumnindexofthecolumnwewishtoretrieve(notethatthe
indexiszero-based,socolumn0isthefirstcolumn,1thesecond
columnandsoon).

NowthatyouknowaboutCursors,addamethodthatcreatesa
Cursor

objectthatreturnsalltheteasinourdatabase.Addan
all

methodto
TeaData

:

01

//src/com/example/brewclock/TeaData.java

02

publicCursorall(Activityactivity){

03


String[]from={_ID,NAME,BREW_TIME};

04


Stringorder=NAME;

05

06


SQLiteDatabasedb=getReadableDatabase();

07


Cursorcursor=db.query(TABLE_NAME,from,

null

,

null

,

null

,

null

,order);

08


activity.startManagingCursor(cursor);

09

10


return

cursor;

11

}

Let’s
gooverthismethodindetail,becauseitlooksalittlestrangeat
first.Again,ratherthanwritingrawSQLtoquerythedatabase,wemake
useofAndroid’sdatabaseinterfacemethods.

First,weneedto
tellAndroidwhichcolumnsfromourdatabasewe’reinterestedin.Todo
this,wecreateanarrayofstrings—eachoneofthecolumnidentifiers
thatwedefinedatthetopof
TeaData

.We’llalsosetthecolumnthatwewanttoordertheresultsbyandstoreitinthe
order

string.

Next,wecreatearead-onlyconnectiontothedatabaseusing
getReadableDatabase()

,andwiththatconnection,wetellAndroidtorunaqueryusingthe
query()

method.The
query()

methodtakesasetofparametersthatAndroidinternallyconvertsintoa
SQLquery.Again,Android’sabstractionlayerensuresthatour
applicationcodewilllikelycontinuetowork,eveniftheunderlying
datastoragechangesinafutureversionofAndroid.

Becausewejustwanttoreturneveryteainthedatabase,wedon’tapplyanyjoins,filtersorgroups(i.e.
WHERE

,
JOIN

,and
GROUPBY

clausesinSQL)tothemethod.The
from

and
order

variablestellthequerywhatcolumnstoreturnonthedatabaseandtheorderinwhichtheyareretrieved.Weusethe
SQLiteDatabase.query()

methodasaninterfacetothedatabase.

Last,weaskthesuppliedActivity(inthiscase,our
BrewClockActivity

)
tomanagetheCursor.Usually,aCursormustbemanuallyrefreshedto
reloadanynewdata,soifweaddedanewteatoourdatabase,wewould
havetoremembertorefreshourCursor.Instead,Androidcantakecare
ofthisforus,recreatingtheresultswhenevertheActivityis
suspendedandresumed,bycalling
startManagingCursor()

.

Finally,
we’lladdanotherutilitymethodtoreturnthenumberofrecordsinthe
table.Onceagain,Androidprovidesahandyutilitytodothisforus
inthe
DatabaseUtils

class:

Addthefollowingmethod,
count

,toyour
TeaData

class:

1

//src/com/example/brewclock/TeaData.java

2


publiclongcount(){

3


SQLiteDatabasedb=getReadableDatabase();

4


return

DatabaseUtils.queryNumEntries(db,TABLE_NAME);

5


}

Savethe
TeaData

class,andfixanymissingimportsusingEclipse(Source→Organize
Imports).Withourdataclassfinished,it’stimetochangeBrewClock’s
interfacetomakeuseofthedatabase!

ModifyBrewClock’sInterfacetoAllowTeaSelection

The
purposeofstoringpresetteasandbrewtimesistolettheuser
quicklyselecttheirfavoriteteafromthepresets.Tofacilitatethis,
we’lladda
Spinner

(analogoustoapop-upmenuindesktopinterfaces)tothemainBrewClockinterface,populatedwiththelistofteasfrom
TeaData

.

As
intheprevioustutorial,useEclipse’slayouteditortoaddthe
SpinnertoBrewClock’smaininterfacelayoutXMLfile.Addthefollowing
codejustbelowthe
LinearLayout

forthebrewcountlabel
(aroundline24).Remember,youcanswitchtothe“CodeView”tabalong
thebottomofthewindowifEclipseopensthevisuallayouteditor.

01

<!--/res/layout/main.xml-->

02

03

<!--TeaSelection-->

04

<

LinearLayout

05


android:orientation

=

"vertical"

06


android:layout_width

=

"fill_parent"

07


android:layout_height

=

"wrap_content"

>

08

09


<

Spinner

10


android:id

=

"@+id/tea_spinner"

11


android:layout_width

=

"fill_parent"

12


android:layout_height

=

"wrap_content"

/>

13

14

</

LinearLayout

>

Inthe
BrewClockActivity

class,addamembervariabletoreferencetheSpinner,andconnectittotheinterfaceusing
findViewById

:

01

//src/com/example/brewclock/BrewClockActivity.java

02

protectedSpinnerteaSpinner;

03

protectedTeaDatateaData;

04

05

//…

06

07

publicvoidonCreate(BundlesavedInstanceState){

08


//…

09


teaData=

new

TeaData(

this

);

10


teaSpinner=(Spinner)findViewById(R.id.tea_spinner);

11

}

Try
runningyourapplicationtomakesurethenewinterfaceworks
correctly.Youshouldseeablankpop-upmenu(orSpinner)justbelow
thebrewcount.Ifyoutapthespinner,Androidhandlesdisplayinga
pop-upmenusothatyoucanchooseanoptionforthespinner.Atthe
moment,themenuisempty,sowe’llremedythatbybindingtheSpinner
toourteadatabase.



DataBinding

WhenAndroidretrievesdatafromadatabase,itreturnsa
Cursor

object.TheCursorrepresentsasetofresultsfromthedatabaseand
canbemovedthroughtheresultstoretrievevalues.Wecaneasilybind
theseresultstoaview(inthiscase,theSpinner)usingasetof
classesprovidedbyAndroidcalled“Adapters.”Adaptersdoallthehard
workoffetchingdatabaseresultsfroma
Cursor

anddisplayingthemintheinterface.

Rememberthatour
TeaData.all()

methodalreadyreturnsaCursorpopulatedwiththecontentsofourteas
table.UsingthatCursor,allweneedtodoiscreatea
SimpleCursorAdapter

tobinditsdatatoour
teaSpinner

,andAndroidwilltakecareofpopulatingthespinner’soptions.

ConnecttheCursorreturnedby
teaData.all()

totheSpinnerbycreatinga
SimpleCursorAdapter

:

01

//com/example/brewclock/BrewClockActivity.java

02

03

publicvoidonCreate(BundlesavedInstanceState){

04


//…

05


Cursorcursor=teaData.all(

this

);

06

07


SimpleCursorAdapterteaCursorAdapter=

new

SimpleCursorAdapter(

08


this

,

09


android.R.layout.simple_spinner_item,

10


cursor,

11


new

String[]{TeaData.NAME},

12


new

int[]{android.R.id.text1}

13


);

14

15


teaSpinner.setAdapter(teaCursorAdapter);

16


teaCursorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

17

}

Noticethatwe’vemadeuseofAndroid’sbuilt-in
android.R

object.Thisprovidessomegenericdefaultresourcesforyour
application,suchassimpleviewsandlayouts.Inthiscase,we’veused
android.R.layout.simple_spinner_item

,whichisasimpletextlabellayout.

If
youruntheapplicationagain,you’llseethatthespinnerisstill
empty!Eventhoughwe’veconnectedthespinnertoourdatabase,there
arenorecordsinthedatabasetodisplay.

Let’sgivetheusera
choiceofteasbyaddingsomedefaultrecordstothedatabasein
BrewClock’sconstructor.Toavoidduplicateentries,we’lladdonlythe
defaultteasifthedatabaseisempty.WecanmakeuseofTeaData’s
count()

methodtocheckifthisisthecase.

Addcodetocreateadefaultsetofteasifthedatabaseisempty.Addthislinejustabovethecodetofetchtheteasfrom
teaData

:

01

//com/example/brewclock/BrewClockActivity.java

02

publicvoidonCreate(BundlesavedInstanceState){

03


//…

04

05


//Addsomedefaultteadata!(Adjusttoyourpreference:)

06


if

(teaData.count()==0){

07


teaData.insert(

"EarlGrey"

,3);

08


teaData.insert(

"Assam"

,3);

09


teaData.insert(

"JasmineGreen"

,1);

10


teaData.insert(

"Darjeeling"

,2);

11


}

12

13


//Codefromthepreviousstep:

14


Cursorcursor=teaData.all(

this

);

15

16


//…

17

}

Now
runtheapplicationagain.You’llnowseethatyourteaSpinnerhasthe
firstteaselected.TappingontheSpinnerletsyouselectoneofthe
teasfromyourdatabase!



Congratulations!
You’vesuccessfullyconnectedyourinterfacetoadatasource.Thisis
oneofthemostimportantaspectsofanysoftwareapplication.Asyou’ve
seen,Androidmakesthistaskfairlyeasy,butitisextremely
powerful.Usingcursorsandadapters,youcantakevirtuallyanydata
source(fromasimplearrayofstringstoacomplexrelationaldatabase
query)andbindittoanytypeofview:aspinner,alistvieworeven
aniTunes-likecover-flowgallery!

Althoughnowwouldbeagood
timeforabrew,ourworkisn’toveryet.Whileyoucanchoosedifferent
teasfromtheSpinner,makingaselectiondoesn’tdoanything.Weneed
tofindoutwhichteatheuserhasselectedandupdatethebrewtime
accordingly.

ReadSelectedTea,andUpdateBrewTime

Todeterminewhichteatheuserhasselectedfromourdatabase,
BrewClockActivity

needstolistenforanevent.Similartothe
OnClickListener

eventthatistriggeredbybuttonpresses,we’llimplementthe
OnItemSelectedListener

.Eventsinthislisteneraretriggeredwhentheusermakesaselectionfromaview,suchasourSpinner.

Enablethe
onItemSelectedListener

in
BrewClockActivity

byaddingittotheclassdeclaration.Remembertoimplementtheinterfacemethods
onItemSelected()

and
onNothingSelected()

:

01

//src/com/example/brewclock/BrewClockActivity.java

02

publicclassBrewClockActivityextendsActivityimplementsOnClickListener,OnItemSelectedListener{

03


//…

04


publicvoidonItemSelected(AdapterView<?>spinner,Viewview,intposition,longid){

05


if

(spinner==teaSpinner){

06


//Updatethebrewtimewiththeselectedtea’sbrewtime

07


Cursorcursor=(Cursor)spinner.getSelectedItem();

08


setBrewTime(cursor.getInt(2));

09


}

10


}

11

12


publicvoidonNothingSelected(AdapterView<?>adapterView){

13


//Donothing

14


}

15

}

Herewecheckwhetherthespinnerthattriggeredthe
onItemSelected

eventwasBrewClock’s
teaSpinner

.Ifso,weretrieveaCursorobjectthatrepresentstheselectedrecord.Thisisallhandledforusbythe
SimpleCursorAdapter

thatconnects
teaData

totheSpinner.AndroidknowswhichquerypopulatestheSpinnerand
whichitemtheuserhasselected.Itusesthesetoreturnthesinglerow
fromthedatabase,representingtheuser’sselectedtea.

Cursor’s
getInt()

methodtakestheindexofthecolumnwewanttoretrieve.RememberthatwhenwebuiltourCursorin
teaData.all()

,thecolumnswereadwere
_ID

,
NAME

and
BREW_TIME

.AssumingwechoseJasmineteain
teaSpinner

,theCursorreturnedbyourselectionwouldbepointingatthatrecordinthedatabase.

WethenasktheCursortoretrievethevaluefromcolumn2(using
getInt(2)

),whichinthisqueryisour
BREW_TIME

column.Thisvalueissuppliedtoourexisting
setBrewTime()

method,whichupdatestheinterfacetoshowtheselectedtea’sbrewingtime.

Finally,weneedtotellthe
teaSpinner

that
BrewClockActivity

islisteningfor
OnItemSelected

events.Addthefollowinglineto
BrewClockActivity

’s
onCreate

method:

1

//src/com/example/brewclock/BrewClockActivity.java

2

publicvoidonCreate(){

3


//…

4


teaSpinner.setOnItemSelectedListener(

this

);

5

}

That
shoulddoit!Runyourapplicationagain,andtryselectingdifferent
teasfromtheSpinner.Eachtimeyouselectatea,itsbrewtimewillbe
shownonthecountdownclock.Therestofourcodealreadyhandles
countingdownfromthecurrentbrewtime,sowenowhaveafullyworking
brewtimer,withalistofpresetteas.

Youcan,ofcourse,go
backintothecodeandaddmorepresetteastothedatabasetosuityour
tastes.ButwhatifwereleasedBrewClocktothemarket?Everytime
someonewantedtoaddanewteatothedatabase,we’dneedtomanually
updatethedatabase,andrepublishtheapplication;everyonewouldneed
toupdate,andeverybodywouldhavethesamelistofteas.Thatsounds
prettyinflexible,andalotofworkforus!



Itwouldbemuchbetteriftheuserhadsomewaytoaddtheirownteasandpreferencestothedatabase.We’lltacklethatnext…

IntroducingActivities

Eachscreeninyourapplicationanditsassociatedcodeisan
Activity

.
Everytimeyougofromonescreentoanother,Androidcreatesanew
Activity.Inreality,althoughanapplicationmaycompriseanynumberof
screens/activities,Androidtreatsthemasseparateentities.
ActivitiesworktogethertoformacohesiveexperiencebecauseAndroid
letsyoueasilypassdatabetweenthem.

Inthisfinalsection,you’lladdanewActivity(
AddTeaActivity

)toyourapplicationandregisteritwiththeAndroidsystem.You’llthenpassdatafromtheoriginal
BrewClockActivity

tothisnewActivity.

First,though,weneedtogivetheuserawaytoswitchtothenewActivity.We’lldothisusinganoptionsmenu.

OptionsMenus

Options
menusarethepop-upmenusthatappearwhentheuserhitsthe“Menu”
keyontheirdevice.Androidhandlesthecreationanddisplayofoptions
menusautomatically;youjustneedtotellitwhatoptionstodisplay
andwhattodowhenanoptionischosenbytheuser.

However,
ratherthanhard-codingourlabelsintothemenuitself,we’llmakeuse
ofAndroidstringresources.Stringresourcesletyoumaintainallthe
human-readablestringsandlabelsforyourapplicationinonefile,
callingthemwithinyourcode.Thismeansthere’sonlyoneplaceinyour
codewhereyouneedtochangestringsinthefuture.

Intheprojectexplorer,navigateto“res/values”andyouwillseethatastrings.xml
filealreadyexists.ThiswascreatedbyEclipsewhenwefirstcreated
theproject,anditisusedtostoreanystringsoftextthatwewantto
usethroughouttheapplication.

Openstrings.xml
bydoubleclickingonit,andswitchtotheXMLviewbyclickingthestrings.xml
tabalongthebottomofthewindow.

Addthefollowinglinewithinthe
<resources>…</resources>

element:

1

<!--res/values/strings.xml-->

2


<

resources

>

3


<!--…-->

4


<

string

name

=

"add_tea_label"

>AddTea</

string

>

5


</

resources

>

Hereyou’vedefinedastring,
add_tea_label

,anditsassociatedtext.Wecanuse
add_tea_label

toreferencethestringthroughouttheapplication’scode.Ifthelabel
needstochangeforsomereasoninthefuture,you’llneedtochangeit
onlyonceinthisfile.

Next,let’screateanewfiletodefine
ouroptionsmenu.Justlikestringsandlayouts,menusaredefinedinan
XMLfile,sowe’llstartbycreatinganewXMLfileinEclipse:

CreateanewAndroidXMLfileinEclipsebychoosingFile→New→Other,andthenselect“AndroidXMLFile.”

Selectaresourcetypeof“Menu,”andsavethefileasmain.xml
.Eclipsewillautomaticallycreateafolder,res/menu
,whereyourmenuXMLfileswillbestored.



Opentheres/menus/main.xml
file,andswitchtoXMLviewbyclickingthe“main.xml”tabalongthebottomofthewindow.

Addanewmenuitem,
add_tea

.

1

<?

xml

version

=

"1.0"

encoding

=

"utf-8"

?>

2

<

menu

xmlns:android

=

"http://schemas.android.com/apk/res/android
"

>

3


<

item

android:id

=

"@+id/add_tea"

android:title

=

"@string/add_tea_label"

/>

4

</

menu

>

Noticethe
android:title

attributeissetto
@string/add_tea_label

.ThistellsAndroidtolookup
add_tea_label

inourstrings.xml
fileandreturntheassociatedlabel.Inthiscase,ourmenuitemwillhavealabel“AddTea.”

Next,we’lltellourActivitytodisplaytheoptionsmenuwhentheuserhitsthe“Menu”keyontheirdevice.

BackinBrewClockActivity.java
,overridethe
onCreateOptionsMenu

methodtotellAndroidtoloadourmenuwhentheuserpressesthe“Menu”button:

1

//src/com/example/brewclock/BrewClockActivity.java

2

@Override

3

publicbooleanonCreateOptionsMenu(Menumenu){

4


MenuInflaterinflater=getMenuInflater();

5


inflater.inflate(R.menu.main,menu);

6

7


return

true

;

8

}

Whentheuserpressesthe“Menu”buttonontheirdevice,Androidwillnowcall
onCreateOptionsMenu

.Inthismethod,wecreatea
MenuInflater

,
whichloadsamenuresourcefromyourapplication’spackage.Justlike
thebuttonsandtextfieldsthatmakeupyourapplication’slayout,themain.xml
resourceisavailableviatheglobal
R

object,soweusethattosupplythe
MenuInflater

withourmenuresource.

To
testthemenu,saveandruntheapplicationintheAndroidemulator.
Whileit’srunning,pressthe“Menu”button,andyou’llseetheoptions
menupopupwithan“AddTea”option.



If
youtapthe“AddTea”option,Androidautomaticallydetectsthetapand
closesthemenu.Inthebackground,Androidwillnotifytheapplication
thattheoptionwastapped.

HandlingMenuTaps

Whenthe
usertapsthe“AddTea”menuoption,wewanttodisplayanewActivity
sothattheycanenterthedetailsoftheteatobeadded.Startby
creatingthatnewActivitybyselectingFile→New→Class.



Namethenewclass
AddTeaActivity

,andmakesureitinheritsfromthe
android.app.Activity

class.Itshouldalsobeinthe
com.example.brewclock

package:

01

//src/com/example/brewclock/AddTeaActivity.java

02

packagecom.example.brewclock;

03

04

importandroid.app.Activity;

05

importandroid.os.Bundle;

06

07

publicclassAddTeaActivityextendsActivity{

08


@Override

09


publicvoidonCreate(BundlesavedInstanceState){

10


super

.onCreate(savedInstanceState);

11


}

12

}

Thissimple,blankActivitywon’tdoanythingyet,butitgivesusenoughtofinishouroptionsmenu.

Addthe
onOptionsItemSelected

overridemethodto
BrewClockActivity

.ThisisthemethodthatAndroidcallswhenyoutapona
MenuItem

(noticeitreceivesthetapped
MenuItem

intheitemparameter):

01

//src/com/example/brewclock/BrewClockActivity.java

02

@Override

03

publicbooleanonOptionsItemSelected(MenuItemitem){

04


switch

(item.getItemId()){

05


case

R.id.add_tea:

06


Intentintent=

new

Intent(

this

,AddTeaActivity.class);

07


startActivity(intent);

08


return

true

;

09

10


default

:

11


return

super

.onOptionsItemSelected(item);

12


}

13

}

Withthiscode,we’vetoldAndroidthatwhenthe“AddTea”menuitemistapped,wewanttostartanewActivity;inthiscase,
AddTeaActivity

.However,ratherthandirectlycreatinganinstanceof
AddTeaActivity

,noticethatwe’veusedan
Intent

.
IntentsareapowerfulfeatureoftheAndroidframework:theybind
Activitiestogethertomakeupanapplicationandallowdatatobe
passedbetweenthem.

Intentsevenletyourapplicationtake
advantageofanyActivitieswithinotherapplicationsthattheuserhas
installed.Forexample,whentheuseraskstodisplayapicturefroma
gallery,Androidautomaticallydisplaysadialoguetotheuserallowing
themtopicktheapplicationthatdisplaystheimage.Anyapplications
thatareregisteredtohandleimagedisplaywillbeshowninthe
dialogue.

Intentsareapowerfulandcomplextopic,soit’sworthreadingaboutthemindetailintheofficialAndroidSDKdocumentation
.

Let’stryrunningourapplicationtotestoutthenew“AddTea”screen.

Runyourproject,tapthe“Menu”buttonandthentap“AddTea.”

Instead
ofseeingyour“AddTea”Activityasexpected,you’llbepresentedwith
adialoguethatisalltoocommonforAndroiddevelopers:



Althoughwecreatedthe
Intent

andtoldittostartour
AddTeaActivity

Activity,theapplicationcrashedbecausewehaven’tyetregisteredit
withinAndroid.Thesystemdoesn’tknowwheretofindtheActivitywe’re
tryingtorun(rememberthatIntentscanstartActivitiesfromany
applicationinstalledonthedevice).Let’sremedythisbyregistering
ourActivitywithintheapplicationmanifestfile.

Openyourapplication’smanifestfile,AndroidManifest.xml
inEclipse,andswitchtothecodeviewbyselectingthe“AndroidManifest.xml”tabalongthebottomofthewindow.

The
application’smanifestfileiswhereyoudefineglobalsettingsand
informationaboutyourapplication.You’llseethatitalreadydeclares
.BrewClockActivity

astheActivitytorunwhentheapplicationislaunched.

Within
<application>

,addanew
<activity>

nodetodescribethe“AddTea”Activity.Usethesame
add_tea_label

stringthatwedeclaredearlierinstrings.xml
fortheActivity’stitle:

1

<!--AndroidManifest.xml-->

2

<

application

…>

3



4


<

activity

android:name

=

".AddTeaActivity"

android:label

=

"@string/add_tea_label"

/>

5

</

application

>

Save
themanifestfilebeforerunningBrewClockagain.Thistime,whenyou
openthemenuandtap“AddTea,”Androidwillstartthe
AddTeaActivity

.Hitthe“Back”buttontogobacktothemainscreen.

WiththeActivitieshookedtogether,it’stimetobuildaninterfaceforaddingtea!

BuildingTheTeaEditorInterface

Building
theinterfacetoaddateaisverysimilartohowwebuiltthemain
BrewClockinterfaceintheprevioustutorial.Startbycreatinganew
layoutfile,andthenaddtheappropriateXML,asbelow.

Alternatively,
youcoulduseAndroid’srecentlyimprovedlayouteditorinEclipseto
buildasuitableinterface.CreateanewXMLfileinwhichtodefinethe
layout.GotoFile→New,thenselect“AndroidXMLFile,”andselecta
“Layout”type.Namethefileadd_tea.xml
.



Replacethecontentsofadd_tea.xml
withthefollowinglayout:

01

<!--res/layouts/add_tea.xml-->

02

<?

xml

version

=

"1.0"

encoding

=

"utf-8"

?>

03

<

LinearLayout

04


xmlns:android

=

"http://schemas.android.com/apk/res/android
"

05


android:layout_width

=

"fill_parent"

06


android:layout_height

=

"fill_parent"

07


android:orientation

=

"vertical"

08


android:padding

=

"10dip"

>

09

10


<

TextView

11


android:text

=

"@string/tea_name_label"

12


android:layout_width

=

"fill_parent"

13


android:layout_height

=

"wrap_content"

/>

14

15


<

EditText

16


android:id

=

"@+id/tea_name"

17


android:layout_width

=

"fill_parent"

18


android:layout_height

=

"wrap_content"

/>

19

20


<

TextView

21


android:text

=

"@string/brew_time_label"

22


android:layout_width

=

"wrap_content"

23


android:layout_height

=

"wrap_content"

/>

24

25


<

SeekBar

26


android:id

=

"@+id/brew_time_seekbar"

27


android:layout_width

=

"fill_parent"

28


android:layout_height

=

"wrap_content"

29


android:progress

=

"2"

30


android:max

=

"9"

/>

31

32


<

TextView

33


android:id

=

"@+id/brew_time_value"

34


android:text

=

"3m"

35


android:textSize

=

"20dip"

36


android:layout_width

=

"fill_parent"

37


android:layout_height

=

"wrap_content"

38


android:gravity

=

"center_horizontal"

/>

39

</

LinearLayout

>

We’llalsoneedtoaddsomenewstringstostrings.xml
forthelabelsusedinthisinterface:

1

<!--res/values/strings.xml-->

2

<

resources

>

3


<!--…-->

4


<

string

name

=

"tea_name_label"

>TeaName</

string

>

5

6


<

string

name

=

"brew_time_label"

>BrewTime</

string

>

7

</

resources

>

In
thislayout,we’veaddedanewtypeofinterfacewidget,theSeekBar.
Thisletstheusereasilyspecifyabrewtimebydraggingathumbfrom
lefttoright.TherangeofvaluesthattheSeekBarproducesalwaysruns
fromzero(0)tothevalueof
android:max

.

Inthis
interface,we’veusedascaleof0to9,whichwewillmaptobrewtimes
of1to10minutes(brewingfor0minuteswouldbeawasteofgood
tea!).First,though,weneedtomakesurethat
AddTeaActivity

loadsournewinterface:

AddthefollowinglineofcodetotheActivity’s
onCreate()

methodthatloadsanddisplaysthe
add_tea

layoutfile:

1

//src/com/example/brewclock/AddTeaActivity.java

2

publicvoidonCreate(BundlesavedInstanceState){

3


super

.onCreate(savedInstanceState);

4


setContentView(R.layout.add_tea);

5

}

Nowtestyourapplicationbyrunningit,pressingthe“Menu”buttonandtapping“AddTea”fromthemenu.



You’ll
seeyournewinterfaceonthe“AddTea”screen.Youcanentertextand
slidetheSeekBarleftandright.Butasyou’dexpect,nothingworksyet
becausewehaven’thookedupanycode.

Declaresomepropertiesin
AddTeaActivity

toreferenceourinterfaceelements:

01

//src/com/example/brewclock/AddTeaActivity.java

02

publicclassAddTeaActivity{

03


//…

04

05


/**Properties**/

06


protectedEditTextteaName;

07


protectedSeekBarbrewTimeSeekBar;

08


protectedTextViewbrewTimeLabel;

09

10


//…

Next,connectthosepropertiestoyourinterface:

1

publicvoidonCreate(BundlesavedInstanceState){

2


//…

3


//Connectinterfaceelementstoproperties

4


teaName=(EditText)findViewById(R.id.tea_name);

5


brewTimeSeekBar=(SeekBar)findViewById(R.id.brew_time_seekbar);

6


brewTimeLabel=(TextView)findViewById(R.id.brew_time_value);

7

}

The
interfaceisfairlysimple,andtheonlyeventsweneedtolistenfor
arechangestotheSeekBar.WhentheusermovestheSeekBarthumbleft
orright,ourapplicationwillneedtoreadthenewvalueandupdatethe
labelbelowwiththeselectedbrewtime.We’llusea
Listener

todetectwhentheSeekBarischanged:

Addan
onSeekBarChangedListener

interfacetothe
AddTeaActivity

classdeclaration,andaddtherequiredmethods:

01

//src/com/example/brewclock/AddTeaActivity.java

02

publicclassAddTeaActivity

03

extendsActivity

04

implementsOnSeekBarChangeListener{

05


//…

06

07


publicvoidonProgressChanged(SeekBarseekBar,intprogress,booleanfromUser){

08


//TODODetectchangeinprogress

09


}

10

11


publicvoidonStartTrackingTouch(SeekBarseekBar){}

12

13


publicvoidonStopTrackingTouch(SeekBarseekBar){}

14

}

Theonlyeventwe’reinterestedinis
onProgressChanged

,
soweneedtoaddthecodebelowtothatmethodtoupdatethebrewtime
labelwiththeselectedvalue.RememberthatourSeekBarvaluesrange
from0to9,sowe’lladd1tothesuppliedvaluesothatitmakesmore
sensetotheuser:

Addthefollowingcodeto
onProgressChanged()

inAddTeaActivity.java
:

1

//src/com/example/brewclock/AddTeaActivity.java

2

publicvoidonProgressChanged(SeekBarseekBar,intprogress,booleanfromUser){

3


if

(seekBar==brewTimeSeekBar){

4


//Updatethebrewtimelabelwiththechosenvalue.

5


brewTimeLabel.setText((progress+1)+

"m"

);

6


}

7

}

SettheSeekBar’slistenertobeour
AddTeaActivity

in
onCreate

:

1

//src/com/example/brewclock/AddTeaActivity.java

2

publicvoidonCreate(BundlesavedInstanceState){

3


//…

4

5


//SetupListeners

6


brewTimeSeekBar.setOnSeekBarChangeListener(

this

);

7

}

NowwhenruntheapplicationandslidetheSeekBarlefttoright,thebrewtimelabelwillbeupdatedwiththecorrectvalue:



SavingTea

With
aworkinginterfaceforaddingteas,allthat’sleftistogivethe
usertheoptiontosavetheirnewteatothedatabase.We’llalsoadda
littlevalidationtotheinterfacesothattheusercannotsaveanempty
teatothedatabase!

Startbyopeningstrings.xml
intheeditorandaddingsomenewlabelsforourapplication:

1

<!--res/values/strings.xml-->

2

<

string

name

=

"save_tea_label"

>SaveTea</

string

>

3

<

string

name

=

"invalid_tea_title"

>Teacouldnotbesaved.</

string

>

4

5

<

string

name

=

"invalid_tea_no_name"

>Enteranameforyourtea.</

string

>

Justlikebefore,we’llneedtocreateanewoptionsmenufor
AddTeaActivity

sothattheusercansavetheirfavoritetea:

CreateanewXMLfile,add_tea.xml
,intheres/menus
folderbychoosingFile→NewandthenOther→AndroidXMLFile.Remembertoselect“Menu”astheresourcetype.

Addanitemtothenewmenuforsavingthetea:

1

<!--res/menus/add_tea.xml-->

2

<?

xml

version

=

"1.0"

encoding

=

"utf-8"

?>

3

<

menu

xmlns:android

=

"http://schemas.android.com/apk/res/android
"

>

4


<

item

android:title

=

"@string/save_tea_label"

android:id

=

"@+id/save_tea"

/>

5

</

menu

>

Backin
AddTeaActivity

,addtheoverridemethodsfor
onCreateOptionsMenu

and
onOptionsItemSelected

,justlikeyoudidin
BrewClockActivity

.However,thistime,you’llsupplytheadd_tea.xml
resourcefiletothe
MenuInflater

:

01

//src/com/example/brewclock/AddTeaActivity.java

02

@Override

03

publicbooleanonCreateOptionsMenu(Menumenu){

04


MenuInflaterinflater=getMenuInflater();

05


inflater.inflate(R.menu.add_tea,menu);

06

07


return

true

;

08

}

09

10

@Override

11

publicbooleanonOptionsItemSelected(MenuItemitem){

12


switch

(item.getItemId()){

13


case

R.id.save_tea:

14


saveTea();

15

16


default

:

17


return

super

.onOptionsItemSelected(item);

18


}

19

}

Next,we’lladdanewmethod,
saveTea()

,tohandlesavingthetea.The
saveTea

methodfirstreadsthenameandbrewtimevalueschosenbytheuser,
validatesthemand(ifalliswell)savesthemtothedatabase:

01

//src/com/example/brewclock/AddTeaActivity.java

02

publicbooleansaveTea(){

03


//Readvaluesfromtheinterface

04


StringteaNameText=teaName.getText().toString();

05


intbrewTimeValue=brewTimeSeekBar.getProgress()+1;

06

07


//Validateanamehasbeenenteredforthetea

08


if

(teaNameText.length()<2){

09


AlertDialog.Builderdialog=

new

AlertDialog.Builder(

this

);

10


dialog.setTitle(R.string.invalid_tea_title);

11


dialog.setMessage(R.string.invalid_tea_no_name);

12


dialog.show();

13

14


return

false

;

15


}

16

17


//Theteaisvalid,soconnecttotheteadatabaseandinsertthetea

18


TeaDatateaData=

new

TeaData(

this

);

19


teaData.insert(teaNameText,brewTimeValue);

20


teaData.close();

21

22


return

true

;

23

}

Thisisquiteaheftychunkofcode,solet’sgooverthelogic.

First,wereadthevaluesoftheEditText
teaName

andtheSeekBar
brewTimeSeekBar

(rememberingtoadd1tothevaluetoensureabrewtimeofbetween1
and10minutes).Next,wevalidatethatanamehasbeenenteredthatis
twoormorecharacters(thisisreallysimplevalidation;youmightwant
toexperimentdoingsomethingmoreelaborate,suchasusingregular
expressions).

Iftheteanameisnotvalid,weneedtolettheuserknow.WemakeuseofoneofAndroid’shelperclasses,
AlertDialog.Builder

,
whichgivesusahandyshortcutforcreatinganddisplayingamodal
dialogwindow.Aftersettingthetitleanderrormessage(usingour
stringresources),thedialogueisdisplayedbycallingits
show()

method.Thisdialogueismodal,sotheuserwillhavetodismissitby
pressingthe“Back”key.Atthispoint,wedon’twanttosaveanydata,
sojustreturn
false

outofthemethod.

Iftheteaisvalid,wecreateanewtemporaryconnectiontoourteadatabaseusingthe
TeaData

class.Thisdemonstratestheadvantageofabstractingyourdatabase
accesstoaseparateclass:youcanaccessitfromanywhereinthe
application!

Aftercalling
teaData.insert()

toaddourteatothedatabase,wenolongerneedthisdatabaseconnection,sowecloseitbeforereturning
true

toindicatethatthesavewassuccessful.

Try
thisoutbyrunningtheapplicationintheemulator,pressing“Menu”
andtapping“AddTea.”Onceonthe“AddTea”screen,trysavinganempty
teabypressing“Menu”againandtapping“SaveTea.”Withyour
validationinplace,you’llbepresentedwithanerrormessage:



Next,
tryenteringanameforyourtea,choosingasuitablebrewtime,and
choosing“SaveTea”fromthemenuagain.Thistime,youwon’tseean
errormessage.Infact,you’llseenothingatall.

ImprovingtheUserExperience

While
functional,thisisn’tagreatuserexperience.Theuserdoesn’tknow
thattheirteahasbeensuccessfullysaved.Infact,theonlywayto
checkistogobackfromthe“AddTea”Activityandcheckthelistof
teas.Notgreat.Lettingtheuserknowthattheirteawassuccessfully
savedwouldbemuchbetter.Let’sshowamessageonthescreenwhena
teahasbeenaddedsuccessfully.

Wewantthemessagetobepassive,ornon-modal,sousingan
AlertDialog

likebeforewon’thelp.Instead,we’llmakeuseofanotherpopularAndroidfeature,the
Toast

.

Toasts
displayashortmessagenearthebottomofthescreenbutdonot
interrupttheuser.They’reoftenusedfornon-criticalnotifications
andstatusupdates.

Startbyaddinganewstringtothestrings.xml
resourcefile.Noticethe
%s

inthestring?We’llusethisinthenextsteptointerpolatethenameofthesavedteaintothemessage!

1

<!--res/values/strings.xml-->

2

<

string

name

=

"save_tea_success"

>%steahasbeensaved.</

string

>

Modifythecodein
onOptionsItemSelected

tocreateandshowa
Toast

pop-upiftheresultof
saveTea()

is
true

.Thesecondparameterusesof
getString()

interpolatethenameofourteaintothe
Toast

message.Finally,weclearthe“TeaName”textsothattheusercanquicklyaddmoreteas!

1

//src/com/example/brewclock/AddTeaActivity.java

2

//…

3

switch

(item.getItemId()){

4


case

R.id.save_tea:

5


if

(saveTea()){

6


Toast.makeText(

this

,getString(R.string.save_tea_success,teaName.getText().toString()),Toast.LENGTH_SHORT).show();

7


teaName.setText(

""

);

8


}

9

//…

Nowre-runyourapplicationandtryaddingandsavinganewtea.You’llseeanice
Toast

popuptoletyouknowtheteahasbeensaved.The
getString()

methodinterpolatesthenameoftheteathatwassavedintotheXMLstring,whereweplacedthe
%s

.



Click
the“Back”buttontoreturntotheapplication’smainscreen,andtap
theteaspinner.Thenewteasyouaddedinthedatabasenowshowupas
optionsinthespinner!

UserPreferences

BrewClockisnow
fullyfunctional.Userscanaddtheirfavoriteteasandtherespective
brewingtimestothedatabase,andtheycanquicklyselectthemtostart
anewbrew.AnyteasaddedtoBrewClockaresavedinthedatabase,so
evenifwequittheapplicationandcomebacktoitlater,ourlistof
teasisstillavailable.

Onethingyoumightnoticewhen
restartingBrewClock,though,isthatthebrewcounterisresetto0.
Thismakeskeepingtrackofourdailyteaintake(avitalstatistic!)
difficult.Asafinalexercise,let’ssavethetotalbrewcounttothe
device.

Ratherthanaddinganothertabletoourteasdatabase,
we’llmakeuseofAndroid’s“SharedPreferences,”asimpledatabasethat
Androidprovidestoyourapplicationforstoringsimpledata(strings,
numbers,etc.),suchashighscoresingamesanduserpreferences.

StartbyaddingacoupleofconstantstothetopofBrewClockActivity.java
.
Thesewillstorethenameofyoursharedpreferencesfileandthename
ofthekeywe’llusetoaccessthebrewcount.Androidtakescareof
savingandpersistingoursharedpreferencesfile.

1

//src/com/example/brewclock/BrewClockActivity.java

2

protectedstaticfinalStringSHARED_PREFS_NAME=

"brew_count_preferences"

;

3

protectedstaticfinalStringBREW_COUNT_SHARED_PREF=

"brew_count"

;

Next,
we’llneedtomakesomechangestothecodesothatwecanreadand
writethebrewcounttotheuserpreferences,ratherthanrelyingonan
initialvalueinourcode.In
BrewClockActivity

’s
onCreate

method,changethelinesaround
setBrewCount(0)

tothefollowing:

01

//src/com/example/brewclock/BrewClockActivity.java

02

publicvoidonCreate(){

03


//…

04

05


//Settheinitialbrewvalues

06


SharedPreferencessharedPreferences=getSharedPreferences(SHARED_PREFS_NAME,MODE_PRIVATE);

07


brewCount=sharedPreferences.getInt(BREW_COUNT_SHARED_PREF,0);

08


setBrewCount(brewCount);

09

10


//…

11

}

Herewe’reretrievinganinstanceoftheapplication’ssharedpreferencesusing
SharedPreferences

,andaskingforthevalueofthe
brew_count

key(identifiedbythe
BREW_COUNT_SHARED_PREF

constantthatwasdeclaredearlier).Ifavalueisfound,itwillbe
returned;ifnot,we’llusethedefaultvalueinthesecondparameterof
getInt

(inthiscase,0).

Nowthatwecanretrievethestoredvalueofbrewcount,weneedtoensureitsvalueissavedto
SharedPreferences

wheneverthecountisupdated.

Addthefollowingcodeto
setBrewCount

in
BrewClockActivity

:

01

//src/com/example/brewclock/BrewClockActivity.java

02


publicvoidsetBrewCount(intcount){

03


brewCount=count;

04


brewCountLabel.setText(String.valueOf(brewCount));

05

06


//UpdatethebrewCountandwritethevaluetothesharedpreferences.

07


SharedPreferences.Editoreditor=getSharedPreferences(SHARED_PREFS_NAME,MODE_PRIVATE).edit();

08


editor.putInt(BREW_COUNT_SHARED_PREF,brewCount);

09


editor.commit();

10


}

Sharedpreferencesareneversaveddirectly.Instead,wemakeuseofAndroid’s
SharedPreferences.Editor

class.Calling
edit()

on
SharedPreferences

returnsan
editor

instance,whichcanthenbeusedtosetvaluesinourpreferences.When
thevaluesarereadytobesavedtothesharedpreferencesfile,we
justcall
commit()

.

Withourapplication’scodeallwrappedup,it’stimetotesteverything!

Run
theapplicationontheemulator,andtimeafewbrews(thisisthe
perfectexcusetogoandmakeawell-deservedteaortwo!),andthen
quittheapplication.Tryrunninganotherapplicationthatisinstalled
ontheemulatortoensureBrewClockisterminated.RememberthatAndroid
doesn’tterminateanActivityuntilitstartstorunoutofmemory.

Whenyounextruntheapplication,you’llseethatyourpreviousbrewcountismaintained,andallyourexistingteasaresaved!

Summary

Congratulations!
You’vebuiltafullyworkingAndroidapplicationthatmakesuseofa
numberofcorecomponentsoftheAndroidSDK.Inthistutorial,youhave
seenhowto:

CreateasimpleSQLitedatabasetostoreyourapplication’sdata;

MakeuseofAndroid’sdatabaseclassesandwriteacustomclasstoabstractthedataaccess;

Addoptionmenustoyourapplication;

CreateandregisternewActivitieswithinyourapplicationandbindthemtogetherintoacoherentinterfaceusingIntents;

Storeandretrievesimpleuserdataandsettingsusingthebuilt-in“SharedPreferences”database.

Data
storageandpersistenceisanimportanttopic,nomatterwhattypeof
applicationyou’rebuilding.Fromutilitiesandbusinesstoolsto3-D
games,nearlyeveryapplicationwillneedtomakeuseofthedatatools
providedbyAndroid.



Activities

BrewClock
isnowonitswaytobeingafullyfunctionalapplication.However,we
couldstillimplementafewmorefeaturestoimprovetheuser
experience.Forexample,youmightliketodevelopyourskillsbytrying
anyofthefollowing:

Checkingforduplicateteanameentriesbeforesavingatea,

Addingamenuoptiontoresetthebrewcounterto0,

Storingthelast-chosenbrewtimeinasharedpreferencesothattheapplicationdefaultstothatvaluewhenrestarted,

Addinganoptionfortheusertodeleteteasfromthedatabase.

SolutionsfortheActivitieswillbeincludedinafuturebranchontheGitHubrepository
,
whereyou’llfindthefullsource-codelistings.Youcandownloadthe
workingtutorialcodebyswitchingyourcopyofthecodetothe
tutorial_2

branch:

1

#Ifyou’venotalreadyclonedtherepository,

2

#you’llneedtodothatfirst:

3

#$gitclonegit://github.com/cblunt/BrewClock.git

4

#$cdBrewClock

5

$gitcheckouttutorial_2

I
hopeyou’veenjoyedworkingthroughthistutorialandthatithelpsyou
indesigningandbuildingyourgreatAndroidapplications.Pleaselet
meknowhowyougetoninthecommentsbelow,orfeelfreetodropmean
email.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: