You are on page 1of 592

. , .

Android.
.
.
.
.
.
.
.

32.973.2-018.2
004.451
. , .
20 Android. . .: , 2014.
592 .: . ( ).
ISBN 978-5-496-00502-9
, -
Android.
IT- Big Nerd Ranch, -
Android, API .
Android-
, Flickr, -
, , -
, .
,
Android .
12+ ( 12 . 29 2010 . 436-.)

ISBN 978-0321804334 . Authorized translation from the English language edition,


entitled Android Programming: The Big Nerd Ranch Guide;
ISBN 0321804334; by Hardy, Brian; and by Phillips, Bill;
published by The Big Nerd Ranch Guide, Inc.
The Big Nerd Ranch, Inc., 2013
ISBN 978-5-496-00502-9 , 2014
,
, 2014
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or
mechanical, including photocopying, recording or by any information storage retrieval system, without permission from
Pearson Education, Inc.
The Big Nerd Ranch, Inc. .

.
, , , -
. , ,

, .

, 192102, -, . (. ), . 3, , . 7.
005-93, 2; 95 3005 .
30.10.13. 70100/16. . . . 47,730. 1700.

. 432049, , . , .27.

............................................................................................................ 16

Android ........................................................................................................ 18

1. Android ....................................................................... 24
................................................................................................. 24
Android ........................................................................................................... 25
Eclipse ..................................................................................................................... 28
..................................................................... 29
............................................................................................................. 33
..................................................................................................................... 34
android:layout_width android:layout_height ............................................................................. 34
android:orientation .................................................................................................................... 34
android:text .............................................................................................................................. 35
..................................................................................................... 35
............................................................................................. 36
XML View ............................................................................................... 36
.......................................................................................... 37
........................................................................................... 40
.................................................................................................................. 40
.................................................................................................... 41
.............................................................................................................. 41
.................................................................................................... 42
............................................................................................................................... 43
.......................................................................................................................... 44
............................................................................................................. 45
: Android.................................................. 47
Android ....................................................................................... 49
2. Android MVC ............................................................................................... 51
.............................................................................................................. 52
get- set- ............................................................................................... 53
-- Android ..................................................... 55
MVC ..................................................................................................................... 56
............................................................................................. 57
................................................................................................. 59
................................................................................................................... 63
......................................................................................................... 63
..................................................................................... 64
6

...................................................................................................................... 64
.................................................................................................... 65
XML......................................................................................................... 66
................................................................................................................................ 67
. TextView ................................................................... 68
. .............................................................................. 68
. Button ImageButton ...................................................................................... 68
3. Activity............................................................................. 71
Activity .......................................................................... 72
............................................................................................... 72
LogCat ................................................................................................................. 74
..................................................................................... 78
................................................................... 78
.............................................................................. 79
....................................................................................... 82
onSaveInstanceState(Bundle) ............................................................................ 83
Activity ................................................................................................ 84
: onSaveInstanceState(Bundle) ................................................ 85
: .................................................................. 87
4. Android ..................................................................... 89
DDMS ...................................................................................................................... 90
................................................................................................. 91
.................................................................................................. 92
................................................................................................. 94
....................................................................................................... 95
................................................................................................... 99
File Explorer ................................................................................................................................100
Android .....................................................................................................101
Android Lint .............................................................................................................................101
R ............................................................................................................103
5. ..................................................................................... 104
......................................................................................................105
.........................................................................................................106
......................................................................................109
....................................................................................110
Cheat QuizActivity ......................................................................................111
......................................................................................................................113
........................................................................................113
......................................................................................................115
.......................................................................................115
.............................................................................................................116
......................................................................118
..............................................................................................................119
.............................................................................................................120
.............................................................................................................121
Android ....................................................................................123
...............................................................................................................................125
7

6. Android SDK ....................................................... 126


Android SDK ....................................................................................................................126
Android ..............................................................................127
Honeycomb ..........................................................................................................128
SDK (Minimum Required SDK)..................................................................129
SDK (Target SDK)............................................................................................130
SDK (Compile With) .............................................................................130
API ...................................................130
Lint ..................................................................................133
Android ..........................................................................................134
. ......................................................................................136
7. UI- FragmentManager ........................................................... 138
....................................................................................139
.........................................................................................................140
CriminalIntent ...............................................................................................141
...........................................................................................................143
..........................................................................................144
Crime ............................................................................................................146
UI-...............................................................................................................147
...................................................................................................148
.........................................................................................149
...........................................................................149
UI- ..........................................................................................................151
CrimeFragment .......................................................................................151
CrimeFragment ..............................................................................................152
.................................................................153
......................................................................................155
UI- FragmentManager ........................................................................156
..........................................................................................................157
FragmentManager .....................................................................159
.................................................................161
: Honeycomb, ICS, Jelly Bean . . ....................................161
8. ..................................................................................... 163
Crime ......................................................................................................................163
.................................................................................................................164
..............................................................................................................166
XML.....................................................................................167
, ...................................................................................................167
, dp sp ...................................................................................................168
Android ........................................................169
..................................................................................................................170
.......................................................................................................................170
................................................................................171
...................................................................................................173
...................................................................................174
...........................................................................175
..............................................................................176
android:layout_weight .........................................................................................177
8

.............................................................................................178
.............................................................178
. ...........................................................................................179
9. ListFragment .................................................................. 180
CriminalIntent ..................................................................................181
........................................................................182
ListFragment ................................................................................................................184
.......................................................................185
...........................................................................185
Activity ......................................................................................................186
.......................................................................................188
CrimeListActivity ...................................................................................................189
ListFragment, ListView ArrayAdapter ..........................................................................................191
ArrayAdapter<T> .........................................................................................................193
..................................................................................................195
......................................................................................................196
...........................................................................................196
.................................................................................................198
10. ........................................................................... 202
................................................................................................202
...........................................................................................................203
.................................................................................................................204
CrimeFragment Crime .....................................................205
...................................................................................................206
...........................................................................................................206
.................................................................................207
............................................................................................................208
...............................................................................................................208
................................................................210
11. ViewPager.................................................................................................. 212
CrimePagerActivity ........................................................................................................213
...........................................................................214
..................................................................................214
ViewPager PagerAdapter ........................................................................................................215
CrimePagerActivity ....................................................................................................216
FragmentStatePagerAdapter FragmentPagerAdapter ................................................................219
: ViewPager............................................................................220
12. ..................................................................................... 223
DialogFragment ............................................................................................................225
DialogFragment ..................................................................................................226
............................................................................228
....................................................................................229
DatePickerFragment .....................................................................................230
CrimeFragment .......................................................................................232
............................................................................................233
...................................................................................234
DialogFragment ..................................................................236
. .........................................................................................238
9

13. MediaPlayer ................................................... 239


.................................................................................................................240
HelloMoonFragment ....................................................................................242
.........................................................................................................243
HelloMoonFragment .......................................................................................244
...........................................................................................244
..............................................................................................................246
...............................................................248
. .............................................................................248
: ...........................................................................249
. HelloMoon ......................................................................249
14. ......................................................................... 250
...............................................................................................................250
.........................................................................................251
: ? ................................................................253
onSaveInstanceState(Bundle) ....................................................................................254
: ........................................................256
15. ............................................................................................. 257
................................................................................................................258
...........................................................................................................258
.........................................................................259
...........................................................................................259
.......................................................................................260
.............................................................................................261
.......................................................................................262
...................................................................................262
...............................................................................................263
....................................................................263
......................................................................................................................264
................................................................................................264
.....................................................................................264
16. ...................................................................................... 266
........................................................................................................................267
XML ....................................................................................268
.........................................................................................270
....................................................................................................270
...........................................................................................................273
........................................................................................275
...............................................................................................275
Up ..............................................................................................................277
...................................................................................................279
.................................................................................280
...............................................................................................281
, ....................................................................................................................281
. .........................................................................283
17. ........................................... 285
CriminalIntent...........................................................................285
JSON ................................................................................287
10

CriminalIntentJSONSerializer ...........................................................................287
JSON Crime .......................................................................288
Crime CrimeLab ......................................................................................289
onPause() ..........................................................................290
...................................................................................291
. .....................................................................293
: Android - Java ........................293
..........................................................................................294
18. ............................ 295
...................................................................................296
..................................................................................................296
..................................................................................................297
.............................................................................................297
...............................................................................................................299
.................................................................................300
.....................................................................................................301
.....................................301
................................................................................304
.........................................305
: ? ..................................................................................306
. CrimeFragment ...................................................................................307
: ActionBarSherlock .....................................................................................307
. ActionBarSherlock ...........................................................................310
ABS CriminalIntent .................................................................................310
............................................................................................311
.....................................................................................311
19. I: Viewfinder ................................................................................ 313
.......................................................................................................315
CrimeCameraFragment ......................................................................................316
CrimeCameraActivity ..........................................................................................317
..........................................................317
API .........................................................................................................318
..........................................................................................318
SurfaceView, SurfaceHolder Surface ........................................................................................320
..................................................323
CrimeCameraActivity CrimeFragment ..........................................................................325
........................................................................328
: .................................................329
20. II: ....................................... 331
......................................................................................................................332
.......................................................................................333
..........................................................................................336
CrimeFragment ................................................................................................337
CrimeCameraActivity ..........................................................337
CrimeCameraFragment......................................................................338
CrimeFragment ...............................................................................339
........................................................................................................340
Photo ........................................................................................................340
11

Crime .........................................................341
........................................................................................342
CrimeFragment .............................................................................342
ImageView ...........................................................................................................343
.........................................................................................................345
ImageView ......................................................345
............................................................................................................347
DialogFragment ...................................................349
. Crime..........................................................................351
. ...........................................................................................351
: Android ..........................................................352
21. ..................................................................................... 354
....................................................................................................................355
.........................................................................................357
......................................................................................................................357
..............................................................................................359
................................................................................................359
.....................................................................................................................360
Android .......................................................................................................362
..................................................................................364
............................................................................................................365
.......................................................................................366
. .........................................................................................366
22. .................................................................. 367
.........................................................................................................................368
SingleFragmentActivity ...........................................................................................369
..................................................................370
- .......................................................................................371
....................................................................................372
: ........................................................................................373
............................................................................374
CrimeFragment.Callbacks .......................................................................................378
: .............................................381
23. .......................................................... 383
NerdLauncher ...........................................................................................383
......................................................................................................385
.......................................................................387
..............................................................................................................389
NerdLauncher .......................................................391
. , .........................................................................392
: ...................................................................................392
24. .................................................................................. 394
RemoteControl ................................................................................................395
RemoteControlActivity ...............................................................................................395
RemoteControlFragment ............................................................................................396
.................................................................................................399
.................................................................................................................401
12

: include merge ........................................................................................404


. ............................................................................................405
25. ............................................................................. 406
XML .........................................................................................................407
.......................................................................................................................409
...........................................................................................................411
9- ...............................................................................................................413
26. HTTP .......................................................................... 420
PhotoGallery.............................................................................................421
........................................................................................................424
................................................................................................425
AsyncTask ....................................................426
......................................................................................................427
...........................................................................................................428
XML Flickr .................................................................................................................429
XmlPullParser ...................................................................................................433
AsyncTask .................................................................................................435
: AsyncTask ...........................................................................438
AsyncTask ..............................................................................................................439
. ...........................................................................................439
27. Looper, Handler HandlerThread ............................................................. 440
GridView ...............................................................................440
...........................................................................................................443
...........................................................................................443
.....................................................................................................444
........................................................................................446
...............................................................................................................447
............................................................................................................447
.................................................................................................448
Handler ....................................................................................................................451
: AsyncTask ..................................................................................455
. ............................................................455
28. ......................................................................................................... 457
Flickr ..............................................................................................................................457
.............................................................................................................459
...........................................................................................459
............................................................................................................462
......................................................................................................464
.................................................................................................................465
...........................................................................................465
........................................467
SearchView Android 3.0 ................................................................470
...............................................................................................................................472
29. ..................................................................................... 474
IntentService................................................................................................................474
.................................................................................................................477
..................................................................477
13

...........................................................................................................478
AlarmManager ...................................................................................480
PendingIntent ..........................................................................................................................482
PendingIntent ..........................................................482
.................................................................................................................483
.................................................................................484
...........................................................................................................................486
: ...............................................................................488
( ) ..................................................................................488
........................................................................................................488
........................................................................................................488
............................................................................................................489
................................................................................................................489
...............................................................................................490
.................................................................................................491
30. ............................................................... 492
.........................................................................................................493
............................................................................493
....................................................................................................495
.................................................................................496
................................................................................496
......................................................................497
............................................................................................................500
................................................................................................502
..............................503
......................................................................................507
31. - WebView .......................................................... 508
Flickr ...................................................................................................508
: .............................................................................................510
: WebView ............................................................................................510
WebChromeClient ...........................................................................................................514
WebView ..................................................................................................................516
: JavaScript .................................................................517
32. ......................... 519
DragAndDraw..................................................................................................520
DragAndDrawActivity ......................................................................................520
DragAndDrawFragment ..................................................................................521
...................................................................................522
BoxDrawingView ............................................................................................522
.......................................................................................................524
......................................................................525
onDraw() ..................................................................................................527
: ...............................................................................................................530
33. ......................................... 531
RunTracker ...............................................................................................531
RunActivity........................................................................................................533
RunFragment .....................................................................................................................533
14

...................................................................................................................534
...........................................................................................................................534
RunFragment .................................................................................................534
LocationManager ...........................................................................................535
...................................................537
.....................................539
: ........................................................543
..............................................................544
34. SQLite .......................................................... 547
..................................................................................547
..........................................................................................554
CursorAdapter .................................................................556
................................................................................................................559
..............................................................................................561
: .....................................................................................567
35. ................................................................ 568
Loader LoaderManager ..............................................................................................................568
RunTracker....................................................................................570
................................................................................................................570
.................................................................................................................574
..........................................................................................577
36. ........................................................................................................ 579
Maps API RunTracker .........................................................................579
....................................................................................579
Google Play services SDK ...............................................................579
Google Maps API ..........................................................................................580
RunTracker .........................................................................................580
..........................................................................581
.........................................................................................................................585
........................................................................589
: .......................................................................................590
37. ............................................................................................. 591
..............................................................................................................591
.........................................................................................................592
......................................................................................................................................592
. ,

,
.
. .

, .
, .
.

(Chris Stewart) (Owen Matthews)


.
-
(Christopher Moor)
,
.
(Bolot Kerimbaev) (Andrew
Lunsford)
.
(Frank Robles), (Jim Steele),
(Laura Cassell), (Mark Dalrymple)
(Magnus Dahl) .
(Aaron Hillegass).
, . -
. ( ,
.)
(Susan Loper)
,
. .
, -
.
NASA.
.
17

( Ellie Volckhausen), .
IntelligentEnglish.com -
, EPUB Kindle. DocBook
.
, Facebook
.

. , ,
,
. -
, . .
Android

Android .
Android : -
, .
, - ,
. , ,
.
Android . c
Java, Java . Android,
. , -
.
. , Big Nerd Ranch, ,
Android :
Android;
, .
. -
Android.
Android,
. - , - -
, ,
.

, ,
. , -
, Android-
.
19


, Java,
, , , , , -
, .
, -
2. Java
. ;
.
- -
, Java , .

Java (, ).
Java ,
.


Big Nerd Ranch.
, .
, -
. , .
,
.
,
,
, , -
.
, . ,
.
:
.
,
.
forums.bignerdranch.com.
Android, .


Android.
, .
,
20 Android

. ,
.
GeoQuiz
Android, , .
CriminalIntent
.
, - ,
, , , .
HelloMoon .
,
, .
NerdLauncher -
.
RemoteControl
, ,
.
PhotoGallery Flickr -
Flickr. , -
, - . .
DragAndDraw -
.
RunTracker
. ,
SQLite, .

.
,
.
. -

.
,
forums.bignerdranch.com.

?
. -
,
. -
, , .
21


, -
Android.
. .
, . -
, .
, -
.
7 -
. .
Android- ,
. ,
. , -
,
.
, Gingerbread Froyo. -
Android Ice Cream Sandwich Jelly Bean,
Key Lime Pie. ,
Froyo Gingerbread. (
Android 6.)
, -
, Froyo Gingerbread.
, ,
Android . ,
, Gingerbread
40 % .


ADT Bundle
ADT (Android Developer Tools) Bundle.
:
Eclipse Android.
Eclipse Java, PC, Mac -
Linux. Eclipse
,
, .
Android Developer Tools Eclipse. ADT
21.1. ,
.
Android SDK Android SDK.
22 Android

Android SDK
.
Android
.


ADT Bundle Android zip-.
1. http://developer.android.com/sdk/index.html.
2. zip- , Eclipse
.
3. eclipse Eclipse.
Windows Eclipse , ,
Java Development Kit ( JDK6),
www.oracle.com.
, http://developer.
android.com/sdk/index.html .

SDK
ADT Bundle SDK
. Android
.
Android SDK Manager.
Eclipse Window Android SDK Manager.
, Android 2.2 (Froyo),
:
SDK Platform;
;
Google API.
, .
Android SDK Manager An-
droid , .
23

. Android SDK Manager


.
Android . , -
.


, ,
comp@piter.com ( , ).
!
- http://www.piter.com
.
1
Android

, -
Android. ,
- .

.
, ,
GeoQuiz. ,
. -
, True False,
GeoQuiz .
. 1.1
False.



GeoQuiz
(activity) (layout):
Ac-
tivity Android SDK.

.
. 1.1. , , -
,
Android 25

Activity.
; .
GeoQuiz ,
Activity QuizActivity. QuizActivity -
, . 1.1.
-
. ,
XML. ,
(, ).
GeoQuiz activity_quiz.xml.
XML ,
. 1.1.
QuizActivity activity_quiz.xml . 1.2.

. 1.2. QuizActivity , activity_quiz.xml

, .

Android
Android. Android ,
. , Eclipse
File New Android Application Project,
.
GeoQuiz (. 1.3).
. Package
Name com.bignerdranch.android.geoquiz.
:
DNS, -
.

Google Play.

Android. GeoQuiz ,
26 1. Android

.
Android 6.

. 1.3.

Android ,
.
; . (
, ,
. . forums.bignerdranch.com,
.)
Next.
Create custom launcher icon (. 1.4). -
GeoQuiz .
, Create activity .
Android 27

. 1.4.

Next.
(. 1.5)
. Blank Activity ( ).

. 1.5.
28 1. Android

Next.
QuizAc-
tivity (. 1.6). Activity .
, ,
.

. 1.6.

activity_quiz -
. , ;
,
.
, , .
Navigation Type None Finish.
Eclipse .

Eclipse
Eclipse (workbench window),
. 1.7. ( Eclipse, -
Eclipse, .)
29

Package Explorer. ,
.
. -
, Eclipse activity_quiz.xml.
.
, X
(. 1.7). .
,
.

. 1.7.

,
Eclipse.
, ;
, .



Eclipse activity_quiz.xml Android,
.
, XML, ,
.
30 1. Android

XML, activity_quiz.xml
.
activity_quiz.xml
. , XML
, 1.1.

1.1.
<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"
tools:context=".QuizActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>

, activity_quiz.xml
:
<?xml version="1.0" encoding="utf-8"?>

ADT 21 Android.
, .
(widgets): RelativeLayout
TextView.
,
. , -
. ,
, .
Android SDK , -
.
View (, TextView Button).
. 1.8 , RelativeLayout TextView,
1.1.
, . QuizActivity
:
LinearLayout;
TextView;
LinearLayout;
Button.
. 1.9 , QuizActivity.
31

. 1.8.

(-
)

(-
)

. 1.9.
32 1. Android

activity_quiz.xml.
activity_quiz.xml , 1.2. -
XML, , ,
XML . -
.
, ;
, . : ,
.
,
android:text, . .
.

1.2. XML (activity_quiz.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"
tools:context=".QuizActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/true_button" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
33

android:text="@string/false_button" />

</LinearLayout>
</LinearLayout>

XML , . 1.9. -
XML.
.
XML. -
.
, ,
.


View, .
. 1.10 XML 1.2.

. 1.10.

Lin-
earLayout. XML Android
http://schemas.android.com/apk/res/android.
34 1. Android

LinearLayout View ViewGroup. ViewGroup


.
, .
ViewGroup FrameLayout, TableLayout RelativeLayout.
ViewGroup, (child) ViewGroup.
LinearLayout : TextView
LinearLayout. LinearLayout Button.


, .

android:layout_width android:layout_height
android:layout_width android:layout_height,
, . ,
match_parent wrap_content:
match_parent .
wrap_content .
( fill_parent.
match_parent.)
LinearLayout match_parent.
LinearLayout , -
, Android
.
wrap_content.
. 1.9 , .
TextView -
android:padding="24dp".
, -
. (, dp? ,
(density-independent pixels),
8.)

android:orientation
android:orientation LinearLayout ,
. Lin-
earLayout ; LinearLayout -
.
.
LinearLayout , , -
. LinearLayout
35

. ( -
, ;
.)

android:text
TextView Button android:text.
,
: -
, .
, XML, -
.
(, android:text="True"), .
, . 15 ,
.
, activity_quiz.xml, .
.


strings.xml.
Package Explorer res/values,
strings.xml.
; strings.xml -
.
.
hello_world .

1.3. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name">GeoQuiz</string>
<string name="hello_world">Hello, world!</string>
<string name="question_text">Constantinople is the largest city in Turkey.</
string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="menu_settings">Settings</string>
</resources>

( menu_settings .
menu_settings , .)
@string/false_button XML GeoQuiz
"False" .
36 1. Android

strings.xml. activity_quiz.xml , -
, . (
, , - .)
strings.xml,
.
. res/values/,
resources string,
.


.
, . -
activity_quiz.xml Graphical Layout
.

. 1.11.
(activity_quiz.xml)

XML View
XML activity_quiz.xml View?
QuizActivity.
37

GeoQuiz Activity -
QuizActivity. QuizActivity src (
Java- ).
Package Explorer src, com.
bignerdranch.android.geoquiz. QuizActivity.java -
( 1.4).
1.4. QuizActivity (QuizActivity.java)
package com.bignerdranch.android.geoquiz;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class QuizActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_quiz, menu);
return true;
}
}

( import,
import, .)
Activity: onCreate(Bundle) onCreateOptionsMenu(Menu).
onCreateOptionsMenu(Menu).
16.
onCreate(Bundle) -
. , -
. ,
Activity:
public void setContentView(int layoutResID)

(inflates) .
, -
. , ,
.


. ,
, , XML . .
38 1. Android

res. Package Explorer


, activity_quiz.xml res/layout/. ,
, res/values/.
.
R.layout.activity_quiz.
GeoQuiz,
Package Explorer gen. R.java.
Android,
, .

1.5. GeoQuiz
/* AUTO-GENERATED FILE. DO NOT MODIFY.
...
*/

package com.bignerdranch.android.geoquiz;

public final class R {


public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int menu_settings=0x7f070003;
}
public static final class layout {
public static final int activity_quiz=0x7f030000;
}
public static final class menu {
public static final int activity_quiz=0x7f060000;
}
public static final class string {
public static final int app_name=0x7f040000;
public static final int false_button=0x7f040003;
public static final int menu_settings=0x7f040006;
public static final int question_text=0x7f040001;
public static final int true_button=0x7f040002;
}
...
}

, R.layout.activity_quiz
activity_quiz layout R.
.
, :
setTitle(R.string.app_name);

Android ,
activity_quiz.xml.
39

.
, .
,
android:id. activity_quiz.xml android:id
.

1.6. (activity_quiz.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/true_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/true_button" />

<Button
android:id="@+id/false_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/false_button" />

</LinearLayout>

</LinearLayout>

: + android:id, -
android:text. , ,
.
activity_quiz.xml. R.java ,
R.id .

1.7. (R.java)
public final class R {
...
public static final class id {
public static final int false_button=0x7f070001;
public static final int menu_settings=0x7f070002;
public static final int true_button=0x7f070000;
}
...
40 1. Android


, , -
QuizActivity. .
QuizActivity.java. ( ;
.) .

1.8. (QuizActivity.java)
public class QuizActivity extends Activity {

private Button mTrueButton;


private Button mFalseButton;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
}
...
}

, m
( ).
Android, .
. -
: Button (Button cannot be resolved
to a type).
, android.widget.Button
QuizActivity.java. :
import android.widget.Button;


Eclipse
, Java Android SDK.
,
.
, :
Command+Shift+O Mac;
Ctrl+Shift+O Windows Linux.
. ( , XML.)
-.
:
41

View;
, -
.


,
Activity:
public View findViewById(int id)

View.
QuizActivity.java -
. ,
View Button.

1.9. (QuizActivity.java)
public class QuizActivity extends Activity {

private Button mTrueButton;


private Button mFalseButton;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);

mTrueButton = (Button)findViewById(R.id.true_button);
mFalseButton = (Button)findViewById(R.id.false_button);
}
...
}


Android (event-driven).
,
, -
. ( ,
, , .)
, ,
. , ,
(listener).
.
Android SDK , -
. -
,
View.OnClickListener.
42 1. Android

True. QuizActivity.java
onCreate() .

1.10. True (QuizActivity.java)


...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);

mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});

mFalseButton = (Button)findViewById(R.id.false_button);
}
}

( View cannot be resolved to a type, -


Command+Shift+O Ctrl+Shift+O
View.)
1.10 , But-
ton mTrueButton. setOnClickListener(OnClickListener)
, OnClickListener.


. , -
; : ,
, setOnClickListener(OnClickListener) .
,
.
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});

-
. ,
,
, .
OnClickListener, -
onClick(View).
43

onClick(View) , . ,
onClick(View) ,
, .
( ,
, Java ,
, .)
False.

1.11. False (QuizActivity.java)


...
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});

mFalseButton = (Button)findViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});
}

- . -
(toast) ,
- ,
, . ,
(. 1.12).
strings.xml ,
.

1.12. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GeoQuiz</string>
<string name="question_text">Constantinople is the largest city
in Turkey.</string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="correct_toast">Correct!</string>
<string name="incorrect_toast">Incorrect!</string>
<string name="menu_settings">Settings</string>
</resources>
44 1. Android

. 1.12.

Toast:
public static Toast makeText(Context context, int resId, int duration)

Context Activity (Activity -


Context). ,
. Context
Toast .
Toast,
.
, Toast.show(),
.
QuizActivity makeText() -
( 1.13). ,
Eclipse.

, -
.
1.13.
Toast, Toast.
45

, Tab,
. ( -
, . Tab
.)
makeText(Context, int, int).
.
;
QuizActivity.this. Tab, -
, , 1.13.

1.13. (QuizActivity.java)
...
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT).show();
}
});

mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
}
});

makeText() QuizActivity Context.


, this . -
, this View.OnClickListener.

, Toast . -
, .


Android -
. Android,
.
Android (AVD, Android Virtual Device),
Window Android Virtual Device Manager.
AVD Manager, New... .
-
. Galaxy Nexus Google APIs
46 1. Android

(Google Inc.) API Level 17, . 1.13.


Windows, , AVD
(RAM) 1024 512. OK.


Windows
512
1024

. 1.13. Android

,
GeoQuiz. Package Explorer
GeoQuiz. Run As Android Application. Eclipse
, -
. Eclipse ,
LogCat .
, , -
GeoQuiz .
. ( ,
, , AVD
. AVD -
.)
: Android 47

GeoQuiz ,
LogCat. ;
. Text
, .

. 1.14. NullException 21

.
.
; , , .
Back (U- ),
Eclipse, .
, -
. 2 GeoQuiz ,
.

:
Android
, , -
Android. , Eclipse
, . -
Android , AndroidManifest.xml
( ) .apk.
, .
( .apk ,
.
Android http://http://developer.
android.com/tools/publishing/preparing.html.)
activity_quiz.xml View ? -
aapt (Android Asset Packaging Tool)
. -
.apk. , setContentView()
onCreate() QuizActivity, QuizActivity LayoutInflater
View, .
48 1. Android

(
XML.
, -
, SDK;
3.)
, XML,
, 8.


Java

-
Java (.class)

-

dalvik

- -
- dalvik (.dex)

Android
(.apk)
apk

. 1.15. GeoQuiz
Android 49

. 1.16. activity_quiz.xml

Android
, ,
Eclipse. ADT -
Android (, aapt),
Eclipse.
, - -
Eclipse. -
. maven
ant. Ant , .
:
, ant .
, tools/ platform-tools/ Android SDK
.
:
$ android update project -p .
50 1. Android

Eclipse build.xml ant. -


build.xml ; .
. .apk,
:
$ ant debug

. .apk, -
bin/--debug.apk. .apk ,
:
$ adb install bin/--debug.apk

,
.
2 Android MVC

GeoQuiz
.

. 2.1. !

GeoQuiz TrueFalse.
/.
TrueFalse,
QuizActivity.
52 2. Android MVC


Package Explorer com.bignerd-
ranch.android.geoquiz New Class. TrueFalse,
java.lang.Object Finish.

. 2.2. TrueFalse

TrueFalse.java :

2.1. TrueFalse
public class TrueFalse {
private int mQuestion;

private boolean mTrueQuestion;

public TrueFalse(int question, boolean trueQuestion) {


mQuestion = question;
mTrueQuestion = trueQuestion;
}
}
get- set- 53

mQuestion int, String?


( int) .
.
mTrueQuestion , .
get- set-. -
Eclipse .

get- set-
Eclipse m -
is get .
Eclipse ( Eclipse Mac, Windows Preferences
Windows). Java Code Style.
Conventions for variable names: Fields (. 2.3).
Edit m . s -
. ( GeoQuiz s ,
.)
, Use 'is' prefix for getters that return boolean -
. OK.

. 2.3. Java
54 2. Android MVC

? Eclipse
get- mQuestion, getQuestion()
getMQuestion() isTrueQuestion() isMTrueQuestion().
TrueFalse.java,
Source Generate Getters And Setters... Select
All, get- set- .


()

. 2.4. GeoQuiz

OK. Eclipse .

2.2. get- set-


public class TrueFalse {
private int mQuestion;

private boolean mTrueQuestion;

public TrueFalse(int question, boolean trueQuestion) {


mQuestion = question;
mTrueQuestion = trueQuestion;
}

public int getQuestion() {


return mQuestion;
}

public void setQuestion(int question) {


mQuestion = question;
}
-- Android 55

public boolean isTrueQuestion() {


return mTrueQuestion;
}

public void setTrueQuestion(boolean trueQuestion) {


mTrueQuestion = trueQuestion;
}
}

TrueFalse . QuizActivity
TrueFalse, , GeoQuiz
.
QuizActivity TrueFalse.
TextView Button
.

--
Android
, , . 2.4 : -
, . Android
, --,
MVC (Model-View-Controller). MVC, -
,
.
-. -
,
, , ,
/ . .
; .
Android
.
.
GeoQuiz TrueFalse.

, . :
- , .
Android .
. -
.
GeoQuiz , -
activity_quiz.xml.
; -
. ,
56 2. Android MVC

,
.
Android Activity, Fragment
Service. ( 7, 29.)
GeoQuiz
QuizActivity.
. 2.5 -
, . :
;
-,
.





- -




,


. 2.5. MVC

MVC
, -
.
; ,
.
, -
;
, .
GeoQuiz ,
. GeoQuiz -
Next.
TrueFalse.
57

MVC .
, ,
.
, TrueFalse ,
/. TrueFalse
. , -
, ,
.


MVC
GeoQuiz Next.
Android
XML . GeoQuiz activity_quiz.xml.
, . 2.6. (
, .)

. 2.6.

, :
android:text TextView.
.
TextView android:id.
, QuizActivity.
Button LinearLayout.
activity_quiz.xml .

2.3. TextView (activity_quiz.xml)


<LinearLayout
... >

<TextView
android:id="@+id/question_text_view"

58 2. Android MVC

2.3 ()
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text"
/>

<LinearLayout
... >

...
</LinearLayout>

<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_button" />

</LinearLayout>

activity_quiz.xml. ,
.
res/values/strings.xml.
.

2.4. (strings.xml)
...
<string name="app_name">GeoQuiz</string>
<string name="question_text">Constantinople is the largest city
in Turkey.</string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="next_button">Next</string>
<string name="correct_toast">Correct!</string>
...

strings.xml , ,
.

2.5. (strings.xml)
...
<string name="incorrect_toast">Incorrect!</string>
<string name="menu_settings">Settings</string>
<string name="question_oceans">The Pacific Ocean is larger than
the Atlantic Ocean.</string>
<string name="question_mideast">The Suez Canal connects the Red Sea
and the Indian Ocean.</string>
<string name="question_africa">The source of the Nile River is in Egypt.</string>
<string name="question_americas">The Amazon River is the longest river
in the Americas.</string>
<string name="question_asia">Lake Baikal is the world\'s oldest and deepest
freshwater lake.</string>
...
59

\' -
.
, \n
.
. activity_quiz.xml
.
, GeoQuiz.
QuizActivity.


GeoQuiz QuizActivity
. , activity_quiz.
xml, .
, , QuizActivity

GeoQuiz.
QuizActivity.java. TextView
Button . TrueFalse
.

2.6. TrueFalse (QuizActivity.java)


public class QuizActivity extends Activity {

private Button mTrueButton;


private Button mFalseButton;
private Button mNextButton;
private TextView mQuestionTextView;

private TrueFalse[] mQuestionBank = new TrueFalse[] {


new TrueFalse(R.string.question_oceans, true),
new TrueFalse(R.string.question_mideast, false),
new TrueFalse(R.string.question_africa, false)
new TrueFalse(R.string.question_americas, true),
new TrueFalse(R.string.question_asia, true),
};

private int mCurrentIndex = 0;


...

TrueFalse -
TrueFalse.
( .
.
.)
mQuestionBank, mCurrentIndex
TrueFalse .
60 2. Android MVC

TextView -
.

2.7. TextView (QuizActivity.java)


public class QuizActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);

mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);

mTrueButton = (Button)findViewById(R.id.true_button);
...
}
}

. GeoQuiz.
TextView.
Next. , -
View.OnClickListener.
TextView.

2.8. (QuizActivity.java)
public class QuizActivity extends Activity {
...

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);

mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
...
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
}
});

mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
61

int question = mQuestionBank[mCurrentIndex].getQuestion();


mQuestionTextView.setText(question);
}
});
}
}

mQuestionTextView .
, 2.9. -
mNextButton onCreate(Bundle)
.

2.9. updateQuestion() (QuizActivity.java)


public class QuizActivity extends Activity {
...
private void updateQuestion() {
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
...
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);

mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
updateQuestion();
}
});

updateQuestion();
}
}

GeoQuiz Next.
, . -
,
.
, QuizActivity, :
private void checkAnswer(boolean userPressedTrue)

, ,
: True False.
TrueFalse. ,
.
62 2. Android MVC

QuizActivity.java checkAnswer(boolean),
2.10.

2.10. checkAnswer(boolean) (QuizActivity.java)


public class QuizActivity extends Activity {
...
private void updateQuestion() {
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}

private void checkAnswer(boolean userPressedTrue) {


boolean answerIsTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();

int messageResId = 0;

if (userPressedTrue == answerIsTrue) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}

Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)


.show();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
}

checkAnswer(boolean), -
2.11.

2.11. checkAnswer(boolean) (QuizActivity.java)


public class QuizActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...

mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT).show();
checkAnswer(true);
}
});
63

mFalseButton = (Button)findViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
checkAnswer(false);
}
});

mNextButton = (Button)findViewById(R.id.next_button);
...
}
}

GeoQuiz .
.


,
GeoQuiz .


. Mac,
. Windows
adb (Android Debug Bridge). Windows
adb, .
, ,
Devices. DDMS,
DDMS Eclipse. Devices
.
AVD, .
, Java
.
, -
adb. Devices
, , .
. Reset adb. , -
.
, -
Android. http://developer.android.com/tools/device.
html forums.bignerdranch.com
.
64 2. Android MVC


, -
Google Play.
Android 4.1 Settings
Applications. , Unknown sources
.
Android 4.2 Settings Security
Unknown sources.
USB.
Android 4.0 Settings Applications
Development USB debugging.
Android 4.0 4.1 Settings Developer options.
Android 4.2 Developer options .
, Settings About Tablet/Phone Build Number
7 . Settings, Developer options
USB debugging.
, .
,
http://developer.android.com/tools/device.html.
GeoQuiz , . Eclipse
,
. ; GeoQuiz
. ( , GeoQuiz
, , -
.)


GeoQuiz , -
, Next ,
.
(http://www.
bignerdranch.com/solutions/AndroidProgramming.zip).
Eclipse .
02_MVC/GeoQuiz/res.
drawable-hdpi, res/drawable-mdpi drawable-xhdpi.
.
mdpi (~160 dpi)
hdpi (~240 dpi)
xhdpi (~320 dpi)
65

( ldpi,
.)
arrow_right.png arrow_left.png.
, .
-
, -
. ,
.



GeoQuiz.
Package Explorer res -
, .
Package Explorer.

. 2.7. drawable GeoQuiz

, , -
. 2.8. Copy Files,
.
66 2. Android MVC

. 2.8. ( )

. .png,
.jpg .gif, res/drawable, -
. (,
.)
, , gen/R.java
R.drawable. -
: R.drawable.arrow_left R.drawable.arrow_right.
,
; -
.
, .
Android 3 .
.

XML
. -
Next , .
XML?
, .
activity_quiz.xml Button.

2.12. Next (activity_quiz.xml)


<LinearLayout
... >
...

<LinearLayout
... >
...
</LinearLayout>

<Button
android:id="@+id/next_button"
67

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_question_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
/>

</LinearLayout>

XML .
@string/.
@drawable/.
res , -
3.
GeoQuiz .
, , .
, GeoQuiz . GeoQuiz
Next, ,
. ( , Control+F12/
Ctrl+F12.)
. -
?
-
3.

. -
,
. ,
.
. ,
-
Android
Android.
- , ,
. ,
forums.bignerdranch.com. -
, ,
.
,
Eclipse .
Package Explorer,
Copy, .
68 2. Android MVC

,
Package Explorer .

. TextView
Next , , -
TextView.
. TextView View.OnClickListener,
Button, TextView -
View.

.
.
, . 2.9.

. 2.9. !

. ,
.

. Button ImageButton
, ,
.
69

. 2.10.

ImageButton (
Button).
ImageButton ImageView
Button, TextView.

. 2.11. ImageButton Button

text drawable Next ImageView:


<Button ImageButton
android:id="@+id/next_button"

70 2. Android MVC

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_question_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
android:src="@drawable/arrow_right"
/>

, QuizActivity,
ImageButton.
ImageButton, Eclipse -
android:contentDescription.
. ,
, ( -
).
, android:contentDescription Image-
Button.
3 Activity

Activity . -
: -
, .
Activity , -
. . 3.1 (

, . )

Activity , -

. 3.1,


.

( )
onCreate(Bundle).
, .
, onCreate()

:

(
setContentView(int));
( )
;
-
-
;

, onCre-

ate() Activity

. -
, Android . 3.1.
. Activity
72 3. Activity

Activity
,
QuizActivity.
.


Android android.util.Log
. Log
. :
public static int d(String tag, String msg)

d debug () .
( Log .)
, .
TAG,
. .
QuizActivity.java TAG QuizActivity.

3.1. TAG (QuizActivity.java)


public class QuizActivity extends Activity {

private static final String TAG = "QuizActivity";


...
}

onCreate() Log.d() .

3.2. OnCreate() (QuizActivity.java)


public class QuizActivity extends Activity {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);

...

Eclipse , Log, Command+Shift+O


(Ctrl+Shift+O) . Eclipse
; android.util.Log.
QuizActivity.
Activity 73

3.3. (QuizActivity.java)
} // onCreate(Bundle)

@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart() called");
}

@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause() called");
}

@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume() called");
}

@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop() called");
}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() called");
}

-
. ,
- onCreate();
.
, @Override.
, .
, :
public class QuizActivity extends Activity {

@Override
public void onCreat(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
}

...
74 3. Activity

Activity onCreat(Bundle),
. ,
QuizActivity.onCreat(Bundle).

LogCat

,
LogCat , -
Android SDK.
LogCat, -
Window Show View Other...
Android LogCat,
OK (. 3.2).
LogCat
,
. ,
LogCat
.
,
LogCat -
-
(. 3.3).
LogCat ,
. 3.2. LogCat .
; LogCat
.
Eclipse ,
. 3.4. LogCat,
(
Eclipse).
GeoQuiz; LogCat .
.
. TAG LogCat TAG,
QuizActivity.
( , , LogCat
. Window Show View Other... De-
vices. , , LogCat.)
, TAG.
LogCat + ;
. QuizActivity
QuizActivity by Log Tag: (. 3.5).
LogCat 75

. 3.3. LogCat

. 3.4. Eclipse LogCat


76 3. Activity

. 3.5. LogCat

OK; ,
QuizActivity (. 3.6). GeoQuiz
QuizActivity.

. 3.6. GeoQuiz ,

( , QuizActivity
LogCat.)
. Back, -
LogCat. onPause(),
onStop() onDestroy().

. 3.7. Back

Back, Android: ,
. Android ,
.
LogCat 77

GeoQuiz. Home
LogCat. onPause() onStop(),
onDestroy().

. 3.8. Home

.
Recents Home (. 3.9).
Recents Home.

Home

Back Recents

. 3.9. Home, Back Recents

GeoQuiz LogCat.
, .
Home Android: ,
. Android ,
.
78 3. Activity

.
, .
, , -
.
.
, .

.
.


, 2. GeoQuiz,
Next ,
. ( , Control+F12/Ctrl+F12).
GeoQuiz . ,
, LogCat.

. 3.10. QuizActivity

, QuizActivity, ,
, . -
.
- . QuizAc-
tivity mCurrentIndex 0,
. , -
, .



. -
,
. , ,
79

, , , ,
, .
,
. ,

.

; . ,
(, ) .
, -
, .
, , ,
Android .


LogCat. ( LogCat,
Window Show View...)
Package Explorer res
. layout-land.

. 3.11.
80 3. Activity

activity_quiz.xml res/layout/ res/layout-land/.


: .
.
, .
-land . -
res Android ,
.
, Android,
http://developer.
android.com/guide/topics/resources/providing-resources.html. -
15.
, Android -
res/layout-land.
res/layout/.
,
. . 3.12.

. 3.12.

LinearLayout FrameLayout -
ViewGroup .

android:layout_gravity.
android:layout_gravity TextView, LinearLayout
Button. Button LinearLayout .
layout-land/activity_quiz.xml , -
. 3.12. 3.4.
81

3.4. (layout-land/activity_quiz.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<TextView
android:id="@+id/question_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"
android:padding="24dp" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_gravity="center_vertical|center_horizontal"
android:orientation="horizontal" >
...
</LinearLayout>

<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_gravity="bottom|right"
android:text="@string/next_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
/>

</LinearLayout>
</FrameLayout>

GeoQuiz. ,
. , ,
QuizActivity.
, .
QuizActivity.
Android ,
. QuizActivity , -
setContentView(R.layout.activity_quiz).
QuizActivity.onCreate().
82 3. Activity

Android QuizActivity
, -
.

. 3.13. QuizActivity

, Android
.
(,
), .


Android .
-
, ,
GeoQuiz.
, QuizActivity, ,
mCurrentIndex.
(, -
). Activity
protected void onSaveInstanceState(Bundle outState)

onPause(), onStop() onDestroy().


onSaveInstanceState() -
Bundle ,
.
Bundle. onCreate(Bundle):
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
onSaveInstanceState(Bundle) 83

onCreate() onCreate() -
Bundle.

.

onSaveInstanceState(Bundle)
onSaveInstanceState() , -
Bundle, onCreate().
mCurrentIndex .
QuizActivity.java , -
-.

3.5. (QuizActivity.java)
public class QuizActivity extends Activity {

private static final String TAG = "QuizActivity";

private static final String KEY_INDEX = "index";

Button mTrueButton;
...

onSaveInstanceState() mCurrentIndex
Bundle .

3.6. onSaveInstanceState() (QuizActivity.java)


mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});

updateQuestion();
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, "onSaveInstanceState");
savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
}

, onCreate() , -
mCurrentIndex.
84 3. Activity

3.7. onCreate() (QuizActivity.java)


...

if (savedInstanceState != null) {
mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
}

updateQuestion();
}

GeoQuiz Next.
, QuizActivity
.
, Bundle
, Serializable.
, onSaveInstanceState(),
Serializable.
onSaveInstanceState()
. ; -
.
,
Android .

Activity
onSaveInstanceState(Bundle) -
. ,
, Android .
Android .
,
. ,
, onSaveInstanceState().
onSaveInstanceState() Bundle. -
Bundle (activity record).
, ,
(. 3.14).
, Activity ,
. -
.
,
onDestroy(). , onPause()
onSaveInstanceState() . onSaveInstanceState()
Bundle, onPause()
.
: onSaveInstanceState(Bundle) 85

(-
-
;
-
)
Android

-
,
- (-

)

(-

- -

(-

)

. 3.14.

Android ,
.
, , .
-
.
? Back,
. .
;
, .

:
onSaveInstanceState(Bundle)
onSaveInstanceState(Bundle), ,
, .
.
86 3. Activity

.
Settings. , -
.

. 3.15. Settings

Settings Development options.


; Dont keep activities.
Home.
. ,
Android .
, , .
Back ( Home)
, . -
Back , .
,
Dev Tools.
http://developer.android.com/tools/debugging/debugging-devtools.html.
: 87

. 3.16.

:

android.util.Log -
, , , -
. Android
(. 3.1). Log.
Log.
3.1.

ERROR Log.e()

WARNING Log.w()

INFO Log.i()

DEBUG Log.d() ( )

VERBOSE Log.v() !
88 3. Activity

:
, Throwable,
,
. 3.8
.
Java String.format, .

3.8. Android
// "debug"
Log.d(TAG, "Current question index: " + mCurrentIndex);

TrueFalse question;
try {
question = mAnswerKey[mCurrentIndex];
} catch (ArrayIndexOutOfBoundsException ex) {
// "error"
//
Log.e(TAG, "Index was out of bounds", ex);
}
4
Android

, , . -
, LogCat, Android Lint Eclipse.
, - .
QuizActivity.java onCreate(Bundle), -
mQuestionTextView.

4.1. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
//mQuestionTextView = (TextView)findViewById(R.id.question_text_view);

mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new View.OnClickListener() {
...
});

...
}

GeoQuiz , .
. 4.1 , .
Android , -
. , , ,
.
90 4. Android

. 4.1. GeoQuiz

DDMS
Eclipse Window Open Perspective DDMS.

. 4.2. DDMS
91

(perspective) Eclipse .
,
; Eclipse .
, .
, Eclipse . -
,
Window Reset Perspective...
, , -
Java. , ,
Eclipse.
.
. 4.2 DDMS (Dalvik Debug Monitor Service). -
DDMS Android. -
DDMS LogCat Devices.
Devices , -
. ,
.
, ,
? -
Reset adb. adb
. , LogCat ?
, , LogCat
.


. , , -
LogCat. ,
. Android.
;
, , ;
, , .
,
. java.lang.NullPointerException.

. , ,
. ; Eclipse
.
mQuestion-
TextView updateQuestion(). NullPointerException -
: .
mQuestionTextView,
.
92 4. Android

. 4.3. LogCat

: ,
LogCat
. ,
.
, .
, . -
, ,
. , -
DDMS Eclipse Devices.
LogCat .



. , -
Next, .
.
93

QuizActivity.java mNextButton -
, mCurrentIndex.

4.2. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

...
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
//mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
...
}

GeoQuiz Next. .

. 4.4. Next

. , -
.
, :
, updateQuestion().
94 4. Android

, .
:
.


updateQuestion()
QuizActivity:

4.3. Exception
public class QuizActivity extends Activity {
...

public void updateQuestion() {


Log.d(TAG, "Updating question text for question #" + mCurrentIndex,
new Exception());
int question = mAnswerKey[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}

Log.d Log.d(String, String, Throwable) -



AndroidRuntime. ,
updateQuestion().
Log.d() .
Exception() -
. ,
.
GeoQuiz, Next LogCat.

. 4.5.
95

,
Exception. , -
set- onTextChanged().
; ,
. ;
.
-
, , , ,
.
, LogCat .
, , ,
.
, -
, , .
http://stackoverflow.com forums.bignerdranch.com, -
.
LogCat, (
LogCat).
, TAG log
QuizActivity.java.

4.4. , (QuizActivity.java)
public class QuizActivity extends Activity {
...

public void updateQuestion() {


Log.d(TAG, "Updating question text for question #" + mCurrentIndex,
new Exception());
int question = mAnswerKey[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}

TAG
. , ,
.


Eclipse.
updateQuestion(), , .
,
, .
QuizActivity.java updateQuestion().
.
; .
96 4. Android

. 4.6.

,
( ).
GeoQuiz
Debug As Android Application.
, ,
, .
, -
. GeoQuiz QuizActivity.
onCreate(Bundle), updateQuestion(),
.
,
Debug. Yes,
Debug.

. 4.7. Debug

Eclipse Debug.
. QuizActivity.java,
, .
Debug .

. , updateQuestion()
onCreate(Bundle). Next,
Resume, .
Next, , (
).
, ,
. Variables. -
.
97

, : this ( QuizActiv-
ity). this
. , . 4.9.
- -
-

. 4.8. Debug

. 4.9.

:
(public) ;
(
);
(protected) ;
(private) .
98 4. Android

this .
, QuizActivity, ,
, Activity, Activity, . .
: mCurrentIndex.
mCurrentIndex. , 0.
. ,
. , Step Over
( , Step Return). (-
access$1(QuizActivity) Step Return
.)
OnClickListener
mNextButton, updateQuestion().
, .
, - , -
. :
, .
, DDMS Devices
-.
: Disconnect (. . 4.8).
Java OnClickListener .

4.5. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...

mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
...
}

. Breakpoints
Variables. ( ,
Window Show View Breakpoints.) -
X .
: -
.
? , ,
.
99

,
. ,
, -
, , .
.
( Debug As Android Application),
, -
.


,
.
mQuestionTextView. Run Add Java
Exception Breakpoint..., .

. 4.10.

,
, .

, .
Android ,
. ,

Suspend on caught exceptions.
100 4. Android

, . Run-
timeException . RuntimeException
NullPointerException, ClassCastException
, ,
.
, ,
-. Debug Breakpoints.
RuntimeException.
Subclasses of this exception,
NullPointerException.
GeoQuiz. , -
, .
, ,
,
. , .

File Explorer
DDMS -
, .
File Explorer .

. 4.11.

, -
.
/data, , -
. Android
/data/data/[ ].
Android 101

. 4.12. GeoQuiz ( )

, 17 -
, .

Android
Android Java.
, Android
(, ), Java .

Android Lint
Android Lint Android.
, .
Android Lint Android
, . ,
Android Lint .
6 , Android Lint -
. , Android Line ,
XML. QuizActivity
.

4.6. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

mQuestionTextView = (TextView)findViewById(R.id.question_text_view);

mTrueButton = (Button)findViewById(R.id.true_button);

102 4. Android

4.6 ()
mTrueButton = (Button)findViewById(R.id.question_text_view);

...
}

-
TextView Button ,
. Java , Android
Lint .
Package Explorer GeoQuiz
Android Tools Run Lint: Check for Common Errors;
Lint Warnings (. 4.13).

. 4.13. Lint

Android Lint .
:
Button . onCreate(Bundle).

4.7. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

mQuestionTextView = (TextView)findViewById(R.id.question_text_view);

mTrueButton = (Button)findViewById(R.id.question_text_view);
mTrueButton = (Button)findViewById(R.id.true_button);

...
}

Android Lint .
merge layout-land/activity_quiz.xml. , Android Lint
. FrameLayout merge, FrameLayout
.
Android 103

R
, -
( , ).
-
, Eclipse , .
. -
, .
Android Lint
Window Run Android Lint. Line -
.

Project Clean. Eclipse , -
.
XML
R.java ,
.
XML. ,
.
, R.java .
gen
Eclipse R.java,
gen. Eclipse
gen R.
( ), -
.
.
Android Lint. -
.
, Eclipse,
http://stackoverflow.com
http://forums.bignerdranch.com.
5

GeoQuiz .
, ;
,
.

. 5.1. CheatActivity

, QuizActivity
, .
105

. 5.2. QuizActivity ,

Android?
:
-
;
( -
onCreate(Bundle));
() (-
) .


.
CheatActivity, CheatActivity.
strings.xml , .

5.1. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<string name="question_asia">Lake Baikal is the world\'s oldest and deepest
freshwater lake.</string>
<string name="cheat_button">Cheat!</string>
<string name="warning_text">Are you sure you want to do this?</string>
<string name="show_answer_button">Show Answer</string>
<string name="judgment_toast">Cheating is wrong.</string>

</resources>
106 5.


. 5.1 , CheatActivity.
. 5.3.

. 5.3. CheatActivity

, res/layout
Package Explorer New Other... Android
Android XML Layout File (. 5.4). Next.

. 5.4.
107

activity_cheat.xml -
LinearLayout. Finish.

. 5.5.

, XML. :

<?xml version="1.0" encoding="utf-8"?>

XML,
, ( -
).
( , ,
. fragment_crime.xml -
res/layout res/layout, Eclipse .
Eclipse;
XML Java , .
, , Android).
LinearLayout . -
android:gravity .
108 5.

XML activity_cheat.xml . 5.3.


8 XML
. 5.3, XML
. 5.2.

. 5.6. activity_cheat.xml

5.2. (activity_cheat.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"
android:gravity="center">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/warning_text_view" />

<TextView
android:id="@+id/answerTextView"
109

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp" />

<Button
android:id="@+id/showAnswerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show_answer_button" />

</LinearLayout>

, ,
.
activity_cheat.xml
, ,
.
-
,
. , .
,
.


Package Explorer com.bignerdranch.
android.geoquiz New Class.
CheatActivity. Super-
class: android.app.Activity (. 5.7).
Finish; Eclipse CheatActivity.java .
onCreate(), ,
activity_cheat.xml, setContentView().

5.3. onCreate() (CheatActivity.java)


public class CheatActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
}

onCreate() CheatActivity -
. : CheatActivity
.
110 5.

. 5.7. CheatActivity


(manifest) XML ,
Android. AndroidMani-
fest.xml .
Package Explorer AndroidManifest.xml -
. ,
AndroidManifest.xml .
,
.
QuizActivity, . CheatActivity
.
AndroidManifest.xml CheatActivity ( 5.4).

5.4. CheatActivity (AndroidManifest.xml)


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.geoquiz"
Cheat QuizActivity 111

android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.bignerdranch.android.geoquiz.QuizActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CheatActivity"
android:label="@string/app_name" />
</application>

</manifest>

android:name .
, ,
package manifest .
,
CheatActivity. -
.

Cheat QuizActivity
, QuizActivity,
CheatActivity. ,
layout/activity_quiz.xml layout-land/activity_quiz.xml.

LinearLayout.
Next.

5.5. Cheat! (layout/activity_quiz.xml)


...
</LinearLayout>

<Button
android:id="@+id/cheat_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

112 5.

5.5 ()
android:text="@string/cheat_button" />

<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_button" />

</LinearLayout>

-
FrameLayout.

5.6. Cheat! (layout-land/activity_quiz.xml)


...
</LinearLayout>

<Button
android:id="@+id/cheat_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:text="@string/cheat_button" />

<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="@string/next_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp" />

</FrameLayout>

QuizActivity.java. , -
View.onClickListener Cheat!.

5.7. Cheat! (QuizActivity.java)


public class QuizActivity extends Activity {
...
private Button mNextButton;
private Button mCheatButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
...

mCheatButton = (Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
113

// CheatActivity
}
});

updateQuestion();
}

...
}

CheatActivity.


,
Activity:
public void startActivity(Intent intent)

, startActivity() ,
Activity. ,
. startActivity(), -
.
, , ActivityManager.
ActivityManager Activity onCreate().

Android

. 5.8.

ActivityManager , ? -
Intent.


(intent) , -
. ,
(services), (broadcast
receivers) (content providers).
114 5.

,
Intent ,
.
ActivityManager, -
, :
public Intent(Context packageContext, Class<?> cls)

Class , ActivityManager.
Context ActivityManager, Class.

Android

. 5.9. : ActivityManager ,

mCheatButton Intent, CheatActiv-


ity, startActivity(Intent) ( 5.8).

5.8. CheatActivity (QuizActivity.java)


...

mCheatButton = (Button)findViewById(R.id.cheat_button);
mCheatButton.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
startActivity(i);
}
});

updateQuestion();
}

, ActivityManager -
, Class. -
, .
, ActivityNotFoundException .
.
115


Intent Class Context (explicit)
. .
, -
ActivityManager, .

.
-
, (implicit) .
21.
GeoQuiz. Cheat!;
.
Back. CheatActivity , -
QuizActivity.


, QuizActivity CheatActivity,
. . 5.10 ,
.

True?

. 5.10. QuizActivity CheatActivity

QuizActivity CheatActivity
CheatActivity.
Back, QuizActivity, -
CheatActivity . QuizActivity
, .
QuizActivity CheatActivity.
116 5.


CheatActivity ,

mAnswerKey[mCurrentIndex].isTrueQuestion();

(extra) Intent, -
startActivity(Intent).
,
.
, .

Android

. 5.11. :

- , -
mCurrentIndex QuizActivity.
onSaveInstanceState(Bundle).
Intent.putExtra()
,
public Intent putExtra(String name, boolean value)

Intent.putExtra() ,
. String,
.
CheatActivity.java .

5.9. (CheatActivity.java)
public class CheatActivity extends Activity {

public static final String EXTRA_ANSWER_IS_TRUE =


"com.bignerdranch.android.geoquiz.answer_is_true";

...
117

, -
, .
5.9,
.
QuizActivity .

5.10. (QuizActivity.java)
...
mCheatButton.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(i);
}
});

updateQuestion();
}

,
Intent .

public boolean getBooleanExtra(String name, boolean defaultValue)

getBooleanExtra() ,
, .
CheatActivity .

5.11. (CheatActivity.java)
public class CheatActivity extends Activity {
...

private boolean mAnswerIsTrue;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);

mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);


}
}

: Activity.getIntent() Intent,
startActivity(Intent).
, CheatActivity , -
TextView Show Answer.
118 5.

5.12. (CheatActivity.java)
public class CheatActivity extends Activity {
...
private boolean mAnswerIsTrue;

private TextView mAnswerTextView;


private Button mShowAnswer;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);

mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);

mAnswerTextView = (TextView)findViewById(R.id.answerTextView);

mShowAnswer = (Button)findViewById(R.id.showAnswerButton);
mShowAnswer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnswerIsTrue) {
mAnswerTextView.setText(R.string.true_button);
} else {
mAnswerTextView.setText(R.string.false_button);
}
}
});
}
}

: TextView TextView.
setText(int). TextView.setText() ;
, .
GeoQuiz. Cheat!, CheatAc-
tivity. Show Answer, .


.
; CheatActivity
QuizActivity, .
,
Activity:
public void startActivityForResult(Intent intent, int requestCode)

, . -
,
, .
,
, .
119

QuizActivity mCheatButton
startActivityForResult(Intent, int).

5.13. startActivityForResult() (QuizActivity.java)


...
mCheatButton.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(i);
startActivityForResult(i, 0);
}
});

updateQuestion();
}

QuizActivity .
,
0.


,
:
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)

, resultCode : Activ-
ity.RESULT_OK Activity.RESULT_CANCELED. (
RESULT_FIRST_USER
.)
, -
, .
, OK Cancel,
,
.
.
setResult() .

, .
,
startActivityForResult() . setResult()
, Back
Activity.RESULT_CANCELED.
120 5.


QuizActivity
. , Intent, -
, Activity.setResult(int, Intent)
QuizActivity.
,
CheatActivity CheatActivity. -
, CheatActivity QuizActivity. ,
CheatActivity.
CheatActivity ,
, CheatActivity.
CheatActivity ,
, .
Show Answer.

5.14. (CheatActivity.java)
public class CheatActivity extends Activity {

public static final String EXTRA_ANSWER_IS_TRUE =


"com.bignerdranch.android.geoquiz.answer_is_true";
public static final String EXTRA_ANSWER_SHOWN =
"com.bignerdranch.android.geoquiz.answer_shown";
...
private void setAnswerShownResult(boolean isAnswerShown) {
Intent data = new Intent();
data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
setResult(RESULT_OK, data);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
...

// ,
//
setAnswerShownResult(false);
...
mShowAnswer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnswerIsTrue) {
mAnswerTextView.setText(R.string.true_button);
} else {
mAnswerTextView.setText(R.string.false_button);
}
setAnswerShownResult(true);
}
});
}
121

Show Answer, CheatActivity


setResult(int, Intent).
, Back QuizActivity,
ActivityManager :
protected void onActivityResult(int requestCode, int resultCode, Intent data)

QuizActivity,
, setResult().
. 5.12.

Android


Cheat!,
onClick(View)


Show Answer,

setResult()


Back

. 5.12. GeoQuiz

: onActivityResult(int, int, Intent)


QuizActivity .


QuizActivity.java , -
CheatActivity. onActivityResult()
.
122 5.

5.15. onactivityResult() (QuizActivity.java)


public class QuizActivity extends Activity {
...
private int mCurrentIndex = 0;

private boolean mIsCheater;


...

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null) {
return;
}
mIsCheater = data.getBooleanExtra(CheatActivity.EXTRA_ANSWER_SHOWN, false);
}
...
}

onActivityResult() QuizActivity ,
.
, .
, checkAnswer(boolean) QuizActivity. -
, , .

5.16. mIsCheater (QuizActivity.java)


private void checkAnswer(boolean userPressedTrue) {
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();

int messageResId = 0;

if (mIsCheater) {
messageResId = R.string.judgment_toast;
} else {
if (userPressedTrue == answerIsTrue) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}
}

Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)


.show();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
...
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
Android 123

mIsCheater = false;
updateQuestion();
}
});
...
}

GeoQuiz. ,
.

Android
,
. , GeoQuiz ,
, . ,
. GeoQuiz
QuizActivity.
GeoQuiz, QuizActivity
. -
intent-filter QuizActivity ( 5.17).

5.17. QuizActivity (AndroidManifest.xml)


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...
<application
... >
<activity
android:name="com.bignerdranch.android.geoquiz.QuizActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CheatActivity"
android:label="@string/app_name" />
</application>

</manifest>

QuizActivity ,
Cheat!. CheatActivity QuizActivity.
(. 5.13).
Back CheatActivity ,
QuizActivity .
124 5.


Cheat! Back

. 5.13. GeoQuiz

Activity.finish() CheatActivity CheatActivity .


GeoQuiz Eclipse Back QuizActivity, -
QuizActivity ,
GeoQuiz.

GeoQuiz
Eclipse Back

. 5.14. Home Eclipse

GeoQuiz (launcher), Back


QuizActivity .
125


GeoQuiz Back

. 5.15. GeoQuiz

Back ,
.
, ActivityManager (back stack)
.
; ,
ActivityManager
, .
, .

, ,
. , ,
.
GeoQuiz ,
. .
,
:
, CheatActivity, -
.
QuizActivity, -
mIsCheater.
Next , ,
, .
!
6 Android SDK

, GeoQuiz, -
Android.
,
.

Android SDK
6.1 SDK, And-
roid, , , 2013 .
6.1. API Android,

%
API

17 Jelly Bean 4.2 1,6

16 4.1 14,9

15 Ice Cream Sandwich (ICS) 4.0.3, 4.0.4 28,6

13 Honeycomb ( ) 3.2 0,9

12 3.1.x 0,3

10 Gingerbread 2.3.32.3.7 43,9

9 2.3.3, 2.3.1, 2.3 0,2

8 Froyo 2.2.x 7,5

7 Eclair 2.1.x 1,9


Android 127

. -
, Ice Cream Sandwich was Android 4.0
(API 14) , -
Android 4.0.3 4.0.4 (API 15).
, . 6.1 , -
: Android -
.
2013 Froyo Gingerbread
SDK. Android 2.3.7 ( Gingerbread)
2011 . , Android 4.2,
2012, 1,6 % .
( http://developer.android.com/
resources/dashboard/platform-versions.html.)
Android?
- Android
. -
, .
,
.
-
Android.
Android
, Google. -
,
Google
, .

Android.

Android
- -
Android.
, Android -
, Android: Froyo,
Gingerbread, Honeycomb, Ice Cream Sandwich Jelly Bean,
-.
, . -
, Android
. ,
( 22).
, Google TV ( Android)
, -
.
128 6. Android SDK

.
,
.

Honeycomb
Android -
Honeycomb. Honeycomb
Android, -
. Honeycomb
Google TV, ,
Ice
Cream Sandwich. , , .
,
Gingerbread ,
, .
, , .
, Android -
Gingerbread (API 10)
Honeycomb (API 11). Android
; , -
.
Android.

, . ,
, (
) .
( , ).
Android,
Gingerbread . ,
.
GeoQuiz
SDK. (, Android SDK API -
.)
, , -
.
SDK (Minimum Required SDK)
SDK (Target SDK) . AndroidManifest.xml
Package Explorer. uses-sdk android:minSdkVersion
android:targetSdkVersion.

6.1. minSdkVersion (AndroidManifest.xml)


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.geoquiz"
Android 129

android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
...

</manifest>

. 6.1. ?

SDK (Minimum Required SDK)


, , -
. minSdkVersion ,
.
API 8 (Froyo), Android GeoQuiz
Froyo . Android GeoQuiz
, , Eclair.
. 6.1, , Froyo
SDK: -
95 % .
130 6. Android SDK

SDK (Target SDK)


targetSdkVersion Android, API -
. Android.
SDK? SDK -

. , ,
, .
http://developer.android.com/
reference/android/os/Build.VERSION_CODES.html. -
, ,
SDK. SDK ,
, .
.

SDK (Compile With)


SDK (CompileWith . 6.1) SDK
. .
SDK , SDK
, .
Android SDK.
SDK, , (build
target), ,
. Eclipse ,
, SDK , SDK -
.
,
GeoQuiz Package Explorer Properties. -
Android; .
Google APIs Android Open Source Project? Google
APIs Android API Google API
Google Maps.
GeoQuiz . Cancel,
.


API
SDK SDK -
, . ,
GeoQuiz , SDK
Froyo (API 8)?
Froyo, .
Android 131

. 6.2.

. -
Android Lint ,
, .
, SDK, Android Lint
.
GeoQuiz API 8 .
API 11 , .
QuizActivity.java. onCreate(Bundle)
, -
.

6.2. (QuizActivity.java)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

132 6. Android SDK

6.2 ()
ActionBar actionBar = getActionBar();
actionBar.setSubtitle("Bodies of Water");

mIsCheater = false;

. -
(
), -
.
, ActionBar.
API 11,
. ActionBar
16, , Froyo.
GeoQuiz Package Explorer
Android Tools Run Lint: Check for Common Errors. -
API 17, .
, Android Lint SDK .
: Class requires API level 11
(current min is8). Android Lint ,
.
? SDK
11. , -
.
Gingerbread , .
, ActionBar -
, Android .

6.3. Android
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


ActionBar actionBar = getActionBar();
actionBar.setSubtitle("Bodies of Water");
}
}

Build.VERSION.SDK_INT Android .
, Honeycomb. (
http://developer.android.com/reference/android/os/Build.VERSION_CODES.
html.)
ActionBar ,
Honeycomb .
Android 133

Froyo, Android Lint .


, .

Lint
, Android Lint , , -
.
onCreate(Bundle).

6.4. Android Lint


@TargetApi(11)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


ActionBar actionBar = getActionBar();
actionBar.setSubtitle("TFFTT");
}
}

. 6.3.

, if ,
Android .

. Android Lint . ,
, .
134 6. Android SDK

6.4
. if : getActionBar(),
if, ,
. @TargetApi(11) (Android
Lint), : .
, , .
, @TargetApi, ,
SDK_INT. ,
.
GeoQuiz Honeycomb
.
GeoQuiz Froyo Gingerbread (-
). ,
, .

Android
Android Lint , API .
, API
, Android.
. Android
SDK , , -
, ,
.
Android -
. http://developer.android.com/.
: (Design), (Develop)
(Distribute).
.
.
, Google
Play . ,
.
.

Android Training , -

API Guides , -

Reference , , , -
. . SDK,

Tools
Android 135

.
, SDK;
docs .
, API getActionBar(),
.
API .
.
, Reference .
;
Activity. .
Methods, Activity.

. 6.4. Activity

, getActionBar() ,
. , getAction-
Bar() API 11.
, Activity , , API 8,
API. ,
136 6. Android SDK

, API level:17.
8. , Android
API 8, .

. 6.5. Activity, API 8

, .
,
,
, . . Android
, - .

.
GeoQuiz TextView API ,
.
TextView , -
. TextView
TextView Android. ,
( CharSequence).
. 137

. 6.6.

XML, -
TextView.
7 UI-
FragmentManager

CriminalIntent.
: , -
, .

. 7.1. CriminalIntent
139

CriminalIntent -
, .
, Twitter,
Facebook . , -
.
CriminalIntent ,
13 . /: -
.

.


, /
:
. -
.
Back
, .
, ,
?
, CriminalIntent .

-
.

- -

. 7.2. /

-
. ,
,
. -
.
:
-
, .
140 7. UI- FragmentManager

.
. , -
setContentView(), .
( )
.
Android.


Android , , -

(fragments).
,
.
.
, , UI-
. UI- , -
.
, .
,
, .
, ,
.
-
, Android .
, /.
-
.
.
-
. ; -
(. 7.3).
.
UI-
, -
/.
, .
, :
, , -
.
11 22,
.
CriminalIntent 141


- -

- -
- - - -

. 7.3.

CriminalIntent
CriminalIntent. . 7.4
, CriminalIntent .

. 7.4. CriminalIntent

. ,
.
, . 7.4, UI- Crime-
Fragment. (host) CrimeFragment
CrimeActivity.
, , -
(. 7.5).
142 7. UI- FragmentManager

.
.

. 7.5. CrimeActivity CrimeFragment

CriminalIntent ;
. . 7.6 CriminalIntent.
, ,
, .
, CrimeFragment , GeoQuiz
: ,
.

-
()

. 7.6. CriminalIntent ( )
143

, . 7.6: Crime, CrimeFragment Crime-


Activity.
Crime .
. -
(,
- !),
Crime.
Crime.
CrimeFragment (mCrime) .
CrimeActivity FrameLayout,
, CrimeFragment.
CrimeFragment LinearLayout EditText.
CrimeFragment EditText (mTitleField)
, .


; . -
Android (New Android Application Project). Crim-
inalIntent com.bignerdranch.android.criminalintent,
. 7.7. API ,
Froyo.

. 7.7. CriminalIntent
144 7. UI- FragmentManager

,
, Next.
Next.
, CrimeActivity Finish
(. 7.8).

. 7.8. CrimeActivity


API 11 Android
. -
CriminalIntent SDK 8,
Android.
, :
Android.
(support library) .
libs/android-support-v4.jar, . -
Fragment (android.support.v4.app.Fragment),
Android API 4 .
-
; .
FragmentActivity (android.
support.v4.app.FragmentActivity). -
, . Honeycomb
145

Android Activity .
Activity
Activity. API -
FragmentActivity Activity,
Activity Android.

Android 4.2 Android 2.3


Android 4.2 Android 2.3

. 7.9.

. 7.9 .
( android.support.v4.app.Fragment)
, ,
.
Package Explorer CrimeActivity.java. -
CrimeActivity FragmentActivity.
onCreateOptionsMenu(Menu). ( CriminalIntent 16.)

7.1. (CrimeActivity.java)
public class CrimeActivity extends Activity FragmentActivity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

146 7. UI- FragmentManager

7.1 ()
getMenuInflater().inflate(R.menu.activity_crime, menu);
return true;
}
}

CrimeActivity, Criminal-
Intent. Crime.

Crime
Package Explorer com.bignerd-
ranch.android.criminalintent New Class.
Crime, java.lang.Object Finish.
Crime.java .

7.2. Crime (Crime.java)


public class Crime {

private UUID mId;


private String mTitle;

public Crime() {
//
mId = UUID.randomUUID();
}
}

mId, ,
get-, mTitle get- set-.

Source Generate Getters and Setters. get- mId,
, ,
getId(), . 7.10.

7.3. get- set- (Crime.java)


public class Crime {
private UUID mId;

private String mTitle;

public Crime() {
mId = UUID.randomUUID();
}

public UUID getId() {


return mId;
}

public String getTitle() {


UI- 147

return mTitle;
}

public void setTitle(String title) {


mTitle = title;
}
}

. 7.10. get- set-

, Crime CriminalIntent
.
, , -
Froyo Gingerbread.
, .

UI-
UI-, :
;
.
148 7. UI- FragmentManager


. 7.11 . -
: ,
, ,
,
.

. 7.11.

.
, . -
,
.

, -
-, . , ;
.
UI- 149

,
CriminalIntent.


UI- -
:
;
.

, .
,

.
, , -
13.
,
. , -
. ,
, .
, -
.
, CrimeActivity CrimeFragment. -
,
CrimeActivity.


UI- -,
.
CrimeActivity FrameLayout, . 7.12.

. 7.12. CrimeActivity

FrameLayout CrimeFragment.
: ;
CrimeFragment. ( )
.
150 7. UI- FragmentManager

CrimeActivity res/layout/activity_crime.xml. -
FrameLayout, . 7.12.
XML 7.4.

7.4. (activity_crime.xml)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

activity_crime.xml -
, ;
, .
CriminalIntent, .
FrameLayout, CrimeActivity
.

. 7.13. FrameLayout

,
FrameLayout. .
UI- 151

UI-
UI- -
:
;
, , -
;
, .

CrimeFragment
CrimeFragment , -
Crime. Crime CrimeFragment
,
.
. 7.14 CrimeFragment. -
LinearLayout, EditText
.

. 7.14. CrimeFragment

, res/layout
Package Explorer New Android XML File. ,
Layout,
fragment_crime.xml. LinearLayout
Finish.
, XML.
LinearLayout . . 7.14,
fragment_crime.xml. 7.5.
152 7. UI- FragmentManager

7.5. (fragment_crime.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"
>
<EditText android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/crime_title_hint"
/>
</LinearLayout>

res/values/strings.xml, crime_title_hint
hello_world menu_settings, -
.

7.6. (res/values/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CriminalIntent</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_crime">CrimeActivity</string>
<string name="crime_title_hint">Enter a title for the crime.</string>
</resources>

. menu_settings
. , Package Explorer
res/menu/activity_crime.xml. ,
, menu_settings.
CriminalIntent,
Package Explorer.
Eclipse .
. -
, fragment_crime.xml.

CrimeFragment
com.bignerdranch.android.criminalintent
New Class. CrimeFragment
Browse, .
Fragment. . android.
support.v4.app.Fragment Fragment .
OK.
( android.support.v4.app.Fragment, ,
CriminalIntent CriminalIntent/libs/.)
UI- 153

. 7.15. Fragment


CrimeFragment , -
.
.
GeoQuiz -
. CriminalIntent
.
Activity,
onCreate(Bundle).
CrimeFragment.java Crime
Fragment.onCreate(Bundle).

7.7. Fragment.onCreate(Bundle) (CrimeFragment.java)


public class CrimeFragment extends Fragment {
private Crime mCrime;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}
}
154 7. UI- FragmentManager

. -, -
Fragment.onCreate(Bundle) , Activity.
onCreate(Bundle) . Fragment.onCreate()
Fragment , -
, .
-, , Bundle
. Fragment.on-
Save InstanceState(Bundle) , Activity.onSave-
InstanceState(Bundle).
, Fragment.onCreate(): -
. Fragment.
onCreate(),
:
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState)

,
View . LayoutInflater ViewGroup
. Bundle , -
.
CrimeFragment.java onCreateView(),
fragment_crime.xml.

7.8. onCreateView() (CrimeFragment.java)


public class CrimeFragment extends Fragment {
private Crime mCrime;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
return v;
}

onCreateView() ,
LayoutInflater.inflate() . -
,
. ,
. false,
.
UI- 155


onCreateView() EditText
. ,
EditText .
7.9. EditText (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(
CharSequence c, int start, int before, int count) {
mCrime.setTitle(c.toString());
}

public void beforeTextChanged(


CharSequence c, int start, int count, int after) {
//
}

public void afterTextChanged(Editable c) {


//
}
});

return v;
}
}

Fragment.onCreateView() ,
Activity.onCreate(). , -
View.findViewById(int). Activity.
findViewById(int), ,
, View.findViewById(int) .
Fragment ,
.
, . -
7.9 ,
TextWatcher. , :
onTextChanged().
onTextChanged() toString() CharSequence,
. ,
Crime.
156 7. UI- FragmentManager

CrimeFragment . ,
CriminalIntent . ,
.
, CrimeFragment
CrimeActivity.

UI- FragmentManager
Honeycomb Fragment, Activity
: , FragmentManager. -

.
FragmentManager :
( ).

. 7.16. FragmentManager

CriminalIntent Fragment-
Manager.
, -
FragmentManager .
FragmentManager. CrimeActivity.java
onCreate().

7.10. FragmentManager (CrimeActivity.java)


public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);

FragmentManager fm = getSupportFragmentManager();
}
}
UI- 157

getSupportFragmentManager(),
FragmentActivity. -
, Honeycomb,
Activity getFragmentManager().


FragmentManager ,
. (
, .)

7.11. CrimeFragment (CrimeActivity.java)


public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);

FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
}

, 7.11, .
add() .
.

7.12. (CrimeActivity.java)
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();

, , ,
. -

. FragmentManager ,
.
FragmentManager.beginTransaction() -
FragmentTransaction . FragmentTransaction -
, FragmentTransaction ,
158 7. UI- FragmentManager

FragmentTransaction void, .
, 7.12 : -
, add, .
add() .
:
CrimeFragment.
: FrameLayout,
activity_crime.xml.
:
FragmentManager, -
.
Fragment-
Manager.
CrimeFragment FragmentManager, -
.

7.13.
(CrimeActivity.java)
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}

, FragmentManager CrimeFragment
FrameLayout. UI-

FragmentManager.
, 7.11,
.
FragmentManager -
R.id.fragmentContainer.
, FragmentManager .
? CrimeActivity.onCre-
ate() CrimeActivity
- .
FragmentManager . -
FragmentManager -
, , .
, -
, fragment null.
UI- 159

CrimeFragment ,
.
CrimeActivity CrimeFragment. ,
CriminalIntent. ,
fragment_crime.xml (. 7.17).

. 7.17. CrimeActivity CrimeFragment

,
, . -
,
CriminalIntent .

FragmentManager
FragmentManager
.
FragmentManager
. onAttach(Activity), onCreate(Bundle) onCreateV-
iew() FragmentManager.
onActivityCreated() onCreate()
-. CrimeFragment CrimeActivity.onCreate(),
.
160 7. UI- FragmentManager

. 7.18. ()

, ,
, ? Fragment-
Manager ,
. ,
, ,
onAttach(Activity), onCreate(Bundle), onCreateView(), onActivityCreated(Bundle),
onStart() onResume().
,
FragmentManager -

.
,
.
-
: Activity.onCreate(),
onActivityCreated() Activity.onCreate().
: Honeycomb, ICS, Jelly Bean . . 161

Activity.onStart(). ? SDK
Honeycomb onActivityCreated()
FragmentActivity,
. ; onStart()
Activity.onCreate().



-
. :
, ,
.
; , .
, ,
.
- , ,
, ( ).
YAGNI.
YAGNI You Arent Gonna Need It ( );
, , , -
. ? YAGNI. YAGNI
.
, -
. UI-
, .
, ,
, -
.
, ,
.
, , , : AUF,
Always Use Fragments ( ). -
, ,
. AUF!

:
Honeycomb, ICS, Jelly Bean . .
,
SDK API 11.
SDK,
162 7. UI- FragmentManager

.
.
,
:
SDK API
11 .
Activity (android.app.Activ-
ity) FragmentActivity. API 11
.
android.app.Fragment android.support.v4.app.Fragment.
FragmentManager, getFragmentMan-
ager() getSupportFragmentManager().
8

,
CriminalIntent.

Crime
Crime.java . Date
, mSolved , .

8.1. Crime (Crime.java)


public class Crime {
private UUID mId;
private String mTitle;
private Date mDate;
private boolean mSolved;

public Crime() {
mId = UUID.randomUUID();
mDate = new Date();
}
...
}

Date Date
mDate .
get- set- (Source Generate
Getters and Setters...).

8.2. get- set- (Crime.java)


public class Crime {
...
public void setTitle(String title) {
mTitle = title;
}

164 8.

8.2 ()
public Date getDate() {
return mDate;
}
public void setDate(Date date) {
mDate = date;
}
public boolean isSolved() {
return mSolved;
}
public void setSolved(boolean solved) {
mSolved = solved;
}
}

fragment_crime.xml -
CrimeFragment.java.


CrimeFragment .

. 8.1. CriminalIntent, 2

, CrimeFragment -
: TextView, Button CheckBox.
fragment_crime.xml , -
8.3. ,
.
Crime 165

8.3. (fragment_crime.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="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_title_label"
style="?android:listSeparatorTextViewStyle"
/>
<EditText android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:hint="@string/crime_title_hint"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"
style="?android:listSeparatorTextViewStyle"
/>
<Button android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
<CheckBox android:id="@+id/crime_solved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="@string/crime_solved_label"
/>
</LinearLayout>

: Button android:text.
, Crime,
.
Button? .
.
12 ,
DatePicker, .
, ,
style margin. CriminalIntent
.
res/values/strings.xml .
166 8.

8.4. (strings.xml)
<resources>
<string name="app_name">CriminalIntent</string>
<string name="title_activity_crime">CrimeActivity</string>
<string name="crime_title_hint">Enter a title for this crime.</string>
<string name="crime_title_label">Title</string>
<string name="crime_details_label">Details</string>
<string name="crime_solved_label">Solved?</string>
</resources>


CheckBox , . -
CheckBox mSolved Crime.
Button mDate.
CrimeFragment.java .

8.5. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
private Button mDateButton;
private CheckBox mSolvedCheckBox;

@Override
public void onCreate(Bundle savedInstanceState) {
...

, DatePicker CheckBox.
onCreateView() ,
.

8.6. Button (CrimeFragment.java)


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...

mTitleField.addTextChangedListener(new TextWatcher() {
...
});

mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);

return v;
}
167

, .
, -
. 12
.
CheckBox: ,
mSolved Crime.

8.7. CheckBox (CrimeFragment.java)


...
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);

mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
//
mCrime.setSolved(isChecked);
}
});

return v;
}

OnCheckedChangeListener Eclipse -
, CompoundButton, ,
RadioGroup. CompoundButton; CheckBox
CompoundButton.
, onCheckedChanged()
@Override, 8.7. -
. , ,
@Override .
CriminalIntent. CheckBox
, .

XML
, fragment_crime.xml,
.

,
(style) XML, ,
. ,
, -
.
168 8.

<style name="BigTextStyle">
<item name="android:textSize">20sp</item>
<item name="android:layout_margin">3dp</item>
</style>

( 24). -
res/values/,
: @style/my_own_style.
TextView fragment_crime.xml;
style, , Android. -
TextView ,
. (theme) .
,
.
Android ,
. CriminalIntent
Holo Light with Dark Action Bar, .

(theme attribute reference). frag-
ment_crime.xml, ?android:listSeparatorTextViewStyle.
Android:
listSeparatorTextViewStyle.
. .
Android listSeparatorTextViewStyle,
.
, TextView -
.
, , 24.

, dp sp
fragment_crime.xml margin dp.
; , .
(
, , ).
, . -
.
,
.
Android
, drawable-ldpi, drawable-mdpi drawable-hdpi.
, , ?
?
169

. 8.2. TextView (: MDPI;


: HDPI; : HDPI )

Android ,
; ,
. Android -
, :
dp ( dip) density-independent pixel (, -
); .
, ,
. dp -
. dp
1/160 .
.
sp scale-independent pixel (, -
). , ,
. sp
.
pt, mm, in ( dp),
(1/72 ), .
:
.

dp sp. Android .

Android
8.3 16dp .
Android,
170 8.

48dp. http://developer.
android.com/design/index.html.
Android -
, .
Android SDK,
.
,
, ActionBarSherlock, -
16.


, ,
layout_ (android:layout_marginLeft),
(android:text).
, layout_,
. -
.
layout_,
. , ,
,
.
(, LinearLayout) ,
, , .
LinearLayout fragment_crime.xml,
android:layout_width android:layout_height.
LinearLayout .
LinearLayout FrameLayout
CrimeActivity.


fragment_crime.xml margin padding. -
,
. , , ,
, . margin
; .
, .
, . android:padding -
,
. ,
. Button ,
.
171

8.8.
<Button android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:padding="80dp"
/>

. 8.3.

, .


, XML. -

CrimeFragment.
, LinearLayout -
.

.
CriminalIntent ,
CrimeFragment .
172 8.

. 8.4. CrimeFragment

; ,
.
res/layout-land ( res/
Package Explorer New Folder), frag-
ment_crime.xml res/layout-land/.
, res/
layout/fragment_crime.xml, . res/layout-land/
fragment_crime.xml Graphical Layout.
-
. .
, .

-
-
-

. 8.5.
173

.
.
.
, .
? . 8.6.

. 8.6. CrimeFragment

:
LinearLayout .
LinearLayout.
Button CheckBox LinearLayout.
Button CheckBox.


,
. Layouts . LinearLayout
(Horizontal) .
LinearLayout LinearLayout, -
LinearLayout.
-
. Layout
, ,
.
174 8.

. 8.7. LinearLayout fragment_crime.xml


LinearLayout ,
. Layout Parameters, -
Margins.
, Linear-
Layout .
Left
16dp. (Right)
.
(
-
.
, ,
. , -
, XML
margin EditText
LinearLayout).
. 8.8. -
XML fragment_crime.
xml
. LinearLayout -
.
175


Button CheckBox
LinearLayout. , -
Button LinearLayout.
,
Button -
LinearLayout .
CheckBox.
,
-
.
, -
:
.

CheckBox Button. -
LinearLayout (match_par- . 8.9. Button
ent) (Button) CheckBox
, Check- LinearLayout
Box (. 8.10).

. 8.10. Button, , CheckBox


176 8.

LinearLayout,
.


. -
Width wrap_content.
16dp . ,
LinearLayout,
.
Weight Layout Parameters
1. -
android:layout_weight . 8.6.
CheckBox
: Width -
wrap_content, Weight 1,
.
-
, .
XML, -
. 8.11. . 8.9
wrap_content XML.
8.9. XML ,
(layout-land/fragment_crime.xml)
...
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"
style="?android:listSeparatorTextViewStyle"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" >
<Button
android:id="@+id/crime_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
<CheckBox
android:id="@+id/crime_solved"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/crime_solved_label" />
</LinearLayout>
</LinearLayout>
177

android:layout_weight
android:layout_weight LinearLayout,
. -
, ,
. LinearLayout
layout_width layout_weight.
LinearLayout .
LinearLayout layout_width ( layout_height -
). layout_width Button, CheckBox
wrap_content, ,
(. 8.12).
( , -
, .
, LinearLayout,
.)

. 8.12. 1: layout_width

LinearLayout
layout_weight (. 8.13).

. 8.13. 2:
layout_weight

Button CheckBox layout_weight, -


50/50.
Button 2, 2/3 -
, CheckBox 1/3 (. 8.14).

. 8.14.
layout_weight 2:1


. .
178 8.

fragment_crime.xml . -
, 1.0 100;
0.66 66 .
, LinearLayout -
50 % ? ,
layout_width 0dp wrap_content.
LinearLayout layout_weight
(. 8.15).

. 8.15. layout_width="0dp" layout_weight


, Android
ADT. -
XML.
XML (
).

. , ,
. 8.6. ,
XML, .


, CriminalIntent, , -
.
,
.
,

null:
Button landscapeOnlyButton = (Button)v.findViewById(R.id.landscapeOnlyButton);
if (landscapeOnlyButton != null) {
//
}

, , , -
android:id ,
.
. 179

.
Date (timestamp),
. toString() Date , -
. ,
,
(, Oct 12, 2012).
android.text.format.DateFormat. -
Android.
DateFormat
.
, -
(Tuesday, Oct 12, 2012).
9
ListFragment

CriminalIntent -
Crime. CriminalIntent,
.
, .

. 9.1.
CriminalIntent 181

. 9.2 CriminalIntent .

. 9.2. CriminalIntent

CrimeLab,
Crime.
CriminalIntent
: CrimeListActivity CrimeListFragment.
CrimeListFragment ListFragment Fragment,
. -
CrimeLab .
( CrimeActivity CrimeFragment . 9.2?
, . 10 -
CriminalIntent.)
. 9.2 , CrimeListActivity
CrimeListFragment. FrameLayout,
. ListView. -
ListFragment ListView
.

CriminalIntent
CriminalIntent
Crime Crime.
ArrayList<E> Java,
. , .
182 9. ListFragment


-
(singleton) . .
, -
, ,
, .
,
get(), . ,
get() . , get() -
.
com.bignerdranch.android.criminalintent -
New Class. CrimeLab Finish.
CrimeLab.java CrimeLab -
get(Context).

9.1. (CrimeLab.java)
public class CrimeLab {
private static CrimeLab sCrimeLab;
private Context mAppContext;

private CrimeLab(Context appContext) {


mAppContext = appContext;
}

public static CrimeLab get(Context c) {


if (sCrimeLab == null) {
sCrimeLab = new CrimeLab(c.getApplicationContext());
}
return sCrimeLab;
}
}

s sCrimeLab. -
Android, , sCrimeLab
.
CrimeLab Context. Android
; Context
, , -
. .
: get(Context) Context .
Context Activity Context ,
Service. , Context -
, CrimeLab
.
,
Context , getApplicationContext()
183

Context . -
.
, .
CrimeLab Crime . -
CrimeLab ArrayList Crime.
: getCrimes() , getCrime(UUID)
Crime ( 9.2).

9.2. ArrayList Crime (CrimeLab.java)


public class CrimeLab {
private ArrayList<Crime> mCrimes;

private static CrimeLab sCrimeLab;


private Context mAppContext;

private CrimeLab(Context appContext) {


mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
}

public static CrimeLab get(Context c) {


...
}

public ArrayList<Crime> getCrimes() {


return mCrimes;
}

public Crime getCrime(UUID id) {


for (Crime c : mCrimes) {
if (c.getId().equals(id))
return c;
}
return null;
}
}

ArrayList Crime, -
, .
100 Crime ( 9.3).

9.3. (CrimeLab.java)
private CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
for (int i = 0; i < 100; i++) {
Crime c = new Crime();
c.setTitle("Crime #" + i);
c.setSolved(i % 2 == 0); //
mCrimes.add(c);
}
}
184 9. ListFragment

100
.

ListFragment
CrimeListFragment. Browse,
. ListFragment android.support.v4.app,
Finish, CrimeListFragment.
ListFragment Honeycomb, -
. , ,
android.support.v4.app.ListFragment.
CrimeListFragment.java onCreate(Bundle),
, .

9.4. onCreate(Bundle)
(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
}

getActivity(). Fragment
-
. Activ-
ity.setTitle(int), (
) .
onCreateView() CrimeList-
Fragment. ListFragment , -
ListView; .
CrimeListFragment.onCreateView()
.
strings.xml .

9.5. (strings.xml)
...
<string name="crime_solved_label">Solved?</string>
<string name="crimes_title">Crimes</string>
</resources

CrimeListFragment ,
CrimeLab. CrimeListFragment.onCreate()
CrimeLab .
185

9.6. CrimeListFragment (CrimeListFragment.java)


public class CrimeListFragment extends ListFragment {
private ArrayList<Crime> mCrimes;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
}
}


CrimeListActivity,
CrimeListFragment.
CrimeListActivity.


CrimeListActivity ,
activity_crime.xml ( 9.7). FrameLayout
,
.

9.7. activity_crime.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

activity_crime.xml , -
,
. activity_fragment.xml, .
activity_crime.xml ( ). Package
Explorer res/layout/activity_crime.xml. (
activity_crime.xml, fragment_crime.xml.)
Refactor Rename... activity_frag-
ment.xml. .
ADT
. Eclipse CrimeActivity.java,
CrimeActivity, 9.8.

9.8. CrimeActivity (CrimeActivity.java)


public class CrimeActivity extends FragmentActivity {
/** . */

186 9. ListFragment

9.8 ()
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
}

Activity
CrimeListActivity Crime-
Activity. , CrimeActivity ( 9.8):
. , -
: CrimeFragment FragmentManager.

9.9. CrimeActivity (CrimeActivity.java)


public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
}

, , -
. ,
.
SingleFragmentActivity CriminalIntent.
FragmentActivity abstract,
SingleFragmentActivity (. 9.3).
187

. 9.3. SingleFragmentActivity

Finish SingleFragmentActiv-
ity.java. , CrimeActivity.

9.10. (SingleFragmentActivity.java)
public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
}
188 9. ListFragment

activity_fragment.xml.
FragmentManager ,
, .
9.10 CrimeActivity -
createFragment(), .
SingleFragmentActivity ,
, .


CrimeListActivity.
CrimeListActivity. SingleFragmentActivity .
Browse, SingleFragmentActivity, Eclipse -
. Finish.

. 9.4. SingleFragmentActivity

Eclipse CrimeListActivity.java,
createFragment(). CrimeListFragment.

9.11. CrimeListActivity (CrimeListActivity.java)


public class CrimeListActivity extends SingleFragmentActivity {

@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}

}
189

, CrimeActivity . -
CrimeActivity.java. CrimeActivity
SingleFragmentActivity, 9.12.

9.12. CrimeListActivity (CrimeListActivity.java)


public class CrimeActivity extends FragmentActivity SingleFragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}

@Override
protected Fragment createFragment() {
return new CrimeFragment();
}
}

SingleFragmentActivity
. ,
.

CrimeListActivity
, CrimeListActivity , .
, ,
CriminalIntent; ,
CrimeListActivity .
CrimeListActivity -
CrimeActivity CrimeListActivity,
9.13.

9.13. CrimeListActivity (AndroidManifest.xml)


...

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name=".CrimeListActivity">

190 9. ListFragment

9.13 ()
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".CrimeActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>

CrimeListActivity . CriminalIn-
tent; FrameLayout CrimeListActivity,
CrimeListFragment.

. 9.5. CrimeListActivity

ListView , ListFragment
. CrimeListFragment -
Crime, , ListView.
.
ListFragment, ListView ArrayAdapter 191

ListFragment, ListView ArrayAdapter


ListView CrimeListFragment
.
Crime.
ListView ViewGroup,
View ListView. -
View .
:
Crime,
View TextView.


TextView

. 9.6. ListView TextView

. 9.6 11 TextView 12-.


, ListView
TextView Crime #12, Crime #13 . .
View? , ListView
? . View
, . ,

.
192 9. ListFragment

, -
. ListView ,
.
ListView ? (adapter) -
, ListView -
, ListView.
:
;
;
ListView.
, Adapter.
ArrayAdapter<T> ,
( ArrayList) T.
. 9.7 ArrayAdapter<T>.
.

. 9.7. ArrayAdapter<T>

ListView ,
. . 9.8
ListView .
ListView ,
getCount() ( ,
).
ArrayAdapter<T> 193

. 9.8. ListView-Adapter

ListView getView(int, View, ViewGroup) .


, ListView.
getView() -
ListView.
,
.
getView() , -
.

ArrayAdapter<T>
ArrayAdapter<T> CrimeListFrag-
ment :
public ArrayAdapter(Context context, int textViewResourceId, T[] objects)

Context,
.
, Ar-
rayAdapter .
.
CrimeListFragment.java ArrayAdapter<T>
ListView CrimeListFragment ( 9.14).
194 9. ListFragment

9.14. ArrayAdapter (CrimeListFragment.java)


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();

ArrayAdapter<Crime> adapter =
new ArrayAdapter<Crime>(getActivity(),
android.R.layout.simple_list_item_1,
mCrimes);
setListAdapter(adapter);
}

setListAdapter(ListAdapter) ListFragment,
ListView,
CrimeListFragment.
, (android.R.layout.simple_list_item_1),
,
Android SDK. TextView.

9.15. android.R.layout.simple_list_item_1
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/listItemFirstLineStyle"
android:paddingTop="2dip"
android:paddingBottom="3dip"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

,
TextView.
, ListFragment, -
. ListView ,
.
CriminalIntent. -
. , , ,
.
ArrayAdapter<T>.getView() toString().
, Crime, toString()
, TextView.
Crime toString() ,
java.lang.Object,
.
Crime,
Crime.java toString() ,
.
ArrayAdapter<T> 195

9.16. Crime.toString() (Crime.java)


...
public Crime() {
mId = UUID.randomUUID();
mDate = new Date();
}

@Override
public String toString() {
return mTitle;
}

...

CriminalIntent. .

. 9.9. . 9.10.

ListView getView()
, .


,
ListFragment:
public void onListItemClick(ListView l, View v, int position, long id)
196 9. ListFragment

: ,
, onListItemClick().
CrimeListFragment.java onListItemClick().
Crime , ,
Crime.

9.17. onListItemClick()
Crime (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";

...

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = (Crime)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
}

getListAdapter() ListFragment, ,
ListFragment. -
getItem(int) position
onListItemClick() Crime.
CriminalIntent. ,
Crime.


-
Crime Crime.toString().
, -
, .
:
XML , ;
ArrayAdapter<T>, , -
, .


CriminalIntent
, (. 9.11).
TextView CheckBox.
, -
. Package Explorer res/
layout New Other... Android XML File.
Layout, list_item_crime.xml,
RelativeLayout Finish.
197

RelativeLayout -
-

. , CheckBox -

RelativeLayout. TextView
CheckBox.
. 9.11. -
. 9.12 -
. Check-
Box ,
. , TextView CheckBox
.

. 9.12. (list_item_crime.xml)
198 9. ListFragment

TextView
TextView .
, -
.
:
+. -
android:id.
-
android:text. -
. , ,
.
, .
,
.


, -
Crime.
Crime, , Crime.
CrimeListFragment.java ArrayAdapter
CrimeListFragment.

9.18.
(CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = (Crime)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
}

private class CrimeAdapter extends ArrayAdapter<Crime> {

public CrimeAdapter(ArrayList<Crime> crimes) {


super(getActivity(), 0, crimes);
}
}
}

Crime.
, -
0.
Array-
Adapter<T>:
public View getView(int position, View convertView, ViewGroup parent)

convertView ,
.
199

,
-
. , ListView, ,

, .
CrimeAdapter getView().
,
Crime ( 9.19).

9.19. getView() (CrimeListFragment.java)


private class CrimeAdapter extends ArrayAdapter<Crime> {
public CrimeAdapter(ArrayList<Crime> crimes) {
super(getActivity(), 0, crimes);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ,
if (convertView == null) {
convertView = getActivity().getLayoutInflater()
.inflate(R.layout.list_item_crime, null);
}

// Crime
Crime c = getItem(position);

TextView titleTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_titleTextView);
titleTextView.setText(c.getTitle());
TextView dateTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_dateTextView);
dateTextView.setText(c.getDate().toString());
CheckBox solvedCheckBox =
(CheckBox)convertView.findViewById(R.id.crime_list_item_solvedCheckBox);
solvedCheckBox.setChecked(c.isSolved());

return convertView;
}
}

getView() , -
, .
.
, getItem(int)
Adapter Crime .
Crime
Crime. -
ListView.
CrimeListFragment.
CrimeListFragment.java onCreate() onListItemClick()
CrimeAdapter, 9.20.
200 9. ListFragment

9.20. CrimeAdapter (CrimeListFragment.java)


ArrayAdapter<Crime> adapter = new ArrayAdapter<Crime>(this,
android.R.layout.simple_list_item_1,
mCrimes);
CrimeAdapter adapter = new CrimeAdapter(mCrimes);
setListAdapter(adapter);
}

public void onListItemClick(ListView l, View v, int position, long id) {


Crime c = (Crime)(getListAdapter()).getItem(position);
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
}

CrimeAdapter
. CrimeAdapter Crime, -
Crime .
. - ,
CheckBox, .
CheckBox .
CheckBox
onListItemClick().

. 9.13.
201

ListView , -
, (, CheckBox
Button), . ,
, .
CheckBox
, :
list_item_crime.xml CheckBox.

9.21. CheckBox (list_item_crime.xml)


...
<CheckBox android:id="@+id/crime_list_item_solvedCheckBox"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_alignParentRight="true"
android:enabled="false"
android:focusable="false"
android:padding="4dp" />
...

.
, CrimeAdapter .
, ,
list_item_crime.xml .
10


CriminalIntent. ,
CrimeActivity,
CrimeFragment
Crime.

. 10.1. CrimeActivity CrimeListActivity

GeoQuiz (QuizActivity) (CheatAc-


tivity). CriminalIntent CrimeActivity
CrimeListFragment.


, -
. Fragment.startActivity(Intent),
Activity .
onListItemClick() CrimeListFragment
, CrimeActivity. (
Crime;
.)
203

10.1. CrimeActivity (CrimeListFragment.java)


public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");

// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
startActivity(i);
}

CrimeListFragment CrimeAc-
tivity. CrimeListFragment getActivity() -
- Context, Intent.
CriminalIntent. ; -
CrimeActivity, CrimeFragment.

. 10.2. CrimeFragment

CrimeFragment Crime,
, Crime .


CrimeFragment, Crime ,
mCrimeId (extra) Intent. onListItemClick()
204 10.

mCrimeId Crime , Crime-


Activity.
Eclipse CrimeFragment.EXTRA_
CRIME_ID . , .

10.2. CrimeActivity (CrimeListFragment.java)


public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);

// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
}

putExtra(),
(mCrimeId).
putExtra(String, Serializable), UUID Serializable.


mCrimeId , CrimeActivity, -
CrimeFragment.
, -
: ,
. ,
.
CrimeFragment getActivity() -
CrimeActivity. CrimeFragment
, onCreate(Bundle)
CrimeActivity Crime.

10.3. Crime (CrimeFragment.java)


public class CrimeFragment extends Fragment {
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";

private Crime mCrime;

...

public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
UUID crimeId = (UUID)getActivity().getIntent()
.getSerializableExtra(EXTRA_CRIME_ID);

mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
}
205

getActivity(), 10.3
. getIntent() -
Intent , CrimeActivity .
getSerializableExtra(String) Intent, UUID .
Crime
CrimeLab. CrimeLab.get() Context,
CrimeFragment CrimeActivity.

CrimeFragment Crime
, CrimeFragment Crime,
Crime. onCreateView(), -
(
).

. 10.3. ,

10.4. (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mTitleField = (EditText)v.findViewById(R.id.crime_title);

206 10.

10.4 ()
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
...
});
...
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
...
});
...

return v;
}

CriminalIntent. Crime #4 ,
CrimeFragment -
.


, -,
. , . CrimeFragment
, -
, ,
Intent, EXTRA_CRIME_ID.
, CrimeFragment , ,
CrimeFragment -
.
, mCrimeId , -
CrimeFragment ( CrimeAc-
tivity). CrimeFragment ,
. ,
, (arguments bundle).


Bundle.
-, ,
Activity. (argument).
, Bundle,
put- Bundle (
Intent) .
Bundle args = new Bundle();
args.putSerializable(EXTRA_MY_OBJECT, myObject);
args.putInt(EXTRA_MY_INT, myInt);
args.putCharSequence(EXTRA_MY_STRING, myString);
207


, Fragment.
setArguments(Bundle).
, .
Android Fragment
newInstance(). -
, .
- ,
newInstance().
newInstance() ,
.
CrimeFragment newInstance(UUID), UUID,
, ,
.

10.5. newInstance(UUID) (CrimeFragment.java)


public static CrimeFragment newInstance(UUID crimeId) {
Bundle args = new Bundle();
args.putSerializable(EXTRA_CRIME_ID, crimeId);
CrimeFragment fragment = new CrimeFragment();
fragment.setArguments(args);
return fragment;
}

CrimeActivity CrimeFragment.newInstance(UUID)
, CrimeFragment.
UUID, . CrimeActivity,
createFragment() CrimeActivity -
CrimeFragment.newInstance(UUID).

10.6. newInstance(UUID) (CrimeActivity.java)


@Override
protected Fragment createFragment() {
return new CrimeFragment();

UUID crimeId = (UUID)getIntent()


.getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);

return CrimeFragment.newInstance(crimeId);
}

, . CrimeAc-
tivity CrimeFragment , ,
newInstance(UUID). ; -
, ,
(
).
208 10.


,
getArguments() Fragment, get- Bundle -
.
CrimeFragment.onCreate()
UUID .

10.7. (CrimeFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

UUID crimeId = (UUID)getActivity().getIntent()


.getSerializableExtra(EXTRA_CRIME_ID);

UUID crimeId = (UUID)getArguments().getSerializable(EXTRA_CRIME_ID);

mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
}

CriminalIntent. ,
CrimeFragment .
, ,
CriminalIntent.


, .
CriminalIntent,
. ,
.
, -
( ),
. ActivityManager,
.
CrimeListFragment CrimeActivity,
. CrimeActivity,
, .
Back ,
CrimeActivity . CrimeListAc-
tivity .
CrimeListActivity ,
onResume() . CrimeListActivity
FragmentManager onResume() ,
209

. CrimeList-
Fragment.


Back

. 10.4. CriminalIntent

CrimeListFragment onResume() .

10.8. onResume() (CrimeListFragment.java)


@Override
public void onResume() {
super.onResume();
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}


onResume(), onStart()? , -
.
, .
, onStart(),
. -
, ,
onResume().
CriminalIntent.
. ,
.
CriminalIntent .
.
210 10.

. 10.5. CriminalIntent



, -
. ? ,
GeoQuiz. startActivityForResult() Activity
Fragment.startActivityForResult(). Activity.onActivityRe-
sult() Fragment.onActivityResult().
public class CrimeListFragment extends ListFragment {
private static final int REQUEST_CRIME = 1;

...

public void onListItemClick(ListView l, View v, int position, long id) {


// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
startActivityForResult(i, REQUEST_CRIME);
}
211

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CRIME) {
//
}
}
}

Fragment.startActivityForResult(Intent,int)
Activity.
-.
.
,
. , Fragment
startActivityForResult() onActivityResult(), setResult().
- ;
:
public class CrimeFragment extends Fragment {
...

public void returnResult() {


getActivity().setResult(Activity.RESULT_OK, null);
}
}


CriminalIntent 20.
11 ViewPager

, Crime-
Fragment. ViewPager.
ViewPager
, .

. 11.1.

. 11.2 CriminalIntent. -
CrimePagerActivity CrimeActivity.
ViewPager.
, , -
.
CriminalIntent . ,
CrimeFragment
CrimeFragment, 10.
CrimePagerActivity 213

. 11.2. CrimePagerActivity

:
CrimePagerActivity;
, ViewPager;
ViewPager CrimePagerActivity;
CrimeListFragment.onListItemClick() ,
CrimePagerActivity CrimeActivity.

CrimePagerActivity
CrimePagerActivity FragmentActivity. -
ViewPager.
CrimePagerActivity.
FragmentActivity. onCreate(Bundle)
ViewPager .
11.1. ViewPager (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity {
private ViewPager mViewPager;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
214 11. ViewPager


XML-
. , Android ,
.
.
XML . -
, .
;
. , XML .
;
ViewPager . FragmentManager , ,
, .
ViewPager ,
.
:
ViewPager;
ViewPager mViewPager;
, ;
ViewPager .


-
: XML res/values.
Android XML res/values/ids.xml
viewPager.

11.2. (res/values/ids.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">

<item type="id" name="viewPager" />

</resources>

, -
ViewPager. ViewPager
.

11.3. (CrimePagerActivity.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
}
215

ViewPager . Fragment
ViewPager ; SDK -
ViewPager.

ViewPager PagerAdapter
ViewPager - AdapterView ( ListView).
AdapterView , Adapter.
ViewPager PagerAdapter.
ViewPager PagerAdapter -
AdapterView Adapter. ,
FragmentStatePagerAdapter PagerAdapter,
.
FragmentStatePagerAdapter : get-
Count() getItem(int). getItem(int)
CrimeFragment,
.
CrimePagerActivity PagerAdapter
ViewPager getCount() getItem(int).

11.4. PagerAdapter (CrimePagerActivity.java)


public class CrimePagerActivity extends FragmentActivity {
private ViewPager mViewPager;
private ArrayList<Crime> mCrimes;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);

mCrimes = CrimeLab.get(this).getCrimes();

FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
@Override
public int getCount() {
return mCrimes.size();
}

@Override
public Fragment getItem(int pos) {
Crime crime = mCrimes.get(pos);
return CrimeFragment.newInstance(crime.getId());
}
});
}
}
216 11. ViewPager

. CrimeLab -
ArrayList Crime. Frag-
mentManager .
Fragment-
StatePagerAdapter. FragmentStatePagerAdapter
FragmentManager. , FragmentStatePagerAdapter ,
ViewPager.
, getItem(int),
. Frag-
mentManager.
( ? -
ViewPager
.
).
PagerAdapter . getCount() -
. getItem(int).
Crime ,
-
CrimeFragment.

CrimePagerActivity
CrimeActivity
CrimePagerActivity.
CrimeListFragment
CrimePagerActivity CrimeActivity.
CrimeListFragment.java onListItemClick() ,
CrimePagerActivity.

11.5. (CrimeListFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
// CrimePagerActivity rime
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
}

CrimePagerActivity , -
. ,
CrimeActivity.
CrimePagerActivity 217

11.6. CrimePagerActivity (AndroidManifest.xml)


<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
...
<application ...>
...
<activity
android:name=".CrimeActivity"
android:label="@string/app_name" >
</activity>
<activity android:name=".CrimePagerActivity"
android:label="@string/app_name">
</activity>

</application>

</manifest>

, , CrimeActivity.java Package
Explorer.
CriminalIntent. Crime #0, -
. ,
. :
. ViewPager ,
, -
, .
setOffscreenPageLimit(int).
ViewPager .
Back . -
, .
ViewPager PagerAdapter
. , , -
ViewPager .
CrimePagerActivity.onCreate() -
; .
Crime, mId crimeId
, Crime.

11.7. (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
...

FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
...
});

218 11. ViewPager

11.7 ()
UUID crimeId = (UUID)getIntent()
.getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);
for (int i = 0; i < mCrimes.size(); i++) {
if (mCrimes.get(i).getId().equals(crimeId)) {
mViewPager.setCurrentItem(i);
break;
}
}
}
}

CriminalIntent.
Crime.
:
, ( -
), Crime.
ViewPager.OnPageChangeListener.

11.8. OnPageListener (CrimePagerActivity.java)


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);

mCrimes = CrimeLab.get(this).getCrimes();

FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
...
});

mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrollStateChanged(int state) { }
public void onPageScrolled(int pos, float posOffset, int posOffsetPixels) {
}
public void onPageSelected(int pos) {
Crime crime = mCrimes.get(pos);
if (crime.getTitle() != null) {
setTitle(crime.getTitle());
}
}
});
...
}

onPageChangeListener ,
ViewPager.
CrimePagerActivity Crime.
, ,
onPageSelected(). onPageScrolled() ,
CrimePagerActivity 219

, onPageScrollStateChanged() -
,
.
CriminalIntent ,
mTitle
Crime. ! ViewPager .

FragmentStatePagerAdapter FragmentPagerAdapter
PagerAdapter, -
; FragmentPagerAdapter.
FragmentPagerAdapter , FragmentStatePagerAdapter,
.

Fragment Fragment Fragment


Item1 Item2 Item3

- - -

Fragment Fragment Fragment


Item1 Item2 Item3

- -

. 11.3. FragmentStatePagerAdapter

FragmentStatePagerAdapter
. -
FragmentManager . Fragment-
StatePagerAdapter ,
Bundle onSaveInstanceState(Bundle).
,
.
, FragmentPagerAdapter .
, FragmentPagerAdapter
detach(Fragment) remove(Fragment).
220 11. ViewPager

, FragmentMan-
ager. , , FragmentPagerAdapter,
.

Fragment Fragment Fragment


Item1 Item2 Item3

- - -

Fragment Fragment Fragment


Item1 Item2 Item3

- -

. 11.4. FragmentPagerAdapter

. , Fragment-
StatePagerAdapter . CriminalIntent
, ,
. -
, FragmentStatePagerAdapter.
,
, FragmentPagerAdapter .
. -
,
. ViewPager
.
, -
,
.

: ViewPager
ViewPager PagerAdapter
. ,
.
: ViewPager 221

, : -,
ViewPager ,
. -, -
.
PagerAdapter ,
, ViewPager-PagerAdapter
AdapterView-Adapter.
ViewPager, AdapterView? AdapterView Gallery -
. ?
AdapterView ,
Fragment.
Adapter , View .
, FragmentManager, .
, Gallery Adapter ,
.
ViewPager. Adapter -
PagerAdapter. Adapter,
.
.
getView(), , PagerAdapter
:
public Object instantiateItem(ViewGroup container, int position)
public void destroyItem(ViewGroup container, int position, Object object)
public abstract boolean isViewFromObject(View view, Object object)

pagerAdapter.instantiateItem(ViewGroup, int)

ViewGroup; destroyItem(ViewGroup, int, Object)
. : instantiateItem(ViewGroup,
int) . PagerAdapter
.
, ViewPager - -
. , , ViewPager -
isViewFromObject(View, Object). Object ,
instantiateItem(ViewGroup, int). ,
ViewPager instantiateItem(ViewGroup, 5) A,
isViewFromObject(View, A) true, View
5, false .
ViewPager, PagerAdapter, -
,
, . -
PagerAdapter
instantiateItem(ViewGroup, int)
Object. isViewFromObject(View, Object) :
222 11. ViewPager

@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}

PagerAdapter , -
ViewPager, . ,
FragmentPagerAdapter FragmentStatePagerAdapter.
12

. -
.
,
.
CrimeFragment , -
. 12.1.

. 12.1.
224 12.

. 12.1 AlertDialog Dia-


log. Dialog
.
( AlertDialog DatePickerDialog,
. Dat-
ePickerDialog ; AlertDialog ,
.)
AlertDialog . 12.1 DialogFragment
Fragment. , AlertDialog
DialogFragment, Android .
FragmentManager
.
, AlertDialog -
. , AlertDialog ,
.
CriminalIntent DialogFragment
DatePickerFragment. DatePickerFragment
AlertDialog, DatePicker. DatePickerFrag-
ment CrimePagerActivity.
. 12.2 .

. 12.2. CrimePagerActivity
DialogFragment 225

:
DatePickerFragment;
AlertDialog;
FragmentManager.
DatePicker
CrimeFragment DatePickerFragment.
, ( 12.1).

12.1. (values/strings.xml)
<resources>

...
<string name="crime_solved_label">Solved?</string>
<string name="crimes_title">Crimes</string>
<string name="date_picker_title">Date of crime:</string>

</resources>

DialogFragment
DatePickerFragment
DialogFragment : android.support.v4.app.Dia-
logFragment.
DialogFragment :
public Dialog onCreateDialog(Bundle savedInstanceState)

FragmentManager - -
DialogFragment .
DatePickerFragment.java onCreateDialog(),
AlertDialog OK. ( DatePicker
.)

12.2. DialogFragment (DatePickerFragment.java)


public class DatePickerFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok, null)
.create();
}
}

AlertDialog.Builder,
AlertDialog.
Context AlertDialog.Builder,
AlertDialog.Builder.
226 12.

AlertDialog.Builder :
public AlertDialog.Builder setTitle(int titleId)
public AlertDialog.Builder setPositiveButton(int textId,
DialogInterface.OnClickListener listener)

setPositiveButton() ,
DialogInterface.OnClickListener. 12.2 Android
OK null .
.
(Positive)
. AlertDialog
: (Negative) (Neutral).
( ). -
Froyo Gingerbread .
, ).
AlertDialog.Builder.create(),
AlertDialog.
AlertDialog AlertDialog.Builder ; -
.
.

DialogFragment
, DialogFragment
FragmentManager -.
DialogFragment FragmentManager
:
public void show(FragmentManager manager, String tag)
public void show(FragmentTransaction transaction, String tag)

DialogFragment Frag-
mentManager. ( FragmentManager FragmentTransaction)
FragmentManager,
. FragmentManager.
CrimeFragment DatePickerFragment.
onCreateView() , ,
View.OnClickListener, DatePickerFragment
.

12.3. DialogFragment (CrimeFragment.java)


public class CrimeFragment extends Fragment {
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";

private static final String DIALOG_DATE = "date";


...
DialogFragment 227

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);
mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
dialog.show(fm, DIALOG_DATE);
}
});
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
...

return v;
}

...
}

CriminalIntent ,
. OK (. 12.3).

. 12.3. AlertDialog
228 12.


AlertDialog DatePicker Alert-
Dialog.Builder:
public AlertDialog.Builder setView(View view)

View
(-).
Package Explorer dialog_date.xml
DatePicker. View
(DatePicker), setView().
DatePicker , . 12.4.

. 12.4. DatePicker (layout/dialog_date.xml)

DatePickerFragment.onCreateDialog() -
.

12.4. DatePicker AlertDialog (DatePickerFragment.java)


@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View v = getActivity().getLayoutInflater()
.inflate(R.layout.dialog_date, null);

return new AlertDialog.Builder(getActivity())


.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok, null)
.create();
}

CriminalIntent. ,
DatePicker.
, DatePicker
, ?
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
DatePicker dp = new DatePicker(getActivity());

return new AlertDialog.Builder(getActivity())


.setView(dp)
...
.create();
}
DialogFragment 229

. 12.5. AlertDialog DatePicker

.
, , DatePicker -
TimePicker.
, .
, .
Crime , .


;
. -
, CrimeFragment
DatePickerFragment (. . 5.10).
DatePickerFragment ,
newInstance(Date) Date .
CrimeFragment
, Intent
Intent CrimeFragment.onActivityResult().
Fragment.onActivityResult() ,
- Activity.onActivityResult()
. , on-
ActivityResult()
, .
230 12.

. 12.6. CrimeFragment DatePickerFragment

. 12.7.
CrimeFragment DatePickerFragment

DatePickerFragment
DatePickerFragment, -
DatePickerFragment, DatePickerFragment .
-
newInstance(), .
DatePickerFragment.java newInstance(Date).

12.5. newInstance(Date) (DatePickerFragment.java)


public class DatePickerFragment extends DialogFragment {
public static final String EXTRA_DATE =
"com.bignerdranch.android.criminalintent.date";

private Date mDate;

public static DatePickerFragment newInstance(Date date) {


Bundle args = new Bundle();
args.putSerializable(EXTRA_DATE, date);
DialogFragment 231

DatePickerFragment fragment = new DatePickerFragment();


fragment.setArguments(args);

return fragment;
}

...
}

CrimeFragment DatePickerFragment
DatePickerFragment.newInstance(Date).

12.6. newInstance() (CrimeFragment.java)


@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup parent, Bundle savedInstanceState) {
...

mDateButton = (Button)v.findViewById(R.id.crime_date);
updateDate();
mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
DatePickerFragment dialog = DatePickerFragment
.newInstance(mCrime.getDate());
dialog.show(fm, DIALOG_DATE);
}
});

return v;
}

DatePickerFragment DatePicker -
, Date. DatePicker
, . Date
.
, Calendar
Date .
Calendar.
onCreateDialog() Date
Calendar DatePicker.

12.7. Date DatePicker (DatePickerFragment.java)


@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mDate = (Date)getArguments().getSerializable(EXTRA_DATE);

// Calendar ,
Calendar calendar = Calendar.getInstance();

232 12.

12.7 ()
calendar.setTime(mDate);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);

View v = getActivity().getLayoutInflater()
.inflate(R.layout.dialog_date, null);

DatePicker datePicker = (DatePicker)v.findViewById(R.id.dialog_date_datePicker);


datePicker.init(year, month, day, new OnDateChangedListener() {
public void onDateChanged(DatePicker view, int year, int month, int day) {
// , Date
mDate = new GregorianCalendar(year, month, day).getTime();

//
//
getArguments().putSerializable(EXTRA_DATE, mDate);
}
});
...
}

DatePicker OnDat-
eChangedListener. DatePicker,
Date . Date
CrimeFragment.
onDateChanged() Date -
. , mDate .
DatePickerFragment , Frag-
mentManager .
FragmentManager onCreateDialog(),
.
, onSaveInstanceState().
( , ,
: DatePickerFragment?
, -
14. , DialogFragment
, - ,
DatePickerFragment .)
CrimeFragment DatePickerFragment,
. CriminalIntent ,
, .

CrimeFragment
CrimeFragment DatePickerFragment, -
- .
DialogFragment 233

startActivityForResult(), ActivityManager -
.
, ActivityManager ,
.


CrimeFragment -
(target fragment) DatePickerFragment.
Fragment:
public void setTargetFragment(Fragment fragment, int requestCode)

, , ,
startActivityForResult().
, .
FragmentManager . ,
getTargetFragment() getTargetRequestCode() , -
.
CrimeFragment.java ,
CrimeFragment DatePickerFragment.

12.8. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";

private static final String DIALOG_DATE = "date";


private static final int REQUEST_DATE = 0;
...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = DatePickerFragment
.newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
dialog.show(fm, DIALOG_DATE);
}
});

return v;
}

...
}
234 12.


, CrimeFragment DatePickerFragment ,
CrimeFragment. Intent .
?
, DatePickerFragment CrimeFragment.
onActivityResult(int, int, Intent).
Activity.onActivityResult() ActivityManager
.
Activity.onActivityResult() ; Activity-
Manager. , FragmentManager -
Fragment.onActivityResult() .
, -
Fragment.onActivityResult()
. :
, , setTargetFragment(),
, ;
;
Intent, .
DatePickerFragment , ,
, CrimeFragment.onActivi-
tyResult(). onCreateDialog() null setPositiveBut-
ton() DialogInterface.OnClickListener,
.

12.9. (DatePickerFragment.java)
private void sendResult(int resultCode) {
if (getTargetFragment() == null)
return;

Intent i = new Intent();


i.putExtra(EXTRA_DATE, mDate);

getTargetFragment()
.onActivityResult(getTargetRequestCode(), resultCode, i);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...

return new AlertDialog.Builder(getActivity())


.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok, null)
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
DialogFragment 235

sendResult(Activity.RESULT_OK);
}
})
.create();
}

CrimeFragment onActivityResult(), -
, Crime .

12.10. (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mDateButton.setText(mCrime.getDate().toString());
}
}

, , onCreateView().
, up-
dateDate(), onCreateView() onActivityResult().

12.11. updateDate() (CrimeFragment.java)


public class CrimeFragment extends Fragment {
...

public void updateDate() {


mDateButton.setText(mCrime.getDate().toString());
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
updateDate();

...

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mDateButton.setText(mCrime.getDate().toString());
updateDate();
}
}
}
236 12.

.
CriminalIntent ,
. Crime; -
CrimeFragment. Crime
, .

DialogFragment
onActivityResult()
,
.
.
, , , -
.
startActivityForResult()
.
onActivityResult(), -
, .

A B

A B

. 12.8.
DialogFragment 237

, ,
DialogFragment . -
show() .
onActivityResult().

B (
)

B (
)

. 12.9.

onActivityResult() ,
, . -
,
.
, ,
onCreateDialog() -
DialogFragment.onCreateView().
238 12.

.
-
TimePickerFragment . -
TimePicker , CrimeFragment
TimePickerFragment.
,
. , , -
.
.
13
MediaPlayer

CriminalIntent
. -
MediaPlayer.

. 13.1. , !
240 13. MediaPlayer

MediaPlayer Android - .
(,
, ) (WAV, MP3, Ogg Vorbis,
MPEG-4, 3GPP . .).
HelloMoon.
Holo Dark.

. 13.2. HelloMoon Holo Dark

Next. ,
. -
HelloMoonActivity.


HelloMoon ( ) -
(http://www.bignerdranch.com/solutions/AndroidProgramming.zip).
:
13_Audio/HelloMoon/res/drawable-mdpi/armstrong_on_moon.jpg
13_Audio/HelloMoon/res/raw/one_small_step.wav
241

armstrong_on_moon.jpg
(~160 dpi), Android
. armstrong_on_moon.jpg drawable-mdpi.
res/raw. raw
,
Android.
res/raw ,
. ( res
New Folder.) one_small_step.wav .
( res/raw/ 13_Audio/HelloMoon/res/raw/apollo_17_stroll.
mpg.
.)
res/values/strings.xml ,
HelloMoon:
13.1. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="app_name">HelloMoon</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="hellomoon_play">Play</string>
<string name="hellomoon_stop">Stop</string>
<string name="hellomoon_description">Neil Armstrong stepping
onto the moon</string>

</resources>

( HelloMoon
Play Stop? 15 ,
.)
, ;
HelloMoon.
HelloMoon Hello-
MoonActivity, HelloMoonFragment.
AudioPlayer , MediaPlayer.
, MediaPlayer ; HelloMoonFragment
MediaPlayer .
.
AudioPlayer,
.
:
;
;
.
242 13. MediaPlayer

. 13.3. HelloMoon

HelloMoonFragment
XML- Android fragment_hello_moon.xml. -
TableLayout.
fragment_hello_moon.xml . 13.4.

. 13.4. HelloMoon
HelloMoonFragment 243

TableLayout , LinearLayout. Linear-


Layout TableRow.
TableLayout TableRow -
.
ImageView TableRow? TableRow
. , ImageView .
TableRow, TableLayout
.
TableLayout, , , Button
, .
, TableRow .
, TableLayout.
, LinearLayout
.
. ?
HelloMoon Holo Dark.

. , . ( ,
, -
, .)


application :
...
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
</application>
</manifest>

android:theme ; , -
.
, @style/
AppTheme. Package Explorer res/values/styles.xml.
style AppBaseTheme parent android:Theme.
13.2. (res/values/styles.xml)
<style name="AppBaseTheme" parent="android:Theme.Light">
<style name="AppBaseTheme" parent="android:Theme">

res values ;
styles.xml. API.
res/values-11/styles.xml API 1113,
res/values-14/styles.xml API 14 .
244 13. MediaPlayer

res/values-11/styles.xml parent AppBas-


eTheme android:Theme.Holo.
API 11 , res/values-14/ . -
HelloMoon.
. ,
.

HelloMoonFragment
HelloMoonFragment
android.support.v4.app.Fragment.
HelloMoonFragment.onCreateView(),
.

13.3. HelloMoonFragment (HelloMoonFragment.java)


public class HelloMoonFragment extends Fragment {

private Button mPlayButton;


private Button mStopButton;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);

mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);

return v;
}
}


CriminalIntent
. HelloMoon
,
fragment.
activity_hello_moon.xml frag-
ment, 13.4.

13.4. (activity_hello_moon.xml)
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/helloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bignerdranch.android.hellomoon.HelloMoonFragment">

</fragment>
HelloMoonFragment 245

,
HelloMoonActivity. HelloMoonActivity
FragmentActivity:

13.5. HelloMoonActivity FragmentActivity (HelloMoonActivity.java)


public class HelloMoonActivity extends Activity FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_moon);
}
}

HelloMoon. HelloMoonActivity
HelloMoonFragment.
, .
, .
: .setContentView()
activity_hello_moon.xml, fragment.
FragmentManager HelloMoonFragment .
HelloMoonFragment onCreateView() ,
, , fragment.

(/

)

(/

)

(
)

( setContentView()
)

. 13.5.

?
, FragmentManager:
246 13. MediaPlayer

, -
, , .
, , -
. ,
.
. -

FragmentManager. .

.
, HelloMoonFragment, -
.


com.bignerdranch.android.hellomoon
AudioPlayer. java.lang.Object.
AudioPlayer.java MediaPlayer
.

13.6.
MediaPlayer (AudioPlayer.java)
public class AudioPlayer {

private MediaPlayer mPlayer;

public void stop() {


if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}

public void play(Context c) {


mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.start();
}
}

play(Context) MediaPlayer.create(Context, int).


Context MediaPlayer -
. ( MediaPlayer.create(),
, -
URI.)
AudioPlayer.stop() MediaPlayer , mPlayer
null. MediaPlayer.release() .
247

,
. MediaPlayer
release() . -
. MediaPlayer stop()
MediaPlayer ,
.
release(), .
: MediaPlayer
, - .
play(Context).
stop() stop()
.

13.7. (AudioPlayer.java)
...
public void play(Context c) {
stop();

mPlayer = MediaPlayer.create(c, R.raw.one_small_step);

mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
stop();
}
});

mPlayer.start();
}
}

stop() play(Context) -
MediaPlayer, Play .
stop()
MediaPlayer, .
AudioPlayer.stop() HelloMoonFragment,
MediaPlayer -
. HelloMoonFragment onDestroy()
AudioPlayer.stop().

13.8. onDestroy() (HelloMoonFragment.java)


...

@Override
public void onDestroy() {
super.onDestroy();
mPlayer.stop();
}
}
248 13. MediaPlayer

MediaPlayer Hel-
loMoonFragment, MediaPlayer
(thread). HelloMoon.
26.


HelloMoonFragment.java. :
AudioPlayer -
.

13.9. Play (HelloMoonFragment.java)


public class HelloMoonFragment extends Fragment {
private AudioPlayer mPlayer = new AudioPlayer();
private Button mPlayButton;
private Button mStopButton;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);
mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mPlayButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.play(getActivity());
}
});

mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.stop();
}
});
return v;
}

HelloMoon, Play
.
MediaPlayer.
MediaPlayer Android MediaPlayer
Developer Guide http://developer.android.com/guide/topics/media/mediaplayer.html.

.
.
Me-
diaPlayer.
. HelloMoon 249

:
, , -
. MediaPlayer, .
.
( ) Android
SurfaceView. , Surface, -
SurfaceView. Surface,
SurfaceHolder SurfaceView. 19.
, SurfaceHolder MediaPlayer -
MediaPlayer.setDisplay(SurfaceHolder).
VideoView .
VideoView MediaPlayer, SurfaceView.
MediaController,
.
VideoView ,

Uri. Uri, Android,
:
Uri resourceUri = Uri.parse("android.resource://" +
"com.bignerdranch.android.hellomoon/raw/apollo_17_stroll");

URI android.resource, , -
. VideoView.

. HelloMoon
HelloMoon ,
. apollo_17_stroll.mpg ,
res/raw.
.
14

HelloMoon .
, . -
.
, HelloMoonActivity . -
, FragmentManager HelloMoonFragment. FragmentManager
: onPause(), onStop()
onDestroy(). HelloMoonFragment.onDestroy() Media-
Player, .
3 Geo-
Quiz Activity.onSaveInstanceState(Bundle) . -
, . Fragment
onSaveInstanceState(Bundle), .
MediaPlayer
.


, , Me-
diaPlayer .
HelloMoonFragment.onCreate() .
251

14.1. setRetainInstance(true) (HelloMoonFragment.java)


...

private Button mPlayButton;


private Button mStopButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

retainInstance false. ,
,
-. setRetainInstance(true) ,
,
.
, (
mPlayButton, mPlayer mStopButton) . -
, .
HelloMoon. ,
, .


, .
,
.
FragmentManager -
.
,
:
. ,
, .
FragmentManager retainInstance .
false ( ), FragmentManager
. -
FragmentManager .
252 14.

. 14.1. UI-

, retainInstance true, -
, .
FragmentManager
.

. 14.2. UI-

, (detached) -
. ,
-.
: ? 253

. 14.3.

:
setRetainInstance(true);
- ( ).
-
,
.

:
?
: , ? ! .
, -
. -
,
.
254 14.

-
? , Android
. , ,
Android ,
.
, -
.
- , ,
.

onSaveInstanceState(Bundle)
onSaveInstanceState(Bundle) ,
. , -
, onSaveInstanceState() .
CriminalIntent. CrimeFragment
,
, View
. onSaveInstanceState() -

.
Fragment.onSaveInstanceState() -
.
,
.
; ,
Serializable .
, -
.
, , -
.
, GeoQuiz.
, .
,
. ,
, .
GeoQuiz , -
GeoQuiz QuizFragment,
QuizActivity. Fragment.onSaveInstanceState()
QuizFragment ?
. 14.4 ,
: ( ),
.
onSaveInstanceState(Bundle) 255


( )

. 14.4.

;
. .
QuizFragment,
. GeoQuiz ,
QuizFragment . -
, setRetainInstance(true) QuizFragment.
onCreate().

14.2. QuizFragment
public class QuizFragment extends Fragment {
...
private int mCurrentIndex = 0;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

...
}
256 14.


.
. 14.4, QuizFragment -
, ,
,
.
, -
. GeoQuiz 100 -
? ()
. -
. onSaveInstanceState(). ,
,
.
, ,
,
, onSaveInstanceState(Bundle)
.

:

Honeycomb
Ice Cream Sandwich. ,
.
,
, .
? -
onRetainNonConfigurationInstance().
onRetainNonConfigurationInstance(),
. , getLa
stNonConfigurationInstance().
? , .
. ,
.
(, MediaPlayer),
? , onRetainNon-
ConfigurationInstance() ,
onDestroy().
onRetainNonConfigurationInstance() ,

.
, , .
15

(localization) -
. -
HelloMoon ,
. , Android
.

. 15.1. iHola, Luna!


258 15.


. Android -
,
:
.
Android .
ISO 639-1.
-es. HelloMoon -
res: res/raw-es/ res/values-es/.
(http://www.
bignerdranch.com/solutions/AndroidProgramming.zip). :
15_Localization/HelloMoon/res/raw-es/one_small_step.wav
15_Localization/HelloMoon/res/values-es/strings.xml
.
, ,
. ,
( Settings ;
Android Language and input, Language and Keyboard
- .
, (Espaol).
(Espaa Estados Unidos) ,
-es, .
( +
. , -
-es-rES, r , ES
ISO 3166-1-alpha-2. :
,
Android: ,
r ( ).
HelloMoon, Tocar , -
.


-en. -
, -
raw values raw-en/ values-en/.
. .
.
: Android , -
, -
.
259

, strings.xml values-en/ values-es/,


values/, HelloMoon
, , . -

.



. drawable
-mdpi, -hdpi -xhdpi.
drawable Android

.
, Android

.
http://developer.android.com/guide/practices/screens_support.
html, ,
res/drawable/ .



: (, values-es/), (-
, layout-land/), (, drawable-mdpi/) API
(, values-v11/).
. 15.1 -
, Android
.
15.1.

(MCC) (MNC)


260 15.

15.1 ()

(dpi)

API

-
http://developer.android.com/guide/topics/resources/
providing-resources.html#AlternativeResources.



,
. -
, . 15.1.
, , HelloMoon
app_name
. app_name
.
, .
values-land .
, -
, app_name. app_name
, 15.1.

15.1.
(values-land/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hello, Moon! How are you? Is it cold up there?</string>
<string name="app_name">HelloMoon</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="hellomoon_play">Play</string>
<string name="hellomoon_stop">Stop</string>
<string name="hellomoon_image_description">Neil Armstrong stepping
261

onto the moon</string>


</resources>

( values)
,
.
.
app_name: values/
strings.xml, values-es/strings.xml
values-land/strings.xml.
, HelloMoon
. -
, values-es/strings.xml.

. 15.2. Android

, , -
,
( ).
, Ajustes Configuracin
idioma ().


. ,
HelloMoon
, values-es-land.
,
. , values-es-land ,
values-land-es .
(, - , -es-rES
, . -
.)
262 15.

values-land/strings.xml values-es-land/ ,
15.2 (, , res/values-es-
land/strings.xml ).

15.2.
(values-es-land/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hello, Moon! How are you? Is it cold up there?</string>
<string name="app_name">Hola, Luna! Como ests? Hace fro ah arriba?</string>
</resources>

, HelloMoon ,
.

. 15.3.


, Android , app_name
. Android
app_name:
values-es/strings.xml
values-land/strings.xml
values-es-land/strings.xml
.
.


Android -
, .
263

.
, ,
values-land/ values-es-land/
.
-
. , API .
-v11 API 11 .
API. -
, API 17;
-ldltr ( ) -ldrtl ( ).
API,
-ldltr -ldltr-v17. ( -
.)
, . Android
, ;
( ) ,
.


Android
(. . 15.1),
MCC. MCC
, MCC, .
, Android
, -
.
MCC ,
, Android .
(values-es/ values-es-land/) .
(values-land/) , .
Android .
, Android
. values-es/ , values-es-land/.
, Android values-es-land/.



, Android,
, -
.
264 15.



: one_small_step.wav, app_name, armstrong_on_moon.jpg.
XML . ,
@drawable/armstrong_on_moon,
R.drawable.armstrong_on_moon. , -
, , .


res/.
res/ , -
.
res Android
. drawable/, layout/, menu/, raw/ values/.
res ( ) -
http://developer.android.com/guide/topics/resources/providing-resources.
html#ResourceTypes.
, res/, .
res/my_stuff , Android
.
, res/ .
.
;
. . ,
, ,
.
-
Android. ,
(, activity_, dialog_ list_item_). , res/layout/
CriminalIntent activity_crime_pager, activity_fragment,
dialog_date, fragment_crime list_item_crime. -
, .


. ,
, . .
, -
, .
-
.
, , API, . .
265

, fragment_hello_moon.xml -
. ,
.


/ API

. 15.4.

, ,
, -
. , .
, LogCat Resource not
found... .
,
. Ajustes Configura-
cin idioma ().
16

(action bar) Honeycomb.


, -
. ,
.
CriminalIntent.
. ,
Up.



- Up

-

. 16.1.
267


(options menu).
.
. ,
,
, .
18.
.
18.
strings.xml ( 16.1).
, . -
, , .

16.1. (res/values/strings.xml)
...
<string name="crimes_title">Crimes</string>
<string name="crime_date_label">Date:</string>
<string name="date_picker_title">Date of crime:</string>
<string name="new_crime">New Crime</string>
<string name="show_subtitle">Show Subtitle</string>
<string name="hide_subtitle">Hide Subtitle</string>
<string name="subtitle">If you see something, say something.</string>
<string name="delete_crime">Delete</string>
</resources>

. 16.2. Honeycomb
268 16.


, Android.
, .
,
Android. -
, .

XML
, .
XML res/menu . Android -
,
.
Package Explorer res/ menu.
New Android XML File.
, Menu,
fragment_crime_list.xml.

. 16.3.

fragment_crime_list.xml XML. item,


16.2.
269

16.2. CrimeListFragment (fragment_crime_list.xml)


<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_new_crime"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_crime"
android:showAsAction="ifRoom|withText"/>
</menu>

showAsAction ,
(overflow menu).
ifRoom withText, -
.
, . , ,
.
. -
,
.
,
(. 16.4).

. 16.4.
270 16.

showAsAction always never.


always ; ifRoom .
never . ,
,
.
, Android Lint android:showAsAction, -
API 11. XML , Java,
. XML API.
android:icon @android:drawable/ic_menu_add
(system icon). ,
.


. -
, , ,
.
, -
.
. -
, , -
.
Androids Icon Design Guidelines http://developer.android.com/guide/practices/
ui_guidelines/icon_design.html.
, -
,
. -
.
, Android SDK
--android-SDK/platforms/-API/data/res.
, Android 4.2 Mac /Developer/android-
sdk-mac_86/platforms/android-17/data/res.
SDK
ic_menu_add. drawable -
. icon android:icon="@
drawable/ic_menu_add", , -
.



Activity. , Android -
Activity onCreateOptionsMenu(Menu).
271

,
, . Fragment
, CrimeListFrag-
ment.
:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
public boolean onOptionsItemSelected(MenuItem item)

CrimeListFragment.java onCreateOptionsMenu(Menu, Menu-


Inflater) , , fragment_crime_list.xml.

16.3. (CrimeListFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
}

MenuInflater.inflate(int, Menu)
. Menu -
, .
onCreateOptionsMenu() .
, -
,
, . , -
Fragment .
FragmentManager Fragment.onCreateOptionsMenu(Menu, MenuIn-
flater) onCreateOptionsMenu()
. FragmentManager,
onCreateOptionsMenu(). :
public void setHasOptionsMenu(boolean hasMenu)

CrimeListFragment.onCreate() FragmentManager,
CrimeListFragment .

16.4. hasOptionsMenu (CrimeListFragment.java)


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);

getActivity().setTitle(R.string.crimes_title);
...
272 16.

CriminalIntent (. 16.5).
?
.
.

. 16.5. . 16.6.

,
(. 16.7).

. 16.7.
273

CriminalIntent Honeycomb, -
.
. . 16.8
Gingerbread.

. 16.8. Gingerbread

, ;
. -
SDK.
Honeycomb onCreateOptionsMenu()
.
,
. onCreateOptionsMenu()
.
(, .
ActionBarSherlock
API.
ActionBarSherlock 18.)


New Crime,
Crime . CrimeLab.
java .
274 16.

16.5. Crime (CrimeLab.java)


...
public void addCrime(Crime c) {
mCrimes.add(c);
}

public ArrayList<Crime> getCrimes() {


return mCrimes;
}

...

, , -
100 . CrimeLab.java
, .

16.6. ! (CrimeLab.java)
public CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
for (int i = 0; i < 100; i++) {
Crime c = new Crime();
c.setTitle("Crime #" + i);
c.setDate(new Date());
c.setSolved(i % 2 == 0); //
mCrimes.add(c);
}
}

,
onOptionsItemSelected(MenuItem).
MenuItem, .
, .
, ,
.
, .
CrimeListFragment.java onOptionsItemSelected(MenuItem),
. Crime,
CrimeLab CrimePagerActivity -
Crime.

16.7. (CrimeListFragment.java)
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
275

case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivityForResult(i, 0);
return true;
default:
return super.onOptionsItemSelected(item);
}
}

. -
, true; ,
. default ,
.
CriminalIntent . -
.
( .
.)


CriminalIntent Back
. Back
. ,
.
Android
.
( Home
). Android
,
. Up).
Up
CrimePagerActivity. , -
.


, Up, -
,
, . 16.9.
, -
,
:
public abstract void setDisplayHomeAsUpEnabled(boolean showHomeAsUp)
276 16.

API 11, -
, Froyo Gingerbread.
, Android Lint.

. 16.9. Up

CrimeFragment.onCreateView() setDisplayHomeAsUpEnabled(true).

16.8. Up (CrimeFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}

...
}

:
Up.
. . , -
API 1113, ,
setDisplayHomeAsUpEnabled(true).
277

16.8 onCreateView() @TargetApi.


setDisplayHomeAsUpEnabled(true),
onCreateView() ,
API, .
CriminalIntent,
, .

Up
,
onOptionsItemSelected(MenuIt
em). , FragmentManager, CrimeFrag-
ment .
CrimeFragment.onCreate() setHasOptionsMenu(true),
CrimeListFragment.
16.9. (CrimeFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
...
setHasOptionsMenu(true);
}


XML. : android.R.id.home.
CrimeFragment.java onOptionsItemSelected(MenuItem),
.
16.10. (Home)
(CrimeFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
//
return true;
default:
return super.onOptionsItemSelected(item);
}
}

. ,
CrimePagerActivity:
Intent intent = new Intent(getActivity(), CrimeListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();

FLAG_ACTIVITY_CLEAR_TOP Android -
,
, .
278 16.

CriminalListActivity

. 16.10. FLAG_ACTIVITY_CLEAR_TOP

,
, NavUtils
.
. AndroidManifest.xml.
CrimePagerActivity , CrimeListActivity
.

16.11. (AndroidManifest.xml)
<activity android:name=".CrimePagerActivity"
android:label="@string/app_name">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".CrimeListActivity"/>
</activity>
...

, .
PackageManager,
, .
- .
NavUtils,
.
NavUtils:
public static void navigateUpFromSameTask(Activity sourceActivity)

CrimeFragment.onOptionsItemSelected() ,
, , Na-
vUtils.getParentActivityName(Activity). , navigat
eUpFromSameTask(Activity) .

16.12. NavUtils (CrimeFragment.java)


@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (NavUtils.getParentActivityName(getActivity()) != null) {
279

NavUtils.navigateUpFromSameTask(getActivity());
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}

,
. onCreateView()
setDisplayHomeAsUpEnabled(true).

16.13. (CrimeFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


if (NavUtils.getParentActivityName(getActivity()) != null) {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
}

...
}

NavUtils ? -
, NavUtils . , NavUtils
.
, ,
Java.
,
. CrimeFragment ,
; CrimeFragment
.
CriminalIntent.
, . , -
CriminalIntent , navigateUpFromS
ameTask(Activity)
CrimePagerActivity.


, , -
, , -
CrimeListActivity.
280 16.


, , -
. ,
. res menu-v11.
fragment_crime_list.xml .
res/menu-v11/fragment_crime_list.xml Show Subtitle,
.

16.14. Show Subtitle (res/menu-v11/fragment_crime_list.xml)


<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_new_crime"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_crime"
android:showAsAction="ifRoom|withText"/>
<item android:id="@+id/menu_item_show_subtitle"
android:title="@string/show_subtitle"
android:showAsAction="ifRoom"/>
</menu>

onOptionsItemSelected()
.

16.15. Show Subtitle (CrimeListFragment.java)


@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
getActivity().getActionBar().setSubtitle(R.string.subtitle);
return true;
default:
return super.onOptionsItemSelected(item);
}
}

: Android Lint, -
.
, R.id.
menu_item_show_subtitle .
CriminalIntent -
. Froyo Gingerbread (
). , Show Subtitle
. ,
, .
, 281


, :
Show Subtitle. ,
.
onOptionsItemSelected()
.

16.16. (CrimeListFragment.java)
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
item.setTitle(R.string.hide_subtitle);
} else {
getActivity().getActionBar().setSubtitle(null);
item.setTitle(R.string.show_subtitle);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}

, -
Hide Subtitle. ,
, Show Subtitle.
CriminalIntent ,
.

,
Android
. , .
Android : ,
. ,
, . -
,
CrimeListFragment,
.
CrimeListFragment.java , on-
Create() CrimeListFragment .
282 16.

16.17. CrimeListFragment
(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment { private ArrayList<Crime>
mCrimes;
private boolean mSubtitleVisible;
private final String TAG = "CrimeListFragment";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
setRetainInstance(true);
mSubtitleVisible = false;
}

onOptionsItemSelected()
.

16.18. subtitleVisible
(CrimeListFragment.java)
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
mSubtitleVisible = true;
item.setTitle(R.string.hide_subtitle);
}
else {
getActivity().getActionBar().setSubtitle(null);
mSubtitleVisible = false;
item.setTitle(R.string.show_subtitle);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}

, . CrimeListFrag-
ment.java onCreateView() ,
mSubtitleVisible true.

16.19. , mSubtitleVisible
(CrimeListFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
. 283

Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


if (mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}
}

return v;
}

onCreateOptionsMenu()
, .

16.20. mSubtitleVisible
(CrimeListFragment.java)
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem showSubtitle = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible && showSubtitle != null) {
showSubtitle.setTitle(R.string.hide_subtitle);
}
}

CriminalIntent. ,
. ,
.

.
CriminalIntent
. - -
.
ListView AdapterView, -
View,
. , ListView -

, .

AdapterView:
public void setEmptyView(View emptyView)

XML , ListView,
. @android:id/
list @android:id/empty , -
.
284 16.

CrimeListFragment onCreateView(),
.
CrimeListFragment XML- , FrameLayout
, ListView View,
.
(, ).
,
,
.
17

.
CriminalIntent
JSON, .
Android (sand-
box).
(
, ).
/data/data,
. CriminalIntent
/data/data/com.bignerdranch.android.criminalintent.
, ; ,
API.
, .
, SD-, ( )
. SD- ,
-
. ,

, .
() , API
. (,
.)

CriminalIntent
:
.
286 17.

. -
, .
: ,
, .
CriminalIntent JSON,
- Android Context.
. 17.1
CriminalIntent.

Crime
CrimeLab

JSON
Crime Crime JSON

(
( -) -)

crimes.json
( )

. 17.1. CriminalIntent

JSON (JavaScript Object Notation) ,


-. Android org.json,

JSON. Android org.json,
JSON http://json.org.
(XML . Android
XML.
XML 26.)
, , -
-, Context (
: Application, Activity Service,
CriminalIntent 287

).
Java , java.io.File java.io.FileInputStream.

JSON
CriminalIntent CrimeLab
,
JSON CriminalIntentJSONSerializer
Crime.

CriminalIntentJSONSerializer
ArrayList Crime
JSON CriminalIntentJSONSerializer.
com.bignerdranch.android.criminalintent.
java.lang.Object.
, 17.1.
import , , Eclipse Organize Imports.

17.1. CriminalIntentJSONSerializer
public class CriminalIntentJSONSerializer {
private Context mContext;
private String mFilename;

public CriminalIntentJSONSerializer(Context c, String f) {


mContext = c;
mFilename = f;
}

public void saveCrimes(ArrayList<Crime> crimes)


throws JSONException, IOException {
// JSON
JSONArray array = new JSONArray();
for (Crime c : crimes)
array.put(c.toJSON());

//
Writer writer = null;
try {
OutputStream out = mContext
.openFileOutput(mFilename, Context.MODE_PRIVATE);
writer = new OutputStreamWriter(out);
writer.write(array.toString());
} finally {
if (writer != null)
writer.close();
}
}
}

CrimeLab, -
JSON
288 17.

. (unit-testing) ,
.
, ,
Context . ,
, Context.
saveCrimes(ArrayList<Crime>) JSONArray,
toJSON() -
JSONArray. ( toJSON() ,
Crime. .)
, Context.open-
FileOutput(). .
,
.
, Context.getFilesDir(), openFileOutput()
.
,
Java. Writer, OutputStream OutputStreamWriter
java.io. OutputStream, openFileOutput(), -
OutputStreamWriter,
Java String
, OutputStream .

JSON Crime
mCrimes JSON,
Crime JSON. Crime.java
toJSON(), Crime
JSON JSONObject JSONArray.

17.2. JSON (Crime.java)


public class Crime {

private static final String JSON_ID = "id";


private static final String JSON_TITLE = "title";
private static final String JSON_SOLVED = "solved";
private static final String JSON_DATE = "date";

private UUID mId;


private String mTitle;
private boolean mSolved;
private Date mDate = new Date();

public Crime() {
mId = UUID.randomUUID();
}

public JSONObject toJSON() throws JSONException {


JSONObject json = new JSONObject();
json.put(JSON_ID, mId.toString());
Crime CrimeLab 289

json.put(JSON_TITLE, mTitle);
json.put(JSON_SOLVED, mSolved);
json.put(JSON_DATE, mDate.getTime());
return json;
}

@Override
public String toString() {
return mTitle;
}

JSONObject Crime -
, JSON.

Crime CrimeLab
CriminalIntentJSONSerializer Crime
JSON
.
? -
: ,
.
CrimeLab, .
.
, - , -
. CriminalIntent ,
.
, SQLite.
SQLite Android 34.
CrimeLab.java CriminalIntentJSONSerializer onCre-
ate(). saveCrimes(),
. ,
.

17.3. CrimeLab (CrimeLab.java)


public class CrimeLab {
private static final String TAG = "CrimeLab";
private static final String FILENAME = "crimes.json";

private ArrayList<Crime> mCrimes;


private CriminalIntentJSONSerializer mSerializer;

private static CrimeLab sCrimeLab;


private Context mAppContext;

private CrimeLab(Context appContext) {


mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
}
...

290 17.

17.3 ()
public void addCrime(Crime c) {
mCrimes.add(c);
}

public boolean saveCrimes() {


try {
mSerializer.saveCrimes(mCrimes);
Log.d(TAG, "crimes saved to file");
return true;
} catch (Exception e) {
Log.e(TAG, "Error saving crimes: ", e);
return false;
}
}
}

.

, Toast .

onPause()
saveCrimes()? -
onPause(). onStop() onDestroy(),
. ,
, onStop()
onDestroy() .

17.4. onPause()
(CrimeFragment.java)
...

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(getActivity());
return true;
default:
return super.onOptionsItemSelected(item);
}
}

@Override
public void onPause() {
super.onPause();
CrimeLab.get(getActivity()).saveCrimes();
}
}

CriminalIntent.
Home ; ,
. LogCat.
Crime CrimeLab 291


CriminalIntent
.
Crime.java , JSONObject.

17.5. Crime(JSONObject) (Crime.java)


public class Crime {
...
public Crime() {
mId = UUID.randomUUID();
}

public Crime(JSONObject json) throws JSONException {


mId = UUID.fromString(json.getString(JSON_ID));
mTitle = json.getString(JSON_TITLE);
mSolved = json.getBoolean(JSON_SOLVED);
mDate = new Date(json.getLong(JSON_DATE));
}

public JSONObject toJSON() throws JSONException {


JSONObject json = new JSONObject();
json.put(JSON_ID, mId.toString());
json.put(JSON_TITLE, mTitle);
json.put(JSON_SOLVED, mSolved);
json.put(JSON_DATE, mDate.getTime());
return json;
}

CriminalIntentJSONSerializer.java CriminalIntentJSONSe-
rializer .

17.6. loadCrimes() (CriminalIntentJSONSerializer.java)


public CriminalIntentJSONSerializer(Context c, String f) {
mContext = c;
mFilename = f;
}

public ArrayList<Crime> loadCrimes() throws IOException, JSONException {


ArrayList<Crime> crimes = new ArrayList<Crime>();
BufferedReader reader = null;
try {
// StringBuilder
InputStream in = mContext.openFileInput(mFilename);
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder jsonString = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
// Line breaks are omitted and irrelevant
jsonString.append(line);
}
// JSON JSONTokener
JSONArray array = (JSONArray) new JSONTokener(jsonString.toString())
.nextValue();

292 17.

17.6 ()
// Crime JSONObject
for (int i = 0; i < array.length(); i++) {
crimes.add(new Crime(array.getJSONObject(i)));
}
} catch (FileNotFoundException e) {
// " ";
} finally {
if (reader != null)
reader.close();
}
return crimes;
}

public void saveCrimes(ArrayList<Crime> crimes) throws JSONException, IOException {


// JSON
JSONArray array = new JSONArray();
for (Crime c : crimes)
array.put(c.toJSON());
...

Java JSON open-


FileInput() Context
, JSONObject, JSONAr-
ray, ArrayList, .
reader.close() finally. ,

.
, CrimeLab
ArrayList ( ,
). CrimeLab.java -
.
17.7. (CrimeLab.java)
public CrimeLab(Context appContext) {
mAppContext = appContext;
mSerializer = new CriminalIntentJSONSerializer(mAppContext, FILENAME);
mCrimes = new ArrayList<Crime>();

try {
mCrimes = mSerializer.loadCrimes();
} catch (Exception e) {
mCrimes = new ArrayList<Crime>();
Log.e(TAG, "Error loading crimes: ", e);
}
}

public static CrimeLab get(Context c) {


...
}
...

; , -
.
: Android - Java 293

CriminalIntent .
.
, . -
(, ); , CriminalIntent.
, .
Eclipse.
,
. ,

.

.

,
. -
,
.
, (, ,
) .
, ,
(, ).
.
, . android.os.Environment
, . -
( Context,
). ,
CriminalIntentJSONSerializer.

:
Android - Java
Android Linux.
Android Linux Unix.
(/), -
, .
,
Linux.
Android
Java.
APK, /data/app com.bignerdranch.
android.criminalintent-1.apk.
294 17.

,
.


, , -
Context
: Application, Activity Service, .

, . 17.1.
17.1. Context

File getFilesDir()

FileInputStream (
openFileInput(String name) )

FileOutputStream ,
openFileOutput(String name, ( )
int mode)

File getDir(String name, ( , )


int mode)

String[] fileList() (-
, openFileInput(String))

File getCacheDir() ,
-. ,
-

Java
, java.io.File java.io.FileInputStream.
API, , ,
Java. Android java.nio.*.
18



. .
:
( ), .

. 18.1.
296 18.

Honeycomb .
-
.
.
API : -
.
, .
. - -
,
.
API 11
Froyo Gingerbread.
(,
. ,
ActionBarSherlock
API. ActionBarSherlock .)


res/menu/ XML crime_list_item_context.xml,
menu. item, -
18.1.

18.1. (crime_list_item_context.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_delete_crime"
android:icon="@android:drawable/ic_menu_delete"
android:title="@string/delete_crime" />
</menu>


. Fragment
, 16.
:
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo)


Fragment:
public boolean onContextItemSelected(MenuItem item)
297


CrimeListFragment.java onCreateContextMenu() -
.
18.2. (CrimeListFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
default:
return super.onOptionsItemSelected(item);
}
}

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context, menu);
}

onCreateOptionsMenu() -
MenuInflater, MenuInflater,
CrimeListActivity. MenuInflater.inflate()
ContextMenu
, .
,
,
. -
,
View, onCreateContextMenu().


-
.
Fragment,
:
public void registerForContextMenu(View view)


. , -
. ListView,
.
CrimeListFragment.onCreateView() ListView -
.
18.3. ListView (CrimeListFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,


298 18.

18.3 ()
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {


if (mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}
}

ListView listView = (ListView)v.findViewById(android.R.id.list);


registerForContextMenu(listView);

return v;
}

android.R.id.list -
ListView, ListFragment. ListFragment
getListView(), onCreateView() -
, getListView() null
onCreateView().
CriminalIntent,
, Delete
Crime.

. 18.2.
299


, -
. CrimeLab.java deleteCrime(Crime).

18.4. (CrimeLab.java)
public void addCrime(Crime c) {
mCrimes.add(c);
}

public void deleteCrime(Crime c) {


mCrimes.remove(c);
}

onContextItemSelec
ted(MenuItem). MenuItem ,
. , ,
. , .
getMenuInfo() MenuItem.
, ContextMenu.Con-
textMenuInfo.
CrimeListFragment onContextItemSelected(MenuItem),
,
Crime . Crime
.

18.5. (CrimeListFragment.java)
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context, menu);
}

@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
int position = info.position;
CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
Crime crime = adapter.getItem(position);

switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeLab.get(getActivity()).deleteCrime(crime);
adapter.notifyDataSetChanged();
return true;
}
return super.onContextItemSelected(item);
}

18.5 getMenuInfo() AdapterView.Adapt-


erContextMenuInfo, ListView AdapterView.
300 18.

getMenuInfo()
, .
Crime.
CriminalIntent,
. ( ,
.)


, ,
Android. , . 18.2
Jelly Bean.
-
,
.
, , , -
, .
, .

. 18.3.

, -
, . ,
301

, Froyo Gingerbread,
, .


,
. ,
, -
.
CrimeListFragment.onCreateView()
CHOICE_MODE_MULTIPLE_MODAL. -
, ListView, , .

18.6.
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);

...
ListView listView = (ListView)v.findViewById(android.R.id.list);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {


// Froyo Gingerbread
registerForContextMenu(listView);
} else {
// Honeycomb
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
}

return v;
}



ListView ,
AbsListView.MultiChoiceModeListener. ,
.
public abstract void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked)

MultiChoiceModeListener ActionMode.
Callback. , -
ActionMode, ActionMode.Callback
ActionMode. ActionMode.Callback
:
public abstract boolean onCreateActionMode(ActionMode mode, Menu menu)
302 18.

ActionMode. -
, .
public abstract boolean onPrepareActionMode(ActionMode mode, Menu menu)

onCreateActionMode() ,
.
public abstract boolean onActionItemClicked(ActionMode mode, MenuItem item)

, . -
, .
public abstract void onDestroyActionMode(ActionMode mode)

ActionMode - ,
. -
(-).
,
.
CrimeListFragment.onCreateView() ,
MultiChoiceModeListener . -
onCreateActionMode() onActionItemClicked(ActionMode,
MenuItem).

18.7. MultiChoiceModeListener (CrimeListFragment.java)


...
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {

public void onItemCheckedStateChanged(ActionMode mode, int position,


long id, boolean checked) {
// ,
//
}

// ActionMode.Callback
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.crime_list_item_context, menu);
return true;
}

public boolean onPrepareActionMode(ActionMode mode, Menu menu) {


return false;
// ,
//
}

public boolean onActionItemClicked(ActionMode mode, MenuItem item) {


switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
CrimeLab crimeLab = CrimeLab.get(getActivity());
for (int i = adapter.getCount() - 1; i >= 0; i--) {
303

if (getListView().isItemChecked(i)) {
crimeLab.deleteCrime(adapter.getItem(i));
}
}
mode.finish();
adapter.notifyDataSetChanged();
return true;
default:
return false;
}
}
public void onDestroyActionMode(ActionMode mode) {
// ,
//
}
});
return v;
}

Eclipse -
, : , onCreateActionMode(),
false. true; -
false .
onCreateActionMode() ,
MenuInflater ActionMode, .
.
, ActionMode.setTitle()
. MenuInflater
.

. 18.4.
304 18.

onActionItemClicked() Crime
CrimeLab, . -
ActionMode.finish() .
CriminalIntent. -
, .
() .
. .

.

. ,
- .
, ,
.
.


-
.
, .

.
XML. ( -
) ,
. ( , ,
StateListDrawable.)
,
, ,
drawable . res/drawable XML
res/drawable/background_activated.xml. selector
, 18.8.

18.8.
(res/drawable/background_activated.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_activated="true"
android:drawable="@android:color/darker_gray"
/>
</selector>

: ,
, , android:drawable.
. android:state_activated
305

false, ,
.
res/layout/list_item_crime.xml
.

18.9. (res/layout/list_item_crime.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_activated">
...
</RelativeLayout>

CriminalIntent.
.

. 18.5. ,

25.



, , -
ListView. GridView
306 18.

AdapterView, 26. , -
,
ListView, GridView?
, View.OnLongClickListener.
ActionMode Activity.
startActionMode(). ( MultiChoiceModeListener
ActionMode .)
startActionMode() ,
ActionMode.Callback. ActionMode.Callback,
ActionMode, -
:
public abstract boolean onCreateActionMode(ActionMode mode, Menu menu)
public abstract boolean onPrepareActionMode(ActionMode mode, Menu menu)
public abstract boolean onActionItemClicked(ActionMode mode, MenuItem item)
public abstract void onDestroyActionMode(ActionMode mode)

, ActionMode.Callback, startAction-
Mode(), startActionMode().

: ?
,
(gracious fallback):
.
SDK .
. -
,
. (duplication).
:
.
, .
,
.
-
. -
android.support.v4.app.Fragment ,
android.app.Fragment .
,
,
.
Gingerbread, ,
. Action-
BarSherlock ( Jake Wharton) . http://www.actionbarsherlock.com.
ActionBarSherlock Android
: ActionBarSherlock 307

-
Android.
ActionBarSherlock -
.
( ,
. .)
? .
,
Android.
, Android
. ,

. , -
. ,
ICS Jelly Bean
, .
, . -,
,
. , -
. -,
.
, ,
.
,
, .

. CrimeFragment
-
, .
,
. CrimeFragment.

: ActionBarSherlock
-
Android ,
. , -
,
16.
ActionBarSherlock ( ABS, )
.
, ,
308 18.

.
http://www.actionbarsherlock.com. -
Android, .
, ABS
, .
, ABS .
, jar- ABS
Android. Android,
,
. Android -
Android, . ,
Android,
, jar-.
ABS , ABS
:
;
Eclipse ActionBarSherlock;
ActionBarSherlock CriminalIntent;
CriminalIntent ActionBarSher-
lock.
( Action-
BarSherlock, CriminalIntent. Crim-
inalIntent ABS ).

. 18.6. Android
: ActionBarSherlock 309

ABS, http://www.actionbarsherlock.com/download.
html, zip tgz ( )
.
, Eclipse,
Package Explorer New Project....
Android ,
; Android Project From Existing Code (. 18.6).

. Browse ,
ABS.
: library, samples website. library,
Open, Finish.

. 18.7. ABS

Eclipse library -
; , Refactor Rename...
ActionBarSherlock.

-
CriminalIntent.

CriminalIntent
Package Explorer -
Properties...
Android
, -
. . 18.8. Android
310 18.

Android.
, . Add

. 18.9. ActionBarSherlock

, ActionBarSherlock,
.

. ActionBarSherlock
, ActionBarSherlock,
CriminalIntent. ABS ,
-
Android, Activity, Fragment ActionBar. (
) ABS Sherlock,
.
, .
Sherlock-, .

ABS CriminalIntent
ABS :
SingleFragmentActivity CrimePagerActiv-
ity , SherlockFragmentActivity (
FragmentActivity).
, -
SherlockFragment, SherlockDialogFragment SherlockListFragment
( ).
Menu, MenuItem MenuInflater -
com.actionbarsherlock.view.
.
, CrimeFragment CrimeListFragment
311

. , ,
.
CrimeFragment : -
import ,
Command+Shift+O/Ctrl+Shift+O com.actionbarsherlock.view.
CrimeListFragment, -
. , CrimeListFragment
MultiChoiceModeListener,
Android.
?
MenuItem, Menu MenuInflater onCreateOptionsMenu()
onOptionsItemSelected(). :
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
...
}

:
@Override
public void onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu,
com.actionbarsherlock.view.MenuInflater inflater) {
...
}


, , ABS
. -
. getSherlockActivity().
getSupportActionBar() getActivity().getActionBar().
SherlockActivity ,
. res/menu-v11/
fragment_crime_list.xml res/menu,
.


?
CriminalIntent MultiChoic-
eModeListener . ,
MultiChoiceModeListener .
?
ListView: ListView.CHOICE_MODE_MULTIPLE.
ListView.CHOICE_MODE_MULTIPLE_MODAL,
312 18.

Android. ListView.CHOICE_MODE_MULTIPLE -
, :
Android. ListView.CHOICE_MODE_MULTIPLE
ListView .
, ListView.CHOICE_MODE_NONE.
-
, CHOICE_MODE_MULTIPLE_MODAL. ,
, getSherlockActivity().startAc-
tionMode(). , com.action-
barsherlock.view.ActionMode.Callback, Android.
, . -
OnItemLongClickListener ListView.
setOnItemLongClickListener().
19 I: Viewfinder

.
API
.
API -
. ,
, . ,
- : .
?
. . -
Android ,
, MediaStore.ACTION_IMAGE_CAPTURE.
21.
, -
, -
. ,
. CriminalIntent -
, API .
,
SurfaceView
.
314 19. I: Viewfinder

. 19.1.

. 19.2 , .

. 19.2. CriminalIntent

Camera -
. ;
.
315

SurfaceView . SurfaceView
.
CrimeCameraFragment,
CrimeCameraFragment CrimeCameraActivity.
CrimeCameraFragment. , CrimeFragment
CrimeCameraActivity.


Android XML Layout fragment_crime_camera.xml
FrameLayout. , . 19.3.

. 19.3. CrimeCameraFragment (fragment_crime_camera.xml)

FrameLayout LinearLay-
out, - LinearLayout.
; FrameLayout
20.
LinearLayout
layout_width layout_weight. ,
Button, - android:layout_width="wrap_
content", SurfaceView (android:layout_width="0dp").
SurfaceView
layout_weight, SurfaceView.
. 19.4 , .
316 19. I: Viewfinder

. 19.4.

strings.xml .

19.1. (strings.xml)
...
<string name="show_subtitle">Show Subtitle</string>
<string name="subtitle">Sometimes tolerance is not a virtue.</string>
<string name="take">Take!</string>

</resources>

CrimeCameraFragment
CrimeCameraFragment
android.support.v4.app.Fragment. CrimeCameraFragment.java
, 19.2. onCreateView() ,
.
, -;
.

19.2. (CrimeCameraFragment.java)
public class CrimeCameraFragment extends Fragment {
private static final String TAG = "CrimeCameraFragment";

private Camera mCamera;


private SurfaceView mSurfaceView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime_camera, parent, false);
Button takePictureButton = (Button)v
.findViewById(R.id.crime_camera_takePictureButton);
317

takePictureButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getActivity().finish();
}
});

mSurfaceView = (SurfaceView)v.findViewById(R.id.crime_camera_surfaceView);

return v;
}
}

CrimeCameraActivity
SingleFragmentActivity CrimeCameraActivity.
createFragment(),
CrimeCameraFragment.

19.3. (CrimeCameraActivity.java)
public class CrimeCameraActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeCameraFragment();
}
}



CrimeCameraActivity .
, ,
uses-permission.
AndroidManifest.xml , 19.4.

19.4. (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.criminalintent"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16"/>


<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

<application

...

318 19. I: Viewfinder

19.4 ()
<activity android:name=".CrimeCameraActivity"
android:screenOrientation="landscape"
android:label="@string/app_name">
</activity>

</application>

</manifest>

uses-feature ,
. android.hardware.camera ,
Google Play, ,
.
, -

android:screenOrientation.
,
.
screenOrientation . -
, , ,
-
.
<activity>.

API
.
,
.


. CrimeCameraFragment
Camera. ,
, ,
.
.
Camera:
public static Camera open(int cameraId)
public static Camera open()
public final void release()

open(int) API 9, API 8


open() .
CrimeCameraFragment,
, onResume() onPause().
API 319

,
. ( : onResume()
, .)
CrimeCameraFragment.onResume()
Camera.open(int) 0, ,
. ,
(, , Nexus 7), .
API 8 Camera.open() .
Froyo, Camera.open() API
8.

19.5. onResume() (CrimeCameraFragment.java)


@TargetApi(9)
@Override
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(0);
} else {
mCamera = Camera.open();
}
}

(Android Lint
. ,
, 26.)
, ,
. -
onPause() .

19.6. (CrimeCameraFragment.java)
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(0);
} else {
mCamera = Camera.open();
}
}

@Override
public void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}


release(). .
320 19. I: Viewfinder

, ,
,
. - , -
null .

SurfaceView, SurfaceHolder Surface


SurfaceView SurfaceHolder. CrimeCam-
eraFragment.java , SurfaceHolder
SurfaceView.

19.7. SurfaceHolder (CrimeCameraFragment.java)


@Override
@SuppressWarnings("deprecation")
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {

...
mSurfaceView = (SurfaceView)v.findViewById(R.id.crime_camera_surfaceView);
SurfaceHolder holder = mSurfaceView.getHolder();
// setType() SURFACE_TYPE_PUSH_BUFFERS
// , ,
//
// Camera 3.0.
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

return v;
}

setType() SURFACE_TYPE_PUSH_BUFFERS ,
. Eclipse
.
? setType() -
SURFACE_TYPE_PUSH_BUFFERS
Honeycomb.
19.7 @SuppressWarnings.
, -
Android.
Android 20.
SurfaceHolder Surface.
Surface .
Surface :
SurfaceView , SurfaceView -
. , , Surface ,
.
View, SurfaceView,
. (client) Surface ,
API 321

- . CrimeCameraFragment
Camera.


. 19.5. SurfaceView, SurfaceHolder Surface

: , Surface -
, . (. 19.6),
Camera SurfaceHolder Surface
Surface.


. 19.6.
322 19. I: Viewfinder

SurfaceHolder , -
SurfaceHolder.Callback.
Surface Surface .
SurfaceHolder.Callback :
public abstract void surfaceCreated(SurfaceHolder holder)

, Sur-
faceView. Surface .
public abstract void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)

, surfaceChanged().
, .
Surface .
public abstract void surfaceDestroyed(SurfaceHolder holder)

SurfaceView Surface .
Surface Surface.
Camera,
Surface:
public final void setPreviewDisplay(SurfaceHolder holder)

Surface. surfaceCreated().
public final void startPreview()

Surface . sur-
faceChanged().
public final void stopPreview()

Surface. surfa-
ceDestroyed().
CrimeCameraFragment.java SurfaceHolder.Callback
Surface -
.

19.8. SurfaceHolder.Callback (CrimeCameraFragment.java)


...
SurfaceHolder holder = mSurfaceView.getHolder();
// setType() SURFACE_TYPE_PUSH_BUFFERS
// , ,
//
// Camera 3.0.
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

holder.addCallback(new SurfaceHolder.Callback() {

public void surfaceCreated(SurfaceHolder holder) {


//
//
try {
API 323

if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "Error setting up preview display", exception);
}
}

public void surfaceDestroyed(SurfaceHolder holder) {


// ,
// .
if (mCamera != null) {
mCamera.stopPreview();
}
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)


{
if (mCamera == null) return;

// ;
//
Camera.Parameters parameters = mCamera.getParameters();
Size s = null; //
parameters.setPreviewSize(s.width, s.height);
mCamera.setParameters(parameters);
try {
mCamera.startPreview();
} catch (Exception e) {
Log.e(TAG, "Could not start preview", e);
mCamera.release();
mCamera = null;
}
}
});

return v;
}

, .

.
surfaceChanged()
null. ,
.
, .



-
Camera.Parameters,
:
public List<Camera.Size> getSupportedPreviewSizes()
324 19. I: Viewfinder

android.hardware.Camera.Size,
.
Surface,
surfaceChanged(), ,
Surface.
CrimeCameraFragment , -
.
, .

19.9. (CrimeCameraFragment.java)
/** .
* CameraPreview.java
* - ApiDemos Android. */
private Size getBestSupportedSize(List<Size> sizes, int width, int height) {
Size bestSize = sizes.get(0);
int largestArea = bestSize.width * bestSize.height;
for (Size s : sizes) {
int area = s.width * s.height;
if (area > largestArea) {
bestSize = s;
largestArea = area;
}
}
return bestSize;
}


surfaceChanged().

19.10. getBestSupportedSize() (CrimeCameraFragment.java)


...
holder.addCallback(new SurfaceHolder.Callback() {

...

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {


// ;
//
Camera.Parameters parameters = mCamera.getParameters();
Size s = null;
Size s = getBestSupportedSize(parameters.getSupportedPreviewSizes(), w, h);
parameters.setPreviewSize(s.width, s.height);
mCamera.setParameters(parameters);
try {
mCamera.startPreview();
} catch (Exception e) {
Log.e(TAG, "Could not start preview", e);
mCamera.release();
mCamera = null;
}
}
});
API 325

CrimeCameraActivity
CrimeFragment
, Crime-
Fragment . Crime-
Fragment CrimeCameraActivity.

CrimeFragment, . 19.7.
, -
. 19.7,
LinearLayout ImageButton.
. 19.8 , -
CrimeFragment .

. 19.7. CrimeFragment

. 19.8.
(layout/fragment_crime.xml)
326 19. I: Viewfinder

. 19.9.
(layout-land/fragment_crime.xml)

CrimeFragment ImageButton,
OnClickListener, CrimeCameraActivity.

19.11. CrimeCameraActivity (CrimeFragment.java)


public class CrimeFragment extends Fragment {
...
private ImageButton mPhotoButton;

...
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

...

mPhotoButton = (ImageButton)v.findViewById(R.id.crime_imageButton);
mPhotoButton.setOnClickListener(new View.OnClickListener() {
API 327

@Override
public void onClick(View v) {
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);
}
});

return v;
}

, mPhotoButton .
PackageManager.
onCreateView(), ImageButton
.

19.12. (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

...

mPhotoButton = (ImageButton)v.findViewById(R.id.crime_imageButton);
mPhotoButton.setOnClickListener(new View.OnClickListener() {
...
});

// ,
//
PackageManager pm = getActivity().getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) &&
!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
mPhotoButton.setEnabled(false);
}

...
}

PackageManager hasSystemFeature(String),
. :
FEATURE_CAMERA , FEATURE_CAMERA_FRONT -
. ,
ImageButton .
CriminalIntent.
. .
Take! CrimeFragment.
328 19. I: Viewfinder

. 19.10.

-
. ,
.


. 19.10
.
,
. .
, CrimeCameraFragment
; CrimeCameraActivity. CrimeCamer-
aActivity.java onCreate(Bundle).

19.13. (CrimeCameraActivity.java)
public class CrimeCameraActivity extends SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
// .
requestWindowFeature(Window.FEATURE_NO_TITLE);
//
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
}

@Override
protected Fragment createFragment() {
return new CrimeCameraFragment();
}
}

? requestWindowFeature()
addFlags()
: 329

Activity.setContentView(), CrimeCameraActivity -
onCreate(Bundle) .
-. ,
.
CriminalIntent -
.

. 19.11.

API ,
Crime-
Fragment.

:

,
CrimeFragment CrimeCameraActivity. -
,
.


. -
. ,
, .
,
.
: adb.
.
CrimeCameraActivity AndroidManifest.xml:
330 19. I: Viewfinder

<activity android:name=".CrimeCameraActivity"
android:exported="true"
android:screenOrientation="landscape"
android:label="@string/app_name">
</activity>

.
exported true, Android,
. (
exported -
true.)
adb platform-tools Android SDK.
platform-tools tools
.
adb (Android Debug Bridge). -
adb ,
Eclipse. LogCat,
, , -
, ,
.
adb , -
.
CriminalIntent
, . CrimeCam-
eraActivity :
$ adb shell am start -n com.bignerdranch.android.criminalintent/.
CrimeCameraActivity
Starting: Intent { cmp=com.bignerdranch.android.criminalintent/.CrimeCameraActivity
}

, CrimeCameraActivity
.

. -
:
$ adb shell
shell@android:/ $ am start -n com.bignerdranch.android.criminalintent/.
CrimeCameraAct\
ivity

am ,
(Activity Manager). -
Android, .
am adb shell am.
20 II:

JPEG
, JPEG Crime -
CrimeFragment.

DialogFragment.

. 20.1.
332 20. II:


CrimeCameraFragment -
. , ,
.
layout/fragment_crime_camera.xml FrameLayout ProgressBar,
. 20.2.

. 20.2. FrameLayout ProgressBar (fragment_crime_camera.xml)

@android:style/Widget.ProgressBar.Large -
.

. 20.3.
333

FrameLayout ( ProgressBar ) .
, Take!
.
: FrameLayout match_
parent. FrameLayout
. , FrameLayout,
ProgressBar, LinearLayout.
FrameLayout , - -
LinearLayout. ProgressBar
. FrameLayout
match_parent android:clickable="true" ,
FrameLayout (
).
LinearLayout, Take!.
CrimeCameraFragment.java. FrameLayout,
.

20.1. FrameLayout (CrimeCameraFragment.java)


public class CrimeCameraFragment extends Fragment {
...
private View mProgressContainer;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime_camera, parent, false);

mProgressContainer = v.findViewById(R.id.crime_camera_progressContainer);
mProgressContainer.setVisibility(View.INVISIBLE);

...

return v;
}
...
}


, ; -
JPEG.
Camera:
public final void takePicture(Camera.ShutterCallback shutter,
Camera.PictureCallback raw,
Camera.PictureCallback jpeg)

ShutterCallback , ,
.
334 20. II:

PictureCallback
.
PictureCallback JPEG-
..
takePicture().
- , null -
takePicture().
CriminalIntent ShutterCallback
JPEG.
. 20.4 .

. 20.4. CrimeCameraFragment

, .
public static interface Camera.ShutterCallback {
public abstract void onShutter();
}
public static interface Camera.PictureCallback {
public abstract void onPictureTaken (byte[] data, Camera camera);
}

CrimeCameraFragment.java Camera.ShutterCallback,
, Camera.PictureCallback,
JPEG.

20.2. takePicture() (CrimeCameraFragment.java)


...
private View mProgressContainer;

private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {


public void onShutter() {
335

//
mProgressContainer.setVisibility(View.VISIBLE);
}
};

private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {


public void onPictureTaken(byte[] data, Camera camera) {
//
String filename = UUID.randomUUID().toString() + ".jpg";
// jpeg
FileOutputStream os = null;
boolean success = true;
try {
os = getActivity().openFileOutput(filename, Context.MODE_PRIVATE);
os.write(data);
} catch (Exception e) {
Log.e(TAG, "Error writing to file " + filename, e);
success = false;
} finally {
try {
if (os != null)
os.close();
} catch (Exception e) {
Log.e(TAG, "Error closing file " + filename, e);
success = false;
}
}

if (success) {
Log.i(TAG, "JPEG saved at " + filename);
}
getActivity().finish();
}
};
...

onPictureTaken() ,
. - Java
JPEG, Camera.
, .
: mProgressContainer .
, PictureCallback -
, .
Take!,
takePicture(). null
, .

20.3. takePicture() (CrimeCameraFragment.java)


@Override
@SuppressWarnings("deprecation")
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

336 20. II:

20.3 ()
takePictureButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getActivity().finish();
if (mCamera != null) {
mCamera.takePicture(mShutterCallback, null, mJpegCallback);
}
}
});

...

return v;
}


. -
, .
Camera.Parameters:
public List<Camera.Size> getSupportedPictureSizes()

surfaceChanged() getBestSupportedSize()
,
Surface. .
20.4. getBestSupportedSize()
(CrimeCameraFragment.java)
...

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {


if (mCamera == null) return;

// ;
//
Camera.Parameters parameters = mCamera.getParameters();
Size s = getBestSupportedSize(parameters.getSupportedPreviewSizes(), w, h);
parameters.setPreviewSize(s.width, s.height);
s = getBestSupportedSize(parameters.getSupportedPictureSizes(), w, h);
parameters.setPictureSize(s.width, s.height);
mCamera.setParameters(parameters);

...
}
});

CriminalIntent Take!. ,
, LogCat CrimeCameraFragment.
CrimeCameraFragment -
, . API -
. CrimeFragment
.
CrimeFragment 337

CrimeFragment
CrimeFragment , CrimeCameraFragment
. . 20.5
CrimeFragment CrimeCameraFragment.

Android

. 20.5. CrimeCameraActivity

CrimeFragment CrimeCameraActivity .
CrimeCameraFragment -
, , setResult(). ActivityManager
CrimePagerActivity onActivityResult(). Frag-
mentManager, CrimePagerActivity, CrimeFragment
CrimeFragment.onActivityResult().

CrimeCameraActivity
CrimeFragment CrimeCameraActivity.
CrimeFragment.java ,
, CrimeCameraActivity .

20.5. CrimeCameraActivity (CrimeFragment.java)


public class CrimeFragment extends Fragment {
...
private static final int REQUEST_DATE = 0;
private static final int REQUEST_PHOTO = 1;
...

338 20. II:

20.5 ()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mPhotoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);
startActivityForResult(i, REQUEST_PHOTO);
}
});
...
}

CrimeCameraFragment
CrimeCameraFragment
CrimeCameraActivity.setResult(int, Intent). CrimeCameraFragment.
java ,
RESULT_OK, -
, RESULT_CANCELED, - .

20.6.
(CrimeCameraFragment.java)
public class CrimeCameraFragment extends Fragment {
private static final String TAG = "CrimeCameraFragment";

public static final String EXTRA_PHOTO_FILENAME =


"com.bignerdranch.android.criminalintent.photo_filename";

...

private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {


public void onPictureTaken(byte[] data, Camera camera) {
...
try {
...
} catch (Exception e) {
...
} finally {
...
}
Log.i(TAG, "JPEG saved at " + filename);
//
if (success) {
Intent i = new Intent();
i.putExtra(EXTRA_PHOTO_FILENAME, filename);
getActivity().setResult(Activity.RESULT_OK, i);
} else {
CrimeFragment 339

getActivity().setResult(Activity.RESULT_CANCELED);
}
getActivity().finish();
}
};

CrimeFragment
CrimeFragment
CriminalIntent.
CrimeFragment.java onActivityResult(),
, .
TAG CrimeFragment, .

20.7. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private static final String TAG = "CrimeFragment"
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";
...

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;

if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Log.i(TAG, "filename: " + filename);
}
}
}

CriminalIntent CrimeCameraActivity.
LogCat , CrimeFragment -
.
, CrimeFragment ,
:
: Photo,
. Crime mPhoto
Photo. CrimeFragment Photo -
mPhoto Crime.
340 20. II:

CrimeFragment: CrimeFragment
ImageView Crime.
Dia-
logFragment ImageFragment
.


. 20.6 CrimeFragment, Crime Photo.

. 20.6. CrimeFragment

Photo
com.bignerdranch.android.criminalintent. -
Photo java.lang.Object.
Photo.java , 20.8.

20.8. Photo (Photo.java)


...
public class Photo {
private static final String JSON_FILENAME = "filename";

private String mFilename;


/** Photo, */
public Photo(String filename) {
mFilename = filename;
}
341

public Photo(JSONObject json) throws JSONException {


mFilename = json.getString(JSON_FILENAME);
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put(JSON_FILENAME, mFilename);
return json;
}
public String getFilename() {
return mFilename;
}
}

: Photo .
Photo , -
JSON, Crime
Photo.

Crime
Crime Photo,
JSON ( 20.9).

20.9. Photo Crime (Crime.java)


public class Crime {
...
private static final String JSON_DATE = "date";
private static final String JSON_PHOTO = "photo";

...
private Date mDate = new Date();
private Photo mPhoto;

...

public Crime(JSONObject json) throws JSONException {


...
mDate = new Date(json.getLong(JSON_DATE));
if (json.has(JSON_PHOTO))
mPhoto = new Photo(json.getJSONObject(JSON_PHOTO));
}

public JSONObject toJSON() throws JSONException {


JSONObject json = new JSONObject();
...
json.put(JSON_DATE, mDate.getTime());
if (mPhoto != null)
json.put(JSON_PHOTO, mPhoto.toJSON());
return json;
}
342 20. II:

20.9 ()
...

public Photo getPhoto() {


return mPhoto;
}

public void setPhoto(Photo p) {


mPhoto = p;
}
}


CrimeFragment.java onActivityResult(),
Photo Crime.

20.10. Photo
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;

if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Log.i(TAG, "filename: " + filename);

Photo p = new Photo(filename);


mCrime.setPhoto(p);
Log.i(TAG, "Crime: " + mCrime.getTitle() + " has a photo");
}
}
}

CriminalIntent . LogCat -
, Crime .
Photo , Crime
? , ,
Photo - ,
. .

CrimeFragment
.
CrimeFragment ImageView.
343

. 20.7. CrimeFragment ImageView

ImageView
layout/fragment_crime.xml ImageView,
. 20.8.

. 20.8. CrimeFragment ImageView (layout/fragment_crime.xml)


344 20. II:

ImageView (. 20.9).

. 20.9. ImageView (layout-land/fragment_crime.xml)

CrimeFragment.java ImageView
onCreateView().
20.11. ImageView (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private ImageButton mPhotoButton;
private ImageView mPhotoView;

...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mPhotoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Launch the camera activity
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivityForResult(i, REQUEST_PHOTO);
}
});

mPhotoView = (ImageView)v.findViewById(R.id.crime_imageView);
...
}

, ImageView ,
CriminalIntent.
345


ImageView -
, , ,
. - -
. -
, .
Android
8- .
, ,
,
.


ImageView
com.bignerdranch.android.criminalintent
PictureUtils. PictureUtils.java ,
.

20.12. PictureUtils (PictureUtils.java)


public class PictureUtils {
/**
* BitmapDrawable ,
* .
*/
@SuppressWarnings("deprecation")
public static BitmapDrawable getScaledDrawable(Activity a, String path) {
Display display = a.getWindowManager().getDefaultDisplay();
float destWidth = display.getWidth();
float destHeight = display.getHeight();

//
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

float srcWidth = options.outWidth;


float srcHeight = options.outHeight;

int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth) {
if (srcWidth > srcHeight) {
inSampleSize = Math.round(srcHeight / destHeight);
} else {
inSampleSize = Math.round(srcWidth / destWidth);
}
}

options = new BitmapFactory.Options();


options.inSampleSize = inSampleSize;

346 20. II:

20.12 ()
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return new BitmapDrawable(a.getResources(), bitmap);
}
}

Display.getWidth() Display.getHeight() (dep-


recated). .
, -
ImageView. ,
, . ,
onCreateView() ImageView. -
,
. , ,
, .
CrimeFragment , -
ImageView.

20.13. showPhoto() (CrimeFragment.java)


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
}

private void showPhoto() {


// ,
Photo p = mCrime.getPhoto();
BitmapDrawable b = null;
if (p != null) {
String path = getActivity()
.getFileStreamPath(p.getFilename()).getAbsolutePath();
b = PictureUtils.getScaledDrawable(getActivity(), path);
}
mPhotoView.setImageDrawable(b);
}

CrimeFragment.java onStart() showPhoto(), -


, CrimeFragment.

20.14. (CrimeFragment.java)
...
private void showPhoto() {
// ,
Photo p = mCrime.getPhoto();
BitmapDrawable b = null;
if (p != null) {
String path = getActivity()
.getFileStreamPath(p.getFilename()).getAbsolutePath();
b = PictureUtils.getScaledDrawable(getActivity(), path);
}
347

mPhotoButton.setImageDrawable(b);
}

@Override
public void onStart() {
super.onStart();
showPhoto();
}

CrimeFragment.onActivityResult() showPhoto(),
CrimeCameraActivity.

20.15. showPhoto() onActivityResult() (CrimeFragment.java)


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Photo p = new Photo(filename);
mCrime.setPhoto(p);
showPhoto();
Log.i(TAG, "Crime: " + mCrime.getTitle() + "has a photo");
}
}
}


PictureUtils BitmapDrawable,
ImageView ( ).

20.16. (PictureUtils.java)
public class PictureUtils {
/**
* ...
*/
@SuppressWarnings("deprecation")
public static BitmapDrawable getScaledDrawable(Activity a, String path) {
...
}

public static void cleanImageView(ImageView imageView) {


if (!(imageView.getDrawable() instanceof BitmapDrawable))
return;

//
BitmapDrawable b = (BitmapDrawable)imageView.getDrawable();
b.getBitmap().recycle();
imageView.setImageDrawable(null);
}
}
348 20. II:

Bitmap.recycle() . ,
, . Bitmap.recycle()
(native) , .
. ( -
Android.
Honeycomb Java Bitmap.)
recycle(),
. -
(finalizer),
.
.
,
. ,
( ) recycle()
.
CrimeFragment onStop() cleanImageView().
20.17. (CrimeFragment.java)
@Override
public void onStart() {
super.onStart();
showPhoto();
}

@Override
public void onStop() {
super.onStop();
PictureUtils.cleanImageView(mPhotoView);
}

onStart() onStop() .
, .
onResume() onPause(),
.
,
, , .
onResume() onPause(), -
. ,
, ,
.
CriminalIntent. ,
ImageView. CriminalIntent .
,
, .
CrimeCameraActivity ,
. -
, .
.
DialogFragment 349


DialogFragment

Crime.

. 20.10. DialogFragment

com.bignerdranch.android.criminalintent. -
ImageFragment; DialogFragment.
ImageFragment -
Crime. ImageFragment.java newInstance(String),
,
20.18.

20.18. ImageFragment (ImageFragment.java)


public class ImageFragment extends DialogFragment {
public static final String EXTRA_IMAGE_PATH =
"com.bignerdranch.android.criminalintent.image_path";

public static ImageFragment newInstance(String imagePath) {


Bundle args = new Bundle();

350 20. II:

20.18 ()
args.putSerializable(EXTRA_IMAGE_PATH, imagePath);

ImageFragment fragment = new ImageFragment();


fragment.setArguments(args);
fragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0);

return fragment;
}
}

DialogFragment.STYLE_NO_TITLE,
, . 20.10.
ImageFragment , AlertDialog.
, , -
onCreateView()
View ( onCreateDialog() -
Dialog).
ImageFragment.java onCreateView(), -
ImageView .
ImageView.
onDestroyView(), ,
.
20.19. ImageFragment (ImageFragment.java)
public class ImageFragment extends DialogFragment {
public static final String EXTRA_IMAGE_PATH =
"com.bignerdranch.android.criminalintent.image_path";

public static ImageFragment newInstance(String imagePath) {


...
}

private ImageView mImageView;

@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup parent, Bundle savedInstanceState) {
mImageView = new ImageView(getActivity());
String path = (String)getArguments().getSerializable(EXTRA_IMAGE_PATH);
BitmapDrawable image = PictureUtils.getScaledDrawable(getActivity(), path);

mImageView.setImageDrawable(image);

return mImageView;
}

@Override
public void onDestroyView() {
super.onDestroyView();
PictureUtils.cleanImageView(mImageView);
}
}
. 351

, CrimeFragment.
CrimeFragment.java mPhotoView.
ImageFragment FragmentManager
CrimePagerActivity show() ImageFragment.
ImageFragment FragmentManager.
20.20. ImageFragment (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private static final String DIALOG_IMAGE = "image";

...

@Override
@TargetApi(11)
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...

mPhotoView = (ImageView)v.findViewById(R.id.crime_imageView);
mPhotoView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Photo p = mCrime.getPhoto();
if (p == null)
return;

FragmentManager fm = getActivity()
.getSupportFragmentManager();
String path = getActivity()
.getFileStreamPath(p.getFilename()).getAbsolutePath();
ImageFragment.newInstance(path)
.show(fm, DIALOG_IMAGE);
}
});
...
}

CriminalIntent. ,

.

. Crime
. -
API , . -
Photo
CrimeFragment ImageFragment.

.
Crime, -
. onActivityResult(int,
352 20. II:

int, Intent) CrimeFragment -


.

. CrimeFragment
/ , -
. Delete Photo ,
ImageView.

:
Android
19,
. -
.
: ?
, . API
, , .
- ,
SurfaceHolder.setType(int) SurfaceHolder.
SURFACE_TYPE_PUSH_BUFFERS, 19.
Android SurfaceHolder
. , -
setType() .
, ,
- . , BitmapDrawable
BitmapDrawable(Bitmap),
. ,
, , , View.setBackgroundDrawable(Drawable).
Display.getWidth() Display.getHeight(), -
. getSize(Point),
, get-
Width() getHeight().
- ,
. , ,
, Microsoft Apple.
Microsoft API , .
, Microsoft -
. Microsoft
API, .
, -
. - Microsoft .
, Apple API ,
. Apple ,
: Android 353

. , API
. Apple ,
.
Apple , -
:
float destWidth;
float destHeight;

if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2) {


Point size = display.getSize();
destWidth = size.x;
destHeight = size.y;
} else {
destWidth = display.getWidth();
destHeight = display.getHeight();
}

, Apple getWidth() getHeight(), ,


. , -
.
. Android Microsoft,
. Android SDK
, ,
API . ,
. ,
, .
API ,
.
21

Android
(implicit intent).
, .
, , -
.
CriminalIntent

. ,
, .

. 21.1.
355

-
, .
,
, .
, CriminalIntent
:
CrimeFragment
;
Crime mSuspect, -
;
.


CrimeFragment .
, .

21.1. (strings.xml)
<string name="take">Take!</string>
<string name="crime_suspect_text">Choose Suspect</string>
<string name="crime_report_text">Send Crime Report</string>
</resources>

layout/fragment_crime.xml Button,
. 21.2. : LinearLayout,
.

. 21.2.
(layout/fragment_crime.xml)

-
LinearLayout,
.
356 21.

. 21.3.

. 21.3, .
, ScrollView.
. 21.4.
ScrollView,
ScrollView.

. 21.4.
(layout_land/fragment_crime.xml)
357

-
CriminalIntent,
.


Crime.java, JSON
. JSON -
/ JSON .

21.2. (Crime.java)
public class Crime {
...
private static final String JSON_PHOTO = "photo";
private static final String JSON_SUSPECT = "suspect";

...
private Photo mPhoto;
private String mSuspect;

public Crime(JSONObject json) throws JSONException {


mId = UUID.fromString(json.getString(JSON_ID));
...
if (json.has(JSON_PHOTO))
mPhoto = new Photo(json.getJSONObject(JSON_PHOTO));
if (json.has(JSON_SUSPECT))
mSuspect = json.getString(JSON_SUSPECT);
}

public JSONObject toJSON() throws JSONException {


JSONObject json = new JSONObject();
...
if (mPhoto != null)
json.put(JSON_PHOTO, mPhoto.toJSON());
json.put(JSON_SUSPECT, mSuspect);
return json;
}

public void setPhoto(Photo p) {


mPhoto = p;
}

public String getSuspect() {


return mSuspect;
}
public void setSuspect(String suspect) {
mSuspect = suspect;
}
}


-
, .
358 21.

, -
,
. :
<string name="crime_report">%1$s! The crime was discovered on %2$s. %3$s, and %4$s
%1$s, %2$s . . . -
getString()
, .
strings.xml 21.3.

21.3. (strings.xml)
<string name="crime_suspect_text">Choose Suspect</string>
<string name="crime_report_text">Send Crime Report</string>
<string name="crime_report">%1$s!
The crime was discovered on %2$s. %3$s, and %4$s
</string>
<string name="crime_report_solved">The case is solved</string>
<string name="crime_report_unsolved">The case is not solved</string>
<string name="crime_report_no_suspect">There is no suspect.</string>
<string name="crime_report_suspect">The suspect is %s.</string>
<string name="crime_report_subject">CriminalIntent Crime Report</string>
<string name="send_report">Send crime report via</string>

</resources>

CrimeFragment.java , ,
.

21.4. getCrimeReport() (CrimeFragment.java)


private String getCrimeReport() {
String solvedString = null;
if (mCrime.isSolved()) {
solvedString = getString(R.string.crime_report_solved);
} else {
solvedString = getString(R.string.crime_report_unsolved);
}

String dateFormat = "EEE, MMM dd";


String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();

String suspect = mCrime.getSuspect();


if (suspect == null) {
suspect = getString(R.string.crime_report_no_suspect);
} else {
suspect = getString(R.string.crime_report_suspect, suspect);
}
String report = getString(R.string.crime_report,
mCrime.getTitle(), dateString, solvedString, suspect);
return report;
}

,
.
359


Intent , .
, ,
, .
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);

, -
, ,
. , -
.


, -
.
(action)
Intent. , URL- Intent.ACTION_VIEW,
Intent.ACTION_SEND.
,
(, URL -), URI URI
, ContentProvider.
, , MIME (, text/html
audio/mpeg3). ,
.
-
, , ,
. Android android.intent.category.
LAUNCHER ,
. , android.intent.cat-
egory.INFO ,
, .
- Intent.AC-
TION_VIEW Uri URL- .
-
. ( ,
.)
ACTION_VIEW
. , -,
, ACTION_VIEW.
<activity
android:name=".BrowserActivity"
android:label="@string/app_name" >

360 21.

<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="www.bignerdranch.com" />
</intent-filter>
</activity>

DEFAULT . action
, ,
DEFAULT -
. DEFAULT
. ( LAUNCHER,
23.)
, , . -

.
, -
. ,
.


, ,
CriminalIntent. ,
, ;
. ,
ACTION_SEND. , -
text/plain.
CrimeFragment.onCreateView() Send Crime
Report .
startActivity(Intent).

21.5. (CrimeFragment.java)
...
Button reportButton = (Button)v.findViewById(R.id.crime_reportButton);
reportButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.crime_report_subject));
startActivity(i);
}
});

return v;
}
361

Intent, -
, -
.
,
-
.
Intent. -
, , ,
.
-
.
, Intent.
, ,
, -
.
CriminalIntent -
Send Crime Report.

, ,
:
, -
. , - . 21.5. ,

.

.
, : -
,
,
.
,
. , Crim-
inalIntent :
,
.
, -
.
, , Intent
:
public static Intent createChooser(Intent target, String title)

, createChooser(), startActivity().
CrimeFragment.java ,
.
362 21.

21.6. (CrimeFragment.java)
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.crime_report_subject));
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
}

CriminalIntent Send Crime Report.


, ,
.

. 21.6.

Android
, -
. -
.
Intent.ACTION_PICK, ContactsContract.
363

Contacts.CONTENT_URI. , Android
.
,
startActivityForResult() . CrimeFrag-
ment.java .

21.7. (CrimeFragment.java)
...
private static final int REQUEST_PHOTO = 1;
private static final int REQUEST_CONTACT = 2;
...

private ImageButton mPhotoButton;


private Button mSuspectButton;
...

onCreateView() .
startActivity-
ForResult(). (
Crime).

21.8. (CrimeFragment.java)
...

mSuspectButton = (Button)v.findViewById(R.id.crime_suspectButton);
mSuspectButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(i, REQUEST_CONTACT);
}
});

if (mCrime.getSuspect() != null) {
mSuspectButton.setText(mCrime.getSuspect());
}

return v;
}

CriminalIntent Choose Suspect.


.
,
. : -
, .
,
, .
364 21.

. 21.7.


.
, Android
API
ContentProvider. -
. ContentProvider -
ContentResolver.

ACTION_PICK, onActivityResult().
URI , .
CrimeFragment.java onActivityRe-
sult() CrimeFragment.

21.9. (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
...
} else if (requestCode == REQUEST_PHOTO) {
365

...
} else if (requestCode == REQUEST_CONTACT) {
Uri contactUri = data.getData();

// ,
// .
String[] queryFields = new String[] {
ContactsContract.Contacts.DISPLAY_NAME
};
// - contactUri
// "where"
Cursor c = getActivity().getContentResolver()
.query(contactUri, queryFields, null, null, null);

//
if (c.getCount() == 0) {
c.close();
return;
}

// - .
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mSuspectButton.setText(suspect);
c.close();
}
}

21.9 ,
. -
Cursor . ,
,
. ,
Crime Choose Suspect.
( .
. , -
Contacts Provider API: http://developer.android.com/guide/topics/providers/
contacts-provider.html.)


? -
. -
.
URI , Intent.
FLAG_GRANT_READ_URI_PERMISSION. Android,
CriminalIntent
. ,
, .
366 21.


, , - .
Android
.
, ?
, .
,
PackageManager. :
PackageManager pm = getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(yourIntent, 0);
boolean isIntentSafe = activities.size() > 0;

queryIntentActivities() PackageManager.
, , -
. ,
,
.
onCreateView() ,
.

.
, -
. -
.
,
URI :
Uri number = Uri.parse("tel:5551234");

Intent.ACTION_DIAL Intent.ACTION_
CALL. ACTION_CALL
, ; ACTION_DIAL
, .
ACTION_DIAL. ACTION_CALL -
, . ,
.
22

CriminalIntent ,
-
. . 22.1
-.

. 22.1.

368 22.

-
AVD. AVD,
Window Android Virtual Device Manager. New
(Device) AVD , . 22.2.
AVD API 17.

. 22.2. AVD


CrimeListActivity
, . -
,
.
CrimeListActivity CrimeListFrag-
ment, CrimeFragment, . 22.3.

. 22.3.
SingleFragmentActivity 369

:
SingleFragmentActivity,
;
, ;
CrimeListActivity,
, .

SingleFragmentActivity
CrimeListActivity SingleFragmentActivity.
SingleFragmentActivity ,
activity_fragment.xml.
SingleFragmentActivity , ,
.
SingleFragmentActivity.java ,
, .

22.1. SingleFragmentActivity (SingleFragmentActivity.java)


public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();

protected int getLayoutResId() {


return R.layout.activity_fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
setContentView(getLayoutResId());
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);

if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
}

SingleFragmentActivity ,
, getLayoutResId()
, activity_fragment.xml.
370 22.



Package Explorer res/layout/
Android XML. ,
Layout, activity_twopane.xml
LinearLayout.
XML , . 22.4.

. 22.4. (layout/activity_twopane.xml)

: FrameLayout -
fragmentContainer, SingleFragmentActivity.onCreate()
, . ,
createFragment(), .
CrimeListActivity; get-
LayoutResId() , R.layout.activity_twopane.

22.2. (CrimeListActivity.java)
public class CrimeListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}

@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
}
}

CriminalIntent ,
(. 22.5).
371

, -
.
.

. 22.5.

CrimeListActivity -
.
-.

-
- (alias resource) , -
. - res/values/
refs.xml.
-,
activity_fragment.xml, activity_twopane.xml.
Package Explorer res/layout/
Android XML. ,
Values, refs.xml,
resources Finish. , -
22.3.

22.3. - (res/values/refs.xml)
<resources>
<item name="activity_masterdetail" type="layout">@layout/activity_fragment</item>
</resources>

.
: R.layout.activity_masterdetail. :
372 22.

type .
res/values/, R.layout.
R.layout.activi-
ty_fragment. CrimeListActivity.

22.4. (CrimeListActivity.java)
@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
return R.layout.activity_masterdetail;
}

CriminalIntent ,
. CrimeListActivity
.


res/values/, . -
, CrimeListActivity .
, activity_masterdetail
activity_twopane.xml.
Package Explorer res/ -
values-sw600dp. res/values/refs.xml res/
values-sw600dp/ , .

22.5.
(res/values-sw600dp/refs.xml)
<resources>

<item name="activity_masterdetail" type="layout">@layout/activity_fragment</item>


<item name="activity_masterdetail" type="layout">@layout/activity_twopane</item>

</resources>

-sw600dp ? sw
Smallest Width ( ),
, , .
-sw600dp, : -
, 600dp .
.
: sw Android 3.2. ,
Android 3.0 3.1 .
, -
-xlarge.
res/
values-xlarge. res/values-sw600dp/refs.xml res/values-xlarge/.
: 373

, ,
22.6.
22.6. 3.2
(res/values-xlarge/refs.xml)
<resources>

<item name="activity_masterdetail" type="layout">@layout/activity_twopane</item>

</resources>

-xlarge -
720 960dp. , -
Android 3.2. -sw600dp.
CriminalIntent . ,
- , .

:
, , ,
CrimeFragment -
CrimeListActivity.
,
CrimeListFragment.onListItemClick() . -
CrimePagerActivity onListItemClick()
FragmentManager, CrimeListActivity,
, CrimeFragment .
:
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime crime = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimeFragment
Fragment fragment = CrimeFragment.newInstance(crime.getId());
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.beginTransaction()
.add(R.id.detailFragmentContainer, fragment)
.commit();
}

, -
Android. ,
.
FragmentManager , , ,
-, .
, CrimeListFragment CrimeFragment
CrimeListActivity , CrimeListActivity -
detailFragmentContainer.
- CrimeListFragment, CrimeListFragment.
374 22.


-, .
- -
- .


-, -
Callbacks.
, -
-. , ,
.
-
, .

CrimeListFragment.Callbacks
Callbacks,
, Callbacks. - -
Callbacks, .
Fragment:
public void onAttach(Activity activity)

(
, ).
null :
public void onDetach()

null, -
, .
CrimeListFragment.java CrimeListFragment
Call backs . mCallbacks
onAttach(Activity) onDetach(), .

22.7. (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
private ArrayList<Crime> mCrimes;
private boolean mSubtitleVisible;
private Callbacks mCallbacks;

/**
* -.
*/
public interface Callbacks {
void onCrimeSelected(Crime crime);
}

@Override
: 375

public void onAttach(Activity activity) {


super.onAttach(activity);
mCallbacks = (Callbacks)activity;
}

@Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}

CrimeListFragment -. -
, CrimeListFragment.
Callbacks, CrimeListFragment .
, CrimeListFragment -
CrimeListFragment.Callbacks. ,
- CrimeListFragment.Callbacks. -
, .
CrimeListActivity CrimeListFragment.Callbacks.
onCrimeSelected(Crime) .

22.8. (CrimeListActivity.java)
public class CrimeListActivity extends SingleFragmentActivity
implements CrimeListFragment.Callbacks {

@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}

@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
}

public void onCrimeSelected(Crime crime) {


}
}

CrimeListFragment onListItemClick(), ,
.
, CrimeListActivity.
onCrimeSelected(Crime).
onCrimeSelected(Crime) CrimeListActivity
:

CrimePagerActivity;
CrimeFragment
detailFragmentContainer.
376 22.

, , -
. , -
detailFragmentContainer. .
, ,
; ,
detailFragmentContainer CrimeFragment.
detailFragmentContainer, -
, CrimeFragment detail-
FragmentContainer ( ) CrimeFragment,
.
CrimeListActivity.java onCrimeSelected(Crime),
.

22.9. CrimeFragment (CrimeListActivity.java)


public void onCrimeSelected(Crime crime) {
if (findViewById(R.id.detailFragmentContainer) == null) {
// CrimePagerActivity
Intent i = new Intent(this, CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
} else {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment oldDetail = fm.findFragmentById(R.id.detailFragmentContainer);
Fragment newDetail = CrimeFragment.newInstance(crime.getId());
if (oldDetail != null) {
ft.remove(oldDetail);
}
ft.add(R.id.detailFragmentContainer, newDetail);
ft.commit();
}
}

, CrimeListFragment onCrimeSelected(Crime)
, CrimePagerActivity.
CrimeListFragment.java onListItemClick() onOptions-
ItemSelected(MenuItem) , Callbacks.
onCrimeSelected(Crime).

22.10. (CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimePagerActivity
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
mCallbacks.onCrimeSelected(c);
}
: 377

...

@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
mCallbacks.onCrimeSelected(crime);
return true;
...
}
}

onOptionsItemSelected() -
. ,

( ).
CriminalIntent . ;
CrimeFragment detailFragmentContainer.
- , Crime-
Fragment .

. 22.6.

.
-
CrimeListFragment.onResume().
378 22.

CrimeListFragment CrimeFragment. CrimeListFragment


CrimeFragment,
.

CrimeFragment.

CrimeFragment.Callbacks
CrimeFragment :
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}

CrimeFragment onCrimeUpdated(Crime) -
Crime. onCrimeUpdated(Crime)
CrimeListActivity CrimeListFragment.
CrimeFragment, CrimeListFragment
, CrimeListFragment.

22.11. updateUI() (CrimeListFragment.java)


public class CrimeListFragment extends ListFragment {
...
public void updateUI() {
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}

CrimeFragment.java ,
mCallbacks onAttach() onDetach().

22.12. CrimeFragment (CrimeFragment.java)


...
private ImageView mPhotoView;
private Button mSuspectButton;
private Callbacks mCallbacks;

/**
* -
*/
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks)activity;
}

@Override
public void onDetach() {
super.onDetach();
: 379

mCallbacks = null;
}

public static CrimeFragment newInstance(UUID crimeId) {


...
}

CrimeFragment.Callbacks CrimeListActivity,
onCrimeUpdated(Crime).
22.13. (CrimeListActivity.java)
public void onCrimeUpdated(Crime crime) {
FragmentManager fm = getSupportFragmentManager();
CrimeListFragment listFragment = (CrimeListFragment)
fm.findFragmentById(R.id.fragmentContainer);
listFragment.updateUI();
}

CrimeFragment.java onCrimeUpdated(Crime)
Crime.
22.14. onCrimeUpdated(Crime) (CrimeFragment.java)
@Override
@TargetApi(11)
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);

...

mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence c, int start, int before, int count)
{
mCrime.setTitle(c.toString());
mCallbacks.onCrimeUpdated(mCrime);
getActivity().setTitle(mCrime.getTitle());
}
...
});

mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
//
mCrime.setSolved(isChecked);
mCallbacks.onCrimeUpdated(mCrime);
}
});
...

return v;
}
380 22.

onCrimeUpdated(Crime) onActivityResult(),
, . -

, CrimeFragment
.
22.15. onCrimeUpdated(Crime) (2) (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mCallbacks.onCrimeUpdated(mCrime);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Photo p = new Photo(filename);
mCrime.setPhoto(p);
mCallbacks.onCrimeUpdated(mCrime);
showPhoto();
}
} else if (requestCode == REQUEST_CONTACT) {
...

c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mCallbacks.onCrimeUpdated(mCrime);
mSuspectButton.setText(suspect);
c.close();
}
}

CrimeListActivity CrimeFragment.Callbacks.
CriminalIntent . :
, CrimeFragment, CrimeFragment.
Callbacks. CrimeFragment.Callbacks CrimePagerActivity.
CrimePagerActivity , onCrime-
Updated(Crime) . CrimePagerActivity
CrimeFragment, onResume().

22.16. CrimeFragment.Callbacks (CrimePagerActivity.java)


public class CrimePagerActivity extends FragmentActivity
implements CrimeFragment.Callbacks {
...

public void onCrimeUpdated(Crime crime) {


}
}
: 381

CriminalIntent , List-
View CrimeFragment.
, , .

. 22.7. , ,

CriminalIntent . 13
, ,
, . -
?
, CriminalIntent.

:

Android 3.2
.
small, normal, large xlarge.
. 22.1 .
22.1.

small 320 x 426dp

normal 320 x 470dp

large 480 x 640dp

xlarge 720 x 960dp


382 22.

Android 3.2.
, .
. 22.2 .
22.2.

wXXXdp : XXX dp

hXXXdp : XXX dp

swXXXdp : ( )
XXX dp

, ,
, 300dp.
res/layout-w300dp (w width,
). h
(Height, ).
,
.
sw (Smallest Width, ).
,
, . 1024 800, sw
800. 800 1024, sw 800.
23

-,
Android. -
, ,
Android.

NerdLauncher
(New Android Application Project) ,
CriminalIntent (. 23.1).
NerdLauncher com.bignerdranch.android.nerdlauncher.
, . -
. NerdLauncherActivity
Finish.
NerdLauncherActivity SingleFragmentActivity, -
. Package Explorer
SingleFragmentActivity.java CriminalIntent. com.
bignerdranch.android.nerdlauncher. Eclipse -
.
activity_fragment.xml. res/layout/activity_frag-
ment.xml res/layout NerdLauncher.
NerdLauncher .
, .
, .
384 23.

. 23.1. NerdLauncher

NerdLauncherFragment ListFragment ,
ListView,
ListFragment.
NerdLauncherFragment
android.support.v4.app.ListFragment. .
NerdLauncherActivity.java NerdLauncherActivity
SingleFragmentActivity. create-
Fragment() , NerdLauncherFragment.

23.1. SingleFragmentActivity (NerdLauncherActivity.java)


public class NerdLauncherActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nerd_launcher);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_nerd_launcher, menu);
return true;
}

@Override
public Fragment createFragment() {
return new NerdLauncherFragment();
}
}
385


NerdLauncher . Nerd-
Launcher ,
. MAIN
LAUNCHER. :
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

NerdLauncherFragment.java onCreate(Bundle)
. PackageManager , -
.
, PackageManager.

23.2. PackageManager (NerdLauncherFragment.java)


public class NerdLauncherFragment extends ListFragment {
private static final String TAG = "NerdLauncherFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

Intent startupIntent = new Intent(Intent.ACTION_MAIN);


startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);

PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);

Log.i(TAG, "I've found " + activities.size() + " activities.");


}
}

NerdLauncher LogCat, -
PackageManager.
CriminalIntent .
, ,
startActivity(Intent):
Intent i = new Intent(Intent.ACTION_SEND);
... //
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);

? ,
MAIN/LAUNCHER
MAIN/LAUNCHER, startActivity().
, startActivity(Intent) ,
.
.
386 23.

startActivity() ( startActivityForRe-
sult()), Intent.CATEGORY_DEFAULT.
, ,
, startActivity(),
DEFAULT.
MAIN/LAUNCHER
, . , -
,
,
CATEGORY_DEFAULT.
MAIN/LAUNCHER CATEGORY_DEFAULT,
, startAc-
tivity(), .
PackageManager MAIN/LAUNCHER.
ListView -
NerdLauncherFragment. (label) -
, . ,
, , ,
.
Re-
solveInfo, PackageManager.
ResolveInfo , -
PackageManager, ,
ResolveInfo.loadLabel().

23.3. (NerdLauncherFragment.java)
...
Log.i("NerdLauncher", "I've found " + activities.size() + " activities.");

Collections.sort(activities, new Comparator<ResolveInfo>() {


public int compare(ResolveInfo a, ResolveInfo b) {
PackageManager pm = getActivity().getPackageManager();
return String.CASE_INSENSITIVE_ORDER.compare(
a.loadLabel(pm).toString(),
b.loadLabel(pm).toString());
}
});

ArrayAdapter, -
, ListView.

23.4. (NerdLauncherFragment.java)
...
Collections.sort(activities, new Comparator<ResolveInfo></ResolveInfo>() {
...
}
});
387

ArrayAdapter<ResolveInfo> adapter = new ArrayAdapter<ResolveInfo>(


getActivity(), android.R.layout.simple_list_item_1, activities) {
public View getView(int pos, View convertView, ViewGroup parent) {
View v = super.getView(pos, convertView, parent);
// , simple_list_item_1
// TextView; .
TextView tv = (TextView)v;
ResolveInfo ri = getItem(pos);
tv.setText(ri.loadLabel(pm));
return v;
}
};

setListAdapter(adapter);

NerdLauncher; ListView,
.

. 23.2.



. -
.
.
388 23.


ResolveInfo , . -
ResolveInfo ActivityInfo. ( ,
ResolveInfo, .)
NerdLauncherFragment.java onListItemClick() -
ActivityInfo .
, .

23.5. onListItemClick() (NerdLauncherFragment.java)


@Override
public void onListItemClick(ListView l, View v, int position, long id) {
ResolveInfo resolveInfo = (ResolveInfo)l.getAdapter().getItem(position);
ActivityInfo activityInfo = resolveInfo.activityInfo;

if (activityInfo == null) return;

Intent i = new Intent(Intent.ACTION_MAIN);


i.setClassName(activityInfo.applicationInfo.packageName, activityInfo.name);

startActivity(i);
}

: -
. ,
, .

, .
, .
23.5
Intent:
public Intent setClassName(String packageName, String className)

,
. Intent,
Context Class:
public Intent(Context packageContext, Class<?> cls)

ComponentName
, . Activity Class
Intent, Activity.
ComponentName
Intent :
public Intent setComponent(ComponentName component)

setClassName(), -
, .
NerdLauncher , .
389


Android
. (task) -
, .
, -
. Back .
Back , .

. , -
, .

.
.
,
.

Back


( )

. 23.3.
390 23.

.
CriminalIntent .
, Crim-
inalIntent (, ).
, -
, .
, NerdLauncher,
NerdLaucher. , CriminalIntent Nerd-
Launcher . ( Recents,
; Home.)
CriminalIntent. CrimeListAc-
tivity NerdLauncher. NerdLauncher
CriminalIntent,
.

. 23.4. CriminalIntent

, NerdLauncher .

, .
, .
NerdLauncher CriminalIntent.
, CriminalIntent -
.
NerdLauncher 391

23.6. (NerdLauncherFragment.java)
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClassName(activityInfo.applicationInfo.packageName, activityInfo.name);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(i);

. 23.5. CriminalIntent

CriminalIntent NerdLauncher
CriminalIntent. FLAG_ACTIVITY_NEW_TASK
. CrimeListActivity , Android
.

NerdLauncher

, ?
NerdLauncher
. AndroidManifest.xml NerdLauncher
.

23.7. NerdLauncher (AndroidManifest.xml)


<intent-filter>
<action android:name="android.intent.action.MAIN" />

392 23.

<category android:name="android.intent.category.LAUNCHER" />


<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

HOME DEFAULT , NerdLauncher -


. Home
NerdLauncher.
( NerdLauncher ,
, Settings Applications Manage Applications. All,
NerdLauncher Launch by default.
Home
.)

. ,
ResolveInfo.loadLabel()
. ResolveInfo
loadIcon() , .
: Nerd-
Launcher .
, NerdLauncher
.
ActivityManager, , -
, . PackageManager
Activity getActivityManager()
.
ActivityManager Activity.
getSystemService() Activity.ACTIVITY_SERVICE.
getRunningTasks() , -
( ).
, moveTaskToFront().
Android -
.

:
.
, ,
.
, -
, , . .
( , ) (thread). Android
Dalvik.
: 393

, Android -
( ).
,
.
,
. , -
,
(multi-threading),
Android , .
-
. , .
. ,
.
, ,
. ,
CriminalIntent NerdLauncher CriminalIntent ,
CrimeListActivity .
CriminalIntent.
, , ,
.
CriminalIntent, CriminalIntent
.

CriminalIntent

(
( (
)
CriminalIntent) )

. 23.6.

, Back -
, .
. -
Android? ,
Android . -
Home ,
. , , . ,
Google Play ,
.
24


,
, -
. -
,

.
-
,
. -
, -
.
, , -
.
,
-
. ,
;
.

, . 24.1.
. 24.1. RemoteControl
RemoteControl , . 24.1.
.
, .
Delete . Enter
.
RemoteControl 395

RemoteControl
Android Application ,
. 24.2.

. 24.2. RemoteControl

RemoteControlActivity.

RemoteControlActivity
RemoteControlActivity SingleFragmentActivity,
SingleFragmentActivity.java CriminalIntent com.
bignerdranch.android.remotecontrol. activity_fragment.xml
res/layout RemoteControl.
RemoteControlActivity.java. RemoteControlActivity
SingleFragmentActivity; RemoteControl-
Fragment. ( ). ,
RemoteControlActivity.onCreate(),
.

24.1. RemoteControlActivity (RemoteControlActivity.java)


public class RemoteControlActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

396 24.

24.1 ()
setContentView(R.layout.activity_remote_control);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_remote_control, menu);
return true;
}

@Override
protected Fragment createFragment() {
return new RemoteControlFragment();
}

@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
}
}

AndroidManifest.xml -
.

24.2. (AndroidManifest.xml)
<activity
android:name="com.bignerdranch.android.remotecontrol.RemoteControlActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />


</intent-filter>
</activity>

RemoteControlFragment
Package Explorer activity_remote_control.xml fragment_re-
mote_control.xml. , .
fragment_remote_control.xml XML 24.3.

24.3. (layout/fragment_remote_control.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"
tools:context=".RemoteControlActivity" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
RemoteControl 397

android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />

</RelativeLayout>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_remote_control_tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="*" >
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="center"
android:text="0"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="15dp"
android:background="#555555"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textSize="20dp" />
<TableRow android:layout_weight="1" >
<Button
android:id="@+id/fragment_remote_control_zeroButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="1" />
<Button
android:id="@+id/fragment_remote_control_enterButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="Enter" />
</TableRow>
</TableLayout>

android:stretchColumns="*" ,
. ,
dp sp. , -
.
, RemoteControlFragment.
android.support.v4.app.Fragment. RemoteControlFragment.
java onCreateView() .
398 24.

24.4. RemoteControlFragment (RemoteControlFragment.java)


public class RemoteControlFragment extends Fragment {
private TextView mSelectedTextView;
private TextView mWorkingTextView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_remote_control, parent, false);

mSelectedTextView = (TextView)v
.findViewById(R.id.fragment_remote_control_selectedTextView);
mWorkingTextView = (TextView)v
.findViewById(R.id.fragment_remote_control_workingTextView);

View.OnClickListener numberButtonListener = new View.OnClickListener() {


public void onClick(View v) {
TextView textView = (TextView)v;
String working = mWorkingTextView.getText().toString();
String text = textView.getText().toString();
if (working.equals("0")) {
mWorkingTextView.setText(text);
} else {
mWorkingTextView.setText(working + text);
}
}
};

Button zeroButton = (Button)v


.findViewById(R.id.fragment_remote_control_zeroButton);
zeroButton.setOnClickListener(numberButtonListener);

Button oneButton = (Button)v


.findViewById(R.id.fragment_remote_control_oneButton);
oneButton.setOnClickListener(numberButtonListener);

Button enterButton = (Button) v


.findViewById(R.id.fragment_remote_control_enterButton);
enterButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CharSequence working = mWorkingTextView.getText();
if (working.length() > 0)
mSelectedTextView.setText(working);
mWorkingTextView.setText("0");
}
});

return v;
}
}

, .
, .
-
, ,
399

, 0.
Enter
.
RemoteControl. -
. , ?

. 24.3.


, , .
. ,
, . ,
?
, Android ,
.
CSS. XML -. -
: ,
, .
, , <resources> XML
res/values. , , ,
styles.xml.
400 24.

Android styles.xml. ( :
RemoteControl
Android; ,
.)
, ,
RemoteButton. , .

24.5. RemoteControl (values/styles.xml)


<resources>

<style name="AppTheme" parent="android:Theme.Light" />

<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
</style>

</resources>

<style> -
<item>. ,
XML, , .
RemoteButton,
. ,
.
, style .
fragment_remote_control.xml:
.

24.6. (layout/activity_remote.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
>

...

<TableRow android:layout_weight="1" >


<Button
android:id="@+id/fragment_remote_control_zeroButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="1" />
<Button
401

android:id="@+id/fragment_remote_control_enterButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="Enter" />
</TableRow>
</TableLayout>

RemoteControl. -
, ,
.


.
12 ,
.
3 4 . -

. res/layout/
button_row.xml .

24.7. (layout/button_row.xml)
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" >
<Button style="@style/RemoteButton" />
<Button style="@style/RemoteButton" />
<Button style="@style/RemoteButton" />
</TableRow>

. ?
include.

24.8. (layout/fragment_remote_control.xml)
<TableRow android:layout_weight="1" >
<Button
android:id="@+id/fragment_remote_control_zeroButton"
style="@style/RemoteButton"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
style="@style/RemoteButton"
android:text="1" />
<Button
android:id="@+id/fragment_remote_control_enterButton"
style="@style/RemoteButton"
android:text="Enter" />
</TableRow>
<include
android:layout_weight="1"
layout="@layout/button_row" />
402 24.

24.8 ()
<include
android:layout_weight="1"
layout="@layout/button_row" />
<include
android:layout_weight="1"
layout="@layout/button_row" />
<include
android:layout_weight="1"
layout="@layout/button_row" />

, , , layout/button_row.xml,
. ,
, .
,
. .
24.9. (RemoteControlFragment.java)
View.OnClickListener numberButtonListener = new View.OnClickListener() {
...
};

Button zeroButton = (Button)v.findViewById(R.id.fragment_remote_control_zeroButton);


zeroButton.setOnClickListener(numberButtonListener);

Button oneButton = (Button)v.findViewById(R.id.fragment_remote_control_oneButton);


oneButton.setOnClickListener(numberButtonListener);
TableLayout tableLayout = (TableLayout)v
.findViewById(R.id.fragment_remote_control_tableLayout);
int number = 1;
for (int i = 2; i < tableLayout.getChildCount() - 1; i++) {
TableRow row = (TableRow)tableLayout.getChildAt(i);
for (int j = 0; j < row.getChildCount(); j++) {
Button button = (Button)row.getChildAt(j);
button.setText("" + number);
button.setOnClickListener(numberButtonListener);
number++;
}
}

for 2, ,
.
numberButtonListener, , -
.
. :
Delete Enter.
24.10. (RemoteControlFragment.java)
for (int i = 2; i < tableLayout.getChildCount() - 1; i++) {
...
}

TableRow bottomRow = (TableRow)tableLayout


.getChildAt(tableLayout.getChildCount() - 1);
403

Button deleteButton = (Button)bottomRow.getChildAt(0);


deleteButton.setText("Delete");
deleteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mWorkingTextView.setText("0");
}
});

Button zeroButton = (Button)bottomRow.getChildAt(1);


zeroButton.setText("0");
zeroButton.setOnClickListener(numberButtonListener);

Button enterButton = (Button)


v.findViewById(R.id.fragment_remote_control_enterButton);
Button enterButton = (Button)bottomRow.getChildAt(2);
enterButton.setText("Enter");
enterButton.setOnClickListener(new View.OnClickListener() {
...
});

. -
, .
.

. 24.4. ,

, ,
. RemoteButton.
404 24.

24.11. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
</style>

. 24.5. !

: include merge
include -
RemoteButton ( ). :
<include layout="@layout/some_partial_layout"/>


@layout/some_partial_layout.
include
, , -
.
. 405

, , -
. ,
.
include , . -,

;
, . -,
android:id android:layout_*
, include.
,
.
merge include.
.
merge, merge
include,
merge .
, .
, , merge .
: XML ,
.

.
Delete Enter , -
, .
, .
, RemoteButton, -
.
.
, , .
. parent ,
. ,
(, ParentStyleName.
MyStyleName).
25


, .
, , -
Android .
, .

. 25.1. RemoteControl

(drawables). An-
droid ,
XML 407

, , , Drawable,
. : BitmapDrawable,
.
:
, , 9- .
XML,
XML.

XML
XML, -
: android:background .

25.1. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">#ccd7ee</item>
</style>

. 25.2. ?
408 25.

. ,
.
- ?
Button , View .
, Drawable.
.
,
.
XML.
XML , -
drawable ( ).
Package Explorer res/drawable.
XML button_shape_normal.xml.
shape. ( normal, ?
, .)

25.2.
(drawable/button_shape_normal.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >

<corners android:radius="3dp" />

<gradient
android:angle="90"
android:endColor="#cccccc"
android:startColor="#acacac" />

</shape>

. corners
, gradient /
.
shape : ,
, . ., .
http://developer.android.com/guide/topics/resources/
drawable-resource.html.
styles.xml Button, Draw-
able .

25.3. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
409

<item name="android:background">#ccd7ee</item>
<item name="android:background">@drawable/button_shape_normal</item>
</style>

RemoteControl , .

. 25.3.


, .
Button
(state list).
,
View. (
18.) ,
, .
.
, , .
Package Explorer button_shape_normal.xml
button_shape_pressed.xml. button_shape_pressed.xml
180 , .
410 25.

25.4. (drawable/button_shape_pressed.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >

<corners android:radius="3dp" />

<gradient
android:angle="90"
android:angle="270"
android:endColor="#cccccc"
android:startColor="#acacac" />

</shape>

.
selector
item, .
res/drawable/, XML button_shape.xml -
selector.

25.5. (drawable/button_shape.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_shape_normal"
android:state_pressed="false"/>
<item android:drawable="@drawable/button_shape_pressed"
android:state_pressed="true"/>
</selector>

,
. , ,
,
. ,
, .
res/drawable/ -
button_text_color.xml.

25.6.
(drawable/button_text_color.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:color="#ffffff"/>
<item android:state_pressed="true" android:color="#556699"/>
</selector>

styles.xml Button, -
.
411

25.7. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">@drawable/button_shape_normal</item>
<item name="android:background">@drawable/button_shape</item>
<item name="android:textColor">@drawable/button_text_color</item>
</style>

RemoteControl. , .

. 25.4.


Android, ,
. , ,

XML: .
412 25.

, .
layer-list,
inset, .
XML res/drawable/. button_shape_
shadowed.xml layer-list.

25.8. (drawable/button_shape_shadowed.xml)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<corners android:radius="5dp" />

<gradient
android:angle="90"
android:centerColor="#303339"
android:centerY="0.05"
android:endColor="#000000"
android:startColor="#00000000" />
</shape>
</item>
<item>
<inset
android:drawable="@drawable/button_shape"
android:insetBottom="5dp" />
</item>
</layer-list>

,
( ).
inset,
5dp, .
,
.
, ,
.
, , .
, -
drawable/folder . ,
.
styles.xml
.

25.9. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">@drawable/button_shape_normal</item>
9- 413

<item name="android:background">@drawable/button_shape_shadowed</item>
<item name="android:textColor">@drawable/button_text_color</item>
</style>

:
, .
remote_background.xml shape.
(: ,
).

25.10.
(drawable/remote_background.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:centerY="0.05"
android:endColor="#dbdbdb"
android:gradientRadius="500"
android:startColor="#f4f4e9"
android:type="radial" />
</shape>

fragment_remote_control.xml.

25.11. (layout/fragment_remote_control.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_remote_control_tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/remote_background"
android:stretchColumns="*" >

.
( , ,
TableLayout.) , ,
. -
, .

9-
-
,
XML.
. (stretchable)
, Android
9- (9-patch).
TextView
. ,
414 25.

. TextView,
. window.png
TextView , .

. 25.5.

bar.png .
; .

. 25.6.

, 25_XMLDrawables/RemoteCon-
trol/res/drawable-hdpi. /res/drawable-hdpi RemoteControl.
fragment_remote_control.xml
. ,
, , (-
) . TextView
, .

25.12. (layout/fragment_remote_control.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >
<TextView
9- 415

android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:gravity="center"
android:text="0"
android:textColor="#ffffff"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="#555555"
android:background="@drawable/bar"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textStyle="italic"
android:textSize="20dp" />
...
</TableLayout>

, .

. 25.7.
416 25.

-
. ,
. , TextView
.
9- .
9- , Android
, .
, -
, .
9-?
3 3 9 , (patches).
, , -
.

. 25.8. 9-

9- png, -
: .9.png,
. -
. ,
.
9- ,
draw9patch, Android SDK.
tools SDK. -
, File.
, ,
. window.
png, . 25.9.
.
?
, (
). , , -
. ,
.
9- 417

. 25.9. 9-

, window_
patch.9.png. , 9-
;
window.9.png window.png, .
9- bar.png. -
,
4 .

. 25.10. 9-

bar_patch.9.png.
418 25.

res/ Refresh. Eclipse


, Refresh -
.
, TextView 9- -
.

. 25.11. RemoteControl

25.13. 9- (layout/fragment_remote_control.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:background="@drawable/window_patch"
android:gravity="center"
android:text="0"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
9- 419

android:layout_margin="15dp"
android:layout_weight="1"
android:background="@drawable/bar"
android:background="@drawable/bar_patch"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textSize="20dp" />
...
</TableLayout>

. ! -
. , ?
, . ,
.
,
.
26 HTTP

. ,
?
,
.
Android -
PhotoGallery. Flickr,
, Flickr.
26.1 .

. 26.1. PhotoGallery
PhotoGallery 421

( . 26.1
Flickr. Flickr , -
, .
, -
Flickr, http://pressroom.yahoo.net/pr/ycorp/
permissions.aspx.)
PhotoGallery .
XML, . -
: , ,
, -.
HTTP
Android. -
HTTP.
Flickr, (
27).

. 26.2. PhotoGallery

PhotoGallery
Android. , -
. 26.3.
422 26. HTTP

. 26.3. PhotoGallery

PhotoGalleryActivity.
PhotoGallery ,
. PhotoGalleryActivity
SingleFragmentActivity, ,
activity_fragment.xml. ,
PhotoGalleryFragment, .
SingleFragmentActivity.java activity_fragment.xml
.
PhotoGalleryActivity.java PhotoGalleryActivity SingleFrag-
mentActivity ; , ,
createFragment(). createFragment()
PhotoGalleryFragment.
( , .
, PhotoGalleryFragment.)

26.1. (PhotoGalleryActivity.java)
public class PhotoGalleryActivity extends Activity {
public class PhotoGalleryActivity extends SingleFragmentActivity {

/* , */

@Override
public Fragment createFragment() {
return new PhotoGalleryFragment();
}
}
PhotoGallery 423

PhotoGallery GridView,
PhotoGalleryFragment.
GridView AdapterView, -
, ListView. ListView, GridView
GridFragment, . , -
PhotoGalleryFragment.
PhotoGalleryFragment ,
GridView .
, layout/activity_photo_gallery.xml
layout/fragment_photo_gallery.xml.
GridView, . 26.4.

. 26.4. GridView (layout/fragment_photo_gallery.xml)

120dp GridView -
, .
120dp, stretchMode GridView
.
, PhotoGalleryFragment. ,
GridView ( 26.2).

26.2. (PhotoGalleryFragment.java)
package com.bignerdranch.android.photogallery;
...
public class PhotoGalleryFragment extends Fragment {
GridView mGridView;

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

setRetainInstance(true);
}

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

424 26. HTTP

26.2 ()
View v = inflater.inflate(R.layout.fragment_photo_gallery, container,
false);

mGridView = (GridView)v.findViewById(R.id.gridView);

return v;
}
}

, PhotoGal-
lery , (
).


PhotoGallery -
. Java.
Flickr, FlickrFetchr.
F l i c k r F e t c h r :
getUrlBytes(String) getUrl(String) . getUrlBytes(String)
URL .
getUrl(String) getUrlBytes(String) String.
FlickrFetchr.java getUrlBytes(String) getUrl(String)
( 26.3).

26.3. (FlickrFetchr.java)
package com.bignerdranch.android.photogallery;
...
public class FlickrFetchr {
byte[] getUrlBytes(String urlSpec) throws IOException {
URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();

try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return null;
}

int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
}
425

public String getUrl(String urlSpec) throws IOException {


return new String(getUrlBytes(urlSpec));
}
}

URL , http://www.google.com.
openConnection()
URL-. URL.openConnection() URLConnection,
HTTP,
HttpURLConnection. HTTP- -
, , . .
HttpURLConnection ,
getInputStream() ( getOutputStream()
POST-).
.
URL
read(), . InputStream
. , -
ByteArrayOutputStream.
getUrlBytes(String),
getUrl(String). ,
getUrlBytes(String), String.
?
, -
.


: -
. ,
.
,
AndroidManifest.xml.

26.4. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />

...
</manifest>
426 26. HTTP

AsyncTask


. FlickrFetchr.getURL(String)
PhotoGalleryFragment. -
.

AsyncTask. AsyncTask
, doInBackground().
PhotoGalleryFragment.java PhotoGalleryFragment -
FetchItemsTask. AsyncTask.doIn-
Background() .
PhotoGalleryFragment.onCreate().

26.5. AsyncTask (PhotoGalleryFragment.java)


public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";

GridView mGridView;
...

private class FetchItemsTask extends AsyncTask<Void,Void,Void> {


@Override
protected Void doInBackground(Void... params) {
try {
String result = new FlickrFetchr().getUrl("http://www.google.com");
Log.i(TAG, "Fetched contents of URL: " + result);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch URL: ", ioe);
}
return null;
}
}
}

PhotoGalleryFragment.onCreate() execute()
FetchItemsTask.

26.6. AsyncTask (PhotoGalleryFragment.java)


public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";

GridView mGridView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
427

new FetchItemsTask().execute();
}
...
}

execute() AsyncTask,
doInBackground(). , LogCat
HTML Google Javascriptlicious -
, . 26.5.

. 26.5. HTML Google LogCat

, ,
Android.


. - -
- ,
. - Android -
, Honeycomb
Android. , Android
NetworkOnMainThreadException. ? ,
, (thread),
.
(thread) -
. Android
. -
. , -
,
.
, ,
: 1. ,

1
, -
. . .
428 26. HTTP

, ,
. , ,
, .


( Android
)

( )
( )

. 26.6.

,
- . , ? -
, .
, .
. , -
. , -
,
. . (
,
, UI-.)

. , ,
. ,
( , AsyncTask) -
.


:
. -
,
ANR (Application Not Responding). ,
Android ,
, Back. .
XML Flickr 429

. 26.7.

Android, Honeycomb, -
.
,
. Android
.
? AsyncTask.
, AsyncTask.
, -
.

XML Flickr
Flickr XML API. -
www.flickr.com/services/api/.
Request Formats.
REST, API http://api.flickr.com/
services/rest/. , Flickr
.
API API Methods. -
photos flickr.photos.getRecent.
; ,
430 26. HTTP

, flickr. ,
PhotoGallery.
getRecent API.
API, http://www.flickr.com/services/
api/ API keys.
Yahoo. API;
. API
: 4f721bgafa75bf6d2cb9af54f937bb70.
- Flickr.
GET- http://api.flickr.com/services/rest/?method=flickr.
photos.getRecent&api_key=xxx.
.
FlickrFetchr.

26.7. (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";

private static final String ENDPOINT = "http://api.flickr.com/services/rest/";


private static final String API_KEY = "yourApiKeyHere";
private static final String METHOD_GET_RECENT = "flickr.photos.getRecent";
private static final String PARAM_EXTRAS = "extras";

private static final String EXTRA_SMALL_URL = "url_s";

, , API -
extras url_s.
url_s Flickr URL ,
.
, -
URL- .

26.8. fetchItems() (FlickrFetchr.java)


public class FlickrFetchr {

...

String getUrl(String urlSpec) throws IOException {


return new String(getUrlBytes(urlSpec));
}

public void fetchItems() {


try {
String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_GET_RECENT)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.build().toString();
String xmlString = getUrl(url);
XML Flickr 431

Log.i(TAG, "Received xml: " + xmlString);


} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
}
}
}

Uri.Builder URL-
API- Flickr. Uri.Builder -
URL- . Uri.
Builder.appendQueryParameter(String,String)
.
, AsyncTask PhotoGalleryFragment
fetchItems().

26.9. fetchItems() (PhotoGalleryFragment.java)


private class FetchItemsTask extends AsyncTask<Void,Void,Void> {
@Override
protected Void doInBackground(Void... params) {
try {
String result = new FlickrFetchr().getUrl("http://www.google.com");
Log.i(TAG, "Fetched contents of URL: " + result);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch URL: ", ioe);
}
new FlickrFetchr().fetchItems();
return null;
}
}

PhotoGallery. LogCat , -
XML.

. 26.8. XML Flickr


432 26. HTTP

, XML Flickr ; ? , -
.
, PhotoGallery, GalleryItem. . 26.9
PhotoGallery.

. 26.9. XML Flickr

: . 26.9 -,
.
GalleryItem .

26.10. (GalleryItem.java)
package com.bignerdranch.android.photogallery;
public class GalleryItem {
private String mCaption;
private String mId;
private String mUrl;

public String toString() {


return mCaption;
}
}

Eclipse get- set- mId, mCaption mUrl.


XML Flickr 433

,
XML, Flickr. XML
XmlPullParser.

XmlPullParser
XmlPullParser , -
XML. XmlPullParser
Android .
GalleryItem.
FlickrFetchr , XML, -
. , XmlPullParser
XML. GalleryItem
ArrayList.

26.11. Flickr (FlickrFetchr.java)


public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";
private static final String ENDPOINT = "http://api.flickr.com/services/rest/";
private static final String API_KEY = "your API key";
private static final String METHOD_GET_RECENT = "flickr.photos.getRecent";

private static final String XML_PHOTO = "photo";

...

public void fetchItems() {


...
}

void parseItems(ArrayList<GalleryItem> items, XmlPullParser parser)


throws XmlPullParserException, IOException {
int eventType = parser.next();

while (eventType != XmlPullParser.END_DOCUMENT) {


if (eventType == XmlPullParser.START_TAG &&
XML_PHOTO.equals(parser.getName())) {
String id = parser.getAttributeValue(null, "id");
String caption = parser.getAttributeValue(null, "title");
String smallUrl = parser.getAttributeValue(null, EXTRA_SMALL_URL);

GalleryItem item = new GalleryItem();


item.setId(id);
item.setCaption(caption);
item.setUrl(smallUrl);
items.add(item);
}

eventType = parser.next();
}
}
}
434 26. HTTP

, XmlPullParser XML,
, START_TAG, END_TAG END_DOCUMENT.
, getText(), getName() ge-
tAttributeValue(), ,
XmlPullParser. XmlPullParser
XML, next(). ,
, .

. 26.10. XmlPullParser

parseItems() XmlPullParser ArrayList.


xmlString, Flickr.
parseItems() .

26.12. parseItems() (FlickrFetchr.java)


public void fetchItems() {
public ArrayList<GalleryItem> fetchItems() {
ArrayList<GalleryItem> items = new ArrayList<GalleryItem>();

try {
String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_GET_RECENT)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.build().toString();
String xmlString = getUrl(url);
Log.i(TAG, "Received xml: " + xmlString);
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader(xmlString));

parseItems(items, parser);
} catch (IOException ioe) {
AsyncTask 435

Log.e(TAG, "Failed to fetch items", ioe);


} catch (XmlPullParserException xppe) {
Log.e(TAG, "Failed to parse items", xppe);
}
return items;
}

PhotoGallery XML. Pho-


toGallery ArrayList, -
, ,
.

AsyncTask

GridView PhotoGalleryFragment.
GridView, ListView, AdapterView,
, .
PhotoGalleryFragment.java ArrayList Gal-
leryItems, ArrayAdapter , -
Android.
26.13. setupAdapter() (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";

GridView mGridView;
ArrayList<GalleryItem> mItems;

...

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);

mGridView = (GridView)v.findViewById(R.id.gridView);

setupAdapter();

return v;
}

void setupAdapter() {
if (getActivity() == null || mGridView == null) return;

if (mItems != null) {
mGridView.setAdapter(new ArrayAdapter<GalleryItem>(getActivity(),
android.R.layout.simple_gallery_item, mItems));
} else {
mGridView.setAdapter(null);
}
}
436 26. HTTP

GridView GridFragment,
. ,
setupAdapter(). -

GridView. onCreateView(),
GridView -
.
.
android.R.layout.simple_gallery_item TextView. -
, GalleryItem toString()
mCaption . ,
GridView GalleryItem .
: getActivity()
null. , , -
- .
,
. , -
. .
, AsyncTask, -
, , .
, , -
; .
Flickr setupAdapter(). ,
, setupAdapter() doInBackground()
FetchItemsTask. . ,
,
Flickr. , , ,
? ,
.
-
.
, .
? AsyncTask onPostExecute(), -
. onPostExecute() doInBack-
ground(). , onPostExecute() ,
, .
FetchItemsTask, mItems -
setupAdapter() .

26.14. (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask<Void,Void,Void> {
private class FetchItemsTask extends AsyncTask<Void,Void,ArrayList<GalleryItem>> {
@Override
protected Void doInBackground(Void... params) {
protected ArrayList<GalleryItem> doInBackground(Void... params) {
AsyncTask 437

new FlickrFetchr().fetchItems();
return new FlickrFetchr().fetchItems();
return null;
}

@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
mItems = items;
setupAdapter();
}
}

. -,
FetchItemsTask. , -
AsyncTask; , doInBackground(),
onPostExecute(). -, doIn-
Background() , GalleryItem.
.
,
onPostExecute(). -, onPostExecute().
, doInBackground(), mItems
GridView.
. ,
, GalleryItem.

. 26.11. Flickr
438 26. HTTP

: AsyncTask
- AsyncTask,
. ?
- .
:
AsyncTask<String,Void,Void> task = new AsyncTask<String,Void,Void>() {
public Void doInBackground(String... params) {
for (String parameter : params) {
Log.i(TAG, "Received parameter: " + parameter);
}

return null;
}
};

task.execute("First parameter", "Second parameter", "Etc.");

execute(), -
. doInBack-
ground().
- -
. :
final ProgressBar progressBar = /* */;
progressBar.setMax(100);
AsyncTask<Integer,Integer,Void> task = new AsyncTask<Integer,Integer,Void>() {
public Void doInBackground(Integer... params) {
for (Integer progress : params) {
publishProgress(progress);
Thread.sleep(1000);
}
}

public void onProgressUpdate(Integer... params) {


int progress = params[0];
progressBar.setProgress(progress);
}
};
task.execute(25, 50, 75, 100);

.
, -
, AsyncTask
publishProgress() onProgressUpdate().
: doInBackground()
publishProgress(). onPro-
gressUpdate() . , -
onProgressUpdate(), -
publishProgress() doInBackground().
. 439

AsyncTask
AsyncTask -
, .
AsyncTask
.
AsyncTask -
, AsyncTask.cancel(boolean)
AsyncTask.
AsyncTask.cancel(boolean)
. cancel(false), -
true isCancelled(). AsyncTask isCancelled()
doInBackground() .
cancel(true)
, doInBackground(). AsyncTask.cancel(true)
AsyncTask.
.

.
getRecent 100 .
page ,
.
,
.
,
.
27 Looper, Handler
HandlerThread

XML Flickr
.
Looper, Handler HandlerThread
PhotoGallery.

GridView
PhotoGalleryFragment TextView
GridView. TextView
GalleryItem.
, ,
ImageView. Ima-
geView , mUrl GalleryItem.
gal-
lery_item.xml. ImageView (. 27.1).

. 27.1. (res/layout/gallery_item.xml)
GridView 441

ImageView GridView, ,
.
.
ImageView, scaleType centerCrop. -
,
, .
ImageView,
.
brian_up_close.jpg res/drawable-hdpi.
PhotoGalleryFragment ArrayAdapter -
, getView()
ImageView .

27.1. GalleryItemAdapter (PhotoGalleryFragment.java)


public class PhotoGalleryFragment extends Fragment {
...

void setupAdapter() {
if (getActivity() == null || mGridView == null) return;

if (mItems != null) {
mGridView.setAdapter(new ArrayAdapter<GalleryItem>(getActivity(),
android.R.layout.simple_gallery_item, mItems));
mGridView.setAdapter(new GalleryItemAdapter(mItems));
} else {
mGridView.setAdapter(null);
}
}

private class FetchItemsTask extends AsyncTask<Void,Void,ArrayList<GalleryItem>> {


...
}

private class GalleryItemAdapter extends ArrayAdapter<GalleryItem> {


public GalleryItemAdapter(ArrayList<GalleryItem> items) {
super(getActivity(), 0, items);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater()
.inflate(R.layout.gallery_item, parent, false);
}

ImageView imageView = (ImageView)convertView


.findViewById(R.id.gallery_item_imageView);
imageView.setImageResource(R.drawable.brian_up_close);

return convertView;
}
}
}
442 27. Looper, Handler HandlerThread

, AdapterView (GridView ) getView()


.

. 27.2. AdapterView-ArrayAdapter

PhotoGallery, .

. 27.3.
443


PhotoGallery : Photo-
GalleryFragment AsyncTask, XML Flickr
, XML GalleryItem.
GalleryItem URL,
.
. , -
doInBackground()
FetchItemsTask. GalleryItem 100 URL-
. ,
100. onPostExecute() GridView.
. -,
, -
.
.
-, . -
. , 1000? ,
? -
.
-
, .
GridView . -
getView() .
AsyncTask , -

. ( , ,
.)
AsyncTask -
. .


, -
GridView ,
?
-.

, .
, .
. ,
.
444 27. Looper, Handler HandlerThread

. -
, , .
, , -
.
. , -
, , .
.
Android , ,
(message queue). , -
, (message loop);
, (. 27.4).

. 27.4.

Looper, -
.

, ,
.
, .
HandlerThread,
Looper.


ThumbnailDownloader, HandlerThread.
ThumbnailDownloader
445

; Token,
ThumbnailDownloader<Token> .
queueThumbnail()
( 27.2).
27.2. (ThumbnailDownloader.java)
public class ThumbnailDownloader<Token> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";

public ThumbnailDownloader() {
super(TAG);
}

public void queueThumbnail(Token token, String url) {


Log.i(TAG, "Got an URL: " + url);
}
}

: queueThumbnail() Token String.


GalleryItemAdapter getView().
PhotoGalleryFragment.java. PhotoGalleryFragment
ThumbnailDownloader. (token) ThumbnailDownloader
.
ImageView,
. onCreate() .
onDestroy() .

27.3. ThumbnailDownloader (PhotoGalleryFragment.java)


public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";

GridView mGridView;
ArrayList<GalleryItem> mItems;
ThumbnailDownloader<ImageView> mThumbnailThread;

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

setRetainInstance(true);
new FetchItemsTask().execute();

mThumbnailThread = new ThumbnailDownloader<ImageView>();


mThumbnailThread.start();
mThumbnailThread.getLooper();
Log.i(TAG, "Background thread started");
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
}

446 27. Looper, Handler HandlerThread

27.3 ()
@Override
public void onDestroy() {
super.onDestroy();
mThumbnailThread.quit();
Log.i(TAG, "Background thread destroyed");
}
...
}

: -, , getLooper()
start() ThumbnailDownloader. ,
(
Looper ). -, quit()
onDestroy(). . HandlerThread,
, ).
, GalleryItemAdapter.getView()
GalleryItem position, queueThumbnail()
ImageView URL- .
27.4. ThumbnailDownloader (PhotoGalleryFragment.java)
private class GalleryItemAdapter extends ArrayAdapter<GalleryItem> {
...

@Override
public View getView(int position, View convertView, ViewGroup parent) {
...

ImageView imageView = (ImageView)convertView


.findViewById(R.id.gallery_item_imageView);
imageView.setImageResource(R.drawable.brian_up_close);
GalleryItem item = getItem(position);
mThumbnailThread.queueThumbnail(imageView, item.getUrl());

return convertView;
}
}

PhotoGallery LogCat.
GridView LogCat , , ThumbnailDownloader
.
, HandlerThread , -
, queueThumbnail(),
ThumbnailDownloader.


, , -
(message
handler).
447


. , (
),
, ,
.
Message .
:
what int, ;
obj , ;
target , Handler, -
.
Message Handler.
, Handler. -
, Handler ,
.


, -
Handler. Handler ;
.

. 27.5. Looper, Handler, HandlerThread Message

Message Looper,
Looper Message.
Handler Looper.
448 27. Looper, Handler HandlerThread

Handler Looper, Message -


Handler, .
Looper Message.

. 27.6. Handler, Looper

Looper Handler.
, Message Handler -
Handler.


Handler . -
Handler.obtainMessage().
, .
Handler.obtainMessage() ,
Message, ,
.
Message , sendToTarget(),
.
Looper.

queueThumbnail(). what , -
MESSAGE_DOWNLOAD. obj Token
ImageView, queueThumbnail().
Looper ,
. , -
Handler.handleMessage() .
handleMessage() FlickrFetchr
URL- .
, 27.5.
449


Handler Handler Handler

. 27.7.

27.5. , (ThumbnailDownloader.java)
public class ThumbnailDownloader<Token> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;

Handler mHandler;
Map<Token, String> requestMap =
Collections.synchronizedMap(new HashMap<Token, String>());

public ThumbnailDownloader() {
super(TAG);
}

@SuppressLint("HandlerLeak")
@Override
protected void onLooperPrepared() {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_DOWNLOAD) {
@SuppressWarnings("unchecked")
Token token = (Token)msg.obj;
Log.i(TAG, "Got a request for url: " + requestMap.get(token));
handleRequest(token);
}
}
};
}

public void queueThumbnail(Token token, String url) {


Log.i(TAG, "Got a URL: " + url");
requestMap.put(token, url);

mHandler
.obtainMessage(MESSAGE_DOWNLOAD, token)
.sendToTarget();
}

450 27. Looper, Handler HandlerThread

27.5 ()
private void handleRequest(final Token token) {
try {
final String url = requestMap.get(token);
if (url == null)
return;

byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);


final Bitmap bitmap = BitmapFactory
.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap created");

} catch (IOException ioe) {


Log.e(TAG, "Error downloading image", ioe);
}
}

onLooperPrepared()
@SuppressLint("HandlerLeak"). Android Lint -
Handler. Handler -
Looper. , Handler
, -
.
HandlerThread, .
@SuppressWarnings("unchecked") . -
, Token , Message.
obj Object. -
. ,
(type erasure) -
Android.
requestMap HashMap.
, Token , URL-, -
Token.
queueThumbnail() - Token-URL.
, Token obj
.
Handler.handleMessage() Handler onLooper-
Prepared(). HandlerThread.onLooperPrepared() ,
Looper ,
Handler.
Handler.handleMessage() , Token
handleRequest().
handleRequest(). -
URL-,
FlickrFetchr. FlickrFetchr.getUrlBytes(),
.
451

, BitmapFactory -
, getUrlBytes().
PhotoGallery LogCat -
.
, -
ImageView, GalleryItemAdapter.
, -
.
-
.
, ThumbnailDownloader Handler
.

Handler
, HandlerThread -
, Handler HandlerThread.
Looper.
Handler
Looper . Handler
. Handler Looper -.
, Handler ,
. , -
Handler
ThumbnailDownloader.

. 27.8. ThumbnailDownloader


Handler, .
452 27. Looper, Handler HandlerThread

!


ImageView.

. 27.9. ThumbnailDownloader

ThumbnailDownloader.java mRes-
ponseHandler Handler, .
, Handler -
, .

27.6. (ThumbnailDownloader.java)
public class ThumbnailDownloader extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;

Handler mHandler;
Map<Token,String> requestMap =
Collections.synchronizedMap(new HashMap<Token,String>());
Handler mResponseHandler;
Listener<Token> mListener;

public interface Listener<Token> {


void onThumbnailDownloaded(Token token, Bitmap thumbnail);
}

public void setListener(Listener<Token> listener) {


mListener = listener;
}

public ThumbnailDownloader() {
super(TAG);
public ThumbnailDownloader(Handler responseHandler) {
super(TAG);
mResponseHandler = responseHandler;
}

PhotoGalleryFragment , Handler
ThumbnailDownloader, Listener
453

Bitmap ImageView. , Handler -


Looper . Handler
onCreate(), Looper .

27.7. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
new FetchItemsTask().execute();

mThumbnailThread = new ThumbnailDownloader();


mThumbnailThread = new ThumbnailDownloader(new Handler());
mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {
public void onThumbnailDownloaded(ImageView imageView, Bitmap thumbnail) {
if (isVisible()) {
imageView.setImageBitmap(thumbnail);
}
}
});
mThumbnailThread.start();
mThumbnailThread.getLooper();
Log.i(TAG, "Background thread started");
}

ThumbnailDownloader Handler, -
Looper , mResponseHandler. , -
Listener -
Bitmap. : imageView.setImageBitmap(Bitmap)
Fragment.isVisible().
ImageView.

Messsage. Handler
handleMessage().
Handler post(Runnable).
Handler.post(Runnable) -
:
Runnable myRunnable = new Runnable() {
public void run() {
/* */
}
};
Message m = mHandler.obtainMessage();
m.callback = myRunnable;

Message callback, Handler


Runnable callback.
ThumbnailDownloader.handleRequest() .
454 27. Looper, Handler HandlerThread

27.8. (ThumbnailDownloader.java)
...
private void handleRequest(final Token token) {
try {
final String url = requestMap.get(token);
if (url == null)
return;

byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);


final Bitmap bitmap = BitmapFactory
.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap created");

mResponseHandler.post(new Runnable() {
public void run() {
if (requestMap.get(token) != url)
return;

requestMap.remove(token);
mListener.onThumbnailDownloaded(token, bitmap);
}
});
} catch (IOException ioe) {
Log.e(TAG, "Error downloading image", ioe);
}
}

mResponseHandler Looper , -
.
? requestMap. ,
GridView . ,
ThumbnailDownloader Bitmap, , Grid-
View ImageView URL-
. , Token
, .
, Token requestMap Token.
,
, .
, ThumbnailDownloader
ImageView. -
.
.

27.9. (ThumbnailDownloader.java)
public void clearQueue() {
mHandler.removeMessages(MESSAGE_DOWNLOAD);
requestMap.clear();
}

PhotoGalleryFragment .
. 455

27.10. (PhotoGalleryFragment.java)
@Override
public void onDestroyView() {
super.onDestroyView();
mThumbnailThread.clearQueue();
}

. Photo-
Gallery. ,
.
PhotoGallery -
Flickr. -
: Flickr
-.

: AsyncTask
, Handler Looper, AsyncTask
.
, . AsyncTask
HandlerThread?
. ,
AsyncTask .
, . AsyncTask
, . -
AsyncTask , ,
.
, : Android 3.2
AsyncTask . Android 3.2, AsyncTask
AsyncTask. Executor
AsyncTask -
. , AsyncTask ,
AsyncTask AsyncTask
.
AsyncTask
, . -
,
, Handler ,
.

.

, (
). , -
.
456 27. Looper, Handler HandlerThread


:
;
.

Bitmap, .
,
.
LRU (Least Recently Used): -
, .
Android LruCache,
LRU. LruCache -
ThumbnailDownloader. , URL-
Bitmap, . , -
, ,
.
, -
, , -
.
Bitmap .
,
. , -
GalleryItem
10 10 GalleryItem.
28

PhotoGallery -
Flickr. ,
Android.
, , .
Android , ( ) .
, API. -
, ,
Jelly Bean.

Flickr
, Flickr. Flickr
flickr.photos.search. flickr.
photos.search red:
http://api.flickr.com/services/rest/?method=flickr.photos.search
&api_key=XXX&extras=url_s&text=red

, , -
. , XML,
GalleryItem, .
, 28.1, FlickrFetchr
. search getRecent Gal-
leryItem , fetchItems()
, downloadGalleryItems(String).
fetchItems() fetchItems(),
.
458 28.

28.1. Flickr (FlickrFetchr.java)


public class FlickrFetchr {
public static final String TAG = "PhotoFetcher";

private static final String ENDPOINT = "http://api.flickr.com/services/rest/";


private static final String API_KEY = "4f721bbafa75bf6d2cb5af54f937bb70";
private static final String METHOD_GET_RECENT = "flickr.photos.getRecent";
private static final String METHOD_SEARCH = "flickr.photos.search";
private static final String PARAM_EXTRAS = "extras";
private static final String PARAM_TEXT = "text";
...

public ArrayList<GalleryItem> fetchItems() {


public ArrayList<GalleryItem> downloadGalleryItems(String url) {
ArrayList<GalleryItem> items = new ArrayList<GalleryItem>();

try {
String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_GET_RECENT)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.build().toString();
String xmlString = getUrl(url);
Log.i(TAG, "Received xml: " + xmlString);
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader(xmlString));

parseItems(items, parser);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
} catch (XmlPullParserException xppe) {
Log.e(TAG, "Failed to parse items", xppe);
}
return items;
}

public ArrayList<GalleryItem> fetchItems() {


// ,
String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_GET_RECENT)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.build().toString();
return downloadGalleryItems(url);
}

public ArrayList<GalleryItem> search(String query) {


String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_SEARCH)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.appendQueryParameter(PARAM_TEXT, query)
.build().toString();
return downloadGalleryItems(url);
}
}
459

downloadGalleryItems(String) , -
XML search getRecent.
flickr.photos.search
text.
PhotoGalleryFragment.
FetchItemsTask. ,
.
28.2. (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask<Void,Void,ArrayList<GalleryItem>> {
@Override
protected ArrayList<GalleryItem> doInBackground(Void... params) {
String query = "android"; //

if (query != null) {
return new FlickrFetchr().search(query);
} else {
return new FlickrFetchr().fetchItems();
}
}

@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
...
}
}

...
}

getRecent. -
null ( ), FetchItemsTask
.
PhotoGallery . ,
.


PhotoGallery Android.
.


Honeycomb Android . ,
.
Android ,
3.0.
Activity.on-
SearchRequested(). ,
.
460 28.

XML- PhotoGallery res/menu/fragment_photo_gallery.xml.


,
.
28.3. (res/menu/fragment_photo_gallery.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_search"
android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_item_clear"
android:title="@string/clear_search"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction="ifRoom"
/>
</menu>

; strings.xml. ( -
, .)
28.4. (res/values/strings.xml)
<resources>
...

<string name="title_activity_photo_gallery">PhotoGalleryActivity</string>
<string name="search_hint">Search Flickr</string>
<string name="search">Search</string>
<string name="clear_search">Clear Search</string>

</resources>

. ,
onSearchRequested().
.
28.5. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
setHasOptionsMenu(true);
...
}

...

@Override
public void onDestroyView() {
...
}
461

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_photo_gallery, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_search:
getActivity().onSearchRequested();
return true;
case R.id.menu_item_clear:
return true;
default:
return super.onOptionsItemSelected(item);
}
}

, -
.

. 28.1.

, .
onSearchRequested() , PhotoGalleryActivity -
(searchable activity).
462 28.


. XML.
searchable, , -
. res/xml,
XML searchable.xml. searchable.

28.6. (res/xml/searchable.xml)
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/search_hint"
/>

XML (search configuration).


.
. -
, , , ,
.
.
AndroidManifest.xml.
,
PhotoGalleryActivity.
,
XML .
Android,
,
. (SearchManager) ,
.
AndroidManifest.xml .
android:launchMode, 28.7.

28.7. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...

<application
... >
<activity
android:name=".PhotoGalleryActivity"
android:launchMode="singleTop"
android:label="@string/title_activity_photo_gallery" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />


</intent-filter>
<intent-filter>
463

<action android:name="android.intent.action.SEARCH" />


</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable"/>
</activity>
</application>

</manifest>

.
. -
startActivity() , action.
intent.action.SEARCH. .
, ,
, action.intent.action.SEARCH.
. -
16, . android:value
android:resource .
. ,
:
<meta-data android:name="metadata.value"
android:value="@string/app_name" />
<meta-data android:name="metadata.resource"
android:resource="@string/app_name" />

. 28.2.
464 28.

metadata.value, , -
PhotoGallery , @string/app_name.
metadata.resource .
, metadata.resource R.string.
app_name .
. SearchManager
searchable.xml, XML; -
android:resource SearchManager
.
android:launchMode activity?
. ,
.

. PhotoGallery .


, onSearchRequested(),
.
,
3.0 ;
, . 28.3.

. 28.3.
465


Android
. :
.

. AndroidManifest.xml -
, .
,
. , .

Android


,
ACTION_SEARCH

. 28.4.

, -
. . ?
android:launchMode="singleTop" ( 28.7),
.


(launch mode)? ,
,

.
466 28.

28.1.


standard

singleTop
,

singleTask . -
, , ,
,
singleInstance .
,
. ,

, , -
. :
,
.
PhotoGalleryActivity ( -
, SearchView Honeycomb).
singleTop.
,
PhotoGalleryActivity
.
onNewIntent(Intent)
Activity.
PhotoGalleryFragment.
PhotoGalleryFragment updateItems(),
FetchItemsTask .

28.8. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);

new FetchItemsTask().execute();
updateItems();

mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());


mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {
...
});
mThumbnailThread.start();
mThumbnailThread.getLooper();
}
467

public void updateItems() {


new FetchItemsTask().execute();
}

PhotoGalleryActivity onNewIntent(Intent),
PhotoGallery-
Fragment:

28.9. onNewIntent() (PhotoGalleryActivity.java)


public class PhotoGalleryActivity extends SingleFragmentActivity {
private static final String TAG = "PhotoGalleryActivity";

@Override
public Fragment createFragment() {
return new PhotoGalleryFragment();
}

@Override
public void onNewIntent(Intent intent) {
PhotoGalleryFragment fragment = (PhotoGalleryFragment)
getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);

if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.i(TAG, "Received a new search query: " + query);
}

fragment.updateItems();
}
}

LogCat -
PhotoGalleryActivity . PhotoGallery
, .
onNewIntent(Intent):
, -. ,
getIntent(), , . ,
getIntent() , ,
.
-
.
.



-
, 17.
.
468 28.

(shared preferences) -
, Shared-
Preferences. SharedPreferences -
, Bundle,
. , .
, -
XML, SharedPreferences
.
-
, , .
FlickrFetchr.
28.10. (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";

public static final String PREF_SEARCH_QUERY = "searchQuery";

private static final String ENDPOINT = "http://api.flickr.com/services/rest/";


...

SharedPreferences
Context.getSharedPreferences(String,int).
,
. PreferenceManager.ge
tDefaultSharedPreferences(Context),
(private) .
PhotoGalleryActivity SharedPreferences
.
28.11. (PhotoGalleryActivity.java)
@Override
public void onNewIntent(Intent intent) {
PhotoGalleryFragment fragment = (PhotoGalleryFragment)
getSupportFragmentManager()
.findFragmentById(R.id.fragmentContainer);

if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.i(TAG, "Received a new search query: " + query);

PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putString(FlickrFetchr.PREF_SEARCH_QUERY, query)
.commit();
}
fragment.updateItems();
}

SharedPreferences.edit()
SharedPreferences.Editor.
469

SharedPreferences. -
, , FragmentTransaction:
.
, commit() Editor
SharedPreferences.
SharedPref-
erences.getString(), getInt() , -
. PhotoGalleryFragment
SharedPreferences .

28.12. (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask<Void,Void,ArrayList<GalleryItem>> {
@Override
protected ArrayList<GalleryItem> doInBackground(Void... params) {
String query = "android"; // just for testing
Activity activity = getActivity();
if (activity == null)
return new ArrayList<GalleryItem>();

String query = PreferenceManager.getDefaultSharedPreferences(activity)


.getString(FlickrFetchr.PREF_SEARCH_QUERY, null);
if (query != null) {
return new FlickrFetchr().search(query);
} else {
return new FlickrFetchr().fetchItems();
}
}

@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
...
}
}

,
JSON, ?
. PhotoGallery,
- , .
,
updateItems().
28.13. (PhotoGalleryFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_item_clear:
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit()
.putString(FlickrFetchr.PREF_SEARCH_QUERY, null)
.commit();

470 28.

updateItems();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

SearchView Android
3.0
.
Honeycomb.
Honeycomb SearchView. SearchView
(action view) ,
. SearchView
( ,
).
, , .

android:actionViewClass .

28.14. (res/menu/fragment_photo_
gallery.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_search"
android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"
/>
<item android:id="@+id/menu_item_clear"
...
/>
</menu>

, : Android,

. .
, SearchView onOptionsItemSelected().
,
, .
( , , Search-
ViewCompat . , SearchView-
Compat SearchView,
. ,
SearchView , .
.)
SearchView Android 3.0 471

, PhotoGallery ,
SearchView. , . SearchView
, .
onCreateOptionsMenu() , -
SearchView.
SearchManager , -
. SearchManager getSearchableInfo(ComponentName) ,
, -
SearchableInfo.
SearchableInfo SearchView. ,
28.15.

28.15. SearchView (PhotoGalleryFragment.java)


@Override
@TargetApi(11)
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_photo_gallery, menu);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// SearchView
MenuItem searchItem = menu.findItem(R.id.menu_item_search);
SearchView searchView = (SearchView)searchItem.getActionView();

// searchable.xml
// SearchableInfo
SearchManager searchManager = (SearchManager)getActivity()
.getSystemService(Context.SEARCH_SERVICE);
ComponentName name = getActivity().getComponentName();
SearchableInfo searchInfo = searchManager.getSearchableInfo(name);

searchView.setSearchableInfo(searchInfo);
}
}

SearchView. Me-
nuItem ,
getActionView().
SearchManager . SearchManager -
, , .
SearchManager ,
. ,
, , searchable.xml,
SearchableInfo, getSearchabl
eInfo(ComponentName).
SearchableInfo SearchView
setSearchableInfo(SearchableInfo). SearchView
. 3.0
, .
472 28.

. 28.5.

SearchView , -
.
: -
, , .
, SearchView .
. -

.
, sin-
gleTop. ,
,
. , - -
,
.

.
Activity.startSearch().
onSearchRequested() Activity.startS-
earch() .
473

startSearch() ,
EditText, Bundle , -
-, ,
- ( , -
).
Activity.startSearch()
.
,
Toast.
XML, Flickr.
.
29

, , ;
, ,
.
? ,
, ,
RSS?
(service).
PhotoGallery
. ,
, .

IntentService
. IntentSer-
vice. , , , -
. IntentService PollService.
.
onHandleIntent(Intent) PollService
. onHandleIntent(Intent) ,
.

29.1. PollService (PollService.java)


public class PollService extends IntentService {
private static final String TAG = "PollService";

public PollService() {
super(TAG);
}
IntentService 475

@Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "Received an intent: " + intent);
}
}

IntentService. ? - -
. (Service Context)
( onHandleIntent(Intent)).
(commands). -
.
.

1. 1. 2. 2.
.

3. 3. 4. 1.

5. 2 . 6. 3 .
.

. 29.1. IntentService

IntentService , .
IntentService , -
.
IntentService ,
onHandleIntent(Intent) .
476 29.

.
, .
IntentService. -
.
, IntentService, ,
. ! ,
, ,
AndroidManifest.xml. PollService.

29.2. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...

<application
... >
<activity
android:name=".PhotoGalleryActivity"
... >
...
</activity>
<service android:name=".PollService" />
</application>

</manifest>

PhotoGalleryFragment.

29.3. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
setHasOptionsMenu(true);

updateItems();

Intent i = new Intent(getActivity(), PollService.class);


getActivity().startService(i);

mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());


...
}

, (. 29.2).

. 29.2.
477


, : LogCat . -
! ? ?
, , -
-.
: , -
, , .
.
.
Android. ,
.
Android. ,
. ,
, .
, . ,
? , ,
.


Flickr .
, .
Android
. , -
, .
,
ConnectivityManager , .
API , , .
ConnectivityManager.getBackgroundDataSetting(),
ConnectivityManager.getActiveNetworkInfo() null.
29.4 ,
.

29.4. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
@SuppressWarnings("deprecation")
boolean isNetworkAvailable = cm.getBackgroundDataSetting() &&
cm.getActiveNetworkInfo() != null;
if (!isNetworkAvailable) return;

Log.i(TAG, "Received an intent: " + intent);


}
478 29.

? Android
getBackgroundDataSetting() , false.
,
. :
, .
Android 4.0, Ice Cream Sandwich, :
. ,
getActiveNetworkInfo() null. ,
.
, .
getActiveNetworkInfo(),
ACCESS_NETWORK_STATE.

29.5. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
</application>

</manifest>


Flickr ,
. -
SharedPreferences. FlickrFetchr
.

29.6. (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "PhotoFetcher";

public static final String PREF_SEARCH_QUERY = "searchQuery";


public static final String PREF_LAST_RESULT_ID = "lastResultId";
479

private static final String ENDPOINT = "http://api.flickr.com/services/rest/";


private static final String API_KEY = "xxx";
...

. :
1. Shared-
Preferences.
2. FlickrFetchr.
3. , .
4. ,
.
5. SharedPreferences.
PollService.java . -
29.7 , ,
.

29.7. (PollService.java)
@Override
protected void onHandleIntent(Intent intent) {
...

if (!isNetworkAvailable) return;

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);


String query = prefs.getString(FlickrFetchr.PREF_SEARCH_QUERY, null);
String lastResultId = prefs.getString(FlickrFetchr.PREF_LAST_RESULT_ID, null);

ArrayList<GalleryItem> items;
if (query != null) {
items = new FlickrFetchr().search(query);
} else {
items = new FlickrFetchr().fetchItems();
}

if (items.size() == 0)
return;

String resultId = items.get(0).getId();

if (!resultId.equals(lastResultId)) {
Log.i(TAG, "Got a new result: " + resultId);
} else {
Log.i(TAG, "Got an old result: " + resultId);
}

prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}
480 29.

? .
PhotoGallery , .
, , -
.

AlarmManager
, -
-
, , , - .
Handler, Handler.sendMes-
sageDelayed() Handler.postDelayed(). ,
, .
, Handler.
Handler AlarmManager
, .
AlarmManager, ?
PendingIntent. , PendingIntent :
PollService.
, AlarmManager.
PollService setServiceAlarm(Context,boole
an), . -
; ,
PollService, , .

.

29.8. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";

private static final int POLL_INTERVAL = 1000 * 15; // 15

public PollService() {
super(TAG);
}

@Override
public void onHandleIntent(Intent intent) {
...
}

public static void setServiceAlarm(Context context, boolean isOn) {


Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
context, 0, i, 0);

AlarmManager alarmManager = (AlarmManager)


AlarmManager 481

context.getSystemService(Context.ALARM_SERVICE);

if (isOn) {
alarmManager.setRepeating(AlarmManager.RTC,
System.currentTimeMillis(), POLL_INTERVAL, pi);
} else {
alarmManager.cancel(pi);
pi.cancel();
}
}
}

PendingIntent, PollService.
PendingIntent.getService(), -
Context.startService(Intent). :
Context ; , PendingIntent
; Intent; , , -
PendingIntent ( ).
, .
, AlarmManager.setRepeating().
: (
), , , ,
PendingIntent, .
AlarmManager.cancel(PendingIntent).
PendingIntent. ,
PendingIntent .
PhotoGalleryFragment.

29.9. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
setHasOptionsMenu(true);

updateItems();

Intent i = new Intent(getActivity(), PollService.class);


getActivity().startService(i);
PollService.setServiceAlarm(getActivity(), true);

mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());


...
}

PhotoGallery. Back
.
- LogCat? PollService , -
15 . AlarmManager.
482 29.

, AlarmManager ,
PollService.
(, . ,
, .)

PendingIntent
PendingIntent. PendingIntent
-. PendingIntent.getSer-
vice(), : , ,
startService(Intent). send()
PendingIntent, ,
.
, PendingIntent
, -
. PendingIntent ,
. , ( ) -
- PendingIntent , send()
.
PendingIntent ,
PendingIntent. ,
PendingIntent PendingIntent.

PendingIntent
PendingIntent .
setServiceAlarm(boolean) isOn:
AlarmManager.cancel(PendingIntent) ,
PendingIntent, PendingIntent.
PendingIntent , ,
PendingIntent, , .
PendingIntent.FLAG_NO_CREATE PendingIn-
tent.getService(). , PendingIntent ,
null.
isServiceAlarmOn(Context), Pendin-
gIntent.FLAG_NO_CREATE .

29.10. isServiceAlarmOn() (PollService.java)


public static void setServiceAlarm(Context context, boolean isOn) {
...
}

public static boolean isServiceAlarmOn(Context context) {


Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
483

context, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null;
}

PendingIntent ,
null PendingIntent , .


, ( ,
), .
menu/fragment_photo_gallery.xml .

29.11. (menu/fragment_photo_gallery.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_search"
android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"
/>
<item android:id="@+id/menu_item_clear"
android:title="@string/clear_search"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_item_toggle_polling"
android:title="@string/start_polling"
android:showAsAction="ifRoom"
/>
</menu>

,
. (
; .)

29.12. (res/values/strings.xml)
<resources>
...
<string name="search">Search</string>
<string name="clear_search">Clear Search</string>
<string name="start_polling">Poll for new pictures</string>
<string name="stop_polling">Stop polling</string>
<string name="new_pictures_title">New PhotoGallery Pictures</string>
<string name="new_pictures_text">You have new pictures in PhotoGallery.</
string>

</resources>

-
.
484 29.

29.13.
(PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setRetainInstance(true);
setHasOptionsMenu(true);
updateItems();

PollService.setServiceAlarm(getActivity(), true);

mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());


...
}

...

@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_search:
...
case R.id.menu_item_clear:
...

updateItems();
return true;
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);

return true;
default:
return super.onOptionsItemSelected(item);
}
}

.
?


. -

.
, -
.
, onPrepareOptionsMenu(Menu).
,
.
485

onPrepareOptionsMenu(Menu), ,
, menu_item_toggle_polling, -
.

29.14. onPrepareOptionsMenu(Menu) (PhotoGalleryFragment.java)


@Override
public boolean onOptionsItemSelected(MenuItem item) {
...
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);

MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);


if (PollService.isServiceAlarmOn(getActivity())) {
toggleItem.setTitle(R.string.stop_polling);
} else {
toggleItem.setTitle(R.string.start_polling);
}
}

3.0
. ,
. , PhotoGallery
3.0.
3.0 . -
.
onPrepareOptionsMenu(Menu) Activity.invalida-
teOptionsMenu().
onOptionsItemSelected(MenuItem),
3.0 .

29.15. (PhotoGalleryFragment.java)
@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)


getActivity().invalidateOptionsMenu();

return true;
default:
return super.onOptionsItemSelected(item);
}
}
486 29.

4.2.
- .

.
, .
- ,
(notifications) , -
, ,
.
, Notification.
Notification - -
, AlertDialog 12. No-
tification :
, -
;
, ;
,
;
PendingIntent, -
.
Notification ,
notify(int, Notification) NotificationManager.
PollService ,
29.16. Notification
NotificationManager.notify(int, Notification).

29.16. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
...

String resultId = items.get(0).getId();

if (!resultId.equals(lastResultId)) {
Log.i(TAG, "Got a new result: " + resultId);

Resources r = getResources();
PendingIntent pi = PendingIntent
.getActivity(this, 0, new Intent(this, PhotoGalleryActivity.class), 0);

Notification notification = new NotificationCompat.Builder(this)


.setTicker(r.getString(R.string.new_pictures_title))
.setSmallIcon(android.R.drawable.ic_menu_report_image)
.setContentTitle(r.getString(R.string.new_pictures_title))
487

.setContentText(r.getString(R.string.new_pictures_text))
.setContentIntent(pi)
.setAutoCancel(true)
.build();

NotificationManager notificationManager = (NotificationManager)


getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, notification);
}
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}

.
setTicker(CharSequence) setSmallIcon(int).
.
,
, .
setSmallIcon(int).
setContentTitle(CharSequence) setContentText(CharSequence) -
.
, .
AlarmManager, PendingIntent. Pendin-
gIntent, setContentIntent(PendingIntent),
.
setAutoCancel(true) :
.
NotificationManager.notify(). -
,
. ,
, .
.
, . ! -
, ,
.

29.17. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";

public static final int POLL_INTERVAL = 1000 * 15; // 15


public static final int POLL_INTERVAL = 1000 * 60 * 5; // 5

public PollService() {
super(TAG);
}
488 29.

:
IntentService .
IntentService ,
.
, -
.

( )
,
( ). -
- ,
.
.
, IntentService.
, IntentService -
, .
,
.


, startService(Intent), .
.
onCreate() .
onStartCommand(Intent,int,int) ,
startService(Intent).
. ,
,
.
onStartCommand(Intent,int,int),
.
onDestroy() , -
. .
: ? -
. -
, onStartCommand(); Service.
START_NOT_STICKY, START_REDELIVER_INTENT START_STICKY.


IntentService (non-sticky) ,
. ,
: 489

. ,
START_NOT_STICKY START_REDELIVER_INTENT.
Android , stopSelf()
stopSelf(int). , stopSelf(), .
, onStart-
Command().
IntentService stopSelf(int). -
, onStartCommand().
,
( IntentService).
START_NOT_STICKY START_REDELIVER_INTENT ? -
, .
START_NOT_STICKY .
START_REDELIVER_INTENT, , ,
.
START_NOT_STICKY START_REDELIVER_INTENT
. ,
START_NOT_STICKY. PhotoGallery .
, START_NOT_STICKY.
IntentService.
START_REDELIVER_INTENT, IntentService.setIntentRedelivery(true).


(sticky) , -
, Context.stopService(Intent).
, START_STICKY.
,
Context.stopService(Intent). - -
, onStartCommand()
null-.
(,
), ,
. ,
.
, ,
.


(binding) bi
ndService(Intent,ServiceConnection,int). -
.
490 29.

bindService(Intent,ServiceConnection,int). ServiceConnection ,
.
:
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
//
MyBinder binder = (MyBinder)service;
}

public void onServiceDisconnected(ComponentName className) {


}
};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = new Intent(c, MyService.class);
c.bindService(i, mServiceConnection, 0);
}

@Override
public void onDestroy() {
super.onDestroy();
getActivity().getApplicationContext().unbindService(mServiceConnection);
}


:
onBind(Intent) , .
IBinder, ServiceConnection.onSer
viceConnected(ComponentName,IBinder).
onUnbind(Intent) .


MyBinder? ,
Java, . -
(handle), :
private class MyBinder extends IBinder {
public MyService getService() {
return MyService.this;
}
}

@Override
public void onBind(Intent intent) {
return new MyBinder();
}
: 491

Android,
Android .
. -
,
.


,
.
,
. AIDL
Messenger.
30

Android - . WiFi
, ,
.
-
, Android
(broadcast intent).
, , ,
. -
(broadcast receivers).

( ) ( )

Android

( ( (
) ) )

( (
) ( )
)

. 30.1.

,
,
493

. -
, ,
.


PhotoGallery , . -
, .
, ,
. -
, BOOT_COM-
PLETED.


.
Java StartupReceiver, android.content.
BroadcastReceiver. Eclipse
onReceive(Context,Intent). .

30.1. (StartupReceiver.java)
package com.bignerdranch.android.photogallery;
...

public class StartupReceiver extends BroadcastReceiver {


private static final String TAG = "StartupReceiver";

@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received broadcast intent: " + intent.getAction());
}
}

, ,
. ,
-
. ,
(, ).
,
: receiver -
. StartupReceiver BOOT_COMPLETED.
,
uses-permission.
AndroidManifest.xml StartupReceiver.
494 30.

30.2. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.INTERNET" />


<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application
... >
<activity
... >
...
</activity>
<service android:name=".PollService" />
<receiver android:name=".StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>

</manifest>

,
.
,
.
.

Android

. 30.2. BOOT_COMPLETED
495

,

.
onReceive(Context,Intent) -
, .
PhotoGallery,
DDMS. LogCat
. Devices, , ,
PhotoGallery. ,
,
.


, , -
. ,
API , -
onReceive(Context,Intent).
, onReceive(Context,Intent) ,
-
.
, , .
.
; ,
.
, -
. PollService ,
.

30.3. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";

private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes


public static final String PREF_IS_ALARM_ON = "isAlarmOn";

public PollService() {
super(TAG);
}

...

public static void setServiceAlarm(Context context, boolean isOn) {


Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
context, 0, i, 0);

AlarmManager alarmManager = (AlarmManager)



496 30.

30.3 ()
context.getSystemService(Context.ALARM_SERVICE);

if (isOn) {
alarmManager.setRepeating(AlarmManager.RTC,
System.currentTimeMillis(), POLL_INTERVAL, pi);
} else {
alarmManager.cancel(pi);
pi.cancel();
}

PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(PollService.PREF_IS_ALARM_ON, isOn)
.commit();
}

StartupReceiver -
.

30.4. (StartupReceiver.java)
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received broadcast intent: " + intent.getAction());

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);


boolean isOn = prefs.getBoolean(PollService.PREF_IS_ALARM_ON, false);
PollService.setServiceAlarm(context, isOn);
}

PhotoGallery. -
, .


, Photo-
Gallery. , ,
.
, -
.


:
. ,
sendBroadcast(Intent) .
,
.
PollService.
497

30.5. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";

private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes


public static final String PREF_IS_ALARM_ON = "isAlarmOn";

public static final String ACTION_SHOW_NOTIFICATION =


"com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";

public PollService() {
super(TAG);
}

@Override
public void onHandleIntent(Intent intent) {
...

if (!resultId.equals(lastResultId)) {
...

NotificationManager notificationManager = (NotificationManager)


getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, notification);

sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
}

prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}


.
, -
StartupReceiver, .
, PhotoGalleryFragment ,
. , , -
. -
, PhotoGalleryFragment .
.
, .
registerReceiver(BroadcastReceiver, IntentFil-
ter), unregisterReceiver(BroadcastReceiver). -
,
. registerReceiver() unregisterReceiver()
, -
.
498 30.

VisibleFragment, -
Fragment. ,
( 31).

30.6. VisibleFragment (VisibleFragment.java)


package com.bignerdranch.android.photogallery;
...

public abstract class VisibleFragment extends Fragment {


public static final String TAG = "VisibleFragment";

private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {


@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getActivity(),
"Got a broadcast:" + intent.getAction(),
Toast.LENGTH_LONG)
.show();
}
};

@Override
public void onResume() {
super.onResume();
IntentFilter filter = new
IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification, filter);
}

@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mOnShowNotification);
}
}

: IntentFilter,
. IntentFilter ,
XML:
<intent-filter>
<action android:name="com.bignerdranch.android.photogallery.SHOW_NOTIFICATION" />
</intent-filter>

IntentFilter, XML,
. addCategory(String),
addAction(String), addDataPath(String) .

. ,
, , -
Context.unregisterReceiver(Broadcast
Receiver). onResume()
499

onPause(). , onActivi-
tyCreated(), onActivityDestroyed().
(, onCreate() onDestroy() -
. getActivity() onCreate()
onDestroy(), . /
Fragment.onCreate(Bundle) Fragment.onDestroy(),
getActivity().getApplicationContext()).
PhotoGalleryFragment Visible-
Fragment.

30.7. (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
public class PhotoGalleryFragment extends VisibleFragment {
GridView mGridView;
ArrayList<GalleryItem> mItems;
ThumbnailDownloader<ImageView> mThumbnailThread;

PhotoGallery . -
, .

. 30.3.
500 30.



,
. .
-
. -
, receiver
android:exported="false".
. -
, AndroidManifest.xml permission.
XML AndroidManifest.xml,
(private) .

30.8. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />

<permission android:name="com.bignerdranch.android.photogallery.PRIVATE"
android:protectionLevel="signature" />

<uses-permission android:name="android.permission.INTERNET" />


<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.bignerdranch.android.photogallery.PRIVATE" />

<application
... >
...
</application>

</manifest>


signature ( ).
, -
, .
, .
.
, .
, -
. , .
,
sendBroadcast().
501

30.9. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";

private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes


public static final String PREF_IS_ALARM_ON = "isAlarmOn";

public static final String ACTION_SHOW_NOTIFICATION =


"com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";

public static final String PERM_PRIVATE =


"com.bignerdranch.android.photogallery.PRIVATE";

public PollService() {
super(TAG);
}

@Override
public void onHandleIntent(Intent intent) {
...

if (!resultId.equals(lastResultId)) {
...

NotificationManager notificationManager = (NotificationManager)


getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, notification);

sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);
}

prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}

, sendBroadcast().
, -
, .
?
, . -
registerReceiver().
30.10. (VisibleFragment.java)
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_
NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification, filter);
getActivity().registerReceiver(mOnShowNotification, filter,
PollService.PERM_PRIVATE, null);
}

.
502 30.



android:protectionLevel. Android, -
. signature.
, ,
,
. , -
. ,
, .
, , -
, .

30.1. protectionLevel

normal , -
,

.
, -
. android.permission.RECEIVE_BOOT_COMPLETED
, . -
, , ,
,

dangerous , normal
,
, ,
, ,
. -
, ,
. Android -

signature , -
, ,
, .
, -
. ,

, -
, ,
.
-
, ,

signatureOrSystem signature,
Android. -
, ,
, ,
503



,
.

1 2

. 30.4.

,
. onReceive() -
, .
, - ,
, . -

.

. 30.5.


. -

. ,
,
, (result receiver).
504 30.

, -
.
: ,
. ;
.
setResultCode(int) Activity.
RESULT_CANCELED.
VisibleFragment,
SHOW_NOTIFICATION.

30.11. (VisibleFragment.java)
private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getActivity(),
"Got a broadcast:" + intent.getAction(),
Toast.LENGTH_LONG)
.show();
Log.i(TAG, "canceling notification");
setResultCode(Activity.RESULT_CANCELED);
}
};

/,
. ,
setResultData(String) setResultExtras(Bundle).
, setResult(int,String,Bundle).
,
.
- ,
. -
PollService. -
Notification .
Notification ( ,
).
30.12. (PollService.java)
void showBackgroundNotification(int requestCode, Notification notification) {
Intent i = new Intent(ACTION_SHOW_NOTIFICATION);
i.putExtra("REQUEST_CODE", requestCode);
i.putExtra("NOTIFICATION", notification);

sendOrderedBroadcast(i, PERM_PRIVATE, null, null,


Activity.RESULT_OK, null, null);
}

Context.sendOrderedBroadcast(Intent,String,BroadcastReceiver,Handle
r,int,String,Bundle) -
sendBroadcast(Intent,String). , : ,
Handler ,
505

,
.
,
.

.
. -
PollService.
, .
, .
BroadcastReceiver NotificationReceiver. -
.

30.13. (NotificationReceiver.java)
public class NotificationReceiver extends BroadcastReceiver {
private static final String TAG = "NotificationReceiver";

@Override
public void onReceive(Context c, Intent i) {
Log.i(TAG, "received result: " + getResultCode());
if (getResultCode() != Activity.RESULT_OK)
//
//
return;

int requestCode = i.getIntExtra("REQUEST_CODE", 0);


Notification notification = (Notification)
i.getParcelableExtra("NOTIFICATION");

NotificationManager notificationManager = (NotificationManager)


c.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(requestCode, notification);
}
}

, . ,
,
. , .
, 999 (-
1000 ).
,
. android:exported="false",
.

30.14. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...

<application

506 30.

30.14 ()
... >
...
<receiver android:name=".StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".NotificationReceiver"
android:exported="false">
<intent-filter
android:priority="-999">
<action
android:name="com.bignerdranch.android.photogallery.SHOW_NOTIFICATION"
/>
</intent-filter>
</receiver>
</application>

</manifest>

Notifi-
cationManager.

30.15. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
...

if (!resultId.equals(lastResultId)) {
...
Notification notification = new NotificationCompat.Builder(this)
...

.build();

NotificationManager notificationManager = (NotificationManager)


getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, notification);

sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);

showBackgroundNotification(0, notification);
}

prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}

PhotoGallery
. , .
, ,
507

PollService.POLL_INTERVAL 5 ,
.


, ,
, ?
. -
. .
, .

, .
BroadcastReceiver.goAsync().
BroadcastReceiver.PendingResult,
. ,
PendingResult AsyncTask
, ,
PendingResult.
. -, -
. -, : -

, .
, goAsync() : -
.
, . , -
.
31 -
WebView

, Flickr, .
, PhotoGallery -
. -
. , ,
WebView -.

Flickr
URL- Flickr.
XML,
, , .
<photo id="8232706407" owner="70490293@N03" secret="9662732625"
server="8343" farm="9" title="111_8Q1B2033" ispublic="1"
isfriend="0" isfamily="0"
url_s="http://farm9.staticflickr.com/8343/8232706407_9662732625_m.jpg"
height_s="240" width_s="163" />

, XML? , .
Flickr http://www.flickr.com/services/api/misc.urls.
html, , URL- :
http://www.flickr.com/photos/-/-

id XML.
mId GalleryItem.
? , ,
owner XML . ,
owner, URL- XML :
http://www.flickr.com/photos/owner/id

, GalleryItem.
Flickr 509

31.1. URL (GalleryItem.java)


public class GalleryItem {
private String mCaption;
private String mId;
private String mUrl;
private String mOwner;

...

public void setUrl(String url) {


mUrl = url;
}

public String getOwner() {


return mOwner;
}

public void setOwner(String owner) {


mOwner = owner;
}

public String getPhotoPageUrl() {


return "http://www.flickr.com/photos/" + mOwner + "/" + mId;
}

public String toString() {


return mCaption;
}
}

mOwner get-
PhotoPageUrl() URL- , .
parseItems() owner.

31.2. owner (FlickrFetchr.java)


void parseItems(ArrayList<GalleryItem> items, XmlPullParser parser)
throws XmlPullParserException, IOException {
int eventType = parser.next();

while (eventType != XmlPullParser.END_DOCUMENT) {


if (eventType == XmlPullParser.START_TAG &&
XML_PHOTO.equals(parser.getName())) {
String id = parser.getAttributeValue(null, "id");
String caption = parser.getAttributeValue(null, "title");
String smallUrl = parser.getAttributeValue(null, EXTRA_SMALL_URL);
String owner = parser.getAttributeValue(null, "owner");

GalleryItem item = new GalleryItem();


item.setUrl(smallUrl);
item.setOwner(owner);
items.add(item);
}

eventType = parser.next();
}
}

URL- .
510 31. - WebView

:
URL- -
. URL-
.
-
GridView. ,
9 - GridFragment.
onListItemClick() -
, setOnItemClickListener()
GridView.
. -
PhotoGalleryFragment.

31.3. -
(PhotoGalleryFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);

mGridView = (GridView)v.findViewById(R.id.gridView);

setupAdapter();

mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> gridView, View view, int pos,
long id) {
GalleryItem item = mItems.get(pos);

Uri photoPageUri = Uri.parse(item.getPhotoPageUrl());


Intent i = new Intent(Intent.ACTION_VIEW, photoPageUri);

startActivity(i);
}
});

return v;
}

PhotoGallery . -
, .

: WebView
- -
. ,
HTML .
-,
: 511

.
,
- .
- -
WebView. ,
(
).
-
WebView. , , .

. 31.1. (res/layout/fragment_photo_page.xml)

: RelativeLayout
.
.
PhotoPageFragment VisibleFragment,
. , WebView
URL- .

31.4. (PhotoPageFragment.java)
package com.bignerdranch.android.photogallery;
...

public class PhotoPageFragment extends VisibleFragment {


private String mUrl;
private WebView mWebView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mUrl = getActivity().getIntent().getData().toString();
}

512 31. - WebView

31.4 ()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, parent, false);

mWebView = (WebView)v.findViewById(R.id.webView);

return v;
}
}

.
- PhotoPageActivity Single-
FragmentActivity.

31.5. - (PhotoPageActivity.java)
package com.bignerdranch.android.photogallery;
...

public class PhotoPageActivity extends SingleFragmentActivity {


@Override
public Fragment createFragment() {
return new PhotoPageFragment();
}
}

PhotoGalleryFragment,
.

31.6. (PhotoGalleryFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...

mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> gridView, View view, int pos,
long id) {
GalleryItem item = mItems.get(pos);

Uri photoPageUri = Uri.parse(item.getPhotoPageUrl());


Intent i = new Intent(Intent.ACTION_VIEW, photoPageUri);
Intent i = new Intent(getActivity(), PhotoPageActivity.class);
i.setData(photoPageUri);

startActivity(i);
}
});

return v;
}

, .
: 513

31.7. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >

...

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".PhotoGalleryActivity"
android:launchMode="singleTop"
android:label="@string/title_activity_photo_gallery" >
...
</activity>
<activity
android:name=".PhotoPageActivity" />
<service android:name=".PollService" />
<receiver android:name=".StartupReceiver">
...
</receiver>
</application>
</manifest>

PhotoGallery . -
.
- .
WebView Flickr,
. ,
URL- .
JavaScript.
. , Flickr
. Android Lint (- -
), Lint .
, WebViewClient
shouldOverrideUrlLoading(WebView,String) false.
, .

31.8. (PhotoPageFragment.java)
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, parent, false);

mWebView = (WebView)v.findViewById(R.id.webView);

514 31. - WebView

31.8 ()
mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});

mWebView.loadUrl(mUrl);

return v;
}

URL- WebView, -
. JavaScript,
getSettings() WebSettings,
WebSettings.setJavaScriptEnabled(true). WebSettings
WebView. ,
, .
WebViewClient. WebViewClient
. WebViewClient,
. , ,
URL-,
, POST-.
WebViewClient , ; -
.
shouldOverrideUrlLoading(WebView,String) WebViewClient.
, URL- WebView
(, ). true, : -
URL-, . false,
: , WebView, URL-, .
URL, ,
. -
, Flickr
. WebViewClient -
; , .
,
false.
PhotoGallery, WebView .

WebChromeClient
WebView,
, .
fragment_photo_page.xml .
: 515

. 31.2. (fragment_photo_page.xml)

ProgressBar TextView , , -
WebView: WebChromeClient. WebView-
Client , WebChromeClient
,
(chrome) . (alerts)
JavaScript, favicon, . .
onCreateView().
516 31. - WebView

31.9. WebChromeClient (PhotoPageFragment.java)


@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, parent, false);

final ProgressBar progressBar = (ProgressBar)v.findViewById(R.id.progressBar);


progressBar.setMax(100); // 0-100
final TextView titleTextView = (TextView)v.findViewById(R.id.titleTextView);

mWebView = (WebView)v.findViewById(R.id.webView);

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.setWebViewClient(new WebViewClient() {
...
});

mWebView.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView webView, int progress) {
if (progress == 100) {
progressBar.setVisibility(View.INVISIBLE);
} else {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(progress);
}
}

public void onReceivedTitle(WebView webView, String title) {


titleTextView.setText(title);
}
});

mWebView.loadUrl(mUrl);

return v;
}

-
, onProgressChanged(WebView,int) onReceivedTitle(WebView,
String). , onProgressChanged(WebView,int),
0 100. 100, , -
, ProgressBar,
View.INVISIBLE.
PhotoGallery .

WebView
. , -
, WebView -. , WebView
, onSaveInstanceState(),
: JavaScript 517

,
.
( VideoView) Android
. ,

. WebView
.
( , ?
. -
, .)
PhotoPageActivity -
, AndroidManifest.xml.

31.10. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
...

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
<activity
android:name=".PhotoPageActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
...
</application>

</manifest>

, - -
, (
An-
droid 3.2) .
. ;
.

:
JavaScript
, WebViewClient WebChromeClient
, WebView. -
JavaScript ,
518 31. - WebView

WebView . http://
developer.android.com/reference/android/webkit/WebView.html
addJavascriptInterface(Object,String).
.
mWebView.addJavascriptInterface(new Object() {
public void send(String message) {
Log.i(TAG, "Received message: " + message);
}
}, "androidObject");

:
<input type="button" value="In WebView!"
onClick="sendToAndroid('In Android land')" />

<script type="text/javascript">
function sendToAndroid(message) {
androidObject.send(message);
}
</script>


- . -
, HTML
.
32


.
View BoxDrawingView.
, .

. 32.1.
520 32.

DragAndDraw
BoxDrawingView DragAndDraw.
New Android Application Project. ,
. 32.2, DragAndDrawActivity.

. 32.2. DragAndDraw

DragAndDrawActivity
DragAndDrawActivity SingleFragmentActiv-
ity, . Package
Explorer SingleFragmentActivity.java com.bignerdranch.android.
draganddraw. activity_fragment.xml res/layout
DragAndDraw.
DragAndDrawActivity.java DragAndDrawActivity Single-
FragmentActivity. DragAndDrawFragment
(, ).

32.1. (DragAndDrawActivity.java)
public class DragAndDrawActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_and_draw);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_drag_and_draw, menu);
return true;
}
DragAndDraw 521

@Override
public Fragment createFragment() {
return new DragAndDrawFragment();
}
}

DragAndDrawFragment
DragAndDrawFragment, activity_drag_
and_draw.xml fragment_drag_and_draw.xml.
DragAndDrawFragment BoxDrawingView
, . -
BoxDrawingView.
DragAndDrawFragment
android.support.v4.app.ListFragment. onCreateView(),
fragment_drag_and_draw.xml.

32.2. (DragAndDrawFragment.java)
public class DragAndDrawFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_drag_and_draw, parent, false);
return v;
}
}

DragAndDraw , -
.

. 32.3. DragAndDraw
522 32.


Android -
,
, .

:
-
; , -
.
;
. -
,
. -
.
:
. View -
,
.
.
-
,
.
.

BoxDrawingView
BoxDrawingView
View.
BoxDrawingView View .
BoxDrawingView.java .
32.3. BoxDrawingView (BoxDrawingView.java)
public class BoxDrawingView extends View {
//
public BoxDrawingView(Context context) {
this(context, null);
}

// XML
public BoxDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}

,
, . , ,
523

AttributeSet XML, XML.


, .
fragment_drag_and_draw.xml,
.

32.4. Add BoxDrawingView (fragment_drag_and_draw.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"
>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />

</RelativeLayout>

<com.bignerdranch.android.draganddraw.BoxDrawingView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

. 32.4. BoxDrawingView
524 32.

BoxDrawingView,
. ,
View. ,
android.view android.widget.
, , .
, android.view android.widget,
.
DragAndDraw ,
. , , -
.
BoxDrawingView

.



View:
public void setOnTouchListener(View.OnTouchListener l)

, setOnClickListener(View.OnClickListener).
View.OnTouchListener,
, .
View, -
View:
public boolean onTouchEvent(MotionEvent event)

MotionEvent ,
, . .

ACTION_DOWN

ACTION_MOVE

ACTION_UP

ACTION_CANCEL

onTouchEvent() -
MotionEvent:
public final int getAction()

BoxDrawingView.java onTouch(), -
.
525

32.5. BoxDrawingView (BoxDrawingView.java)


public class BoxDrawingView extends View {
public static final String TAG = "BoxDrawingView";
...

public boolean onTouchEvent(MotionEvent event) {


PointF curr = new PointF(event.getX(), event.getY());

Log.i(TAG, "Received event at x=" + curr.x +


", y=" + curr.y + ":");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, " ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, " ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, " ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.i(TAG, " ACTION_CANCEL");
break;
}

return true;
}
}

: X Y PointF. -
. PointF
Android -, .
DragAndDraw LogCat.
. X Y
, BoxDrawingView.


BoxDrawingView , -
. .
:
( );
( ).
,
MotionEvent. Box.
Box , .
526 32.

32.6. Box (Box.java)


public class Box {
private PointF mOrigin;
private PointF mCurrent;

public Box(PointF origin) {


mOrigin = mCurrent = origin;
}

public void setCurrent(PointF current) {


mCurrent = current;
}

public PointF getOrigin() {


return mOrigin;
}
}

BoxDrawingView, Box
(. 32.5).

. 32.5. DragAndDraw

BoxDrawingView , Box
.

32.7.
(BoxDrawingView.java)
public class BoxDrawingView extends View {
public static final String TAG = "BoxDrawingView";

private Box mCurrentBox;


private ArrayList<Box> mBoxes = new ArrayList<Box>();

...

public boolean onTouchEvent(MotionEvent event) {


PointF curr = new PointF(event.getX(), event.getY());
527

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, " ACTION_DOWN");
// Reset drawing state
mCurrentBox = new Box(curr);
mBoxes.add(mCurrentBox);
break;

case MotionEvent.ACTION_MOVE:
Log.i(TAG, " ACTION_MOVE");
if (mCurrentBox != null) {
mCurrentBox.setCurrent(curr);
invalidate();
}
break;

case MotionEvent.ACTION_UP:
Log.i(TAG, " ACTION_UP");
mCurrentBox = null;
break;

case MotionEvent.ACTION_CANCEL:
Log.i(TAG, " ACTION_CANCEL");
mCurrentBox = null;
break;
}

return true;
}
}

ACTION_DOWN mCurrentBox
Box , . Box
( ,
, BoxDrawingView Box ).
mCurrentBox.
mCurrent. , , mCur-
rentBox . Box ;
.
invalidate() ACTION_MOVE.
BoxDrawingView ,
. : -
.

onDraw()
(invalid).
, . Android
draw() View .
, . -
.
528 32.

, View
.
,
View:
protected void onDraw(Canvas canvas)

invalidate(), ACTION_MOVE onTouch-


Event(), BoxDrawingView .
onDraw().
Canvas. Canvas Paint , -
Android.
Canvas . ,
Canvas, , , ,
.
Paint , . , -
Paint,
, ,
. .
BoxDrawingView.java Paint BoxDrawing-
View XML.

32.8. Paint (BoxDrawingView.java)


public class BoxDrawingView extends View {
private static final String TAG = "BoxDrawingView";

private ArrayList<Box> mBoxen = new ArrayList<Box>();


private Box mCurrentBox;
private Paint mBoxPaint;
private Paint mBackgroundPaint;

...

// XML
public BoxDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);

// (ARGB)
mBoxPaint = new Paint();
mBoxPaint.setColor(0x22ff0000);

// -
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(0xfff8efe0);
}
}

Paint
.
529

32.9. onDraw(Canvas) (BoxDrawingView.java)


@Override
protected void onDraw(Canvas canvas) {
//
canvas.drawPaint(mBackgroundPaint);

for (Box box : mBoxes) {


float left = Math.min(box.getOrigin().x, box.getCurrent().x);
float right = Math.max(box.getOrigin().x, box.getCurrent().x);
float top = Math.min(box.getOrigin().y, box.getCurrent().y);
float bottom = Math.max(box.getOrigin().y, box.getCurrent().y);

canvas.drawRect(left, top, right, bottom, mBoxPaint);


}
}

: - ,
.
left, right,
top bottom . left top , bottom
right .
Canvas.drawRect()
.
DragAndDraw .

. 32.6.
530 32.

.
View ? ,
View:
protected Parcelable onSaveInstanceState()
protected void onRestoreInstanceState(Parcelable state)

, onSaveInstanceState(Bundle)
Activity Fragment. Bundle -
, Parcelable. Bundle
Parcelable , Parcelable -
. ( Parcelable .
, .)
, : -
.
MotionEvent.
:
,
;

.
, .

MotionEvent:
public final int getActionMasked()
public final int getActionIndex()
public final int getPointerId(int pointerIndex)
public final float getX(int pointerIndex)
public final float getY(int pointerIndex)

ACTION_POINTER_UP ACTION_
POINTER_DOWN.
33


RunTracker,
GPS :
, . RunTracker
.
RunTracker -
GPS .
RunTracker , -
.

RunTracker
Android ,
. 33.1.
. -, -
SDK API 9. -,
Google API, Android. Google API
.
Google APIs , Android SDK
Manager. Window Android SDK Manager Google
APIs, . 33.2. Install 1 package.
532 33.

. 33.1. RunTracker

. 33.2. Google API SDK 4.2

SDK
Google APIs.
, .
RunActivity.
RunFragment 533

RunActivity
RunActivity ( RunTracker) Single-
FragmentActivity. SingleFragmentActivity.java com.bignerd-
ranch.android.runtracker, activity_fragment.xml res/layout/.
RunActivity.java RunActivity Single-
FragmentActivity, RunFragment. RunFragment
, .

33.1. RunActivity (RunActivity.java)


public class RunActivity extends SingleFragmentActivity {

@Override
protected Fragment createFragment() {
return new RunFragment();
}

RunFragment

RunFragment. RunFragment (. 33.3)
.
.

. 33.3. RunTracker
534 33.


, . 33.3.
res/values/strings.xml , ,
.

33.2. RunTracker (strings.xml)


<resources>
<string name="app_name">RunTracker</string>
<string name="started">Started:</string>
<string name="latitude">Latitude:</string>
<string name="longitude">Longitude:</string>
<string name="altitude">Altitude:</string>
<string name="elapsed_time">Elapsed Time:</string>
<string name="start">Start</string>
<string name="stop">Stop</string>
<string name="gps_enabled">GPS Enabled</string>
<string name="gps_disabled">GPS Disabled</string>
<string name="cell_text">Run at %1$s</string>
</resources>


TableLayout,
. TableLayout TableRow
LinearLayout. TableRow TextView:
, . LinearLay-
out Button.
, . -
, (http://www.
bignerdranch.com/solutions/AndroidProgramming.zip). 33_Location/Run-
Tracker/res/layout/fragment_run.xml res/layout .

RunFragment
RunFragment. -

.

33.3. RunFragment (RunFragment.java)


public class RunFragment extends Fragment {
private Button mStartButton, mStopButton;
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
LocationManager 535

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);

mStartedTextView = (TextView)view.findViewById(R.id.run_startedTextView);
mLatitudeTextView = (TextView)view.findViewById(R.id.run_latitudeTextView);
mLongitudeTextView =
(TextView)view.findViewById(R.id.run_longitudeTextView);
mAltitudeTextView = (TextView)view.findViewById(R.id.run_altitudeTextView);
mDurationTextView = (TextView)view.findViewById(R.id.run_durationTextView);

mStartButton = (Button)view.findViewById(R.id.run_startButton);

mStopButton = (Button)view.findViewById(R.id.run_stopButton);

return view;
}
}

, ,
. 33.3.

LocationManager
, .
Android Loca-
tionManager.
, .
.
( , )
LocationListener. -
( onLocationChanged(Location)),
,
.
LocationListener -
,
. ,
RunFragment, LocationListener
requestLocationUpdates() requestSingleUpdate()
LocationManager .
. RunTracker -
( )
.
, ,
. PendingIntent
API, Android 2.3 (Gingerbread).
536 33.

PendingIntent,
LocationManager
Intent . , ( )
, LocationManager ,
, , -
. , -
,
.
LocationManager,
(. ), RunManager,
33.4.

33.4. RunManager (RunManager.java)


public class RunManager {
private static final String TAG = "RunManager";

public static final String ACTION_LOCATION =


"com.bignerdranch.android.runtracker.ACTION_LOCATION";

private static RunManager sRunManager;


private Context mAppContext;
private LocationManager mLocationManager;

//
// RunManager.get(Context)
private RunManager(Context appContext) {
mAppContext = appContext;
mLocationManager = (LocationManager)mAppContext
.getSystemService(Context.LOCATION_SERVICE);
}

public static RunManager get(Context c) {


if (sRunManager == null) {
//
//
sRunManager = new RunManager(c.getApplicationContext());
}
return sRunManager;
}

private PendingIntent getLocationPendingIntent(boolean shouldCreate) {


Intent broadcast = new Intent(ACTION_LOCATION);
int flags = shouldCreate ? 0 : PendingIntent.FLAG_NO_CREATE;
return PendingIntent.getBroadcast(mAppContext, 0, broadcast, flags);
}

public void startLocationUpdates() {


String provider = LocationManager.GPS_PROVIDER;

// LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
}
537

public void stopLocationUpdates() {


PendingIntent pi = getLocationPendingIntent(false);
if (pi != null) {
mLocationManager.removeUpdates(pi);
pi.cancel();
}
}

public boolean isTrackingRun() {


return getLocationPendingIntent(false) != null;
}
}

: RunManager ,
API. -
, , -
( ,
LocationManager).
startLocationUpdates() LocationManager -
GPS .
requestLocationUpdates(String, long, float, PendingIntent)
( ) -
( ) .
, -
. RunTracker
, ,
.
GPS .
, -
, ,
.
getLocationPendingIntent(boolean) Intent,
.
, shouldCreate PendingIntent.
getBroadcast() ( ),
PendingIntent .
, isTrackingRun() getLocationPen-
dingIntent(false) null ,
PendingIntent .

, -
. .
RunTracker
538 33.

, -
BroadcastReceiver, .
, LocationReceiver
.

33.5. LocationReceiver (LocationReceiver.java)


public class LocationReceiver extends BroadcastReceiver {
private static final String TAG = "LocationReceiver";

@Override
public void onReceive(Context context, Intent intent) {
// Location,
Location loc = (Location)intent
.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
if (loc != null) {
onLocationReceived(context, loc);
return;
}
// , -
if (intent.hasExtra(LocationManager.KEY_PROVIDER_ENABLED)) {
boolean enabled = intent
.getBooleanExtra(LocationManager.KEY_PROVIDER_ENABLED, false);
onProviderEnabledChanged(enabled);
}
}

protected void onLocationReceived(Context context, Location loc) {


Log.d(TAG, this + " Got location from " + loc.getProvider() + ": "
+ loc.getLatitude() + ", " + loc.getLongitude());
}

protected void onProviderEnabledChanged(boolean enabled) {


Log.d(TAG, "Provider " + (enabled ? "enabled" : "disabled"));
}

onReceive(Context, Intent), LocationManager -


. Loca-
tionManager.KEY_LOCATION_CHANGED Location, -
. ,
onLocationReceived(Context, Location)
, .
LocationManager
KEY_PROVIDER_ENABLED; onProviderEnabled(boolean) -
. LocationReceiver,
.
LocationReceiver RunTracker.
ACCESS_FINE_LOCATION uses-feature -
GPS.
539

33.6.
(AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.runtracker"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="15" />


<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:required="true"
android:name="android.hardware.location.gps"/>

<application android:label="@string/app_name"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme">
<activity android:name=".RunActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".LocationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.bignerdranch.android.runtracker.ACTION_
LOCATION"/>
</intent-filter>
</receiver>

</application>

</manifest>

, -
.
, .



, , Start Stop
, RunManager.
updateUI().

33.7. (RunFragment.java)
public class RunFragment extends Fragment {
private RunManager mRunManager;

private Button mStartButton, mStopButton;



540 33.

33.7 ()
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
}

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

mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
updateUI();
}
});

mStopButton = (Button)view.findViewById(R.id.run_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.stopLocationUpdates();
updateUI();
}
});

updateUI();

return view;
}

private void updateUI() {


boolean started = mRunManager.isTrackingRun();

mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
}
}

RunTracker , , LogCat
.
Emulator Control DDMS

GPS. , -
.
,
541


.
LogCat -
. , RunFragment
LocationReceiver, Location
. , Run,
. -
Run, ,
.

33.8. Run (Run.java)


public class Run {
private Date mStartDate;

public Run() {
mStartDate = new Date();
}

public Date getStartDate() {


return mStartDate;
}

public void setStartDate(Date startDate) {


mStartDate = startDate;
}

public int getDurationSeconds(long endMillis) {


return (int)((endMillis - mStartDate.getTime()) / 1000);
}

public static String formatDuration(int durationSeconds) {


int seconds = durationSeconds % 60;
int minutes = ((durationSeconds - seconds) / 60) % 60;
int hours = (durationSeconds - (minutes * 60) - seconds) / 3600;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}

Run RunFragment.

33.9. (RunFragment.java)
public class RunFragment extends Fragment {

private BroadcastReceiver mLocationReceiver = new LocationReceiver() {

@Override
protected void onLocationReceived(Context context, Location loc) {
mLastLocation = loc;
if (isVisible())
updateUI();
}

542 33.

33.9 ()
@Override
protected void onProviderEnabledChanged(boolean enabled) {
int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
}

};

private RunManager mRunManager;

private Run mRun;


private Location mLastLocation;
private Button mStartButton, mStopButton;

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

mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
mRun = new Run();
updateUI();
}
});

...
}

@Override
public void onStart() {
super.onStart();
getActivity().registerReceiver(mLocationReceiver,
new IntentFilter(RunManager.ACTION_LOCATION));
}

@Override
public void onStop() {
getActivity().unregisterReceiver(mLocationReceiver);
super.onStop();
}

private void updateUI() {


boolean started = mRunManager.isTrackingRun();

if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());

int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
: 543

mLatitudeTextView.setText(Double.toString(mLastLocation.getLatitude()));
mLongitudeTextView.setText(Double.toString(mLastLocation.getLongitude()));
mAltitudeTextView.setText(Double.toString(mLastLocation.getAltitude()));
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));

mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
}

Run -
Location.
, updateUI(). Run
.
LocationReceiver mLocationRe-
ceiver, -
. ,
GPS .
, onStart() onStop()
, -
. onCreate(Bundle) onDe-
stroy(), mLastLocation
, .
RunTracker.
.


, ,
, . , ,
LocationManager -
.
GPS, -
. -
,
Intent LocationManager.

33.10. (RunManager.java)
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;

//
// ( ).

544 33.

33.10 ()
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
//
lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}

// LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
}

private void broadcastLocation(Location location) {


Intent broadcast = new Intent(ACTION_LOCATION);
broadcast.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
mAppContext.sendBroadcast(broadcast);
}

,
GPS. , ,
, ; .
LocationManager -
.
getAllProviders(). ,
,
.
.



, RunTracker,
. ,
, , . -
.
LocationManager , -
.
Emulator Control DDMS. -
,
( ), GPX
KML , .

, .
.
1. ACCESS_MOCK_LOCATION.
545

2. LocationManager.addTest-
Provider().
3. setTestProviderEnabled().
4. setTestProviderStatus().
5. setTestProviderLocation().
6. removeTestProvider().
, . Big Nerd Ranch
TestProvider, ,
.
Android Course Resources Github
https://github.com/bignerdranch/AndroidCourseResources
TestProvider Eclipse.
RunTracker GPS
, ,
.

33.11. (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";

public static final String ACTION_LOCATION =


"com.bignerdranch.android.runtracker.ACTION_LOCATION";

private static final String TEST_PROVIDER = "TEST_PROVIDER";

private static RunManager sRunManager;


private Context mAppContext;
private LocationManager mLocationManager;

...

public void startLocationUpdates() {


String provider = LocationManager.GPS_PROVIDER;
// ,
// .
if (mLocationManager.getProvider(TEST_PROVIDER) != null &&
mLocationManager.isProviderEnabled(TEST_PROVIDER)) {
provider = TEST_PROVIDER;
}
Log.d(TAG, "Using provider " + provider);

//
// , .
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
//
lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}
546 33.

, TestProvider ,
Allow mock locations Developer options Settings (. 33.4).
TestProvider ,
.
RunTracker .
, ,
.

. 33.4.
34
SQLite

-
JSON. , -
RunTracker
, .
Android SQLite.
SQLite , ,
API
.
Android Java- SQLite SQLiteDatabase, -
Cursor.
RunTracker ,
. -
.


- , -
. Android,
. SQLiteOpenHelper -
,
.
RunTracker SQLiteOpenHelper RunDatabaseH-
elper. RunManager RunDatabaseHelper
API , -
. RunDatabaseHelper ,
RunManager API.
API -
SQLiteOpenHelper ,
548 34. SQLite

.
SQKite . -
, RunTracker, SQLiteOpenHelper,
.
-

. RunTracker
: Run Location.
: run ( )
location ( ). Run
Location, location
run_id, _id run.
. 34.1.

. 34.1. RunTracker

RunDatabaseHelper , 34.1.

34.1. RunDatabaseHelper (RunDatabaseHelper.java)


public class RunDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "runs.sqlite";
private static final int VERSION = 1;

private static final String TABLE_RUN = "run";


private static final String COLUMN_RUN_START_DATE = "start_date";

public RunDatabaseHelper(Context context) {


super(context, DB_NAME, null, VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
// "run"
db.execSQL("create table run (" +
549

"_id integer primary key autoincrement, start_date integer)");


// "location"
db.execSQL("create table location (" +
" timestamp integer, latitude real, longitude real, altitude real," +
" provider varchar(100), run_id integer references run(_id))");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//
//
}

public long insertRun(Run run) {


ContentValues cv = new ContentValues();
cv.put(COLUMN_RUN_START_DATE, run.getStartDate().getTime());
return getWritableDatabase().insert(TABLE_RUN, null, cv);
}
}

SQLiteOpenHelper :
onCreate(SQLiteDatabase) onUpgrade(SQLiteDatabase, int, int). onCre-
ate() , onUpgrade()
.
,
, . -
, null
CursorFactory .
RunTracker , SQLiteOpenHelper
. -
, , 1.

onUpgrade()
, .
onCreate() -
SQL CREATE TABLE. insertRun(Run),
run .
;
long ContentValues,
.
SQLiteOpenHelper , -
SQLiteDatabase: getWritableDatabase() getReadableDatabase().
, getWritableDatabase(),
getReadableDatabase().
SQLiteDatabase
SQLiteOpenHelper, (, -
) , , ,
.
550 34. SQLite


, Run -
. , 34.2.
34.2. Run
public class Run {
private long mId;
private Date mStartDate;

public Run() {
mId = -1;
mStartDate = new Date();
}

public long getId() {


return mId;
}

public void setId(long id) {


mId = id;
}

public Date getStartDate() {


return mStartDate;
}

RunManager,
. API, -
.
, Run.

34.3. (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";

private static final String PREFS_FILE = "runs";


private static final String PREF_CURRENT_RUN_ID = "RunManager.currentRunId";

public static final String ACTION_LOCATION =


"com.bignerdranch.android.runtracker.ACTION_LOCATION";

private static final String TEST_PROVIDER = "TEST_PROVIDER";

private static RunManager sRunManager;


private Context mAppContext;
private LocationManager mLocationManager;
private RunDatabaseHelper mHelper;
private SharedPreferences mPrefs;
private long mCurrentRunId;

private RunManager(Context appContext) {


mAppContext = appContext;
mLocationManager = (LocationManager)mAppContext
.getSystemService(Context.LOCATION_SERVICE);
mHelper = new RunDatabaseHelper(mAppContext);
mPrefs = mAppContext.getSharedPreferences(PREFS_FILE, Context.MODE_PRIVATE);
551

mCurrentRunId = mPrefs.getLong(PREF_CURRENT_RUN_ID, -1);


}

...

private void broadcastLocation(Location location) {


Intent broadcast = new Intent(ACTION_LOCATION);
broadcast.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
mAppContext.sendBroadcast(broadcast);
}

public Run startNewRun() {


// Run
Run run = insertRun();
//
startTrackingRun(run);
return run;
}

public void startTrackingRun(Run run) {


//
mCurrentRunId = run.getId();
//
mPrefs.edit().putLong(PREF_CURRENT_RUN_ID, mCurrentRunId).commit();
//
startLocationUpdates();
}
public void stopRun() {
stopLocationUpdates();
mCurrentRunId = -1;
mPrefs.edit().remove(PREF_CURRENT_RUN_ID).commit();
}
private Run insertRun() {
Run run = new Run();
run.setId(mHelper.insertRun(run));
return run;
}
}

, RunManager. startNewRun()
insertRun() Run,
startTrackingRun(Run) , ,
.
RunFragment Start .
RunFragment startTrackingRun(Run) -
.
Run .

;
RunManager.
, stopRun()
. RunFragment Stop.
RunFragment,
RunManager. , 34.4.
552 34. SQLite

34.4. (RunFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);
...

mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
mRun = new Run();
mRun = mRunManager.startNewRun();
updateUI();
}
});

mStopButton = (Button)view.findViewById(R.id.run_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.stopLocationUpdates();
mRunManager.stopRun();
updateUI();
}
});

updateUI();

return view;
}

Location -
LocationManager. Run,
RunDatabaseHelper RunManager
. Run RunTracker
, -
.
BroadcastReceiver.
insertLocation(long, Location) RunDatabaseHelper.

34.5. (RunDatabaseHelper.java)
public class RunDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "runs.sqlite";
private static final int VERSION = 1;

private static final String TABLE_RUN = "run";


private static final String COLUMN_RUN_START_DATE = "start_date";

private static final String TABLE_LOCATION = "location";


private static final String COLUMN_LOCATION_LATITUDE = "latitude";
private static final String COLUMN_LOCATION_LONGITUDE = "longitude";
553

private static final String COLUMN_LOCATION_ALTITUDE = "altitude";


private static final String COLUMN_LOCATION_TIMESTAMP = "timestamp";
private static final String COLUMN_LOCATION_PROVIDER = "provider";
private static final String COLUMN_LOCATION_RUN_ID = "run_id";
...

public long insertLocation(long runId, Location location) {


ContentValues cv = new ContentValues();
cv.put(COLUMN_LOCATION_LATITUDE, location.getLatitude());
cv.put(COLUMN_LOCATION_LONGITUDE, location.getLongitude());
cv.put(COLUMN_LOCATION_ALTITUDE, location.getAltitude());
cv.put(COLUMN_LOCATION_TIMESTAMP, location.getTime());
cv.put(COLUMN_LOCATION_PROVIDER, location.getProvider());
cv.put(COLUMN_LOCATION_RUN_ID, runId);
return getWritableDatabase().insert(TABLE_LOCATION, null, cv);
}

RunManager .

34.6. (RunManager.java)
private Run insertRun() {
Run run = new Run();
run.setId(mHelper.insertRun(run));
return run;
}

public void insertLocation(Location loc) {


if (mCurrentRunId != -1) {
mHelper.insertLocation(mCurrentRunId, loc);
} else {
Log.e(TAG, "Location received with no tracking run; ignoring.");
}
}
}

,
insertLocation(Location).
BroadcastReceiver , -
RunTracker.
LocationReceiver -
TrackingLocationReceiver
.
TrackingLocationReceiver .

34.7. TrackingLocationReceiver: ,
(TrackingLocationReceiver.java)
public class TrackingLocationReceiver extends LocationReceiver {

@Override
protected void onLocationReceived(Context c, Location loc) {
RunManager.get(c).insertLocation(loc);
}
}
554 34. SQLite

, -
ACTION_LOCATION.

34.8. TrackingLocationReceiver (AndroidManifest.xml)


<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">

...

<receiver android:name=".LocationReceiver"
<receiver android:name=".TrackingLocationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.bignerdranch.android.runtracker.ACTION_LOCATION"/>
</intent-filter>
</receiver>

</application>

,
. RunTracker
, ,
. , , ,
onLocationReceived()
TrackingLocationReceiver. ,
, LogCat.


RunTracker
, RunFragment
Start.
.
. ,
CriminalIntent , ,
, , RunTracker
.
SQLiteDatabase Cursor, .
API , -
.
,
String.
, Java-
, Run Location.
, , ,
Cursor .
555

Cursor CursorWrapper, -
Cursor .
,

.
RunDatabaseHelper queryRuns(), -
RunCursor , .

34.9. (RunDatabaseHelper.java)
public class RunDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "runs.sqlite";
private static final int VERSION = 1;

private static final String TABLE_RUN = "run";


private static final String COLUMN_RUN_ID = "_id";
private static final String COLUMN_RUN_START_DATE = "start_date";

...

public RunCursor queryRuns() {


// "select * from run order by start_date asc"
Cursor wrapped = getReadableDatabase().query(TABLE_RUN,
null, null, null, null, null, COLUMN_RUN_START_DATE + " asc");
return new RunCursor(wrapped);
}

/**
* , "run".
* {@link getRun()} Run,
* .
*/
public static class RunCursor extends CursorWrapper {

public RunCursor(Cursor c) {
super(c);
}

/**
* Run, ,
* null, .
*/
public Run getRun() {
if (isBeforeFirst() || isAfterLast())
return null;
Run run = new Run();
long runId = getLong(getColumnIndex(COLUMN_RUN_ID));
run.setId(runId);
long startDate = getLong(getColumnIndex(COLUMN_RUN_START_DATE));
run.setStartDate(new Date(startDate));
return run;
}
}
}

RunCursor : getRun().
getRun() , ,
556 34. SQLite

Run . -
RunCursor getRun()
,
.
RunCursor
run Run,
.
queryRuns() SQL -
RunCursor, -
. RunManager,
RunListFragment.

34.10. (RunManager.java)
private Run insertRun() {
Run run = new Run();
run.setId(mHelper.insertRun(run));
return run;
}

public RunCursor queryRuns() {


return mHelper.queryRuns();
}

public void insertLocation(Location loc) {


if (mCurrentRunId != -1) {
mHelper.insertLocation(mCurrentRunId, loc);
} else {
Log.e(TAG, "Location received with no tracking run; ignoring.");
}
}


CursorAdapter
, -
RunListActivity
. RunListFragment .

34.11. RunListActivity (RunListActivity.java)


public class RunListActivity extends SingleFragmentActivity {

@Override
protected Fragment createFragment() {
return new RunListFragment();
}

}
CursorAdapter 557

34.12. RunListActivity (AndroidManifest.xml)


<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".RunActivity"
<activity android:name=".RunListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RunActivity"
android:label="@string/app_name" />
<receiver android:name=".TrackingLocationReceiver"
android:exported="false">
<intent-filter>

RunListFragment. -
onCreate(Bundle) onDestroy(),
,
(UI-),
ANR (Application Not Responding).
, Loader
.

34.13. RunListFragment (RunListFragment.java)


public class RunListFragment extends ListFragment {

private RunCursor mCursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//
mCursor = RunManager.get(getActivity()).queryRuns();
}

@Override
public void onDestroy() {
mCursor.close();
super.onDestroy();
}

RunCursor
ListView, RunListFragment. Android API (
) CursorAdapter, , .
.
CursorAdapter
, .
558 34. SQLite

RunListFragment RunCursorAdapter.

34.14. RunCursorAdapter (RunListFragment.java)


public class RunListFragment extends ListFragment {

private RunCursor mCursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//
mCursor = RunManager.get(getActivity()).queryRuns();
// ,
RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
setListAdapter(adapter);
}

@Override
public void onDestroy() {
mCursor.close();
super.onDestroy();
}

private static class RunCursorAdapter extends CursorAdapter {

private RunCursor mRunCursor;

public RunCursorAdapter(Context context, RunCursor cursor) {


super(context, cursor, 0);
mRunCursor = cursor;
}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//
//
LayoutInflater inflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflater
.inflate(android.R.layout.simple_list_item_1, parent, false);
}

@Override
public void bindView(View view, Context context, Cursor cursor) {
//
Run run = mRunCursor.getRun();

//
TextView startDateTextView = (TextView)view;
String cellText =
context.getString(R.string.cell_text, run.getStartDate());
startDateTextView.setText(cellText);
}
}
}
559

CursorAdapter Context, Cursor


. , -
, .
RunCursor ,
.
newView(Context, Cursor, ViewGroup),
View .
android.R.layout.simple_list_item_1,
TextView. -
, , .
bindView(View, Context, Cursor) CursorAdapter,

. View,
newView().
bindView() . RunCursor
Run ( CursorAdapter).
, Text-
View, Run.
RunTracker;
(,
,
). , :
.


-
, CriminalIntent.
.
34.15. (run_list_options.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/menu_item_new_run"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_run"/>
</menu>

; strings.xml.
34.16. New Run (strings.xml)
<string name="stop">Stop</string>
<string name="gps_enabled">GPS Enabled</string>
<string name="gps_disabled">GPS Disabled</string>
<string name="cell_text">Run at %1$s</string>
<string name="new_run">New Run</string>
</resources>
560 34. SQLite

RunListFragment
, .
34.17. (RunListFragment.java)
public class RunListFragment extends ListFragment {
private static final int REQUEST_NEW_RUN = 0;

private RunCursor mCursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
//
mCursor = RunManager.get(getActivity()).queryRuns();
// ,
RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
setListAdapter(adapter);
}

...

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.run_list_options, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_run:
Intent i = new Intent(getActivity(), RunActivity.class);
startActivityForResult(i, REQUEST_NEW_RUN);
return true;
default:
return super.onOptionsItemSelected(item);
}
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_NEW_RUN == requestCode) {
mCursor.requery();
((RunCursorAdapter)getListAdapter()).notifyDataSetChanged();
}
}

private static class RunCursorAdapter extends CursorAdapter {

private RunCursor mRunCursor;


onActivityResult() ,
.
561

, ( ), -
. Loader.



. , RunFragment
. RunFragement
RunActivity, -
.
RunFragment newInstance(long), -
.

34.18. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";

...
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;

public static RunFragment newInstance(long runId) {


Bundle args = new Bundle();
args.putLong(ARG_RUN_ID, runId);
RunFragment rf = new RunFragment();
rf.setArguments(args);
return rf;
}

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

RunActivity.
RUN_ID, RunFragment
newInstance(long) .
, .

34.19. (RunActivity.java)
public class RunActivity extends SingleFragmentActivity {
/** long */
public static final String EXTRA_RUN_ID =
"com.bignerdranch.android.runtracker.run_id";

@Override
protected Fragment createFragment() {
return new RunFragment();
long runId = getIntent().getLongExtra(EXTRA_RUN_ID, -1);
if (runId != -1) {

562 34. SQLite

34.19 ()
return RunFragment.newInstance(runId);
} else {
return new RunFragment();
}
}
}

, ,
RunListFragment RunActivity -
.

34.20. onListItemClick()
(RunListFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// id ;
// CursorAdapter .
Intent i = new Intent(getActivity(), RunActivity.class);
i.putExtra(RunActivity.EXTRA_RUN_ID, id);
startActivity(i);
}

private static class RunCursorAdapter extends CursorAdapter {

. -
run _id, CursorAdapter
id onListItemClick().
RunActivity . !
, . RunFragment
-
. -
, , -
.
, , .
RunDatabaseHelper, queryRun(long),
RunCursor .

34.21. (RunDatabaseHelper.java)
public RunCursor queryRun(long id) {
Cursor wrapped = getReadableDatabase().query(TABLE_RUN,
null, //
COLUMN_RUN_ID + " = ?", //
new String[]{ String.valueOf(id) }, //
null, // group by
null, // order by
null, // having
"1"); // 1
return new RunCursor(wrapped);
}
563

query() .
TABLE_RUN ,
WHERE
. ,
RunCursor .
RunManager getRun(long),
queryRun(long) Run
( ).

34.22. getRun(long) (RunManager.java)


public Run getRun(long id) {
Run run = null;
RunCursor cursor = mHelper.queryRun(id);
cursor.moveToFirst();
// ,
if (!cursor.isAfterLast())
run = cursor.getRun();
cursor.close();
return run;
}

Run RunCursor,
queryRun(long). RunCursor
. , isAfterLast()
false, Run .
RunCursor ,
close() ,
.
, RunFragment
.
, 34.23.

34.23. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";

private BroadcastReceiver mLocationReceiver = new LocationReceiver() {

@Override
protected void onLocationReceived(Context context, Location loc) {
if (!mRunManager.isTrackingRun(mRun))
return;
mLastLocation = loc;
if (isVisible())
updateUI();
}

@Override

564 34. SQLite

34.23 ()
protected void onProviderEnabledChanged(boolean enabled) {
int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
}

};

...

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());<

// Run
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
mRun = mRunManager.getRun(runId);
}
}
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);

...

mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRun = mRunManager.startNewRun();
if (mRun == null) {
mRun = mRunManager.startNewRun();
} else {
mRunManager.startTrackingRun(mRun);
}
updateUI();
}
});

...
return view;
}

...

private void updateUI() {


boolean started = mRunManager.isTrackingRun();
boolean trackingThisRun = mRunManager.isTrackingRun(mRun);
565

if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());

int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
mLatitudeTextView.setText(Double.toString(mLastLocation.
getLatitude()));
mLongitudeTextView.setText(Double.toString(mLastLocation.
getLongitude()))
mAltitudeTextView.setText(Double.toString(mLastLocation.
getAltitude()));
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));

mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
mStopButton.setEnabled(started && trackingThisRun);
}

: -
RunFragment
. Run,
LocationCursor Location.
-
LocationCursor RunDatabaseHelper.

34.24. (RunDatabaseHelper.java)
public LocationCursor queryLastLocationForRun(long runId) {
Cursor wrapped = getReadableDatabase().query(TABLE_LOCATION,
null, //
COLUMN_LOCATION_RUN_ID + " = ?", //
new String[]{ String.valueOf(runId) },
null, // group by
null, // having
COLUMN_LOCATION_TIMESTAMP + " desc", //
"1"); // limit 1
return new LocationCursor(wrapped);
}

// ... RunCursor ...

public static class LocationCursor extends CursorWrapper {

public LocationCursor(Cursor c) {
super(c);
}

public Location getLocation() {


if (isBeforeFirst() || isAfterLast())
return null;
//

566 34. SQLite

34.24 ()
String provider = getString(getColumnIndex(COLUMN_LOCATION_PROVIDER));
Location loc = new Location(provider);
//
loc.setLongitude(getDouble(getColumnIndex(COLUMN_LOCATION_LONGITUDE)));
loc.setLatitude(getDouble(getColumnIndex(COLUMN_LOCATION_LATITUDE)));
loc.setAltitude(getDouble(getColumnIndex(COLUMN_LOCATION_ALTITUDE)));
loc.setTime(getLong(getColumnIndex(COLUMN_LOCATION_TIMESTAMP)));
return loc;
}
}

LocationCursor , RunCursor, -
location,
Location. :
Location ,
, .
queryLastLocationForRun(long) queryRun(long), -
, -
LocationCursor.
queryRun(long), RunManager
Location .

34.25. (RunManager.java)
public Location getLastLocationForRun(long runId) {
Location location = null;
LocationCursor cursor = mHelper.queryLastLocationForRun(runId);
cursor.moveToFirst();
// ,
if (!cursor.isAfterLast())
location = cursor.getLocation();
cursor.close();
return location;
}

RunFragment
.

34.26.
(RunFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());

// Run
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
. 567

mRun = mRunManager.getRun(runId);
mLastLocation = mRunManager.getLastLocationForRun(runId);
}
}
}

RunTracker
, ( ) .
, .
!

.
:
.
,
.
: , -
(, ).
,
,
RunActivity.
35

34 SQLite -
Cursor .
;
.
Loader
. Loader Android 3.0 (Honeycomb); -
,
.

Loader LoaderManager
(loader) () -
. , , ContentProvider,
.
, .
: Loader, AsyncTaskLoader Cursor-
Loader (. 35.1). Loader , .
API, LoaderManager
.
AsyncTaskLoader Loader, AsyncTask -
. ,
, AsyncTaskLoader.
, CursorLoader AsyncTaskLoader Cursor Con-
tentProvider ContentResolver. , RunTracker
Loader LoaderManager 569

Cursor-
Loader , SQLiteDatabase.

LoaderManager. -
,
, .
Fragment Activity
LoaderManager get-
LoaderManager().
initLoader(int, Bundle, LoaderCallbacks<D>)
Loader. -
-
, Bundle (
null),
LoaderCallbacks<D>.
,
LoaderCallbacks,
Fragment. . 35.1.
Loader
restartLoader(int, Bundle, Loader Call-
backs<D>) .
( )
.
LoaderCallbacks<D> : onCreateLoader() ,
onLoadFinished() onLoaderReset().
RunTracker.
, ,
AsyncTask? , LoaderManager

(, ).
AsyncTask , -

, . -
setRetainInstance(true) Fragment,
,
.
( !) .
,
, ,
. ,
(retained) ; ,
,
.
570 35.

RunTracker
RunTracker : (RunCur-
sor), (Run) (Location).
SQLite,
Loader
.
AsyncTaskLoader.
, SQLiteCursorLoader,
CursorLoader, Cursor,
. , DataLoader<D>, ;
AsyncTaskLoader .


RunListFragment RunManager
RunCursor, , onCreate(Bundle).
,
. RunListFragment LoaderManager ( )
LoaderCallbacks
.
RunListFragment ( , ),
AsyncTaskLoader SQLiteCur-
sorLoader ( 35.1). CursorLoader,
ContentProvider.

35.1. SQLite (SQLiteCursorLoader.java)


public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
private Cursor mCursor;

public SQLiteCursorLoader(Context context) {


super(context);
}

protected abstract Cursor loadCursor();

@Override
public Cursor loadInBackground() {
Cursor cursor = loadCursor();
if (cursor != null) {
// ,
cursor.getCount();
}
return cursor;
}

@Override
public void deliverResult(Cursor data) {
571

Cursor oldCursor = mCursor;


mCursor = data;

if (isStarted()) {
super.deliverResult(data);
}

if (oldCursor != null && oldCursor != data && !oldCursor.isClosed()) {


oldCursor.close();
}
}

@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}

@Override
protected void onStopLoading() {
// , .
cancelLoad();
}

@Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}

@Override
protected void onReset() {
super.onReset();

// ,
onStopLoading();

if (mCursor != null && !mCursor.isClosed()) {


mCursor.close();
}
mCursor = null;
}
}

SQLiteCursorLoader AsyncTaskLoader API


Cursor mCursor. loadInBackground()
loadCursor() Cursor, getCount() ,
.
deliverResult(Cursor) . ( -
, ), deliverResult()
572 35.

. , -
. -
, ,
.
RunTracker,
API Async-
TaskLoader.
RunListCursorLoader RunListFragment .

35.2. RunListCursorLoader (RunListFragment.java)


@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// id ;
// CursorAdapter .
Intent i = new Intent(getActivity(), RunActivity.class);
i.putExtra(RunActivity.EXTRA_RUN_ID, id);
startActivity(i);
}

private static class RunListCursorLoader extends SQLiteCursorLoader {


public RunListCursorLoader(Context context) {
super(context);
}
@Override
protected Cursor loadCursor() {
//
return RunManager.get(getContext()).queryRuns();
}

private static class RunCursorAdapter extends CursorAdapter {

RunListFragment LoaderCall-
backs Cursor.
.

35.3. LoaderCallbacks<Cursor> (RunListFragment.java)


public class RunListFragment extends ListFragment implements LoaderCallbacks<Cursor>
{

...

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new RunListCursorLoader(getActivity());
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// ,
RunCursorAdapter adapter =
573

new RunCursorAdapter(getActivity(), (RunCursor)cursor);


setListAdapter(adapter);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
// ( )
setListAdapter(null);
}

onCreateLoader(int, Bundle) LoaderManager,


. id
, , Bundle
. -
; RunListCursorLoader,
Activity .
onLoadFinished(Loader<Cursor>, Cursor)
, . -
ListView RunCursorAdapter, .
, onLoaderReset(Loader<Cursor>)
. , -
, null.
, LoaderManager
. mCursor onDestroy(),
.

35.4. Loader (RunListFragment.java)


public class RunListFragment extends ListFragment implements LoaderCallbacks<Cursor>
{
private static final int REQUEST_NEW_RUN = 0;

private RunCursor mCursor;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
//
mCursor = RunManager.get(getActivity()).queryRuns();
// ,
RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
setListAdapter(adapter);
//
getLoaderManager().initLoader(0, null, this);
}

...

@Override
public void onDestroy() {
mCursor.close();

574 35.

35.4 ()
super.onDestroy();
}

...

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_NEW_RUN == requestCode) {
mCursor.requery();
((RunCursorAdapter)getListAdapter()).notifyDataSetChanged();
//
getLoaderManager().restartLoader(0, null, this);
}
}

, -
, .
,
. ListFragment
, null.


SQLiteCursorLoader ,
(, ), RunFragment -
, RunManager.
.
DataLoader,
AsyncTaskLoader. DataLoader ,
AsyncTaskLoader, -
loadInBackground().
DataLoader 35.5.

35.5. (DataLoader.java)
public abstract class DataLoader<D> extends AsyncTaskLoader<D> {
private D mData;

public DataLoader(Context context) {


super(context);
}

@Override
protected void onStartLoading() {
if (mData != null) {
deliverResult(mData);
} else {
forceLoad();
}
}
575

@Override
public void deliverResult(D data) {
mData = data;
if (isStarted())
super.deliverResult(data);
}
}

DataLoader D -
. onStartLoading() ,
.
forceLoad() .
deliverResult(D) ,
.
, DataLoader -
RunLoader RunFragment.

35.6. (RunLoader.java)
public class RunLoader extends DataLoader<Run> {
private long mRunId;

public RunLoader(Context context, long runId) {


super(context);
mRunId = runId;
}

@Override
public Run loadInBackground() {
return RunManager.get(getContext()).getRun(mRunId);
}
}

RunLoader Context (Activity) long,


. loadInBackground()
RunManager .
, RunLoader RunFragment
RunManager . -
: RunFragment
, , LoaderCallbacks<D>
Java
RunFragment. ,
, LoaderCallbacks<D> Run Location -
, initLoader()
LoaderManager.
RunLoaderCallbacks
RunFragment.
576 35.

35.7. RunLoaderCallbacks (RunFragment.java)


private class RunLoaderCallbacks implements LoaderCallbacks<Run> {

@Override
public Loader<Run> onCreateLoader(int id, Bundle args) {
return new RunLoader(getActivity(), args.getLong(ARG_RUN_ID));
}

@Override
public void onLoadFinished(Loader<Run> loader, Run run) {
mRun = run;
updateUI();
}

@Override
public void onLoaderReset(Loader<Run> loader) {
//
}
}

onCreateLoader(int, Bundle) RunLoader, -


,
. onCreate(Bundle).
onLoadFinished() mRun -
updateUI(),
.
onLoaderReset() , Run
.
LoaderManager
onCreate(Bundle) RunFragment. LOAD_RUN, -
LoaderManager
RunFragment.

35.8. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";
private static final int LOAD_RUN = 0;

...

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());

//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
577

if (runId != -1) {
mRun = mRunManager.getRun(runId);
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_RUN, args, new RunLoaderCallbacks());
}
}
}

RunTracker , ,
, .
( ), , -
, ,
.


.
,
, .
LastLocationLoader, -
.

35.9. LastLocationLoader (LastLocationLoader.java)


public class LastLocationLoader extends DataLoader<Location> {
private long mRunId;

public LastLocationLoader(Context context, long runId) {


super(context);
mRunId = runId;
}
@Override
public Location loadInBackground() {
return RunManager.get(getContext()).getLastLocationForRun(mRunId);
}
}

RunLoader, ,
getLastLocationForRun(long) RunManager .
LocationLoaderCallbacks RunFragment.

35.10. LocationLoaderCallbacks (RunFragment.java)


private class LocationLoaderCallbacks implements LoaderCallbacks<Location> {

@Override
public Loader<Location> onCreateLoader(int id, Bundle args) {
return new LastLocationLoader(getActivity(), args.getLong(ARG_RUN_ID));
}

@Override
public void onLoadFinished(Loader<Location> loader, Location location) {

578 35.

35.10 ()
mLastLocation = location;
updateUI();
}

@Override
public void onLoaderReset(Loader<Location> loader) {
//
}
}

RunLoaderCallbacks, , -
mLastLocation .
RunManager
onCreate(Bundle), ID LOAD_LOCATION.

35.11. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";
private static final int LOAD_RUN = 0;
private static final int LOAD_LOCATION = 1;
...

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());

//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_RUN, args, new RunLoaderCallbacks());
mLastLocation = mRunManager.getLastLocationForRun(runId);
lm.initLoader(LOAD_LOCATION, args, new LocationLoaderCallbacks());
}
}
}

RunTracker -
. ,
, .
36

RunTracker
. Google Maps API ( 2) .
RunMapFragment,
,
.
, ,
Maps API.

Maps API RunTracker


Maps API ( 2) Google Play services SDK -
, .


Google Play services SDK ( , Maps API)
Android 2.2
Google Play. .

Google Play services SDK


Maps API ,
Google Play services SDK
. ,
http://developer.android.com/google/
play-services/.
580 36.

1. Android SDK Manager Google Play services


Extras. extras/google/google_play_services
Android SDK.
2. Eclipse
File Import Existing Android Code Into Workspace. -
Google Play services libproject/google-play-services_lib.
Copy projects into workspace
, .
3. RunTracker -
Android, Library. Add
google-play-services_lib.

Google Maps API


Maps API, API -
. , Google
, .
https://developers.google.com/maps/documentation/android/start -
.

RunTracker
Google Play services Maps API
(
API). XML
RunTracker. , -
MAPS_RECEIVE RunTracker.
RunMapActivity.

36.1. Maps API (AndroidManifest.xml)


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.runtracker"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="15" />

<permission
android:name="com.bignerdranch.android.runtracker.permission.MAPS_RECEIVE"
android:protectionLevel="signature"/>
<uses-permission
android:name="com.bignerdranch.android.runtracker.permission.MAPS_RECEIVE"/>

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
581

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<uses-feature android:required="true"
android:name="android.hardware.location.gps" />
<uses-feature
android:required="true"
android:glEsVersion="0x00020000"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".RunListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".RunActivity"
android:label="@string/app_name" />
<activity android:name=".RunMapActivity"
android:label="@string/app_name" />
<receiver android:name=".TrackingLocationReceiver"
android:exported="false">
<intent-filter>
<action
android:name="com.bignerdranch.android.runtracker.ACTION_LOCATION"/>
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="your-maps-API-key-here"/>
</application>

</manifest>


-
. Maps API MapFragment SupportMapFragment,
MapView
GoogleMap.
SupportMapFragment RunMapFragment ,
36.2.

36.2. RunMapFragment (RunMapFragment.java)


public class RunMapFragment extends SupportMapFragment {
private static final String ARG_RUN_ID = "RUN_ID";

private GoogleMap mGoogleMap;



582 36.

36.2 ()
public static RunMapFragment newInstance(long runId) {
Bundle args = new Bundle();
args.putLong(ARG_RUN_ID, runId);
RunMapFragment rf = new RunMapFragment();
rf.setArguments(args);
return rf;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);

// GoogleMap
mGoogleMap = getMap();
//
mGoogleMap.setMyLocationEnabled(true);

return v;
}

newInstance(long) RunMapFragment
,
RunFragment. .
onCreateView()
, ,
GoogleMap . GoogleMap
, MapView,
.
setMyLocationEnabled(boolean),
.
RunMapFragment RunMapActivity
. ,
.
36.3. - (RunMapActivity.java)
public class RunMapActivity extends SingleFragmentActivity {
/** long */
public static final String EXTRA_RUN_ID =
"com.bignerdranch.android.runtracker.run_id";

@Override
protected Fragment createFragment() {
long runId = getIntent().getLongExtra(EXTRA_RUN_ID, -1);
if (runId != -1) {
return RunMapFragment.newInstance(runId);
} else {
return new RunMapFragment();
}
}
}
583

RunMapActivity RunFragment -
. ,
. , , , -
,
.

36.4. (res/values/strings.xml)
<string name="new_run">New Run</string>
<string name="map">Map</string>
<string name="run_start">Run Start</string>
<string name="run_started_at_format">Run started at %s</string>
<string name="run_finish">Run Finish</string>
<string name="run_finished_at_format">Run finished at %s</string>
</resources>

RunFragment Map.

36.5. Map (fragment_run.xml)


<Button android:id="@+id/run_stopButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/stop"
/>
<Button android:id="@+id/run_mapButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/map"
/>
</LinearLayout>
</TableLayout>

RunFragment -
.

36.6. Map (RunFragment.java)


public class RunFragment extends Fragment {
...

private RunManager mRunManager;

private Run mRun;


private Location mLastLocation;

private Button mStartButton, mStopButton, mMapButton;


private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;

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

584 36.

36.6 ()
...

mMapButton = (Button)view.findViewById(R.id.run_mapButton);
mMapButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getActivity(), RunMapActivity.class);
i.putExtra(RunMapActivity.EXTRA_RUN_ID, mRun.getId());
startActivity(i);
}
});
updateUI();

return view;
}

...

private void updateUI() {


boolean started = mRunManager.isTrackingRun();
boolean trackingThisRun = mRunManager.isTrackingRun(mRun);

if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());

int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
mLatitudeTextView.setText(Double.toString
(mLastLocation.getLatitude()));
mLongitudeTextView.setText(Double.toString
(mLastLocation.getLongitude()));
mAltitudeTextView.setText(Double.toString
(mLastLocation.getAltitude()));
mMapButton.setEnabled(true);
} else {
mMapButton.setEnabled(false);
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));

mStartButton.setEnabled(!started);
mStopButton.setEnabled(started && trackingThisRun);
}

RunTracker -
. , Map.
. 36.1, ,
- Big Nerd Ranch.
585

. 36.1. RunTracker


, . Maps
API , -
. RunDatabaseHelper RunMan-
ager , LocationCursor .

36.7. (RunDatabaseHelper.java)
public LocationCursor queryLocationsForRun(long runId) {
Cursor wrapped = getReadableDatabase().query(TABLE_LOCATION,
null,
COLUMN_LOCATION_RUN_ID + " = ?", //
new String[]{ String.valueOf(runId) },
null, // group by
null, // having
COLUMN_LOCATION_TIMESTAMP + " asc"); //
return new LocationCursor(wrapped); //
}

queryLocationsForRun(long) queryLastLocation-
ForRun(long) , SQLite,
, .
586 36.

RunManager, (faade)
RunMapFragment.

36.8. , II (RunManager.java)
public LocationCursor queryLocationsForRun(long runId) {
return mHelper.queryLocationsForRun(runId);
}

RunMapFragment . -
,
Loader. LocationListCursorLoader .

36.9. (LocationListCursorLoader.java)
public class LocationListCursorLoader extends SQLiteCursorLoader {
private long mRunId;

public LocationListCursorLoader(Context c, long runId) {


super(c);
mRunId = runId;
}

@Override
protected Cursor loadCursor() {
return RunManager.get(getContext()).queryLocationsForRun(mRunId);
}
}

RunMapFragment .

36.10. RunMapFragment (RunMapFragment.java)


public class RunMapFragment extends SupportMapFragment
implements LoaderCallbacks<Cursor> {
private static final String ARG_RUN_ID = "RUN_ID";
private static final int LOAD_LOCATIONS = 0;

private GoogleMap mGoogleMap;


private LocationCursor mLocationCursor;

...

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

//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_LOCATIONS, args, this);
}
}
}
587

...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
long runId = args.getLong(ARG_RUN_ID, -1);
return new LocationListCursorLoader(getActivity(), runId);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mLocationCursor = (LocationCursor)cursor;
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
//
mLocationCursor.close();
mLocationCursor = null;
}
}

RunMapFragment LocationCursor .
.
onLoaderReset(Loader<Cursor>) ,
.
LoaderManager -
, .
, , .
:
GoogleMap. updateUI() ,
onLoadFinished().

36.11. (RunMapFragment.java)
private void updateUI() {
if (mGoogleMap == null || mLocationCursor == null)
return;

// .
// .
PolylineOptions line = new PolylineOptions();
// LatLngBounds .
LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
//
mLocationCursor.moveToFirst();
while (!mLocationCursor.isAfterLast()) {
Location loc = mLocationCursor.getLocation();
LatLng latLng = new LatLng(loc.getLatitude(), loc.getLongitude());
line.add(latLng);
latLngBuilder.include(latLng);
mLocationCursor.moveToNext();
}
//
mGoogleMap.addPolyline(line);
//

588 36.

36.11 ()
//
// .
Display display = getActivity().getWindowManager().getDefaultDisplay();
// .
LatLngBounds latLngBounds = latLngBuilder.build();
CameraUpdate movement = CameraUpdateFactory.newLatLngBounds(latLngBounds,
display.getWidth(), display.getHeight(), 15);
mGoogleMap.moveCamera(movement);
}

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
long runId = args.getLong(ARG_RUN_ID, -1);
return new LocationListCursorLoader(getActivity(), runId);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mLocationCursor = (LocationCursor)cursor;
updateUI();
}

Maps API . -
PolylineOptions, ,
, LatLngBounds.Builder -
.
LocationCursor Location
LatLng . LatLng PolylineOptions,
LatLngBounds
.
addPolyline(PolylineOptions)
GoogleMap, .
,
. , -
, CameraUpdate
moveCamera(CameraUpdate). -

newLatLngBounds(LatLngBounds, int, int, int) CameraUpdateFactory.

, , -
. newLatLngBounds(LatLngBounds,
int), IllegalStateException,
, MapView
. ,
updateUI(), .
RunTracker
, . ,
PolylineOptions .
589


, , ,
, .
, -
.
updateUI() , .
36.12. (RunMapFragment.java)
private void updateUI() {
if (mGoogleMap == null || mLocationCursor == null)
return;

// .
// .
PolylineOptions line = new PolylineOptions();
// LatLngBounds .
LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
//
mLocationCursor.moveToFirst();
while (!mLocationCursor.isAfterLast()) {
Location loc = mLocationCursor.getLocation();
LatLng latLng = new LatLng(loc.getLatitude(), loc.getLongitude());

Resources r = getResources();

// ,
if (mLocationCursor.isFirst()) {
String startDate = new Date(loc.getTime()).toString();
MarkerOptions startMarkerOptions = new MarkerOptions()
.position(latLng)
.title(r.getString(R.string.run_start))
.snippet(r.getString(R.string.run_started_at_format, startDate));
mGoogleMap.addMarker(startMarkerOptions);
} else if (mLocationCursor.isLast()) {
// ,
// ,
String endDate = new Date(loc.getTime()).toString();
MarkerOptions finishMarkerOptions = new MarkerOptions()
.position(latLng)
.title(r.getString(R.string.run_finish))
.snippet(r.getString(R.string.run_finished_at_format, endDate));
mGoogleMap.addMarker(finishMarkerOptions);
}

line.add(latLng);
latLngBuilder.include(latLng);
mLocationCursor.moveToNext();
}
//
mGoogleMap.addPolyline(line);

MarkerOp-
tions , .
590 36.

, -
.
,
( )
icon(BitmapDescriptor) BitmapDescriptorFactory.
, .
RunTracker
. !

.
RunMapFragment
, . -

. LocationReceiver RunMapFragment,
.
(overlay) , -
.
37

! .
, ; , .
; Android.


, : -
Android. -,
.
, ? .
. . ,
, . -
. ,
: .
. . -
?
.
, .
. Android -
#android-dev irc.freenode.net. Android
Developer Office Hours (https://plus.google.com/+AndroidDevelopers/posts)
Android
.
.
.
Android http://www.github.com. -
, , .
, .
592 37.


, Big Nerd Ranch
http://www.bignerdranch.com/books. -
,
. ,
, .
http://www.bignerdranch.com.

, , .
, .