Вы находитесь на странице: 1из 560

32.973.2-018.

2
004.451
27

27

. , . , . , .
Android : . .: , 2013.
560 .: .
ISBN 978-5-459-01646-8
Android Market ( Google Play)
! , , Android
Android Market. - ,
16 Android. , Android Market
.
, Deitel & Associates Inc.
, Java, C#, C++, C,
, JavaScript, XML, Visual Basi c, Visual C++, Perl, Python .
Android Imerj
32.973.2-018.2
004.451

Prentice Hall, Inc. Upper Sadle River, New Jersey 07458.


. .
, , ,
. , ,

, .

ISBN 978-0132121361 .
ISBN 978-5-459-01646-8

2012 Pearson Education, Inc.


, 2013
, , 2013


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1. Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2. Google Play -, . . . . 74
3. Welcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
4. Tip Calculator App . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
5. Favorite Twitter Searches . . . . . . . . . . . . . . . . . . . . . . . . . . 171
6. Flag Quiz Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
7. Cannon Game. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
8. SpotOn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
9. Doodlz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
10. Address Book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
11. Route Tracker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
12. Slideshow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
13. Enhanced Slideshow App. . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
14. Weather Viewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
Deitel & Associates, Inc . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557


. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
, . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Deitel Online Android Resource Centers . . . . . . . . . . . . . . . . . . . . . . . . 19
Deitel & Associates, Inc. Online . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
. . . . . . . . . . . . . . . 23
Java Development Kit (JDK) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Eclipse IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Android SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
ADT Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Android (AVD)
Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
AVD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
() Android . . . . . . . . . . 29
() Android . . . 30
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1. Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.2. Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
. . . . . . . . . . . . . . . . . 35
Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Android . . . . . . . . . . . . . . 38
1.3. Android 2.2 (Froyo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Android 2.2, . . . . . 41
1.4. Android 2.3 (Gingerbread) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.5. Android 3.0 (Honeycomb) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46


1.6. Android Ice Cream Sandwich . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.7. Android Market . . . . . . . . . . . . . . . . . . . . . . . . .
1.8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.9. Android Software Development Kit (SDK) . . . . . . . . . . . . . . . . . . . . . . . . .
Eclipse . . . . . . . . . . . . . . . . . . . . . .
Android Development Tools (ADT) Eclipse . . . . . . . . . . . . . . .
Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.10. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
- . . . . . . . . . . . . .
1.11. Doodlz AVD . . . .
Doodlz Android . . . . . . . . . . . . .
1.12. Deitel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.13. Android- . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.14. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
50
51
52
54
54
55
55
57
57
58
58
58
58
59
59
59
59
60
68
70
71
73

2. Google Play -,
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
2.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
2.2. Android- . . . . . . . . . . . . . . . . . . . . . 75
2.3. Android- . . . . . . . . . . 76
2.3.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
2.3.2. . . . . . . . . . . . . . . . . . . . . . . . . . . 79
2.3.3. Android . . . . . . . 79
2.4. Google Play . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
2.5. Google Checkout Merchant . . . . . . . . . . . . . . . 85
2.6. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
2.7. . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.8. Google Play . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
2.9. Android . . . . . . . . . . . . . . . . . . . . . . . . . . 96
2.10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.11. . . . . . . . . . . 99
2.12. :
In-app Billing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
2.13. Market . . . . . . 103
2.14. , Google Play . . . . . . . . . 103
2.15. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104

2.16. . . . . . . . . . . . . . . . . . . .
2.17. Android- . . . . . . . . . . . . . . . . . . . . . .
2.18. Android . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.19. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

110
111
112
112

3. Welcome: Eclipse
ADT Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
3.3. Eclipse . . . . . . . . . . . . . . . . . . . . . . 115
Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
3.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Package Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
3.5.
Welcome ADT . . . 120
, . . . . . 121
Visual Layout Editor
Android SDK . . . . . . . . . . . . . . . . . . . . . . 122
main.xml . . . . . . . . . . . . . . . . . . . . . . . 122
Visual Layout Editor . . . . . 123
. . . . . . . . . . . . . . . . . . . 124
1. . . . . . . . . . . . . . . . . . . . . . . 125
2. Id RelativeLayout . . . . . . . . . . . . . . . 126
3. Background RelativeLayout . . . . 127
4. TextView . . . . . . . . . . . . . . . . . . . . . . . 127
5. Text TextView
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6. Text size Padding top
TextView ,
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
7. TextView . . . . . 131
8. Android Deitel Bug
ImageViews . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
3.6. main.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
welcomeRelativeLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
welcomeTextView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
droidImageView . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
3.7. Welcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
3.8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4. Tip Calculator App: Android
Java. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
4.2. Tip Calculator . . . . . . . . . . . . . . . . . . . . . . . . 142
4.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143


4.4. . . . . . . . . . . . . . . . . .
4.4.1. TableLayout . . . . . . . . . . . . . . . . . . . . . . . .
4.4.2. , TableLayout .
4.4.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.4. . . . . . . . . . . .
4.4.5. XML- GUI Tip Calculator . . . . .
4.4.6 strings.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5. . . . . . . . . . . . . . . .
4.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9
143
144
145
149
150
154
157
157
169

5. Favorite Twitter Searches: Shared


Preferences, , , ,
Alert Dialogs, XML-
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
5.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
5.2. Favorite Twitter Searches . . . . . . . . . . . . . . . 173
5.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
5.4. . . 177
5.4.1. main.xml TableLayout . . . . . . . . . . . . . . . . . . . . . . . . . 178
5.4.2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
5.4.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
5.4.4. TableLayout . . . . . . . . . . . . . . 181
5.4.5. TableRow,
Search Edit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
5.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
5.6. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
5.7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
6. Flag Quiz Game: , AssetManager,
, ,
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
6.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
6.2. Flag Quiz Game . . . . . . . . . . . . . . . . . . . . . . 207
6.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
6.4. . . 210
6.4.1. main.xml LinearLayout . . . . . . . . . . . . . . . . . . . . . . . . . 210
6.4.2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
6.4.3. . . . . . . . . . . . . . . . 211
6.4.4. LinearLayout . . . . . . . . . . . . . . 213
6.4.5. . . . . . . . . . . . . . . 216
6.4.6. . . . . . . . . . . . . . . . 216
6.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
6.6. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
6.7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236

10

7. Cannon Game:
, , , , ,
SurfaceView SurfaceHolder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
7.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
7.2. Cannon Game . . . . . . . . . . . . . . . . . . . . . . . 239
7.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
7.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
7.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
7.4.2. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
7.4.3. strings.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
7.4.4. main.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
7.4.5. . . . . . . . . . . . . . . . . . . . . . . . . 245
7.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
7.5.1. Line . . . . . . . . . . . 246
7.5.2. CannonGame Activity . . . . . . . . . . . . . . . . . . . . . 246
7.5.3. CannonView View . . . . . . . . . . . . . . . . . . . . . . . . 250
7.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
8. SpotOn: , ViewPropertyAnimator,
AnimatorListener, - ,
SharedPreferences,
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
8.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
8.2. SpotOn Game . . . . . . . . . . . . . . . . . . . . . . . . 271
8.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
8.4. . . 274
8.4.1. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
8.4.2. main.xml RelativeLayout . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
8.4.3. untouched.xml ImageView . . 276
8.4.4. life.xml ImageView . . . . . . . . . 277
8.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
8.5.1. SpotOn Activity . . . . . . . . . . . . . . . . . . . . . . . . . . 277
8.5.2. SpotOnView View . . . . . . . . . . . . . . . . . . . . . . . . 279
8.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
9. Doodlz: ,
SensorManager, - Toast . . . . . . . . . . . 294
9.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
9.2. Doodlz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
9.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
9.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
9.4.2. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299


9.4.3. strings.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.4.4. main.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.4.5. color_dialog.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.4.6. width_dialog.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.5.1. Doodlz Activity . . . . . . . . . . . . . . . . . . . . . . . . .
9.5.2. DoodleView View . . . . . . . . . . . . . . . . . . . . . . .
9.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11
300
301
301
303
303
304
318
329

10. Address Book: ListActivity,


AdapterViews, , , SQLite,
GUI, MenuInater . . . . . . . . . . . . . . . . . . . . 330
10.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
10.2. Address Book . . . . . . . . . . . . . . . . . . . . . . . 331
10.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334
10.4. . . 336
10.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
10.4.2. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
10.4.3. styles.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
10.4.4. textview_border.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
10.4.5. AddressBook Activity:
contact_list_item.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
10.4.6. ViewContact Activity:
view_contact.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
10.4.7. AddEditContact Activity:
add_contact.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
10.4.8. MenuItems
, XML- . . . . . . . . 341
10.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
10.5.1. AddressBook ListActivity . . . . . . . . . . . . . . . . . . 343
10.5.2. ViewContact Activity . . . . . . . . . . . . . . . . . . . . 350
10.5.3. AddEditContact Activity . . . . . . . . . . . . . . . . . . 356
10.5.4. DatabaseConnector . . . . . . . . . . . . . . . . . . . . . . 360
10.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
11. Route Tracker: Google Maps API, GPS,
LocationManager, MapActivity, MapView Overlay . . . . . . . . . . . . . 368
11.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
11.2. Route Tracker . . . . . . . . . . . . . . . . . . . . . . . 371
11.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
11.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
11.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
11.4.2. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
11.4.3. Route Tracker: main.xml . . . . . . . . . 378

12

11.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.5.1. RouteTracker MapActivity . . . . . . . . . . . . . . . . .
11.5.2. BearingFrameLayout FrameLayout . . . . . . . . . . .
11.5.3. RouteOverlay Overlay . . . . . . . . . . . . . . . . . . .
11.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

378
379
391
395
399

12. Slideshow: Gallery Media,


Content Providers, MediaPlayer,
,
Custom ListActivity View-Holder . . . . . . . . . . . . . . . . . . . . 401
12.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
12.2. Slideshow App . . . . . . . . . . . . . . . . . . . . . . 405
12.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
12.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
12.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
12.4.2. Android
. . . . . . . . . . . . . . . . . 411
12.4.3. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
12.4.4. ListView ListActivity
Slideshow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
12.4.5. ListActivity Slideshow . . . . . . . . . . . . . . . . . . 412
12.4.6. EditText,
Set Slideshow Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
12.4.7. ListActivity SlideshowEditor . . . . . . . . . . . . 413
12.4.8. ListView SlideshowEditor . . . . . . . . . . . . . . . 414
12.4.9. Activity SlideshowPlayer . . . . . . . . . . . . . . 414
12.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
12.5.1. SlideshowInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
12.5.2. Slideshow ListActivity . . . . . . . . . . . . . . . . . . . . 417
12.5.3. SlideshowEditor ListActivity . . . . . . . . . . . . . . . . 428
12.5.4. SlideshowPlayer ListActivity . . . . . . . . . . . . . . . 437
12.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
13. Enhanced Slideshow App: ,
Camera
VideoView . . . . . . 447
13.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
13.2. Enhanced Slideshow App . . . . . . . . . . . . . . . 448
13.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
13.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
13.4.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
13.4.2. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
13.4.3. SlideshowEditor ListActivity . . . . . . . . . 454


13.4.4. PictureTaker Activity . . . . . . . . . . . . . . . . . . . . .
13.4.5. SlideshowPlayer Activity . . . . . . . .
13.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.5.1. MediaItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.5.2. SlideshowInfo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.5.3. Slideshow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.5.4. SlideshowEditor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.5.5. PictureTaker Activity . . . . . . . . . . . . . . . . . . . . .
13.5.6. SlideshowPlayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13
454
454
455
455
456
458
464
467
474
480

14. Weather Viewer: -, JSON,


, ListFragment, DialogFragment, ActionBar,
, ,
Broadcast Intents BroadcastReceivers . . . . . . . . . . . . . . . . . . . . . . 481
14.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481
14.2. Weather Viewer . . . . . . . . . . . . . . . . . . . . 484
14.3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
14.4.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
14.4.1. AndroidManifest.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . 488
14.4.2. WeatherViewerActivity,
main.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
14.4.3. arrays.xml
. . . . . . . . . . . . . . 490
14.4.4. WeatherViewerActivity,
actionmenu.xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
14.4.5.
WeatherProvider . . . . . . . . . . . . . . . . . . . . . 491
14.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
14.5.1. WeatherViewerActivity . . . . . . . . . . . . . . . . . . . . . . . . . . 492
14.5.2. CitiesFragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
14.5.3. AddCityDialogFragment . . . . . . . . . . . . . . . . . . . . . . . . . 516
14.5.4. ForecastFragment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 519
14.5.5. SingleForecastFragment . . . . . . . . . . . . . . . . . . . . . . . . . 520
14.5.6. ReadLocationTask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527
14.5.7. ReadForecastTask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
14.5.8. FiveDayForecastFragment . . . . . . . . . . . . . . . . . . . . . . . . 537
14.5.9. ReadFiveDayForecastTask . . . . . . . . . . . . . . . . . . . . . . . . 544
14.5.10. DailyForecast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548
14.5.11. WeatherProvider . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 550
14.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
Deitel & Associates, Inc . . . . . . . . . . . . . . . . . . . . . . . . 557

- (Daniel
McCracken).

.
, ,


Android, Android Software Development Kit (SDK) 2.3.x 3.x, Java Eclipse (IDE).
, . ,
.
Android, Android. ,
. , .
.
www.deitel.com/books/AndroidFP/.
Android Android-
. Android
2008 . ,
comScore, 2011 Android 41,8 %
, Apple iPhone 27 %, BlackBerry 21,7 %1. , Google Play2, .
500 000 Android. , Android, .
, ,
. ,
comScore, 234
, 2011 ; 40,6 % 3.
1

www.comscore.com/Press_Events/Press_Releases/2011/8/comScore_Reports_July_2011_U.S._
Mobile_Subscriber_Market_Share.
2
6 2012 Android Market Google Play. . .
3
www.comscore.com/Press_Events/Press_Releases/2011/8/comScore_Reports_July_2011_U.S._
Mobile_Subscriber_Market_Share.

16


(Android, BlackBerry, iPhone, Palm, Symbian, Windows Phone 7 )
. Android
Android.
300 Android.
! Android
Android- Android-.
Android-.
Android,
, , , , Bluetooth, , ,
GPS, , , , 3D- .
, Android-.
- Doodlz, 1.
3 . 2
Google Play , .
, ,
Google Play . , , , .

, .


Android-, ,
Deitel & Associates, Inc. , ,
Creative Commons Attribution 3.0 Unported License (creativecommons.org/licenses/by/3.0/). ,
- , .
.
- , deitel@deitel.com.


, Java, XML. ,
, , Java XML,
- C#/.NET, Objective-C/
Cocoa C++ ( ),
, Java, -
Java XML.

17

Java XML, Android-.


Java, :


Java for Programmers (www.deitel.com/books/javafp/).

Java Fundamentals: Parts I and II LiveLessons videos (www.deitel.com/books/


LiveLessons/).

Java How to Program, 9/e (www.deitel.com/books/jhtp9/).


, . ,
, , , , -
, . , ;
, , Android API, . . 1
, , ,
.
1. ,

3, Welcome

Dive-Into Eclipse ADT

4, Tip Calculator

Android-
Java

5, Favorite
Twitter Searches

6, Flag Quiz

7, Cannon Game

8, Spot-On Game

9, Doodlz

10, Address Book

AdapterViews

11, Route Tracker

API

12, Slideshow

Photos Audio

13, Enhanced
Slideshow

14, Weather Viewer

-, -

18

Android SDK 2.x. ,


Android Software Development Kit (SDK) 2.x, Bluetooth,
Google Maps, Camera API, API .
Android SDK 3. x . Android SDK 3.x, .
, , ,
.
Android Maps APIs. Route Tracker Android Maps API, Google Maps.
, Maps API,
, Android Maps API Terms of Service (
Legal Notices and Privacy Policy),
code.google.com/android/maps-api-tos.pdf.
Eclipse. Eclipse
(IDE) Android SDK Java Development Kit
( JDK) , Android.
. Android, , , ,
, , , .
Android-. ,
Android-.
Android Best Practices Resource Center, www.deitel.com/
AndroidBestPractices/.
-. -
, ,
. Route Tracker, 11,
Android Maps APIs - Google Maps.
14 Weather Viewer -
WeatherBugs1.

,
.
.
. ,
, .
OfficinaSans (, File), Java Android
Consolas (, int x = 5;).
1

apireg.weatherbug.com/defaultAPI.aspx.

19

. 
. , FileNew ,
New File.
.
:
www.deitel.com/books/AndroidFP/
www.informit.com/title/9780132121361

. Android Java,
Android-, developer.android.com. Eclipse www.eclipse.org/
documentation.
. .
, , . ,
Android.

Deitel Online Android Resource Centers


Android Resource Centers , , , , , , , , ,
. .
, Android:


Android (www.deitel.com/android/);
Android Best Practices (www.deitel.com/androidbestpractices/);
 Java (www.deitel.com/java/);
 Eclipse (www.deitel.com/Eclipse/);


SQLite 3 (www.deitel.com/SQLite3/).

Resource Center Deitel Buzz Online Twitter Facebook (. ).

Deitel & Associates, Inc. Online


Deitel, , Resource Centers, ,
Deitel Buzz Online: www.deitel.com/newsletter/subscribe.html.
@deitel www.deitel.com/deitelfan/.


, ,
. deitel@deitel.com.

20

www.deitel.
com/books/AndroidFP/, .


Prentice Hall/
Pearson. 16- . (Mark L. Taub), Pearson Technology Group. (Olivia Basegio)

Android . (Chuti Prasertsith)
, . , .
( John Fuller)
Deitel Developer Series books.
(Rich Wong)
Accel Partners,
Android-.
AWS Convergence Technologies, Inc. WeatherBug (weather.weatherbug.com/) ,
- Weather Viewer, 14.
(Eric Kern), , iPhone for Programmers: An App-Driven Approach,
, .

.
, .








(Paul Beusterien), Mobile Developer


Solutions.
. (Eric J. Bowden), Safe Driving
Systems, LLC.
. (Ian G. Clifton), Android.
(Daniel Galpin), Intro to Android
Application Development.
(Douglas Jones), -, Fullpower
Technologies.
(Sebastian Nykopp), , Reaktor.
(Ronan Zero Schwarz),
, OpenIntents.

21

, ! Android : ,
Android. , ,
!
, , , 2011 .


. (Paul J. Deitel),
Deitel & Associates, Inc., (MIT)
(Information Technology). Deitel
& Associates, Inc. Java, C++, C, C#, Visual Basic , Cisco, IBM, Siemens, Sun
Microsystems, Dell, Lucent Technologies, Fidelity, NASA (
), National Severe Storm Laboratory, White Sands Missile Range, Rogue Wave
Software, Boeing, SunGard Higher Education, Stratus, Cambridge Technology Partners,
One Wave, Hyperion Software, Adra Systems, Entergy, CableData Systems, Nortel Networks, Puma, iRobot, Invensys . , - . ,
,
.
. (Dr. Harvey M. Deitel),
Deitel & Associates, Inc., 50-
. .

. .
Deitel & Associates, Inc.
LiveLessons.
, ,
, , , , , , ,
, , . , ,
.
(Abbey Deitel), Deitel & Associates, Inc.,
Tepper - .
Deitel & Associates, Inc. 14 .
Deitel & Associates
iPhone for Programmers: An App-Driven Approach Internet & World Wide
Web How to Program, 5/e.
(Michael Morgano), Android-
Imerj, - ,
. iPhone for
Programmers: An App-Driven Approach.

22


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

,
,
, . -
www.deitel.
com/books/AndroidFP/.



Android,
Windows, Linux Mac OS X.
developer.android.com/sdk/requirements.html.
, , :


Java SE 6 Software Development Kit;

Eclipse 3.6.2 (Helios) IDE for Java Developers;

Android SDK, 2.2, 2.3.3 3.x;

Eclipse ADT (Android Development Tools);


.

Java Development Kit (JDK)


Android- Java Development Kit ( JDK)
5 6 ( JDK 5 JDK 6). JDK 6. JDK
Linux Windows, www.oracle.com/technetwork/java/javase/downloads/
index.html.
JDK.
www.oracle.com/technetwork/java/javase/index-137561.html.
Mac OS X Java SE 6. ,
Apple.

24

Eclipse IDE
Android-
Eclipse, ,
. Eclipse IDE
for Java Developers - www.eclipse.org/downloads/.
- Eclipse (3.7.1
). ,
(3.6.2), Older Versions,
. ,
(Windows, Mac Linux). Eclipse,
. Windows 7
C:\Eclipse.
Eclipse bit.ly/InstallingEclipse.
. , Eclipse JDK 6,
:
.
1. Eclipse Eclipse
2. Workspace Launcher ( )
OK.
3. WindowPreferences (),
Preferences.
4. Java Compiler (). JDK Compliance
( JDK) Compiler compliance level (
) 1.6.
5. Eclipse.

Android SDK
Android Software Development Kit (SDK) ,
, Android.
Android SDK, developer.android.com/sdk/index.html.
SDK, , : Windows, Mac OS X Linux. .
SDK Android,
Android SDK.

ADT Eclipse
Android Development Tools (ADT) Eclipse
Android SDK Android
Eclipse.
ADT, - developer.android.com/sdk/eclipse-adt.html

Android

25

.
- , ,
.

Android
Android,
. Android 2.2, 2.3.3 3.x. Android SDK,
:
1. Eclipse

2. Workspace Launcher ( ) ,
, OK.
3. WindowPreferences (),
Preferences. Android, SDK Location
( SDK) , Android
SDK. Windows Android SDK c:\android-sdkwindows. OK.
4. WindowAndroid SDK Manager ( Android SDK),
Android SDK Manager (. 1).

. 1. Android SDK Manager

5. Name () , Android
, .
, , . 2.

26

, Extras, .
Google USB Driver package Android Windows-. Google Market Licensing
package ,
Google Play, , . Google
Market Billing package ,
.

. 2.

. 3. Choose Packages to Install

Android (AVD)

27

6. Install () Choose Packages


to Install ( ), . 3.
.
Accept All ( ),
Install.
Android SDK Manager.
Eclipse.

Android (AVD)
Android
Android, Android SDK,
Android , Android. ,
Android Virtual Device (AVD).
, .
( ), ,
, SD, , .
Android, AVD,
. ,
:
1. Eclipse.
2. WindowAVD Manager ( AVD), Android Virtual Device Manager ( Android),
. 4.
3. New () Create new Android Virtual
Device (AVD) ( Android),
. 5. Create AVD
( Android).
Android-, .
Samsung Nexus S Android 2.3.3.
AVD
, config.ini.

developer.android.com/guide/developing/devices/managing-avds.html.
4. AVD, Motorola
Xoom. Android 3.1, , .
. 6.

28

. 4. Android AVD Manager

. 5. Create new Android Virtual Device (AVD)

() Android

29

. 6. Create new Android Virtual Device (AVD) Motorola Xoom

AVD
AVD . AVD, Enabled ()
Snapshot ().

()
Android
,
Android. ,
developer.android.com/guide/developing/device.html.
Microsoft Windows, Windows USB Android, (
) , . 2.
USB-, .
USB-, ,
developer.android.com/sdk/oem-usb.html.

30

()
Android
, , Eclipse.
Android-,
. Android-
,
(, Motorola) Android. - developer.android.com/guide/developing/projects/projectscmdline.html , Android-
. . 2.
2. ,
Android-

URL-

Android

developer.android.com/guide/
developing/tools/index.html

, AVD; Android;
Android SDK

Android Emulator

developer.android.com/guide/
developing/tools/emulator.html

Android

Android Debug
Bridge (adb)

developer.android.com/guide/
developing/tools/adb.html

Apache Ant

ant.apache.org/

Keytool and Jarsigner (


)

developer.android.com/guide/
publishing/app-signing.html

JDK.
Keytool ,
Android.
Jarsigner


, , - www.deitel.com/
books/androidFP/.
-, www.deitel.
com Register (),
-. .
, .
, ,

31

Deitel Buzz Online


www.deitel.com/newsletter/subscribe.html.
-
, ,
. ,
, www.deitel.com.
deitel.com,
.
www.deitel.com , Login
(), -.
www.deitel.com/books/androidFP/. Examples
(), Examples.zip .
Examples.zip .
Android- Android : . !

Android










Android Android SDK.


Android Market.
.
, Android-,
Android SDK, Java SDK Eclipse.
Android.
- Android .
Deitel .

1.1.
Android-! ,
Android : , , . ,
Java, -
XML. , , , Java XML,
- C#/.NET, Objective-C/Cocoa
C++ ( ), , Java, - Java
XML, Android-.
Android, .
.
Eclipse ( ), Java Android SDK
(Software Development Kit), . ,

1.1.

33

Eclipse. .
- .
, , . , , - www.deitel.com/
books/AndroidFP/. . 1.1 Android,
.
1.1. Android,

URL-

Android Developer Guide

developer.android.com/guide/index.html

Using the Android Emulator

developer.android.com/guide/developing/devices/
emulator.htm

Android Package Index

developer.android.com/reference/packages.html

Android Class Index

developer.android.com/reference/classes.html

User Interface Guidelines

developer.android.com/guide/practices/ui_
guidelines/index.html

Data Backup

developer.android.com/guide/topics/data/backup.
html

Security and Permissions

developer.android.com/guide/topics/security/
security.html

Managing Projects from Eclipse with ADT developer.android.com/guide/developing/projects/


projects-eclipse.html
Debugging Tasks

developer.android.com/guide/developing/
debug-tasks.html

Tools Overview

developer.android.com/guide/developing/tools/
index.html

Publishing Your Apps

developer.android.com/guide/publishing/publishing.
html

Android Market Getting Started

market.android.com/support/bin/topic.py?hl=en&
topic=15866

Android Market Developer Distribution www.android.com/us/developer-distributionagree


ment.html
Agreement

, ,
Android. - Android Developer
, (. 1.21), .

34

Android

Google (Google Play), - play.


google.com/apps/publish/signup. c Google Play
$25 Google Checkout Google Play.
Android Market 2.
Android , , , . .
Android,

(. 1.2).
1.2. Android

Android Discuss


Google Groups:
android- discuss

androiddiscuss subscribe@
googlegroups.com

Android,

Stack Overflow

stackoverflow.com/
questi ons/tagged/
android

,

Android,

Java Eclipse, ,
,

Android Developers ( Android-)


Google Groups:
android- developers

android-deve
loperssubscribe@google
groups.com

,
Android,
, ,

Android Market www.google.com/sup


Help Forum (- port/forum/p/Android+
market
Android Market)


Android

Android Forums www.androidforums.


( Android) com/

,

Android,
Android-

1.2. Android

35

1.2. Android
, Android ,
2008 . ,
Gartner1, Android
2010 .
, Nielsen2, , 2011 Android 37 %
, , Apple iPhone
27 %, BlackBerry 22 %. 2010 3
200 000 Android,
100 000 Android
. 2011 500 000
Android. 300
Android.
Android Android, Inc.
2005 Google. 2007
Open Handset Alliance,
34 , 81 (www.openhandsetalliance.com/oha_members.html). Android, ,
,
Android . Android ,
.


Android .
Android
.
Android , .
Android. (
- source.android.com/source/report-bugs.html)
Open Source Project (source.android.com/community/index.html).
Android ,
Google (. 1.3). . 1.4
, Android, , ,
.
1

www.gartner.com/it/page.jsp?id=1372013.
blog.nielsen.com/nielsenwire/online_mobile/u-s-smartphone-market-whos-the-mostwanted/.
3
www.wired.com/gadgetlab/2010/08/google-200000-android-phones/.
2

36

Android

1.3. Android

URL-

En.wikipedia.org/wiki/List_of_open_source_Android_
, - applications
(, .)
Google code.google.com/p/apps-for-android/
Android
, developer.android.com/resources/browser.html?tag=
- sample
Android
Android www.techdrivein.com/2010/11/12-open-sourceandroidapplications.html

Android - www.techdrivein.com/2010/12/15-nice-andsimpleopen-source-android.html

1.4. , Android

URL-

Android

source.android.com/source/download.html

source.android.com/about/philosophy.html
Android

source.android.com/source/licenses.html


(FAQ)

source.android.com/faqs.html#aosp

Java
Android Java, . Java
Android, , .
Java ,
-, , ( , ), .
Java . , .
Java Android Android API ,
.

1.2. Android

37

.
OEM- 48 ,
Android- 59 , Android1.
, ,
.
Java - ,
.
. ,
, ,
.
, Eclipse,
,
, ,
. Eclipse ADT (Android Development Tools) ,
Android, .


Android , -, MP3-, ,
.
.
, . 1.5.
1.5. , Android


, Google
Maps -

-

-



code.google.com/events/io/2010/.

38

Android

1.5 ()

- List
View ( ) DatePicker

View TimePicker View ( )


, (
- )

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


Android ,
. (Phone), (Contacts), (Mail), (Browser) .
. ,
.

Android
Android ( ):


Android 1.6 (Donut);


Android 2.02.1 (Eclair);
 Android 2.2 (Froyo);
 Android 2.3 (Gingerbread);
 Android 3.0 (Honeycomb).


1.3. Android 2.2 (Froyo)


Android 2.2, Froyo ( ), 2010 .
(. 1.6).
Android 2.3 (Gingerbread, ) Android 3.0 (Honeycomb, ).

1.3. Android 2.2 (Froyo)

39

1.6. Android 2.2 (developer.android.com/sdk/


android-2.2-highlights.html)

:
Dalvik Virtual Machine

25
Android 2.1.
Chrome V8 -, JavaScript.

Exchange Exchange Android


Exchange
Calendar (Calendar)

Global Address
Lists (GAL,
)
Microsoft Exchange,

,
, .

Android, Remote Wipe ( ).


,
.
. (
SMS-) , , .
Remote Wipe
.

Quick Contact ( )
Android (, , SMS-
). (, , ,
),
Quick Contact .
Quick Contact
,


40

Android

1.6 ()

Android 2.2
() .
, (,
, ),
(, )
.
, ,

. Android
.
,

,
.
-
. HTML5,
.

, , Adobe Flash. (.
Flash.)

.
, . (
Android ),
SettingsLanguage & keyboardAndroid keyboardInput languages
(  Android )

Android Stagefright HTTP-



HyperText Transfer Protocol ( )
.
Android , OpenCORE

1.3. Android 2.2 (Froyo)

41

Bluetooth

Android-
Bluetooth,
- ( ),
Bluetooth

WiFi-

Android 2.x
.
. ,
WiFi, , USB-.
. www.engadget.com/2010/05/13/android-22-froyo-toincludeusb-tethering-wifi-hotspot-funct/

Android 2.2,

C2DM (Android Cloud to Device Messaging,
)
, Android,
. 1.
Android Market
Android (Android Application Error Reports),
,
.
Android 2.2 API,
(. 1.7). .
-, .
-, ,
(, RSS, Atom, XML, JSON .) (. 1.8). , www.housingmaps.com - Craigslist (www.craigslist.org) Google Maps ( API,
).
. -
WeatherBug 14.
1

code.google.com/android/c2dm/.

42

Android

1.7. API, Android 2.2 (developer.android.com/sdk/


android-2.2-highlights.html)

API

,
(Apps on external storage)
Android,
( )
(Camera and , API
camcorder)
Camera Preview:
(20 ), , ,
.
CamcorderProfile
(Data
backup)

, (Device policy management)



(, )
(Graphics)

API,
OpenGL ES 2.0. API
Android NDK , ,

(developer.android.com/sdk/ndk/overview.html)

(Media frame- API, ,


work)
(- ), ,

- ( (UI framework)
, )
UiModeManager . API

,
TabWidget

1.3. Android 2.2 (Froyo)

43

1.8. - (www.programmableweb.com/apis/
directory/1?sort=mashups)

Google Maps

Facebook

Foursquare

LinkedIn

YouTube

Twitter

Groupon

Netflix

eBay

Wikipedia

PayPal

Last.fm

Amazon

: ,
. .

Salesforce.com

Skype

Microsoft

Bing

Flickr

Zillow

Yahoo!

Yahoo!

WeatherBug

. 1.9 .
1.9. -

URL-

ProgrammableWeb

www.programmableweb.com

Webmashup.com

www.webmashup.com/

Webapi.org

www.webapi.org/webapi-directory/

Google Code API Directory

code.google.com/apis/gdata/docs/directory.html

APIfinder

www.apifinder.com/

44

Android

1.4. Android 2.3 (Gingerbread)


Android 2.3 (Gingerbread ),
2010 ( Android 2.3.3 , 2011), ,
, ,
.
. 1.10.
1.10. Android 2.3 (developer.android.com/sdk/
android-2.3-highlights.html)

- ,

,
, , Android
.
,

Manage Appli- Manage Applications ( cations
), Options () Home ( ),
. ,
(
, Bluetooth ),
.

Near-field communi- Near Field Communication, NFC ( )
cations (- ) ,
, 10 .

,
. NFC
,
NFC ,
. NFC ,
, 1
- , ,
,

, , .

1.4. Android 2.3 (Gingerbread)

45

Android Session Initiation Protocol



(SIP, )
Internet Engineering Task Force (IETF). , . ,
SIP (
), , SIP,
. , SIP
Android.
SIP-, - www.
cs.columbia.edu/sip/service-providers.html
Down- ,
loads
, , Downloads ()

, , (. 1.11).
- developer.android.com/sdk/android-2.3-highlights.html.
1.11. Android 2.3, (developer.
android.com/sdk/android-2.3-highlights.html)

SIP
- (
)

API Near- , field communications


NFC
. Android 2.3.3

. ,
NFC
API Audio ( effects
), ( ),
( ) (-)



46

Android

1.11 ()

Advanced Audio Coding (AAC,


MP3) Adaptive
Multi-Rate Wideband (AMRWB, ),

WebM
VP8

API Camera

API Camera, ,
,

1.5. Android 3.0 (Honeycomb)


, 2015
20 % 1.

Android. , Consumer Electronic Show,
2011 85 Android2. Android 3.0
(Honeycomb, )
, ( ).
,
, 3D- , System
() Action (), ,
(. 1.12). ,
,
(. 1.13).
1.12. Android 3 (developer.android.com/sdk/android-3.0-highlights.
html)

1
2

www.forrester.com/ER/Press/Release/0,1769,1340,00.html.
www.computerworld.com/s/article/9206219/Google_Android_tablets_gain_traction_with_
developers?source=CTWNLE_nlt_dailyam_2011-01-25.

1.5. Android 3.0 (Honeycomb)

47

System ()

Action ()

, ,
(, )

Recent Apps ( ),
System, ,
.

Android
USB Bluetooth

Photo Transfer
Protocol (PTP, ) Media Transfer
Protocol (MTP, )

, Microsoft, , Android . ,

,

Bluetooth

Wi-Fi 3G
, Android-

Browser

Browser
() , - ( ,
).

, JavaScript . Google Google Chrome

Camera


.
, , ,
Android.

, .



48

Android

1.12 ()

Contacts
()

, ,
, .

Email
(E_mail)

Action ()
.
e-mail,
,

Gallery
()

, ,

1.13. Android 3,
(developer.android.com/sdk/android-3.0-highlights.html)

Android 3.x , Android


(Holographic UI)

Android 3

,
, ,

Activity fragments
( )

, .
Google API,
Android 1.6




Home

, , 3D ,
/ . ,
Home, .

Action Bar


Action,

:
,
,
.

1.5. Android 3.0 (Honeycomb)

49

, .
(, ,
) 3D.
API Khronos OpenSL ES API
.
Khronos EGL
.
Activity Lifecycle (
) API, .
API Native Asset Manager ( ) Storage Manager ( ).

- (builder)
,

DragEvent

Media/Picture
Transfer Protocol (MTP/ Android
PTP,
/)
Android 3.x - (
)
http Live
Streaming (HLS)

URL- ,
HTTP Live Streaming.


Renderscript 3D

3D- , , , ,
(GPU, Graphics Processing Unit)

OpenGL

50

Android

1.13 ()

Bluetooth A2DP HSP

API Bluetooth Advanced Audio


Distribution Profile (A2DP) Headset Profile (HSP) Bluetooth,

Digital Rights API,


Management (DRM,


)
- ,
, ,

1.6. Android Ice Cream Sandwich


Android Ice Cream Sandwich ( )
2012 . Android 2.3 (Gingerbread) Android 3.0
(Honeycomb) Android.
Android , Honeycomb
( , ,
), , . , Ice Cream Sandwich
y (. 1.14).
1.14. Android Ice Cream

0-click NFC Peer-to-Peer Android-


Sharing ( - (, )
NFC,
)
Head tracking (-
)
, .
,
,
(
)
Virtual camera operator
( . , )
,

1.7. Android Market

51

Android@Home framework Android, ( Android@ , Home)


( ), ,

1.7. Android Market


Google Android Market
, . . 1.15 Android. Android Market Android. Android Market
.
1.15. Android Android Market

Android Market

Comics ()

Marvel Superheroes, Dilbert Calendar, Jerry Seinfeld


Jokes

Communication ()

Google Voice, Skype mobile, Wi-Fi Locator, Easy

Entertainment ()

Face Melter, Fingerprint Scanner, Fandango Movies

Finance ()

Mint.com Personal Finance, PayPal, Debt Payoff Planner

Games: Arcade & Action (: NESoid, Droid Breakout, Raging Thunder 2 Lite,
)
Whac 'em!
Games: Brain & Puzzle (: Enjoy Sudoku, Spin Cube Lite, Ultimate Simpson Puzzle
)
Games: Cards & Casino (: Texas Hold'em Poker, Tarot Cards, Chessmaster
)
Games: Casual (: - City Mayor, LOL Libs, Paper Toss, SuperYatzy Free
)
Edition
Health ()

Fast Food Calorie Counter, CardioTrainer, StopSmoking

Lifestyle ( )

Zillow Real Estate, Epicurious Recipe App, Family


Locator

Multimedia ()

Pandora Radio, Shazam, Last.fm, iSyncr, Camera Illusion

News & Weather ( - The Weather Channel, CNN, NYTimes, FeedR News
)
Reader
Productivity ()

Adobe Reader, Documents To Go 2.0 Main App

Reference ()

Google Sky Map, Dictionary.com, Wikidroid for Wikipedia

Shopping ()

Gluten Free, Amazon.com, Barcode Scanner, Pkt Auctions


eBay


52

Android

1.15 ()

Android Market

Social ( )

Facebook, Twitter for Android, MySpace, Bump, AIM

Sports ()

NFL Mobile, Nascar Mobile, Google Scoreboard

Themes ()

Pixel Zombies Live Wallpaper, Aquarium Live Wallpaper

Tools ()

Compass, Droidlight LED Flashlight, AppAlarm Pro

Travel ()

Google Earth, Yelp, Urbanspoon, WHERE, XE Currency

Demo (-)

Screen Crack, Bubbles, CouponMap, SnowGlobe

Software libraries ( Translate Tool, Security Guarder, Car Locator Bluetooth


)
Plugin

- market.android.com (. 1.16).
, . ,
Android Market, , 70 %
. ,
, . ,
, .
2.10 2.
1.16. Android -

URL-

AppBrain

www.appbrain.com/

AndroidLib

www.androlib.com/

Android Tapp

www.androidtapp.com/

Appolicious

www.androidapps.com/

AndroidZoom

www.androidzoom.com/

doubleTwist

www.doubletwist.com/apps/

mplayit

mplayit.com/#homepage

1.8.
Android ,
. Android,
Java Google.
Android, . Java

1.8.

53

Java-. Android Android-


. . 1.17
, .
Android - developer.android.com/reference/packages.html.
1.17. Android, Java Google, , ,

android.app

Android
( Tip Calculator 4)

android.os

( Tip Calculator 4)

android.text

( Tip Calculator 4)

android.widget

,
( Tip Calculator 4)

android.net

( Favorite Twitter Searches 5)

android.view

, ( Favorite Twitter
Searches 5)

java.io

,
( Flag Quiz 6)

java.util

( Favorite Twitter Searches 5)

android.content.
res

, (, , ,
), , ( Flag Quiz Game 6)

android.graphics.
drawable

, (, ) ( Flag Quiz Game 6)

android.media

,
( Spotz Game 8)

android.util

XML ( Cannon Game 7)

android.content

( Doodlz
9)

android.hardware

( Doodlz
9 Enhanced Slideshow 13)

android.provider

Android ( Doodlz 9)

android.database

, ( Address Book 10)

android.database.
sqlite

SQLite ( Address Book 10)




54

Android

1.17 ()

android.graphics

,
( Route Tracker 11)

android.location

, ( Route Tracker 11)

com.google.
android. maps

Route Tracker, 11

android.appwidget

Weather Viewer, 14

java.net

(,
HTTP) ( Weather Viewer 14)

javax.xml.parsers

XML- ( Weather Viewer 14)

org.xml.sax

Simple API XML (SAX API),


XML- ( Weather Viewer 14)

1.9. Android Software Development Kit (SDK)


Android SDK , Android. - Android Developers (
) (. ,
, Android-). Java SE,
Eclipse, Android SDK 3.x ADT Eclipse.

Eclipse
Eclipse Android-. , Android
. Eclipse ,
Java, C++, C, Python, Perl, Ruby Rails . Android Java. Eclipse
:


( );

1.9. Android Software Development Kit (SDK)

55

Eclipse 1.11 Doodlz. 3, Welcome, Eclipse


.

Android Development Tools (ADT) Eclipse


ADT (Android Development Tools, Android-)
Eclipse Eclipse.
,
Android, (,
Android Market) . ADT
, . ,
, . ADT 3,
Welcome.

Android
Android, Android SDK, Android Windows, Mac OS X
Linux.
Android. AVD (Android
Virtual Device, Android ). Android, . , , , ,
.
Android devices, AVD, .
, Android.
Android (. 1.18) (. 1.19), . ,
,
Android. ,
GPS- , ,
GPS. ( /
),
( ).
, Android.
11, Route Tracker. 3 AVD
Welcome.

56

Android

1.18. Android (developer.android.com/guide/


developing/tools/emulator.html)

( Tip Calculator 4)

(
Cannon Game 7)

, ( Cannon Game 7)

, , ( Address Book 10)

, , ,
(
Address Book 10)

- Ctrl.

, . ,
, , (
Route Tracker 11)
1.19. Android (
developer.android.com/guide/developing/tools/emulator.html)

Android

Back ()

Esc

F3

Ctrl+5 , Ctrl+F3

F4

Home ()

Home

( )

F2 Page Up

F7

1.10.

Android

F5

* ( )

Shift+F2 PageDown

7 , Ctrl+F11

9 , Ctrl+F12

F8

+ , Ctrl+F5

- , Ctrl+F6

57

1.10.

, , . (
, ,
, . 3). , , ,
, .
(, , ) (, , ). ,
-
.
.
- ,
.


,
. , , . ,
, ?
, .
(),
. . , , ,
, , .

58

Android

, ,
.
,
, , .
,
. ,
, , , ,
.



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


, ,
.
. .


, , .
.
, , . ,

,
, .


, ( ). .

1.10.

59

,
. ,
, .


, , , , .
, , (
).
. . ,
, ,
.
, , , .
. , bankaccount ,
. bankaccount
,
.
.

(
). ,
,
.
.

.
,
. ,
.
, , .

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

60

Android

- ?
1000 ,
?
, , .
,
(
, ) ,
( , ).
(
-).
- ,
OOAD (object-oriented analysis and design, ). , Java,
-. -
.

1.11. Doodlz
AVD

Android. Doodlz
, .
9. Eclipse
AVD (Android Virtual Device, Android).
AVD ,
. ,
Android.
, , , Windows 7, Java SE 6, Eclipse 3.6.1, Android 2.2/2.3/3.0
ADT Eclipse.
1. . ,
, , .
2. Eclipse. Eclipse, , Eclipse, Eclipse. Eclipse
, Welcome ( ),
. 1.1. Workbench ( ),
, , , Java perspective in Eclipse.

1.11. Doodlz AVD

61

. 1.1. Welcome Eclipse

3. Import (). FileImport


(), Import (. 1.2).

. 1.2. Import

62

Android

4. Doodlz. Import
General (), Existing Projects into Workspace (
), Next> (>) Import Projects ( ), . 1.3.
Select root directory ( ),
Browse (). Browse For Folder ( ),
. 1.4, Doodlz,
, OK. Finish (),
Eclipse. Package Explorer ( ), . 1.5.
Eclipse.

. 1.3. Import Projects, Import

5. Doodlz. Eclipse Doodlz Package


Explorer (. . 1.5), Run AsAndroid Application (  Android), Run As
(. 1.6).
Doodlz NexusS Android
Virtual Device (AVD) (. 1.7), .
AVD-, WindowAndroid SDK and AVD
Manager ( SDK AVD), AVD
Start ().

1.11. Doodlz AVD

63

AVD, Android Device


Chooser ( Android), AVD, . Android Device
Chooser .

. 1.4. Browser For Folder

. 1.5. Package Explorer Eclipse

. 1.6. Doodlz

6. AVD. AVD . (. 1.8) ,


Android,
, .
. . AVD
Android, AVD. Android

64

Android

. 1.7. Doodlz Android


. AVD .
7. . ,
(Menu). ,
. (. 1.9).
: Color (), Line Width ( ), Erase
(), Clear () Save Image ( ).
Color, , . Line Width,
. Erase
( ). Clear .
Save Image, .
.

1.11. Doodlz AVD

65

. 1.8. AVD, Doodlz

. 1.9. Doodlz

8.

. ,
Color (. 1.10, ). RGBA,
( 0 255) :

66

Android

, , . SeekBar
Red (), Green (), Blue () Alpha ().
,
, .
, SeekBar.
. , SeekBar Red
(. 1.10, ). Done (),
. ( )
(. 1.10, ).

. 1.10. :
;

9. -. ,
Color. -
Green SeekBar , Red Blue SeekBars (. 1.11, ).
10. . ,
Menu, Line Width. SeekBar, , (. 1.11, ).
Done, . .
9 10,
, (. 1.12).

1.11. Doodlz AVD

. 1.11. :
;

. 1.12.

67

68

Android

11. . 910,
(. 1.13, ) (. 1.13, ). (. 1.14).

. 1.13. :
;

12. . Gallery (). Menu (),


Save Image ( ). , ,
Gallery.
13. . AVD,
Home (), AVD.

Doodlz Android
Android, .
1. -, .
Settings (), ,
ApplicationsDevelopment () , USB debugging ( USB) .

1.11. Doodlz AVD

69

. 1.14.

2. USB (
).
3. Eclipse Package Explorer, Doodlz, Run AsAndroid Application,
Run As (. 1.6).
AVD, Android, Eclipse
. AVD / Android, Android Device
Chooser (. . 1.15), AVD
, . AVD
,
. AVD, Android
Android.
Android (. 1.15)
, (
), AVD. AVD
AVD, (NexusS MotorolaXoom, ). AVD,
OK

70

Android

. 1.15. Android Device Chooser

AVD. AVD,
,
AVD, Android Device
Chooser.
, Android
Market, . ,
. ,
AVD, ,
.
,
AVD. config.ini
AVD, Setting hardware emulation options
developer.android.com/guide/developing/tools/avd.html.
config.ini , ADT Eclipse.
Android
.

1.12. Deitel
- (www.deitel.com) 100
(Resource Center), , , , Web 2.0, .

1.13. Android-

71

, ,
.
, , , , , , , , , , .
, .
Deitel Buzz
Online, . . 1.20 Deitel,
Android.
1.20. Deitel, Android-

URL-

Android for Programmers: An www.deitel.com/books/AndroidFP/


App-Driven Approach
Android Resource Center

www.deitel.com/android/

Android Best Practices Resource Center

www.deitel.com/androidbestpractices/

Java Resource Center

www.deitel.com/java/

Eclipse Resource Center

www.deitel.com/Eclipse/

SQLite 3 Resource Center

www.deitel.com/SQLite3/

Deitel Resource
Centers

www.deitel.com/ResourceCenters.html

Deitel

www.deitel.com/DeitelFan/

Deitel

@deitel

Deitel Buzz Online, www.deitel.com/newsletter/subscribe.html


1.13. Android-
. 1.21 , Android-.
. 1.22 Android-, -
developer.android.com. ,
Android - www.deitel.com/android.
1.21. Android

Android

URL-

YouTube Android Developers

www.youtube.com/user/androiddevelopers

Android- Google

code.google.com/p/apps-for-android/

OReilly Ten Tips for Android Appli- answers.oreilly.com/topic/862-ten-tipsforandroid-application-development/


cation Development


72

Android

1.21 ()

Android

URL-

- Bright Hub, - www.brighthub.com/mobile/googleandroid.


aspx
Android
10 User Experience Tips for Succes- www.androidtapp.com/10-user-experiencetipsfor-successful-android-apps/
sful Android Apps
Android- www.droidnova.com/

Working with XML on Android: www.ibm.com/developerworks/opensource/


Build Java applications for mobile devices library/x-android/index.html
(Michael Galpin), eBay
Android-

android-developers.blogspot.com/

Sprint Application Developers developer.sprint.com/site/global/develop/


mobile_platforms/android/android.jsp
Program
- Android- T-Mobile

developer website developer.t-mobile.com/


site/global/resources/partner_hubs/android/p_
android.jsp

Android development developer.htc.com/


Windows Mobile HTC
- Android-
Motorola

developer.motorola.com/

1.22. Android-

URL-

Androidology, Part 1 of 3: Architecture


Overview

developer.android.com/videos/index.html#v=
QBGfUs9mQYY

Androidology, Part 2 of 3: Application


Lifecycle

developer.android.com/videos/index.html#v=
fL6gSd4ugSI

Androidology, Part 3 of 3: APIs

developer.android.com/videos/index.html#v=
MPukbH6D-lY

Android Developer Soapbox: Easy for Java


Developers, Build Desktop Widgets

developer.android.com/videos/index.html#v=
FTAxE6SIWeI

A Beginners Guide to Android

developer.android.com/videos/index.html#v=
yqCj83leYRE

The World of List View

developer.android.com/videos/index.html#v=
wDBM6wVEO70

1.14.

URL-

Android UI Design Patterns

developer.android.com/videos/index.html#v=
M1ZBjlCRfz0

Writing Zippy Android Apps

developer.android.com/videos/index.html#v=
c4znvD-7VDA

Casting a Wide Net for All Android Devices

developer.android.com/videos/index.html#v=
zNmohaZYvPw

Building Push Applications for Android

developer.android.com/videos/index.html#v=
PLM4LajwDVc

73

1.14.
Android .
Android 2.2, 2.3 3.0. , ,
. Android Market
. Android, Android
Android. Java, Android Google,
Android-. .
Java Android SDK.
, ,
, . Doodlz
Android.
2 - Android-.
, Android
Market. .

Google Play
-,














Android.
.
Google Play.
Google Play.
.
.
Google Play .
.
Android.
-, .
Android-.

2.1.
Android-.
( Android) .
( Google Play ).

. Google Play Google Checkout,
.
Google Play.

2.2. Android-

75


. , .
, , ,
Android.
Android- .

2.2. Android-
Google Play 200 000 1
, , ,
.
, ? ,
Google Play Android
. , ,
? 2.1, ,
.
2.1. ,

Android (developer.android.com/sdk/1.5_
r3/upgrading.html#FutureProofYourApps).
, .
.
, Android-.
(,
, ).
.
, .
, , .
( ).
(www.google.com/
accessibility/).

(, ).
1

googleblog.blogspot.com/2011/05/android-momentum-mobile-and-more-at.html.

76

Google Play -,

, ( , ).
.
.

.
.
.
.
.
, .
.
.

.
(, , ).
.
( ,
, , .).
- (,
-, , ).
.

2.3.
Android-
Best Practices Android Developers Guide (
Dev Guide) , , ,
,
.
, - (. 2.1).
2.1. -

URL-

Compatibility ()

developer.android.com/guide/practices/
compatibility.html

Supporting Multiple Screens (


)

developer.android.com/guide/practices/
screens_support.html

2.3. Android-

77

URL-

User Interface Guidelines ( - developer.android.com/guide/practices/


ui_guide lines/index.html
)
Designing for Performance ( developer.android.com/guide/practices/design/
performance.html
)
Designing for Responsiveness (- developer.android.com/guide/practices/design/
responsiveness.html
)
Designing for Seamlessness ( developer.android.com/guide/practices/design/
- seamlessness.html
)

2.3.1.
Android
, . <uses-feature>,
(. 2.2.). Google Play , ,
.
2.2.
, (developer.android.com/guide/
topics/manifest/uses-featureelement.html)

android.hardware.audio.low_latency

Bluetooth

android.hardware.bluetooth

android.hardware.camera

android.hardware.camera.autofocus

flash android.hardware.camera.flash

android.hardware.camera.front

android.hardware.location.network

GPS

android.hardware.location.gps

android.hardware.microphone

android.hardware.nfc

android.hardware.sensor.accelerometer

android.hardware.sensor.barometer

android.hardware.sensor.compass


78

Google Play -,

2.2 ()

android.hardware.sensor.gyroscope

android.hardware.sensor.light

android.hardware.sensor.proximity

android.hardware.telephony

CDMA-

android.hardware.telephony.cdma

GSM-

android.hardware.telephony.gsm

android.hardware.faketouch

android.hardware.touchscreen

( android.hardware.touchscreen.multitouch
)
(- android.hardware.touchscreen.multitouch.
distinct
,
)
- android.hardware.touchscreen.multitouch.
( - jazzhand
)
Wi-Fi

android.hardware.wifi

android.software.live_wallpaper

SIP

android.software.sip

SIP/VoIP

android.software.sip.voip


. , Verizon ,
.
Google Play, .

. , , , ,
.

Android, - developer.android.com/guide/practices/
compatibility.html. Market, , - developer.
android.com/guide/appendix/market-filters.html.

2.3. Android-

79

2.3.2.
Android SDK 1.6 ( ) (
). ,
, (
).
Android
(, , ) (, ,
). , .
, Android .
(, , ) . , Android
,
, (
). <supportsscreens> AndroidManifest.xml ,
, .
Supporting Multiple Screens, - developer.
android.com/guide/practices/screens_support.html.

2.3.3. Android
Android
, , , , Android User Interface Guidelines, - developer.android.
com/guide/practices/ui_guidelines/index.html.


Icon Design Guidelines ( )
, (
, , , , ),
(, , ,
).
Android Icon Templates Pack, Adobe Photoshop Adobe Illustrator.


Widget Design Guidelines ( )
,
,
, , (. 2.3). (. 14, Weather Viewer
App), ,

80

Google Play -,

. , ScoreCenter,
ESPN, ,
.
, , .
.
2.3. Android

ESPN ScoreCenter

Pandora Radio

-
Pandora (,
)

WeatherBug Elite

Twidroyd PRO

Shazam Encore

Weather & Toggle Widget

, .
Android
(, , / Wi-Fi
)

BatteryLife

System Info

, , (,
SD)

Stock Alert

, ,

The Coupons App

, ,

Favorite Quotes

ecoTips

- Wildlife Fund

Difficult Logic Riddles Pro

(
)

App Protector Pro

,
(, SMS, Market .)

Android Agenda Widget

,
, Google Calendar

2.3. Android-

81


Activity and Task Design Guidelines ( ) :


. ,
. , , . .
4.
 . ,
Back ().
 . ,
.


Menu Design Guidelines ( )
Options () Context (). Options
Menu () .
, . , Options Messaging
, Compose (), Delete Threads ( ),
Search () Settings (). Context
Messaging ( , , )
, , ,
Select all ( ), Select text ( ), Cut all ( ), Copy all
( ), Paste () Input method ( ).
2.2 2.3 ,
, , ,
.
.
2.2.
:
Best Practices Dev Guide ( User Interface
Guidelines).
, .
.
, .
(, , , ).

82

Google Play -,

Android (. . 1.5).
,
, .
(. developer.motorola.
com/docstools/library/Best_Practices_for_User_Interfaces/).
:
.
,
.
.
,
( ), .
, Android.
, ,
Android.

.
,
(SP); , (DIP
DP), (. stackoverow.com/
questions/2025282/difference-of-pxdp-dip-and-sp-in-android).
( ) ,
.
, (. developer.android.com/guide/practices/screens_
support.html), .
2.3. ,

(developer.android.com/guide/practices/design/
performance.html):
, Android
, .
( ) UI, .
-.
(, ).

2.3. Android-

83

,
(, , SMS MMS).
,
.
, SD (
).
(developer.android.com/guide/practices/design/
responsiveness.html):
, .
,
, , ,
. .
(
, - ). .
(developer.
android.com/guide/practices/design/seamlessness.html):
, /
.

.
ContentProvider , .
NoticationManager.
Activity UI .

, ,
.


Android ,
, (, ). (Text-toSpeech, TTS), , , ,
, .
( ) ( ).

84

Google Play -,


, . , , (
) . ,
, , . .
, Android ,
.
, , -
developer.android.com/guide/topics/resources/localization.html.

2.4. Google Play


Google Play,
- play.google.com/apps/publish/signup.
. , Google Play
.
, Android Market Content Policy for
Developers. , .
( 2.4).
2.4.
ANDROID MARKET CONTENT POLICY FOR DEVELOPERS
( , ).
.

, 18 .
, .
.
.
.
.
, .
.
.
.

2.5. Google Checkout Merchant

85

2.5. Google Checkout Merchant


Google Play,
Google Checkout Merchant.
Google Play, 29
(. 2.4)1. Google
Play market.android.com/publish/ Setup Merchant Account
( ). :


, Google ;

,
;

, Google ;

(Terms of Service), , , ,
, .

2.4. , Google Checkout


Merchant

Google Checkout
. , 2, 30 %
Google Play. Google Checkout
.
PayPal, Google Checkout
.
Google Play , PayPal.
1

checkout.google.com/support/sell/bin/answer.py?answer=150324&cbid=-eqo3objy740w&src=
cb&lev=%20index.
2
checkout.google.com/termsOfService?type=SELLER.

86

Google Play -,

2.6. AndroidManifest.xml
AndroidManifest.xml, , , Android Google Play.
, Google Play , . ,
, (
).
ADT Plugin Eclipse, Google Play
. ,
XML-, Android
Manifest Editor. ADT Plugin Eclipse.
Android Manifest Editor Eclipse, Packages Explorer ( )
AndroidManifest.xml, .
Eclipse. Manifest (),
, Manifest General Attributes (
). , , . . 2.5
, .
- developer.android.com/guide/topics/manifest/manifest-intro.htm.l
, Manifest General Attributes (.
2.8).
2.5.

Uses Feature ( )

, . . 2.3.1,

Protected
Broadcast ( )

,
,

Supports Screens
( )

(Small (), Normal


(), Large (), XLarge (), Resizeable ( )) (
), .
true false

Uses Configuration
( )

. :
Touch screen ( ), Keyboard type ( ), Hard
keyboard ( ), Navigation () (, ) Five way nav key (

2.7.

87

) ( ,
, , ,
)

Uses SDK ( SDK)

SDK
(, Android 2.3, 3.0 ). ,
, Android
SDK, ,
SDK. API,

Application (), , , ,
, , . Permissions () ,
(, , SMS-, ).
Google Play ,
. ,
. - developer.android.
com/reference/android/Manifest.permission.html.
2.7.

2.7.
Preparing to Publish: A Checklist ( : ) Dev Guide, - developer.android.com/guide/
publishing/preparing.html,
Google Play, :


Android;

() (End User License Agreement);

(, 1.0, 1.1, 2.0, 2.3, 3.0);

, ;

.
.

88

Google Play -,


Google Play,
, . , ,
,
Android. 2.5
Android, .
2.5. ANDROID,

(
).
USB-.
.
, .
.
, .
/ SD-.
Bluetooth.
.
(, , , , ).
OpenGL ES 2.0 (and non-software rendered OpenGL).

Android , Settings ApplicationsDevelopment ( ) USB (Universal Serial Bus) Debugging


( USB).


(End User
License Agreement, EULA). .
,
, ,
. EULA
. EULA, -
www.developer-resource.com/sample-eula.htm.


(),
Google Play . ,

2.7.

89

.
, :


: 7272 ;

: 4848 ;

: 3636 .

Google Play 1. :


512512 ;

32- PNG ;

1024 .

, Icon Design Guidelines ( ),


- developer.android.com/guide/practices/ui_guidelines/
icon_design.html. ,
(. 2.6).

65 400 ( ).
. Android Manifest Editor Application,
.
2.6. ,

URL-

glyFX

www.glyfx.com/index.html

Androidicons

www.androidicons.com/

Iconiza

www.iconiza.com/portfolio/
appicon.html

Aha-Soft

www.aha-soft.com/icon-design.
htm

Elance

www.elance.com

market.android.com/support/bin/answer.py?answer=1078870.

90

Google Play -,


. Application Android Manifest Editor Debuggable false.
( ).


( )
( , Google Play),
. , 1.0,
1.1 1.2, 2.0.
Versioning Your Applications ( ),
- developer.android.com/guide/publishing/versioning.html.

,
Google Play ,
. ,
,
, ,
,
.
, - developer.android.com/guide/publishing/licensing.html.
,
Google Play,
. ProGuard,
,
.apk, . ProGuard,
- developer.android.com/guide/developing/tools/proguard.html.

, , , - android-developers.blogspot.
com/2010/09/securing-android-lvlapplications.html.

,

, Google Play
, .apk ( Android). ,
.
, .
( , ).
(

2.7.

91

). Eclipse
,
. Google
Play. Java Development Kit ( JDK) ,
. Keytool ,
Jarsigner .apk .
Eclipse , ADT Plugin, Keytool
.apk ( ).
zipalign, .
Eclipse ADT Plugin, Export Wizard ,
.apk .
1. Package Explorer FileExport (
).
2. Android, ,
Export Android Application ( Android) Next
().
3. (, )
Next.
4. Create new keystore ( ).
Location (),
(, c:\android\keystore).
(Password), (Confirm) , Next,
Key Creation ( ).
5. Alias () (, releasekey).
, . Password ,
Confirm (). Validity ()
, .
Google Play, 22
2033 . Google ,
25 (, ).
. , . , (First Name) (Last
Name), (Organizational Unit), (Organization), (City)
(Locality), (State) (Province) (Country Code), US. Next.
-
developer.android.com/guide/publishing/app-signing.html.

92

Google Play -,



, Google Play (. 2.7). .
, .
,
, .
Google Play URL-
.
2.7.

320480 ( ) 480854 ( ) (
)

24- PNG JPEG

, ,
Dalvik Debug Monitor Service (DDMS),
ADT Plugin Eclipse. DDMS
, .
:
1. (. 1.11).
2. Eclipse WindowOpen PerspectiveDDMS (
DDMS). DDMS.
3. Devices (. 2.1) ,
.

. 2.1. Devices DDMS

4. Screen Capture Device Screen Capture


( ), . 2.2.

2.8. Google Play

93

. 2.2. Device Screen Capture


Tip Calculator ( 4)

5. Save () .
,
,
Refresh (), Device Screen Capture,
.

2.8. Google Play


,
, developer.android.com/guide/publishing/publishing.html.
Google Play, market.android.com/publish (. 2.4),
Upload Application ( ), . .


1. App .apk file ( .apk ). Choose File (
), Android (.apk) file,
( .dex), ,
. Upload ().

94

Google Play -,

2. Screenshots ( ). Choose File , Google


Play. Upload.
3. High-resolution app icon ( ).
Choose File 512512
, Google Play
Upload.
4. Promotional graphic (-) (). - Google Play, Google
(, Google Play). 180120 ( ) , 24-
PNG JPEG .

. Choose File,
Upload.
5. Feature Graphic ( ) (). ,
Featured ( ) Google Play.
1024500 ( ) , 24- PNG
JPEG 1. ,
Choose File, Upload.
6. Promotional video (-) (). URL-
- (,
YouTube, ).
7. Marketing opt-out ( ). Marketing opt-out ( ), , Google
Google Play Google.


1. Language (). .
, add language ( )
(. 2.8), OK. Language ()
Listing Details ( ). ,
, -.
2.8. , Google Play

market.android.com/support/bin/answer.py?hl=en&answer=1078870.

2.8. Google Play

95

2. Title (). , Google Play (


30 ).
Android.
3. Description (). ( 4000 ).
, .
4. Recent changes ( ). ( 500 ).
5. Promo text (-). , ( 80 ).
6. App type ( ). Applications ()
Games ().
7. Category (). (. . 1.15),
.
8. Price (). Free (). , Setup a Merchant Account at Google
Checkout ( Google Checkout).


1. Content rating ( ). : Mature
(), Teen (), Pre-teen ( ) All ().
Google Play
Developer Program Policies Content Rating Guidelines - market.android.com/
support/bin/answer.py?answer=188189.
2. Locations (). All Locations
( ). ,
Google Play ( ).
Google Play, , All Locations,
. .


1. Website (-). Google Play URL- -.
, , ,
: , ,
, .
2. E-mail ( ). Google Play
, , .
3. Phone number ( ). Google Play
,

96

Google Play -,

, .
.


1. Android Content Guidelines, www.android.com/market/terms/developer-content-policy.html (. 2.4),
This application meets Android Content Guidelines ( Android Content Guidelines).
2. ,
United States ( , ). ,

. .
Learn More ( ).
, Publish
(). Save (), .

2.9. Android
Google Play
(. 2.9) -
, AndroidLicenser (www.androidlicenser.com).
Google Play (Terms of Service) , Google Play,
.
2.9. Android

URL-

Amazon Appstore

developer.amazon.com/welcome.html

AndAppStore

www.andappstore.com

Androidguys

store.androidguys.com/home.asp

Andspot Market

www.andspot.com

GetJar

www.getjar.com

Handango

www.handango.com

Mplayit

www.mplayit.com

PocketGear

www.pocketgear.com

Shop4Apps

developer.motorola.com/shop4apps/

2.10.

URL-

SlideMe

www.slideme.org

Youpark

www.youpark.com

Zeewe

www.zeewe.com

97

2.10.
,
Google Play. ,

. , . 2.6
.
2.6.
Google Play
Android.
.
(. 2.12).
,
(. 2.14).
.
, .


,
Distimo (www.distimo.com/),
Android $3,6261 ( $2,7272).
, ,
, !
AdMob (www.admob.com/), Android 3. ,
. ?
?
? , ?
?
1

gizmodo.com/5479298/android-app-store-is-57-free-compared-to-apples-25.
android-apps.com/tag/median-price/.
3
metrics.admob.com/2010/06/may-2010-mobile-metrics-report/.
2

98

Google Play -,

Google Play Google Checkout (checkout.google.com). , (, AT&T,


Sprint T-Mobile),
,
. Google 30 % , , 70 % . Google Checkout
1.
, . ,
Google Play.


, Android,
iPhone2. 57 %
Google Play , 3.
, ,
. ,
, ,
.
,
, Google Play.
( ).
, AdMob, ,
4.

, (. 2.10).
2.10. Android,

Amazon

Amazon

checkout.google.com/support/sell/bin/answer.py?hl=en&answer=25400.

techcrunch.com/2011/04/27/there-are-now-more-free-apps-for-android-than-for-theiosplatform-distimo/?utm_source=feedburner&utm_medium=email&utm_campaign=Feed%3A+
Techcrunch+%28TechCrunch%29.

gizmodo.com/5479298/android-app-store-is-57-free-compared-to-apples-25.
metrics.admob.com/wp-content/uploads/2009/08/AdMob-Mobile-Metrics-July-09.pdf.

2.11.

99

Bank of America

Best Buy

Best Buy

Epicurious Recipe

, ,
Conde Nast, Gourmet
Bon Appetit

ESPN ScoreCenter

,

( )

Mens Health Workouts

NFL Mobile

NFL, , NFL

UPS Mobile

,
,

NYTimes

New York Times (


)

Pocket Agent

State Farm Insurance,


, , , State
Farm ,

ING Direct ATM Finder

GPS

Progressive Insurance

, ,

USA Today

USA Today

Wells Fargo


, ,

2.11.

, .
, -. ,

100

Google Play -,

AdMob (www.admob.com/) Google AdSense for Mobile (www.google.com/mobileads/


publisher_home.html), (. 2.15).
.
,
. ,
, ,
, . , Pinch Media, 20 % ,
iPhone, ,
5 % 1. Android-, ,
, . , .

2.12. :
In-app Billing
Google Play In-app Billing (,
) ( )
, Android 2.3
(. 2.11). Google, ,
, ,
. , Google
Play, 2. In-app Billing Service
, Google Play. , .
, ,
Google Play (. 2.4), Google Checkout
(. 2.5). Google 5 % ,
30 %
.
(
), 3.
2010 1,6 ( 10
4). 2011
1

www.techcrunch.com/2009/02/19/pinch-media-data-shows-the-average-shelf-life-ofan-iphoneapp-is-less-than-30-days/.
2
www.youtube.com/watch?v=GxU8N21wfrM.
3
www.virtualgoodsnews.com/2009/04/super-rewards-brings-virtual-currency-platformto-socialweb.html.
4
www.internetretailer.com/2010/05/28/consumers-are-buying-digital-goods-new-ways.

2.12. :

101

2,1 1. -,
Second Life, World of Warcraft, Farmville Stardoll.
. , Frank N. Magid Associates,
70 , , 16 % $41
, 2.
2.11.

, ,
:
1. com.android.vending.BILLING.
, 2.8.
2. Google Play, ,
- market.android.com/publish.
3. All Google Play Listings ( Google Play).
.
In-app Products ( ). , .
4. Add in-app product ( ).
Create New In-app Product,
.
5. In-app product ID ( , ).
( 100 ), .
, ,
(_) (.).
6. Purchase type ( ). Managed per user
account ( ),
.
1

www.bloomberg.com/news/2010-09-28/u-s-virtual-goods-sales-to-top-2-billion-in-2011-reportsays.html.
2
www.webwire.com/ViewPressRel.asp?aId=118878.

102

Google Play -,

Unmanaged ( ), .
7. Publishing state ( ).
, publishing state Published ().
8. Language (). ,
.
9. Title (). ( 25 ), .
10. Description (). ( 80 ) , .
11. Price (). , .
12. Publish (),
, Save (),
.
, ,
, , - developer.android.com/guide/market/billing/index.html.



(. 2.9), .
, API (. 2.12).
(,
). - , , ,
, .
.
Boku, , 25 45 % 1.
2.12. ,

URL-

PayPal
Mobile
Payments
Library

www.x.com/
community/ppx/
xspaces/mobile/mep

Pay with PayPal


( PayPal),
PayPal,
Pay ()

www.boku.com/help/faq/publisher/.

2.14. , Google Play

103

URL-

Zong

www.zong.com/
android

Buy () ,

. ,

Boku

www.boku.com

Pay by Mobile (
) , ,
,

2.13. Market

, Market (Google Play)
( , ).
, ,
.
Market.
Market. -, Google Play
, , . ,
, Google Play, ,
. , Market, ,
. Market
Market.
, Market , Publishing Your Applications: Using
Intents to Launch the Market Application on a Device, -
developer.android.com/guide/publishing/publishing.html#marketintent.

2.14. ,
Google Play
Google Play Developer Console
, ( 0 5 ),
( ).

104

Google Play -,

Android, .
Android (Android Application Error Reports) , .
,
. Market,
,
. , , ( Google ,
, Terms of Service).

2.15.
1.
( )
, Facebook, Twitter YouTube.
. comScore, YouTube 10 % , Facebook 17 %2.
. 2.13 .
.
2.13. -

URL-

Facebook

www.facebook.com

Twitter

www.twitter.com

Groupon

www.groupon.com

Foursquare

www.foursquare.com

Gowalla

www.gowalla.com

YouTube

www.youtube.com

LinkedIn

www.linkedin.com

Flickr

www.flickr.com

Digg

www.digg.com

StumbleUpon

www.stumbleupon.com

Delicious

www.delicious.com

Android-, Android Apps Marketing: Secrets to Selling Your Android App,


( Jeffrey Hughes).
2
tech.fortune.cnn.com/2010/07/29/google-the-search-party-is-over/.
1

2.15.

105

URL-

Bebo

www.bebo.com

Tipd

www.tipd.com

Blogger

www.blogger.com

Wordpress

www.wordpress.com

Squidoo

www.squidoo.com

, , 600
( 200 2009 1).
130 2,
5 % ! ( ).
.
:


Google Play, .

, , .
.
, ,
.
1
2

topics.nytimes.com/top/news/business/companies/facebook_inc/index.html.
techcrunch.com/2010/07/15/facebook-500-million-users/?utm_source=feedburner&utm_
medium=email&utm_campaign=Feed:+Techcrunch+(TechCrunch).

106

Google Play -,

,
190 1. 140 .
( - 8,5 ). . , (
, , ,
). ,
. - (#) . , ,
, Twitter, @deitel - #AndroidFP.
-, . , Android
for Programmers.


,
(, YouTube, Dailymotion, Bing Videos, Yahoo! Video),
(, , , MySpace),
. , ,
.

,
,
. Google Play, .
(, ).
,
.


,
(. 2.14), . - (. 2.10). ,
,
. YouTube (. 2.15).
1

techcrunch.com/2010/06/08/twitter-190-million-users/.

2.15.

107

2.14. -, Android-

URL-

Android Tapp

www.androidtapp.com/

Appolicious

www.androidapps.com

AppBrain

www.appbrain.com

Best Android Apps Review

www.bestandroidappsreview.com

AppStoreHQ

android.appstorehq.com

Android App Review Source

www.androidappreviewsource.com

Androinica

www.androinica.com

AndroidZoom

www.androidzoom.com

AndroidLib

www.androlib.com

Android and Me

www.androidandme.com

AndroidGuys

www.androidguys.com/category/reviews/

Android Police

www.androidpolice.com/

Phandroid

www.phandroid.com

2.15. ,
Android-

URL-

ADW Launcher

www.youtube.com/watch?v=u5gRgpuQE_k

Daily App Show

dailyappshow.com

Timeriffic

androidandme.com/2010/03/news/android-app-video-rseviewtimeriffic/

Frackulous

frackulous.com/141-glympse-android-app-review/

Moto X Mayhem

www.appvee.com/games/articles/6968-android-appvideoreview-moto-x-mayhem

-
,
. , Web 2.0, , , , , RSS-
PR-. . 2.16
-, ,
-, - . , Internet Public Relations Resource Center - www.deitel.com/InternetPR/.

108

Google Play -,

2.16. , -

URL-


ClickPress

www.clickpress.com

( ).

ClickPress,

PRLog

www.prlog.org/
pub/

i-Newswire

www.i-newswire.
com

openPR

www.openpr.com


PR Leap

www.prleap.com

Marketwire

www.marketwire.
com

-,
: , . .

InternetNews-Bu
reau.com

www.internetnews
bureau.com

- ,

Mobility PR

www.mobilitypr.
com

, ,

Press Release Writing

www.press-release
writing.com

-, ,
, -.
-


(, , ,
) . (. 2.17) , Android ( ).
( )
Android. ,
, .

2.15.

109

. , , (,
Android, iPhone, BlackBerry ) .
2.17.

URL-

AdMob

www.admob.com/

Google
AdSense
for Mobile

www.google.com/
mobileads/

Google (
) -.

YouTube

AdWhirl
(by AdMob)

www.adwhirl.com

, , (, )

Medialets

www.medialets.
com

SDK . SDK

Nexage

www.nexage.com

SDK ,
,

Smaato

www.smaato.net

Smaatos SOMA
(Smaato Open Mobile Advertising)
50

Decktrade

www.decktrade.
com

Flurry

www.flurry.com/

, ,
Android

,
eCPM (effective cost per 1000 impressions,
1000 ) Android 0,09 4 ,

110

Google Play -,

1.
Android (CTR, clickthrough rate),
. CTR
, , . CTR
1 2 % (
).

2.16.
,
Android- , iPhone BlackBerry
(. 2.18). AdMob, 70 % iPhone-
Android , 48 % Android iPhone2.
, iPhone Macintosh,
. ObjectiveC, . Android
Windows, Linux Macintosh
Java,
. BlackBerry Playbook
Android ( BlackBerry
App World store).
2.18. ( Android)

URL-

BlackBerry (RIM)

na.blackberry.com/eng/services/appworld/?

iOS (Apple)

developer.apple.com/iphone/

webOS (Palm)

developer.palm.com

Windows Phone 7

developer.windowsphone.com

Symbian

developer.symbian.org

1
2

Facebook

developers.facebook.com

Twitter

apiwiki.twitter.com

Foursquare

developer.foursquare.com

seoamit.wordpress.com/2010/02/06/monetizing-mobile-apps-android-and-iphone/.
metrics.admob.com/wp-content/uploads/2010/03/AdMob-Mobile-Metrics-Mar-10-PublisherSurvey.pdf.

2.17. Android-

URL-

Gowalla

gowalla.com/api/docs

Google

code.google.com

Yahoo!

developer.yahoo.com

Bing

www.bing.com/developers

Chrome

code.google.com/chromium/

LinkedIn

developer.linkedin.com/index.jspa

111

2.17. Android-
. 2.19 ,
Android-. -
developer.android.com/.
2.19. , Android-

URL-

Application Fundamentals

developer.android.com/guide/topics/fundamentals.html

Manifest.permission Summary

developer.android.com/reference/android/Manifest.permission.html

AndroidManifest.xml File
<usesfeature> Element

developer.android.com/guide/topics/manifest/uses-featureelement.html

Android Compatibility

developer.android.com/guide/practices/compatibility.html

Supporting Multiple
Screens

developer.android.com/guide/practices/screens_support.html

Designing for Performance

developer.android.com/guide/practices/design/performance.html

Designing for Responsiveness

developer.android.com/guide/practices/design/responsiveness.html

Designing for Seamlessness

developer.android.com/guide/practices/design/seamlessness.html

Android User Interface


Guidelines

developer.android.com/guide/practices/ui_guidelines/index.html

Icon Design Guidelines

developer.android.com/guide/practices/ui_guidelines/icon_design.
html

Android Market Content


Policy for Developers

www.android.com/market/terms/developer-content-policy.html

In-app Billing

developer.android.com/guide/market/billing/index.html

Android Emulator

developer.android.com/guide/developing/tools/emulator.html


112

Google Play -,

2.19 ()

URL-

Versioning Your Applica- developer.android.com/guide/publishing/versioning.html


tions
Preparing to Publish: developer.android.com/guide/publishing/preparing.html
A Checklist
Market Filters

developer.android.com/guide/appendix/market-filters.html

Localization

developer.android.com/guide/topics/resources/localization.html

Technical Articles

developer.android.com/resources/articles/index.html

Sample Apps

developer.android.com/resources/samples/index.html

Android FAQs

developer.android.com/resources/faq/index.html

Common Tasks and How


to Do Them in Android

developer.android.com/resources/faq/commontasks.html

Using Text-to-Speech

developer.android.com/resources/articles/tts.html

Speech Input

developer.android.com/resources/articles/speech-input.html

2.18. Android
. 2.20 -, , Android.
2.20. Android-

crenk.com/android-vs-iphonehumor/

,
Android iPhone

www.collegehumor.com/
video:1925037

CollegeHumor,
Android

www.youtube.com/watch?v=
MAHwDx0lI-M

www.youtube.com/watch?v=MAHwDx0lI-M , Samsung Behold II Man Adventures Part 1.

www.theonion.com/video/newgooglephone-service-whisperstargetedads-dir, 17470/

Onion, New Google Phone Service


Whispers Targeted Ads Directly in Users Ears.

www.collegehumor.com/
article:1762453

A Few Problems with the New Google Phone


CollegeHumor, Did-YouMean Google Search.

2.19.
Google Play
Google Checkout, .

2.19.

113

Google Play, Android, . Android User Interface


Guidelines AndroidManifest.xmle.
Google Play. , .
. ,

.
, Google Play.
3
Eclipse Android- .
Eclipse. 4 Android-
Java.

Welcome

Eclipse
ADT Plugin



Eclipse, , Android-.

Eclipse, .

( ) ADT Visual Layout Editor.

Android Android Virtual Device (AVD)

3.1.
Welcome.
,
. Eclipse ADT ,
Android-.
Eclipse ,
Android (. 3.1) ADT.
. , Android
Virtual Device (AVD).

3.2.
Eclipse ADT Plugin. Eclipse

3.3. Eclipse

115

. 3.1. Welcome

. ADT Visual Layout Editor,


ImageViews
TextView. GUI
( Text TextView Src ImageView),
Eclipse Properties (),
Android Virtual Device (AVD).

3.3. Eclipse
( ) Android SDK ( 2.3.3 3.0),
Eclipse IDE ADT (Android Development
Tools) Plugin. , Java SE
Development Kit ( JDK), Android SDK Eclipse.
.

Eclipse
Eclipse , , , . ADT Plugin
Eclipse ,
Android-.
ADT Plugin Android.
, ,
Android.
Eclipse Welcome ( ),
. 3.2. -, . 3.1. Workbench ( ),

116

Welcome

Java, Android-.
Eclipse . Eclipse
. .

, .

. 3.2. Welcome to the Eclipse IDE for Java Developers Eclipse


3.1. , Welcome Eclipse

Overview ()

Whats New ( )


Eclipse, Eclipse

(DE)

Samples ()


Eclipse

Tutorials ()

,
Java- Eclipse Eclipse

Workbench ( )

3.4.

117

3.4.
Android Eclipse,
FileNewProject ()
New Project ( ). Android, Android Project
( Android) Next> (>). New Android Project ( Android), . 3.3.
New ()
. FileNew
New Android Project ( Android).
, ,
. New Android Project

. 3.3. New Android Project

118

Welcome

(,
).
:
1. Project name: ( :) Welcome.
Package Explorer ( ) Eclipse.
2. Contents () Create new project in workspace
( ),
, Create project from existing source (
),
Java.
3. Build Target ( ) Android.
2.3.3, , ,
.
Properties () :
1. Application name: ( :) Welcome.
, , .
, ,
.
2. Android Java , ,
(, com.deitel). Package name: ( :) com.deitel.
welcome. deitel.com ( ), . ,
, Java. Android, Android
Market .
3. Create Activity: ( :) Welcome. ,
. ,
, .
4. Min SDK Version: ( SDK:)
API, . API .
API 10, Android 2.3.3,
API 11, Android 3.0. Android 2.2 , API 8. , ,
Android. . 3.2
Android SDK API. SDK . - developer.android.com/resources/
dashboard/platform-versions.html Android, .
5. , Finish ().

3.4.

119

3.2. Android SDK API (developer.android.com/sdk/index.html)

Android SDK

API

3.0

11

2.3.3

10

2.2

2.1

1.6

1.5

, Android SDK
.

Package Explorer
( ) ,
Package Explorer ( ),
. . 3.4 Welcome. Welcome .
(IDE). .

. 3.4. Package Explorer

,
:



src , Java;

gen , Java, IDE;


 Android 2.3.3 , Android framework,
;

120


Welcome

res , , ,
GUI , .
.

3.5.
Welcome
ADT
Welcome.
ADT Visual Layout Editor
GUI,
Buttons, TextViews, ImageViews . Android- Eclipse
XML-, main.xml. GUI XML-
.
res . GUI
layout. main.xml,
/res/layout, Visual Layout Editor,
(. 3.5). XML- (. 3.6),
( , main.xml). Visual Layout Editor, Graphical Layout ( ).
XML- 3.6.

. 3.5. Visual Layout Editor


3.5. Windows

121

. 3.6. XML- GUI.

,
, Android, LinearLayout.
TextView, "Hello World, Welcome!" (. . 3.5).
LinearLayout GUI
. TextView . AVD ,
.
. 3.3 , android.widget1.
GUI,
. , - developer.android.com/reference/android/widget/package-summary.html.
3.3. Android (package android.widget)

FrameLayout

. ,
.

LinearLayout

Android SDK AbsoluteLayout,


. . FrameLayout,
RelativeLayout (. developer.android.com/reference/android/
widget/AbsoluteLayout.html).

122

Welcome

3.3 ()

RelativeLayout

TableLayout

, . TableRow
layout ( LinearLayout)

, ,
RelativeLayout TableLayout.

Visual Layout Editor


Android SDK
Android SDK, ADT Plugin SDK,
.
Graphical Layout ( ).
. 3.7, Android 2.3.3 SDK selector ( SDK),
Graphic Layout. Android 2.3.3.

main.xml

main.xml , RelativeLayout (
).
main.xml, :
1. , main.xml ,
( /res/layout ) Delete () .
2. New (),
NewOther().
3. Android Android XML File (XML- Android)
Next> (>), New Android XML File
( XML- Android).
4. , main.xml (. . 3.7), Finish ().

3.5. Windows

123

. 3.7. main.xml New Android XML File

Visual Layout Editor


. 3.8 main.xml Visual Layout Editor. Android
, Visual Layout Editor , . Device Configurations (
).
Graphic Layout (. 3.8). ,
,
.

Samsung Nexus S, 4- 480800 (WVGA). Android
GUI,
.

124

Welcome

. 3.8. Visual Layout Editor


GUI

Visual Layout Editor . . . 3.8 3.7in


WVGA (Nexus One). WVGA, Nexus S,
.
480800 480854 .


Android , ( DPI),
( ), . res
drawable-hdpi ( ), drawable-mdpi (
) drawable-ldpi ( ).
(. 3.4).
3.4. Android


ldpi

120

mdpi

160

3.5. Windows

125


hdpi

240

xhdpi

320

nodpi

, ,
, drawable-hdpi. ,
, ,
drawable-mdpi drawable-ldpi . Android 2.2, drawable-xhdpi res, ,
. Android

.

Android, - developer.android.com/guide/
practices/screens_support.html.

, .
.

1.
Welcome.
Visual Layout Editor Outline (), XML-.
XML.

Android- GUI
XML, Visual Layout Editor . XML- XML Eclipse ,
, ,
. XML-.

Deitel bug (bug.


png) Android (android.png). images . ,
:

126

Welcome

1. Package Explorer res.


2. images, ,
, drawable-hdpi res.
.

2. Id RelativeLayout
Properties ()
XML-.
Properties , RelativeLayout
Outline (). Window Show
ViewOther ( ),
Properties General () Show View ( ). Visual
Layout Editor Outline (. 3.9). Properties
, XML.

. 3.9. GUI Outline

. ,
(.
). Id.
,
XML-. ,
Id
RelativeLayout.

. 3.10. Properties,
Id RelativeLayout

3.5. Windows

127

RelativeLayout, , Properties,
Id :
@+id/welcomeRelativeLayout

+ @+id (
), /. Properties
Outline (. 3.10).

3. Background
RelativeLayout
, (, ). , , RGB-. 0
255. ,
, .
(IDE)
. RGB- 00FF.
, Background Properties
#FFFFFF (. 3.11).
. #RRGGBB ,
, . Android - (),
0255. 0 , 255 . -,
#AARRGGBB, -.
, #RGB #ARGB. , #FFF #FFFFFF.

. 3.11. Properties
Background RelativeLayout

4. TextView
TextView.
Form Widgets ( ), Visual Layout Editor,
TextView (. 3.12).

Properties.

128

Welcome

. 3.12. TextView ,

5. Text
TextView
Android (developer.android.com/guide/
topics/resources/index.html) , ,
, , , ,
. ,
,
, .
, , .
. res
values, strings.xml. . ,
values .
, values-fr strings.xml
, values-es strings.xml . . ,
values-en-rUS strings.xml , values-en-rGB strings.xml ,
.
, :


developer.android.com/guide/topics/resources/;

providing-resources.html#AlternativeResources;

developer.android.com/guide/topics/resources/localization.html.

3.5. Windows

129

Text TextView,
strings.xml.
1. TextView.
2. Properties Text, , , .
Resource
Chooser ( ).
3. Resource Chooser New String ( ), Create New Android String (
Android), . 3.13.
4. String New R.string (. . 3.13), OK,
Create New Android String Resource
Chooser.
5. welcome. OK .
Properties
Text (. 3.14). @string ,

. 3.13. Create New Android String

130

Welcome

. 3.14. Properties Text TextView

strings.xml, welcome ,
.
,
XML- .
New R.string
String. Android ,
, .
- developer.android.com/guide/topics/resources/
localization.html.

6. Text size Padding top


TextView ,

GUI Android
(. 3.5). , , - developer.android.com/guide/practices/
screens_support.html GUI , ,
, .
3.5.

px

dp dip

sp

in

mm

, (dp dip),
Android .

3.5. Windows

131

, ,
160 dpi ( ).
240 dpi , , 240/160 ( 1,5). , ,
100 , , 150 . 120
120/160 (
0,75). , 100
75 . , ,
, , , , .


, .

TextView
TextView, .
1. , TextView,
Text size 40sp.
2. TextView,
Layout margin top property Misc () Properties
10dp.

7. TextView
TextView :
1. Id @+id/welcomeTextView.
2. Text color #00F ().
3. Text style bold. , Value ,
, . bold, OK .
4. TextView Gravity center.
Value ,

Gravity (. 3.15). center OK
.
Visual Layout Editor (. 3.16).

132

Welcome

. 3.15. gravity

. 3.16. Visual Layout Editor


TextView

8. Android Deitel Bug


ImageViews
ImageViews,
, 1.
ImageView Visual Layout

3.5. Windows

133

Editor . ImageViews Outline. :


1. ImageView, Images & Media ( ) Visual Layout Editor, Outline (), . 3.17. ImageView welcomeTextView.
- , ,
TextView GUI. ,
Layout below, .

ImageView welcomeTextView , welcomeTextView


. ImageView , Visual Layout Editor .

. 3.17. ImageView Outline

2. Id ImageView @+id/droidImageView.
Outline droidImageView.
3. Layout below droidImageView @id/welcomeTextView. ImageView
welcomeTextView. , Value

. 3.18.
Layout below droidImageView

. 3.19.
Src droidImageView

134

Welcome

, , Reference Chooser ( ), . 3.18. ID GUI. ID welcomeTextView.


4. Layout center horizontal droidImageView true,
ImageView .
5. Src droidImageView ,
. Value

. 3.20. Visual Layout Editor GUI

3.6. main.xml

135

, ,
Reference Chooser (. 3.19). Drawable , drawable, res.
Drawable android,
android.png.
6. 15 bugImageView. Id @+id/bugImageView, Src
bug, Layout below droidImageView.
Visual Layout Editor , . 3.20.

3.6. main.xml
XML GUI. , , ,
, ,
, . ADT Plugin XML , . 3.1 main.xml,
, 3.5.
XML- , XML-.
( , Eclipse SourceFormat
( ).) XML- ,
XML, , , Properties .
, XML android:paddingTop Padding top
Properties. IDE , ,
.
3.1. XML- Welcome
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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


<!-- main.xml -->
<! XML- Welcome. -->
<! RelativeLayout, GUI -->
<RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/welcomeRelativeLayout" android:background="#FFFFFF">
<! TextView, "Welcome to Android App Development!" -->
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/welcome"
android:textSize="40sp" android:id="@+id/welcomeTextView"
android:textColor="#00F" android:textStyle="bold"
android:layout_centerHorizontal="true" android:gravity="center"
android:layout_marginTop="10dp"></TextView>

136

Welcome

3.1 ()
19
20 <! ImageView, Android -->
21 <ImageView android:layout_height="wrap_content"
22
android:layout_width="wrap_content" android:id="@+id/droidImageView"
23
android:layout_centerHorizontal="true"
24
android:src="@drawable/android"
25
android:layout_below="@id/welcomeTextView"></ImageView>
26
27 <! ImageView, Deitel bug -->
28 <ImageView android:layout_height="wrap_content"
29
android:layout_width="wrap_content" android:id="@+id/bugImageView"
30
android:src="@drawable/bug"
31
android:layout_below="@id/droidImageView"
32
android:layout_centerHorizontal="true"></ImageView>
33 </RelativeLayout>

welcomeRelativeLayout
welcomeRelativeLayout ( 633) GUI .


XML ( 69)
RelativeLayout.
 6 xmlns, , android XML.
IDE XML.
 78 match_parent android:layout_width
android:layout_height. (, ).
RelativeLayout XML,
( ).


9 android:id android:background
welcomeRelativeLayout.

welcomeTextView
welcomeRelativeLayout welcomeTextView (
1218).


12 13 android:layout_width android:layout_height wrap_content. ,


, , , .

3.7. Welcome

137

14 android:text , welcome, 5 3.5.

15 android:textSize 40sp,
android:id "@+id/welcomeTextView".

16 android:textColor "#00F" (
), android:textStyle "bold".

17 android:layout_centerHorizontal "true",
. android:gravity
"center", TextView.
android:gravity , TextView, , TextView.

18 android:marginTop 10dp,

TextView .

droidImageView
, welcomeRelativeLayout, droidImageView ( 2125) bugImageView ( 2832). ImageViews
,
droidImageView.


21 22 android:layout_width android:layout_height wrap_content. 22 android:id

"@+id/droidImageView".


23 android:layout_centerHorizontal "true",
.

24 android:src , drawable
android. android.png.

25 android:layout_below "@id/
welcomeTextView".

RelativeLayout . ImageView
welcomeTextView.

3.7. Welcome
Android Virtual Device
(AVD), Package
Explorer Run AsAndroid Application ( 
Android). . 3.21 .

138

Welcome

. 3.21. Welcome,
AVD

3.8.

Eclipse IDE ADT Visual Layout Editor. Visual Layout Editor Android
- . GUI TextView ImageView .
RelativeLayout. GUI-,
. Android Virtual Device (AVD). ,
XML-, GUI.
Android- Java.
Android
Java XML. Java
.

3.8.

139

Tip Calculator . (GUI)


Java,
.

Tip
Calculator App
C
Android Java












TableLayout.
Outline ADT Plugin Eclipse
GUI TableLayout.
XML GUI ,
Visual Layout Editor Properties
Eclipse.
GUI TextView, EditText SeekBar.
- Java, , , , ,
Android.
GUI.

EditText SeekBar.

4.1.
Tip Calculator (. 4.1) .
( ).
: 10 %, 15 % 20 %. , .

4.1.

141

Seekbar, , Seekbar.
18 %
. ,
, .
.

, . 4.1, AVD Android,


.

. 4.1. :
, EditText Bill
total ;
$123,45 17 %

,
. , .
(GUI) Outline Eclipse.
GUI. Visual Layout Editor
, GUI. XML- GUI
ADT. XML, , Properties.
.

142

Tip Calculator App

4.2. Tip Calculator



Eclipse, Tip Calculator
:
1. Import (). Import,
FileImport ().
2. Tip Calculator. Import General (), Existing Projects into Workspace
( ), Next>
(>) Import Projects ( ). Select root directory ( ) Browse
(). Browse For Folder ( )
TipCalculator ( ), OK. Finish () Eclipse.
Package Explorer, Eclipse.
3. Tip Calculator. Eclipse
TipCalculator Package Explorer, Run AsAndroid Application (  Android).
Tip Calculator AVD, ,
.

AVD
- Android, .


EditText, Bill Total, . 123,45.

,
EditText Bill Total . Input method (
). Android .

, , . EditText , 10 %, 15 % 20
%,
(. 4.1, ). EditText
,
18 %, .

4.4.

143

EditText, ,
.


Seekbar ,
. Seekbar ,
17 %. EditText,
Seekbar. Seekbar
, 0 100.

4.3.
- Java,
, , , , . Android Activity, , , Tip Calculator.
EditText, TextView SeekBar. Visual Layout Editor
Outline Eclipse, XML-,
GUI. EditText,
GUI-, TextView
( 3). EditText ,
. SeekBar, GUI-
, , 0100,
.
.

4.4.
Tip Calculator ADT Plugin. XML,
ADT Plugin .
, .
, XML-
.

, , , . 4.1
, , 4.4.2 4.4.4.

144

Tip Calculator App

4.4.1. TableLayout
, , TableLayout (. . 4.2)
GUI .
, TableLayout, , , .
0 4 . 4.2 ,
. TableRow.
TableLayout TableRow,
.
( . 4.2, 1 4 ).
(
, ). .
, 0.
TableLayout -: developer.
android.com/reference/android/widget/TableLayout.html, TableRow
-: developer.android.com/reference/android/widget/TableRow.html.

. 4.2. TableLayout ,
GUI Tip Calculator

. 4.3 GUI,
.
GUI
Id XML- Java.

4.4.

145

fifteenTextView
billEditText
billTextView

twentyTextView
tip10EditText
tip15EditText
tip20EditText
total10EditText
total15EditText
total20EditText

tenTextView
tipTextView

totalTextView
customTextView

customTipTextView

tipCustomTextView

totalCustomEditText

tipCustomEditText
customSeekBar

totalCustomTextView

. 4.3. GUI Tip Calculator,


Id

4.4.2. ,
TableLayout
GUI, . 4.2.
, , .
TableLayout Id Text (. . 4.3).
3.5 , strings.xml,
res/values . ,
.
TextView 10 %, 15 % 20 % . , , GUI,
. ,
Outline main.xml.
Outline
TableRow TableLayout.
, TableLayouts, Visual Layout Editor
. Outline
GUI, . , TableLayout
GUI.

146

Tip Calculator App

1. TipCalculator
Eclipse
.
Tip Calculator, .

Delete ().
Delete project contents on disk is not selected (
) OK. , , ,
. Android TipCalculator. New Android Project ( Android)
Finish ():


Build Target ( ): Android 2.3.3.

Application name ( ): Tip Calculator.


Package name ( ): com.deitel.tipcalculator.
 Create Activity ( ): TipCalculator.
 Min SDK Version ( SDK): 10. (. SDK Android 2.3.3,
, Android 2.3.3. Android (AVD) ,
Android, Min SDK
. , 8 , Android 2.2 .)


2. main.xml
main.
xml , TableLayout, . main.xml,
:
1. mail.xml, ( /res/layout) Delete ().
2. layout NewOther
(). New ().
3. Android Android XML File (XML- Android). Next> (>) New Android XML File (
XML- Android).
4. main.xml TableLayout,
Finish.

3. Visual Layout Editor


Android SDK
Visual Layout Editor
main.xml. Android SDK, ADT

4.4.

147

Plugin , ( Graphical Layout), SDK,


. SDK, Graphical Layout, Android 2.3.3
(. . 3.7).
Android 2.3.3.

4. Visual Layout Editor


Device Configurations ( ), Graphical Layout (. . 3.11),
3.7in WVGA (Nexus One).
, 480800 (WVGA).

5. TableLayout
Outline TableLayout. Properties, .


Background (): #FFF.

Id (): @+id/tableLayou.t.
Padding (): 5 dp.
 Stretch columns ( ): 1, 2, 3.



Layout width Layout height


match_parent, . Padding 5 dp,
5 . Stretch
columns ( XML android:stretchColumns, . 4.1,
8) 13,
. 0
.

6. TableRow
Outline TableRow
TableLayout. :
1. Outline tableLayout
Outline.
2. .

tableLayout , TableRow TableLayout. Id
TableRow tableRow1 tableRow6
. 0,
Id TableRow,
tableRow0 tableRow5 . TableRow Layout width match_parent.
. TableRow

148

Tip Calculator App

, TableRow, Outline.
, Shift, TableRow
Outline, . .

7. tableRow
TextView EditText tableRow0.
:
1. TextView (billTextView) Form Widgets (
) tableRow0, Outline.
2. EditText (billEditText) Form Widgets ( )
tableRow0, Outline.
3. Id Text .
,
Outline Edit ID( ) Edit Text( ) .
TableRow
Outline, TableRow.

8. tableRow1
TextView tableRow1, :
1. TextView (tenTextView) tableRow1,
Outline.
2. , fifteenTextView twentyTextView.
3. Id Text .

9. tableRow2
TextView EditText tableRow2, :
1. Outline TextView (tipTextView) tableRow2.
2. Outline EditText tableRow2 (tip10EditText,
tip15EditText tip20EditText).
3. Id Text .

10. tableRow3
TextView EditText tableRow3, :
1. Outline TextView (totalTextView) tableRow3.
2. Outline EditText tableRow3 (total10EditText,
total15EditText total20EditText).
3. Id Text .

4.4.

149

11. tableRow4
TextView, SeekBar TextView tableRow4, :
1.
2.
3.
4.

Outline TextView (customTextView) tableRow4.


Outline SeekBar (customSeekBar) tableRow4.
Outline TextView (customTipTextView) tableRow4.
Id Text TextView.

12. tableRow5
TextView, EditText, TextView
EditText tableRow5, :
1.
2.
3.
4.
5.

Outline TextView (tipCustomTextView) tableRow5.


Outline EditText (tipCustomEditText) tableRow5.
Outline TextView (totalCustomTextView) tableRow5.
Outline EditText (totalCustomEditText) tableRow5.
Id Text .

4.4.3.
,
. 4.4.

. 4.4. Tip Calculator


, Id Text

. 4.2 .


billEditText customSeekBar .
 TextView - ,
.

150

Tip Calculator App

. , TextView, 10 %, 15 % 20 %, tableRow1,
TextView 18 % tableRow4.
, customSeekBar .
 . 4.2 ,
. 4.4 .

4.4.4.

.

13. Text color TextView


Outline .
Ctrl ( Control). Properties
. .
TextView, .
Text color
TextView, :
1. Ctrl ( Control), TextView
.
2. Properties Text color #000.

14. TextView
10 %, 15 % 20 %
. 4.2, 10 %, 15 % 20 % , .
TableRow ,
. . ,
.
, Properties.
XML.
1. XML-, main.xml Visual Layout Editor.
2. <TextView> android:id, "@+id/
tenTextView".
3. XML- TextView /
:
android:layout_column="1"

4.4.

151

TextView 10 %
( 0).

. ,
android:layout_column , , . XML-
, Misc
Properties.

15. TextView
tableRow1 EditText
tableRow2, tableRow3 tableRow5,
EditText
. 4.2, , , . , ,
Gravity. Graphical Layout
Visual Layout Editor :
1. Outline TextView tableRow1.
2. Properties Gravity center.
3. EditText, tableRow2, tableRow3
tableRow5.
4. Properties Gravity center.
5. Text size 14sp.
EditText,
.

16.
billEditText customSeekBar
. 4.2 billEditText 13, customSeekBar
12. , ,
XML-:
1. , main.xml
Visual Layout Editor.
2. <EditText> android:id, "@+id/
billEditText".
3. XML- EditText /:
android:layout_span="3"

4. <SeekBar>.
5. XML- SeekBar /:
android:layout_span="2"

152

Tip Calculator App

billEditText 13,
customSeekBar 12.

17. TextView
TextView 0
TextView, tableRow5. TextView , 5dp.
, .
1. Graphical Layout Visual Layout
Editor.
2. Outline TextView 0,
TextView.
3. Gravity right, Padding right 5dp.

18.
TextView tableRow4
Gravity, TextView,
tableRow4:
1. Outline customTextView tableRow4.
2. Gravity ,
, Gravity.
3. center_vertical. right
center_vertical.
4. , OK.
5. Outline customTipTextView,
tableRow4.
6. Gravity center_vertical.
7. OK.
8. Outline TextView tableRow4,
Layout height match_parent, Padding bottom
5dp. TextView ,
SeekBar, Gravity
SeekBar. Padding bottom SeekBar.
TextView SeekBar.
9. , Padding left customTipTextView 5dp, TextView SeekBar.

19. Progress Property


Padding customSeekBar
,
Progress, Padding left Padding right SeekBar.

4.4.

153

SeekBar, 18 %,
TextView SeekBar. SeekBar.
SeekBar ( 0 100 ),
, SeekBar
, .
1.
2.
3.
4.

Outline customSeekBar.
Progress 18.
Padding left Padding right 8dp.
Padding bottom 5dp,
.
5. Focusable false.
SeekBar billEditText .
, ( ,
).

20.
, EditText
billEditText,
, EditText
. , .
EditText Focusable.
EditText, EditText.
. :
1. Outline EditText
billEditText.
2. Focusable, Long clickable Cursor visible false.

21. billEditText
,
billEditText. :
1. Outline billEditText.
2. Input type numberDecimal.

22.

Layout weight ()
. Layout weight 0.
. Layout weight 1 TextView,

154

Tip Calculator App

. , TextView,
, ,
TextView .
Layout weight, 1,
. .
Layout weight 2,
, Layout weight,
1 ( ).
. XML-, Visual
Layout Editor. 4.5 .

4.4.5. XML- GUI Tip Calculator


GUI , . 4.2. 4.1 XML- Tip Calculator. XML- ,
, .
, GUI, 4.4.2
4.4.4.
4.1. XML- Tip Calculator
1
2
3
4
5
6

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


<!-- main.xml -->
<! XML- Tip Calculator -->

<TableLayot xmlns:android=http://schemas.android.com/apk/res/android
android:layout_width="match_parent"
android:layout_height="match_parent"
7
android:background="#FFF" android:id="@+id/tableLayout"
8
android:stretchColumns="1,2,3" android:padding="5dp">
9
10 <!-- tableRow0 -->
11 <TableRow android:layout_height="wrap_content"
12
android:layout_width="match_parent" android:id="@+id/tableRow0">
13
<TextView android:id="@+id/billTextView"
14
android:layout_width="wrap_content"
15
android:layout_height="wrap_content"
16
android:text="@string/billTotal" android:textColor="#000"
17
android:gravity="right" android:paddingRight="5dp"></TextView>
18
<EditText android:layout_width="wrap_content"
19
android:id="@+id/billEditText"
20
android:layout_height="wrap_content" android:layout_span="3"
21
android:inputType="numberDecimal" android:layout_weight="1">
22
</EditText>
23 </TableRow>
24
25 <!-- tableRow1 -->
26 <TableRow android:layout_height="wrap_content"

4.4.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

155

android:layout_width="match_parent" android:id="@+id/tableRow1">
<TextView android:id="@+id/tenTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="10 %"
android:textColor="#000" android:layout_column="1"
android:gravity="center" android:layout_weight="1"></TextView>
<TextView android:id="@+id/fteenTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="15 %"
android:textColor="#000" android:gravity="center"
android:layout_weight="1"></TextView>
<TextView android:id="@+id/twentyTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="20 %"
android:textColor="#000" android:gravity="center"
android:layout_weight="1"></TextView>
</TableRow>
<!-- tableRow2 -->
<TableRow android:layout_height="wrap_content"
android:layout_width="match_parent" android:id="@+id/tableRow2">
<TextView android:id="@+id/tipTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tip" android:textColor="#000"
android:gravity="right" android:paddingRight="5dp"></TextView>
<EditText android:layout_width="wrap_content"
android:id="@+id/tip10EditText"
android:layout_height="wrap_content" android:text="@string/zero"
android:gravity="center" android:focusable="false"
android:layout_weight="1" android:textSize="14sp"
android:cursorVisible="false" android:longClickable="false">
</EditText>
<EditText android:layout_width="wrap_content"
android:id="@+id/tip15EditText"
android:layout_height="wrap_content" android:text="@string/zero"
android:gravity="center" android:focusable="false"
android:layout_weight="1" android:textSize="14sp"
android:cursorVisible="false" android:longClickable="false">
</EditText>
<EditText android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/tip20EditText" android:text="@string/zero"
android:gravity="center" android:focusable="false"
android:layout_weight="1" android:textSize="14sp"
android:cursorVisible="false" android:longClickable="false">
</EditText>
</TableRow>
<!-- tableRow3 -->
<TableRow android:layout_height="wrap_content"

156

Tip Calculator App

4.1 ()
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

android:layout_width="match_parent" android:id="@+id/tableRow3">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/totalTextView" android:text="@string/total"
android:textColor="#000" android:gravity="right"
android:paddingRight="5dp"></TextView>
<EditText android:layout_width="wrap_content"
android:text="@string/zero" android:layout_height="wrap_content"
android:id="@+id/total10EditText" android:gravity="center"
android:focusable="false" android:layout_weight="1"
android:textSize="14sp" android:cursorVisible="false"
android:longClickable="false"></EditText>
<EditText android:layout_width="wrap_content"
android:text="@string/zero" android:layout_height="wrap_content"
android:id="@+id/total15EditText" android:gravity="center"
android:focusable="false" android:layout_weight="1"
android:textSize="14sp" android:cursorVisible="false"
android:longClickable="false"></EditText>
<EditText android:layout_width="wrap_content"
android:text="@string/zero" android:layout_height="wrap_content"
android:id="@+id/total20EditText" android:gravity="center"
android:focusable="false" android:layout_weight="1"
android:textSize="14sp" android:cursorVisible="false"
android:longClickable="false"></EditText>
</TableRow>
<!-- tableRow4 -->
<TableRow android:layout_height="wrap_content"
android:layout_width="match_parent" android:id="@+id/tableRow4">
<TextView android:id="@+id/customTextView"
android:layout_width="wrap_content" android:text="@string/custom"
android:textColor="#000" android:paddingRight="5dp"
android:gravity="right|center_vertical"
android:layout_height="match_parent" android:paddingBottom="5dp"
android:focusable="false"></TextView>
<SeekBar android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/customSeekBar" android:layout_span="2"
android:progress="18" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingBottom="5dp"
android:layout_weight="1"></SeekBar>
<TextView android:id="@+id/customTipTextView"
android:layout_width="wrap_content" android:text="18 %"
android:textColor="#000" android:gravity="center_vertical"
android:layout_height="match_parent" android:paddingLeft="5dp"
android:paddingBottom="5dp" android:focusable="false"
android:layout_weight="1"></TextView>
</TableRow>
<!-- tableRow5 -->

4.5.

157

128 <TableRow android:layout_height="wrap_content"


129
android:layout_width="match_parent" android:id="@+id/tableRow5">
130
<TextView android:layout_width="wrap_content"
131
android:layout_height="wrap_content"
132
android:id="@+id/tipCustomTextView" android:text="@string/tip"
133
android:textColor="#000" android:gravity="right"
134
android:paddingRight="5dp"></TextView>
135
<EditText android:layout_width="wrap_content"
136
android:layout_height="wrap_content"
137
android:id="@+id/tipCustomEditText" android:text="@string/zero"
138
android:gravity="center" android:focusable="false"
139
android:layout_weight="1" android:textSize="14sp"
140
android:cursorVisible="false" android:longClickable="false">
141
</EditText>
142
<TextView android:id="@+id/totalCustomTextView"
143
android:layout_width="wrap_content"
144
android:layout_height="wrap_content" android:text="@string/total"
145
android:textColor="#000" android:gravity="right"
146
android:paddingRight="5dp" android:layout_weight="1"></TextView>
147
<EditText android:layout_height="wrap_content"
148
android:layout_width="wrap_content"
149
android:id="@+id/totalCustomEditText" android:text="@string/zero"
150
android:gravity="center" android:focusable="false"
151
android:layout_weight="1" android:textSize="14sp"
152
android:cursorVisible="false" android:longClickable="false">
153
</EditText>
154 </TableRow>
155 </TableLayout>

4.4.6 strings.xml
4.2 , 4.1.
4.2. , strings.xml
1
2
3
4
5
6
7
8
9

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


<resources>
<string name="app_name">Tip Calculator</string>
<string name="billTotal">Bill total</string>
<string name="tip">Tip</string>
<string name="total">Total</string>
<string name="custom">Custom</string>
<string name="zero">0.00</string>
</resources>

4.5.
4.34.11 Tip Calculator
TipCalculator. , 10 %, 15 %, 20 %, .

158

Tip Calculator App

, , ( ).

package import
4.3 package import,
TipCalculator.java. package, 3, ,
package com.deitel.tipcalculator.
1 4.4.
4.3. package import, TipCalculator
1 // TipCalculator.java
2 // 5, 10, 15
// .
3 package com.deitel.tipcalculator;
4
5 import android.app.Activity;
6 import android.os.Bundle;
7 import android.text.Editable;
8 import android.text.TextWatcher;
9 import android.widget.EditText;
10 import android.widget.SeekBar;
11 import android.widget.SeekBar.OnSeekBarChangeListener;
12 import android.widget.TextView;
13

import, 512, , .


Activity, android.app ( 5),


, ( ).

Bundle android.os ( 6) .
(,
).

Editable android.text ( 7)
GUI.

TextWatcher android.text ( 8)
, EditText.

android.widget ( 912) ( GUI)


, Android GUI, EditText ( 9), SeekBar ( 10) TextView ( 12).

SeekBar.OnSeekBarChangeListener android.widget ( 11)


, SeekBar.

4.5.

159

Activity
Tip Calculator
Android (main method).
: , , . . .
( ),
GUI. .
TipCalculator (. 4.44.11) Activity Tip Calculator. ,
, . TipCalculator () Activity
( 15). TipCalculator ADT Plugin
Activity
onCreate, Activity.
.
4.4. TipCalculator Activity
14 // Activity TipCalculator
15 public class TipCalculator extends Activity
16 {


: (), .
.


( )
( ).
.

, .
( , , ). ,
.

, Activity
(developer.android.com/reference/android/app/Activity.html). Tip Calculator
: onCreate onSaveInstanceState.
: onStart, onPause, onRestart, onResume,
onStop onDestroy. .

160

Tip Calculator App

onCreate Activity.
,
Activity.
 onSaveInstanceState (,
,
Motorola Droid).
,
onCreate (
). ,
,
, GUI .
( ,
).
, ,
.
.


1832 4.5 TipCalculator. EditText,
,
( ). ( 1819)
/
. /
SaveInstanceState onCreate
( ).
4.5. TipCalculator
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

// , /
private static nal String BILL_TOTAL = "BILL_TOTAL";
private static nal String CUSTOM_PERCENT = "CUSTOM_PERCENT";
private
private
private
private
private
private
private
private
private
private
private
private

double currentBillTotal;
int currentCustomPercent;
EditText tip10EditText;
EditText total10EditText;
EditText tip15EditText;
EditText total15EditText;
EditText billEditText;
EditText tip20EditText;
EditText total20EditText;
TextView customTipTextView;
EditText tipCustomEditText;
EditText totalCustomEditText;

//
//
//
//
//
//
//
//
//
//
//
//
//

,
% , SeekBar
10%-
, 10%-
15%-
, 15%-

20%-
, 20%-
%


4.5.

161

, EditText billEditText,
currentBillTotal.
, . , Seekbar
( , 0100), currentCustomPercent.
0,01 double,
. ,
, tipCustomEditText
totalCustomEditText . 30 TextView,
,
SeekBar (. 18 % . 4.1, ).
, 10 %, 15 % 20 %, ( ) EditText. ,
10 %, ( ,
10 %) tip10EditText total10EditText .
, 15 %, ( , 15 %) tip15EditText
total15EditText . , 20 %,
( , 20 %)
tip20EditText total20EditText .

OnCreate Activity
onCreate (. 4.6)
Activity. onCreate
Activity GUI.
, .
, ,
ANR (Application Not Responding, ). ,
. ,
, , onCreate.
4.6. onCreate Activity
34
35
36
37
38
39
40
41
42
43
44
45
46

// activity.
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); //
setContentView(R.layout.main);
// GUI
// ?
if ( savedInstanceState == null )
//
{
currentBillTotal = 0.0;
//
currentCustomPercent = 18; //
// 18 %
}
// if

162

Tip Calculator App

4.6 ()
47
48
49
50
51
52
53
54
55 }
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

else //
{
//
currentBillTotal = savedInstanceState.getDouble(BILL_TOTAL);
//
//
currentCustomPercent =
savedInstanceState.getInt(CUSTOM_PERCENT);
// else
// 10 %, 15 %, 20 % EditTexts
tip10EditText = (EditText) ndViewById(R.id.tip10EditText);
total10EditText = (EditText) ndViewById(R.id.total10EditText);
tip15EditText = (EditText) ndViewById(R.id.tip15EditText);
total15EditText = (EditText) ndViewById(R.id.total15EditText);
tip20EditText = (EditText) ndViewById(R.id.tip20EditText);
total20EditText = (EditText) ndViewById(R.id.total20EditText);
// TextView,
customTipTextView = (TextView) ndViewById(R.id.customTipTextView);
// EditTexts
tipCustomEditText = (EditText) ndViewById(R.id.tipCustomEditText);
totalCustomEditText =
(EditText) ndViewById(R.id.totalCustomEditText);
// billEditText
billEditText = (EditText) ndViewById(R.id.billEditText);
// billEditTextWatcher onTextChanged
// billEditText
billEditText.addTextChangedListener(billEditTextWatcher);
// SeekBar,
//
SeekBar customSeekBar = (SeekBar) ndViewById(R.id.customSeekBar);
customSeekBar.setOnSeekBarChangeListener(customSeekBarListener);
} // onCreate


. .
onCreate, Bundle savedInstanceState.
(
). , , onSaveInstanceState Activity (. 4.9). (
savedInstanceState, 4255.) 38

4.5.

163

onCreate ,
Activity.

(, strings.xml GUI- main.xml) ADT R.
, , res. R gen,
. , R, static final intst,
( ).
, R:


drawable. drawable, ,
drawable, res.

id. GUI-,
XML-.

layout. ,
(, main.xml).

string. ,
strings.xml.

setContentView ( 39) R.layout.main, XML-, GUI . main.xml.


setContentView XML,
. GUI.
4255 ,
.
savedInstanceState null ( 42), .
4445 currentBillTotal currentCustomPercent , .
, 50 getString
savedInstanceState, ( double). 5354 getInt
savedInstanceState,
( int).
XML- findViewById Activity
. int (GUI-)
. GUI R.id
android:id GUI main.xml. ,
billEditText R.id.billEditText.
5863 EditText,
( 10 %, 15 % 20 %),

164

Tip Calculator App

( ). 66 TextView,
. 6971
EditText,
.
74 billEditText, 77
addTextChangedListener. TextChangedListener,
,
billEditText. listener 4.11.
80 customSeekBar, 81
setOnSeekBar-ChangeListener, OnSeekBarChangeListener.
, customSeekBar,
. listener 4.10.

updateStandard TipCalculator
updateStandard (. 4.7) EditText,
, 10 %, 15 % 20 %,
. .
currentBillTotal
10 % ( 8895), 15 % ( 98106) 20 %
( 109116). static format String
, EditText.
4.7. updateStandard TipCalculator

(10 %, 15 % 20 %)
84 // , EditTexts, 10, 15 20 %
85 private void updateStandard()
86 {
87
// , 10 %
88
double tenPercentTip = currentBillTotal * .1;
89
double tenPercentTotal = currentBillTotal + tenPercentTip;
90
91
// tipTenEditText tenPercentTip
92
tip10EditText.setText(String.format(" %.02f", tenPercentTip));
93
94
// totalTenEditText tenPercentTotal
95
total10EditText.setText(String.format(" %.02f", tenPercentTotal));
96
97
// 15 %
98
double fteenPercentTip = currentBillTotal * .15;
99
double fteenPercentTotal = currentBillTotal + fteenPercentTip;
100
101
// tipFifteenEditText
// fteenPercentTip

4.5.
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

165

tip15EditText.setText(String.format(" %.02f", fteenPercentTip));


// totalFifteenEditText
// fteenPercentTotal
total15EditText.setText(
String.format(" %.02f", fteenPercentTotal));
// 20 %
double twentyPercentTip = currentBillTotal * .20;
double twentyPercentTotal = currentBillTotal + twentyPercentTip;
// tipTwentyEditText
// twentyPercentTip
tip20EditText.setText(String.format(" %.02f", twentyPercentTip));
// totalTwentyEditText
// twentyPercentTotal
total20EditText.setText(String.format(" %.02f", twentyPercentTotal));
} // updateStandard

updateCustom TipCalculator
updateCustom (. 4.8) EditText, . , customSeekBar.
123 customTipTextView , SeekBar. 126127
customTipAmount. 130 customTotalAmount. 133135
customTipAmount customTotalAmount ,
tipCustomEditText totalCustomEditText .
4.8. updateCustom TipCalculator
,
customSeekBar
119
120
121
122
123
124
125
126
127
128
129
130
131

// EditText,
//
private void updateCustom()
{
// customTipTextView
// SeekBar
customTipTextView.setText(currentCustomPercent + " %");
//
double customTipAmount =
currentBillTotal * currentCustomPercent * .01;
// ,
double customTotalAmount = currentBillTotal + customTipAmount;


166

Tip Calculator App

4.8 ()
132
133
134
135
136
137

//
tipCustomEditText.setText(String.format(" %.02f", customTipAmount));
totalCustomEditText.setText(
String.format(" %.02f", customTotalAmount));
// updateCustom

onSaveInstanceState Activity
139146 (. 4.9) onSaveInstanceState
Activity. (,
).
Eclipse, SourceOverride/Implement Methods (
/ ). , .
onSaveInstanceState, , IDE
, OK .
4.9. onSaveInstanceState Activity

138
139
140
141
142
143
144
145
146
147

// billEditText customSeekBar
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);

outState.putDouble( BILL_TOTAL, currentBillTotal );


outState.putInt( CUSTOM_PERCENT, currentCustomPercent );
// onSaveInstanceState


onSaveInstanceState , / Bundle,
. 144
, 145 ( SeekBar).
onCreate, .
Activity. -:
bit.ly/ActivityLifeCycle.

,
OnSeekBarChangeListener
149171 (. 4.10)
customSeekBarListener, customSeekBar.

4.5.

167

, , Oracle
Java Tutorial bit.ly/AnonymousInnerClasses.
81 customSeekBarListener ,
customSeekBar. 153170
OnSeekBarChangeListener.
4.10. , OnSeekBarChangeListener , customSeekBar
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172

// SeekBar
private OnSeekBarChangeListener customSeekBarListener =
new OnSeekBarChangeListener()
{
// currentCustomPercent, updateCustom
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
// currentCustomPercent SeekBar
currentCustomPercent = seekBar.getProgress();
updateCustom(); // EditTexts
//
} // onProgressChanged

@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
// onStartTrackingTouch

@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
} // onStopTrackingTouch
}; // OnSeekBarChangeListener

onProgressChanged
OnSeekBarChangeListener
153160 onProgressChanged. 158
getProgress SeekBar , 0100. SeekBar currentCustomPercent. 159 updateCustom,
customCurrentPercent .

onStartTrackingTouch onStopTrackingTouch
OnSeekBarChangeListener
Java . ,
Java, ( 162170).

168

Tip Calculator App

, TextWatcher
174206 4.11 billEditTextWatcher,
, billEditText. 77
billEditTextWatcher , billEditText.
177205 TextWatcher.
4.11. , TextWatcher,
,
billEditText
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

// , billEditText
private TextWatcher billEditTextWatcher = new TextWatcher()
{
//
@Override
public void onTextChanged(CharSequence s, int start,
int before, int count)
{
// billEditText double
try
{
currentBillTotal = Double.parseDouble(s.toString());
} // try
catch (NumberFormatException e)
{
currentBillTotal = 0.0; //
} // catch

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

// EditTexts,
//
updateStandard(); // 10, 15 20 % EditTexts
updateCustom();
// EditTexts
//
// onTextChanged

@Override
public void afterTextChanged(Editable s)
{
} // afterTextChanged

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after)
{
} // beforeTextChanged
}; // billEditTextWatcher
// TipCalculator

4.6.

169

onTextChanged TextWatcher
onTextChanged ( 177194) , billEditText. ( 178179). CharSequence
s, billEditText. ,
, count,
, start, , before.
184 , billEditText, double. 192 updateStandard,
EditText 10 %, 15 % 20 %,
( ). 193
updateCustom, EditText, . , SeekBar.

beforeTextChanged afterTextChanged
billEditTextWatcherTextWatcher
TextWatcher.
( 196205) .

4.6.
Android Tip Calculator.
,
. ADT Plugin
Eclipse ( Visual Layout Editor, Outline Properties).
GUI, . Activity
Tip Calculator, , ,
.

TableLayout, GUI
. , TableLayout
. , .
TableRow
TableRow, .
,
, (
). TextView GUI-, EditText ,

170

Tip Calculator App

. EditText
,
, SeekBar
. XML-
ADT Plugin, XML- ,
Properties.
- Java, ,
, , , .

XML- , Android Activity Activity. onCreate, , onSaveInstanceState,
.
findViewById Activity,
onCreate GUI, . billEditText , TextWatcher.

EditText . customSeekBar
, OnSeekBarChangeListener.
.
SeekBar.
Favorite Twitter Searches,
,
.

Favorite
Twitter Searches

Shared Preferences,
, ,
, Alert
Dialogs, XML-











.
ScrollView ,
.
GUI
XML-.
/ , ,
SharedPreferences.
/ , ,
SharedPreferences.Editor.
AlertDialogs AlertDialog.Builder.
- .
.

5.1.
Favorite Twitter Searches (
) ,

172

Favorite Twitter Searches

. .
(dev.twitter.com/docs/using-search).
, ,

.
. . 5.1, .

.
EditText, .
Save Button ( ) . , -. . 5.1,
Google Button ( Google) Google,
from:Google. , Edit Button ( ),
.
, (
). Clear Tags Button (
), , , . ,
.

. 5.1. Favorite Twitter Searches:


;
Google Button

5.2. Favorite Twitter Searches

173

5.2. Favorite Twitter Searches



Eclipse, Favorite Twitter Searches. :
1. Import. ,
FileImport ().
2. Favorite Twitter Searches. Import
General () Existing Projects into Workspace
( ), Next > ( >) Import Projects ( ).
Select root directory ( ), Browse
(). Browse For Folder ( )
FavoriteTwitterSearches, examples , OK.
Finish () Eclipse.
Package Explorer, Eclipse.
3. Favorite Twitter Searches. Eclipse FavoriteTwitterSearches, Package
Explorer, Run AsAndroid Application
(  Android). Favorite Twitter Searches AVD,
,
. . 5.2.

. 5.2. Favorite Twitter Searches

174

Favorite Twitter Searches

EditText ,
Tagged Searches ( )
( ).


from:Google, , EditText.
EditText Google (. 5.3, ). , Tagged Searches.
,
Save Button. Tagged Searches Google Button (. 5.3, ).
. , .

. 5.3. :
;


(Edit Button).
,
EditText, ,
. ,
l 2011 .
since:20110401 (. 5.4). Save
.

5.3.

175

, .
.

. 5.4.


, Google search query Button.
-, -
(. 5.5).

5.3.
GUI- EditText, ScrollView Button.
ScrollView ViewGroup, (Views), (layout).
. ScrollView ,
, .
Button ,
.

SharedPreferences
,
/, .

176

Favorite Twitter Searches

. 5.5.

, .
, .
/ SharedPreferences
( android.content). ,
SharedPreferences.Editor ( android.content). ,
.
refreshButtons
. onCreate, Activity.
, .
Android , UI thread.
GUI . ,
UI thread /, .
10.

. , .
Button, ,
URL-, . (Intent) URL- -,
startActivity, Activity
Context. URL- startActivity -
, . .

5.4.

177

LayoutInater

Button. Button , . GUI
XML- LayoutInflater.
LayoutInflater XML-,
, XML-. Button,
Button GUI .

AlertDialog
,
. EditText , .
Clear Tags
( ).
AlertDialog. ( ) . ,
AlertDialog.Builder, AlertDialog.

AndroidManifest.xml
AndroidManifest.xml ADT Plugin Eclipse. ,
, , SDK SDK,
Activity .
. ,
,
.

5.4.


Favorite Twitter Searches. XML, ADT Plugin .
GUI, XML- XML-. XML-,
Edit .
.

178

Favorite Twitter Searches

5.4.1. main.xml TableLayout


4,
TableLayout (. 5.6), . android:stretchColumns TableLayout "*".
,
.

. 5.6. TableLayout
Favorite Twitter Searches

. 5.7 GUI. .
GUI- Id
XML- Java.

5.4.2.
Android FavoriteTwitterSearches. New Android Project ( Android)
Finish ():


Build Target ( ): Android 2.3.3.

Application name ( ): Favorite Twitter Searches.

Package name ( ): com.deitel.favoritetwittersearches.

Create Activity ( ): FavoriteTwitterSearches.

Min SDK Version ( SDK): 10.

5.4.

179

queryEditText
tagEditText

saveButton

taggedSearchesTextView

queryScrollView

clearTagsButton

. 5.7. GUI Favorite Twitter Searches


Id

SDK Android 2.3.3,


, Android 2.3.3. Android (AVD)
Android, Min SDK . , 8 , Android 2.2 .

5.4.3.

colors.xml dimen.xml .
, res/
values. , ,
R.java ,
.
, :
1. Package Explorer NewOther (), New
() Android Android XML File (XML- Android).
New Android XML File ( XML-
Android).
2. File () colors.xml.
3. What type of resource would you like to create? (
) Values ().
res/values .

180

Favorite Twitter Searches

4. Finish ().
5. dimen.xml.
5.15.2. XML- , .
Android
R.color. , strings.xml .

colors.xml
XML-, , resources,
. (. 5.1)
, (light_orange).
color ( 3) name,
, , .
5.1. , colors.xml
1
2
3
4

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


<resources>
<color name="light_orange">#8f90</color>
</resources>

dimen.xml
5.2 dimen,
Edit. , (dp dip),
, (sp). Android . ,

.
5.2. , dimen.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <resources>
3
<dimen name="tagButtonWidth">230dp</dimen>
4
<dimen name="editButtonWidth">50dp</dimen>
5 </resources>

strings.xml
5.3 , . 4 searchURL. URL-
- .

5.4.

181

5.3. , strings.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <resources>
3
<string name="app_name">Favorite Twitter Searches</string>
4
<string name="searchURL">http://search.twitter.com/search?q=</string>
5
<string name="tagPrompt"> </string>
6
<string name="queryPrompt"> </string>
7
<string name="taggedSearches"> </string>
8
<string name="edit"></string>
9
<string name="clearTags"> </string>
10
<string name="save"></string>
11
<string name="erase"></string>
12
<string name="cancel"></string>
13
<string name="OK">OK</string>
14
<string name="missingTitle"> </string>
15
<string name="missingMessage">
16
.</string>
17
<string name="conrmTitle"> ?</string>
18
<string name="conrmMessage">
19
</string>
20 </resources>

5.4.4. TableLayout
, 4,
(. . 5.65.7).
, . TableLayout
Id Text (. . 5.7). GUI
strings.xml, res/values
. Outline TableRows
TableLayout.

1. main.xml
main.xml TableLayout, .
main.xml, :
1. mail.xml, ( /res/layout) Delete ().
2. layout NewOther
(). New ().
3. Android Android XML File (XML- Android). Next> (>) New Android XML File (
XML- Android).
4. main.xml TableLayout,
Finish.

182

Favorite Twitter Searches

2. Visual Layout Editor


Android SDK
SDK, Graphical Layout, Android 2.3.3 ( . 3.7). Android 2.3.3.

3. Visual Layout Editor


Device Configurations ( ), Graphical Layout (. . 3.11),
3.7in WVGA (Nexus One).
, 480800 (WVGA).

4. TableLayout
Outline TableLayout :





Background (): @android:color/white;


Id (): @+id/tableLayout;
Padding (): 5dp;
Stretch columns ( ): *.

Background
Android, white (), R.color. ,
, - developer.android.com/reference/
android/R.color.html.
,
@android:color/, .
, Layout width Layout
height match_parent. Padding
5dp, , 5 . , Stretch,
.

5. TableRow
Outline TableLayout TableRow (. 4).
TableRow TableLayout, TableRows TableLayout. Id
TableRow, tableRow0, tableRow1, tableRow2, tableRow3 tableRow4 . TableRow Layout
width match_parent. .

6. TableRows
. 5.6 5.7 , EditText, Button,
TextView ScrollView. TableLayout ScrollView.
(. . 5.7). XML- main.
xml ( 5.4), ,

5.4.

183

GUI-. , 5.4, .
5.4. XML- Favorite Twitter Search
1 <?xml version="1.0" encoding="utf-8"?>
2 <TableLayout xmlns:android=http://schemas.android.com/apk/res/android
3
android:id="@+id/tableLayout" android:layout_width="match_parent"
4
android:layout_height="match_parent" android:padding="5dp"
5
android:stretchColumns="*" android:background="@android:color/white">
6
7
<!-- tableRow0 -->
8
<TableRow android:id="@+id/tableRow0"
9
android:layout_height="wrap_content"
10
android:layout_width="match_parent">
11
<EditText android:layout_width="match_parent"
12
android:layout_height="wrap_content" android:layout_span="2"
13
android:inputType="text" android:id="@+id/queryEditText"
14
android:hint="@string/queryPrompt"
15
android:imeOptions="actionNext">
16
</EditText>
17
</TableRow>
18
19
<!-- tableRow1 -->
20
<TableRow android:id="@+id/tableRow1"
21
android:layout_height="wrap_content"
22
android:layout_width="match_parent">
23
<EditText android:layout_height="wrap_content"
24
android:hint="@string/tagPrompt" android:inputType="text"
25
android:id="@+id/tagEditText" android:imeOptions="actionDone"
26
android:layout_gravity="center_vertical"></EditText>
27
<Button android:id="@+id/saveButton"
28
android:layout_height="wrap_content"
29
android:layout_width="wrap_content"
30
android:layout_gravity="center_vertical"
31
android:text="@string/save"></Button>
32
</TableRow>
33
34
<!-- tableRow2 -->
35
<TableRow android:id="@+id/tableRow2"
36
android:layout_height="wrap_content"
37
android:layout_width="match_parent"
38
android:background="@color/light_orange">
39
40
<TextView android:layout_height="wrap_content"
41
android:id="@+id/taggedSearchesTextView"
42
android:text="@string/taggedSearches"
43
android:layout_width="match_parent"
44
android:layout_gravity="center_horizontal"
45
android:layout_span="2" android:textSize="18sp"
46
android:textColor="@android:color/black"

184

Favorite Twitter Searches

5.4 ()
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

android:padding="5dp"></TextView>
</TableRow>
<!-- tableRow3 -->
<TableRow android:id="@+id/tableRow3"
android:background="@color/light_orange"
android:layout_height="wrap_content"
android:layout_width="match_parent"> android:layout_weight="1"
<ScrollView android:id="@+id/queryScrollView"
android:layout_width="match_parent"
android:layout_span="2" android:padding="5dp">
<TableLayout android:id="@+id/queryTableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" android:padding="5dp"
android:stretchColumns="*"> </TableLayout>
</ScrollView>
</TableRow>
<!-- tableRow4 -->
<TableRow android:id="@+id/tableRow4"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/clearTags"
android:id="@+id/clearTagsButton"
android:layout_span="2"
android:layout_marginTop="5dp"></Button>
</TableRow>
</TableLayout>

main.xml
4 android:layout_span ( 12, 45, 58 75)
XML-, Properties,
. , colors.xml,
dimen.xml strings.xml, GUI, .
XML-, :


. @string/, . , 14 31 android:hint,
EditText. EditText ,
EditText.
GUI-, Button ( 31 73) TextView
( 41).

5.4.


185

. @color/, . , 38 52 ,
tableRow2 ScrollView .

15 25 android:imeOptions EditText, . ,
queryEditText , Next ().
android:imeOptions actionNext ( 15).
, ,
, tagEditText. tagEditText ,
Done ().
android:imeOptions actionDone ( 25).
, .
2731 7175 Button,
. 5663 ScrollView, TableLayout
( 5962), (
). android:stretchColumns TableLayout
"*", TableRow,
TableLayout,
. , , ScrollView,
Button TableLayout. 5.5,
TableLayout TableRows,
.
54 android:layout_weight tableRow3 1. tableRow3
, . android:layout_weight
tableRow3, , , .

5.4.5. TableRow,
Search Edit
TableRow, ,

. 5.5 , Button TableRow
queryTableLayout (. 5.4, 5962).
XML-, :
1. layout
NewOther New.
2. Android Android XML File Next>
New Android XML File.

186

Favorite Twitter Searches

3. File () new_tag_view.xml.
4. What type of resource would you like to create? ( )
Layout (). new_tag_view.
xml res/layout.
5. .
TableRow.
6. Finish ,
XML.
7. Graphical Layout ( ) Visual
Layout Editor, SDK Selector,
Graphical Layout, Android 2.3.3.
Device Configurations ( ),
Graphical Layout, 3.7in WVGA (Nexus One).
Button. Button layout (.
5.5). android:text newTagButton ,
Button. android:background TableLayout
transparent (), 6. ScrollView
TableRow ScrollView. ScrollView
, ( tableRow3). 9
12 @dimen/ ,
Button.
5.5. newTagTableRow,

1 <?xml version="1.0" encoding="UTF-8"?>
2 <TableRow xmlns:android=http://schemas.android.com/apk/res/android
3
android:id="@+id/newTagTableRow"
4
android:layout_width="match_parent"
5
android:layout_height="wrap_content"
6
android:background="@android:color/transparent">
7
8
<Button android:id="@+id/newTagButton"
9
android:layout_width="@dimen/tagButtonWidth"
10
android:layout_height="wrap_content"></Button>
11
<Button android:id="@+id/newEditButton"
12
android:layout_width="@dimen/editButtonWidth"
13
android:layout_height="wrap_content"
14
android:text="@string/edit"></Button>
15 </TableRow>

5.5.
5.65.16 Favorite Twitter Searches, FavoriteTwitterSearches, Activity.

5.5.

187

package import
5.6 package import, .
package ( 4) , ,
, i com.deitel.favoritetwittersearches. (IDE) . import
( 623) , .
5.6. package import,
FavoriteTwitterSearches
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

// FavoriteTwitterSearches.java
// ,
// .
package com.deitel.favoritetwittersearches;
import java.util.Arrays;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.content.Context;
android.content.DialogInterface;
android.content.Intent;
android.content.SharedPreferences;
android.net.Uri;
android.os.Bundle;
android.view.LayoutInater;
android.view.View;
android.view.View.OnClickListener;
android.view.inputmethod.InputMethodManager;
android.widget.Button;
android.widget.EditText;
android.widget.TableLayout;
android.widget.TableRow;

6 Arrays java.util. sort


, .
. import, , .


AlertDialog android.app ( 9)
.
 Context android.content ( 10) , , Android.
LayoutInflater ( )
GUI.

188





Favorite Twitter Searches

DialogInterface android.content ( 11)


OnClickListener. ,
AlertDialog.
Intent android.content ( 12)
(Intents). Intent
Android Intent .
SharedPreferences android.content ( 13) /, ,
.
Uri android.net ( 14) URL-
, Intent - . URI- URL-
5.5.
LayoutInflater android.view ( 16) XML-, GUI- .
InputMethodManager android.view.inputmethod ( 19)
.
android.widget ( 2023) ( GUI) , Android GUI. Button android.widget
(line 20) , . View.OnClickListener android.view ( 18)
,
Button.

Activity Favorite Twitter Searches


FavoriteTwitterSearches (. 5.75.16) Activity
Favorite Twitter Searches. FavoriteTwitterSearches
ADT Plugin Activity (. 5.7, 26) onCreate.
Activity.
5.7. FavoriteTwitterSearches Activity
25
26
27 {
28

// ( ) Activity
// Favorite Twitter Searches
public class FavoriteTwitterSearches extends Activity

29
30
31
32

private SharedPreferences savedSearches; //


//
private TableLayout queryTableLayout; //
private EditText queryEditText; //
private EditText tagEditText;
//

5.5.

189

28 savedSearches
SharedPreferences. SharedPreferences /, ,
. SharedPreferences . 29 TableLayout,
GUI,
. 3031 EditText,
,
.

OnCreate Activity
onCreate (. 5.8) :


;
,
, ;
 , / .


Activity GUI. .
37 onCreate .
, setContentView
( 38) R.layout.main GUI
main.xml. setContentView
XML-, GUI.
5.8. onCreate Activity
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); //
setContentView(R.layout.main);
//
// SharedPreferences,
//
savedSearches = getSharedPreferences("searches", MODE_PRIVATE);
// queryTableLayout
queryTableLayout =
(TableLayout) ndViewById(R.id.queryTableLayout);
// EditTexts Save
queryEditText = (EditText) ndViewById(R.id.queryEditText);
tagEditText = (EditText) ndViewById(R.id.tagEditText);


190

Favorite Twitter Searches

5.8 ()
51
52
53
54
55
56
57
58
59
60

// Save Clear Tags


Button saveButton = (Button) ndViewById(R.id.saveButton);
saveButton.setOnClickListener(saveButtonListener);
Button clearTagsButton =
(Button) ndViewById(R.id.clearTagsButton);
clearTagsButton.setOnClickListener(clearTagsButtonListener);

refreshButtons(null); //
// GUI
// onCreate

41 getSharedPreferences (
Context) SharedPreferences, /
, .
, .
:


MODE_PRIVATE. .
getSharedPreferences.
 MODE_WORLD_READABLE. , ,
.
 MODE_WORLD_WRITABLE. , ,
.
OR (|).
,
onCreate .
UI thread,
Application Not Responding (ANR),
. ANR
developer.android.com/
guide/practices/design/responsiveness.html.
4449 queryTableLayout, queryEditText
tagEditText . 5256 saveButton clearTagsButton
. , 58
refreshButtons (. 5.9), Button,
, Edit,
.

refreshButtons FavoriteTwitterSearches
refreshButtons FavoriteTwitterSearches (. 5.9)
( null), (
null).

5.5.

191

Button , . 6667 , SharedPreferences. getAll SharedPreferences


(Map), /. keySet , Set
. toArray (
) Set,
Set , 68. Arrays.
sort ( Arrays java.util)
.
, ,
. Arrays.sort
Comparator<String> String.CASE_INSENSITIVE_ORDER .
5.9. refreshButtons FavoriteTwitterSearches


61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

// Edit
// ;
// null Edit.
private void refreshButtons(String newTag)
{
// store saved tags in the tags array
String[] tags =
savedSearches.getAll().keySet().toArray(new String[0]);
Arrays.sort(tags, String.CASE_INSENSITIVE_ORDER); //
// GUI
//
if (newTag != null)
{
makeTagGUI(newTag, Arrays.binarySearch(tags, newTag));
} // if
else // GUI
{
//
for (int index = 0; index < tags.length; ++index)
makeTagGUI(tags[index], index);
} // else
} // refreshButtons

7180 , GUI,
. 73 makeTagGUI (. 5.11),
GUI . Arrays.binarySearch
, .

192

Favorite Twitter Searches

refreshButtons null, 7879


makeTagGUI .

makeTag FavoriteTwitterSearches
makeTag FavoriteTwitterSearches (. 5.10)
savedSearches . 87
getString SharedPreferences (
), . , ( null).
refreshButtons ( 96), GUI .
5.10. makeTag FavoriteTwitterSearches
, Button
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// , Button
private void makeTag(String query, String tag)
{
// originalQuery null
String originalQuery = savedSearches.getString(tag, null);
// SharedPreferences.Editor
// "/"
SharedPreferences.Editor preferencesEditor = savedSearches.edit();
preferencesEditor.putString(tag, query); //
preferencesEditor.apply(); //
// , GUI
if (originalQuery == null)
refreshButtons(tag); //
} // makeTag

9092
. , SharedPreferences,
edit,
SharedPreferences.Editor ( 90). ,
/, / , SharedPreferences. 91 puttring ,
() . 92 apply SharedPreferences.Editor,
.

makeTagGUI FavoriteTwitterSearches
makeTagGUI FavoriteTwitterSearches (. 5.11) queryTableLayout , , Edit.
, new_tag_view.xml, 5.4.5. TableRow, newTagButton newEditButton.

5.5.

193

Android ,
. , ( 103104) getSystemService, Activity.
Context.LAYOUT_INFLATER_SERVICE.
getSystemService ,
LayoutInflater. 107
LayoutInflater R.layout.new_tag_view,
new_tag_view.xml.
(View), TableRow,
Button. 110113 newTagButton,

OnClickListener. 116118 newEditButton
OnClickListener. 121
newTagView queryTableLayout , .
5.11. makeTagGUI FavoriteTwitterSearches
Edit Button , queryTableLayout,

99
100
101
102
103
104
105
106

// GUI
private void makeTagGUI(String tag, int index)
{
// LayoutInater
LayoutInater inater = (LayoutInater) getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// new_tag_view.xml
//
View newTagView = inater.inate(R.layout.new_tag_view, null);

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

// newTagButton,
Button newTagButton =
(Button) newTagView.ndViewById(R.id.newTagButton);
newTagButton.setText(tag);
newTagButton.setOnClickListener(queryButtonListener);
// newEditButton
Button newEditButton =
(Button) newTagView.ndViewById(R.id.newEditButton);
newEditButton.setOnClickListener(editButtonListener);

//
// queryTableLayout
queryTableLayout.addView(newTagView, index);
// makeTagGUI

194

Favorite Twitter Searches

clearButtons FavoriteTwitterSearches
clearButtons (. 5.12) (Button) . 128 removeAllViews
queryTableLayout, TableRow,
Button.
5.12. clearButtons FavoriteTwitterSearches
Button,

124
125
126
127
128
129
130

//
private void clearButtons()
{
//
queryTableLayout.removeAllViews();
} // clearButtons

,
OnClickListener saveButton
132170 (. 5.13)
saveButtonListener, OnClickListener. 53 saveButtonListener saveButtons.
134169 OnClick OnClickListener. ( 138139), makeTag ( 5.10),
/ ( 141142),
EditText ( 143144) ( 147149).
,
AlertDialog ( 151168), ,
. AlertDialog.Builder
( 154155) AlertDialog.
Context, .
FavoriteTwitterSearches Activity,
.
, , . 157
AlertDialog R.string.missingTitle.
.
5.13. , OnClickListener
saveButton
131
132
133
134
135
136
137

// Button ScrollView
public OnClickListener saveButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// , queryEditText tagEditText

5.5.
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

195

if (queryEditText.getText().length() > 0 &&


tagEditText.getText().length() > 0)
{
makeTag(queryEditText.getText().toString(),
tagEditText.getText().toString());
queryEditText.setText(""); // queryEditText
tagEditText.setText("");
// tagEditText
//
((InputMethodManager) getSystemService(
Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(
tagEditText.getWindowToken(), 0);
}
// if
else //
//
{
// AlertDialog Builder
AlertDialog.Builder builder =
new AlertDialog.Builder(FavoriteTwitterSearches.this);
builder.setTitle(R.string.missingTitle); //
// OK,
builder.setPositiveButton(R.string.OK, null);
//
builder.setMessage(R.string.missingMessage);
// AlertDialog AlertDialog.Builder
AlertDialog errorDialog = builder.create();
errorDialog.show(); // display the Dialog
} // else
} // onClick
};// OnClickListener

.
,
. ( 160).
setPositiveButton ( R.string.OK)
.
, null.
, .
163 , (
R.string.missingMessage). 166 AlertDialog create AlertDialog.Builder. 167
show AlertDialog, .

196

Favorite Twitter Searches

,
OnClickListener clearTagsButton
173213 5.14
clearTagsButtonListener, OnClickListener. 56
clearTagsButtons. 175212 onClick OnClickListener.
AlertDialog,
.
5.14. , OnClickListener
clearTagsButton
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

//
public OnClickListener clearTagsButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// AlertDialog Builder
AlertDialog.Builder builder =
new AlertDialog.Builder(FavoriteTwitterSearches.this);
builder.setTitle(R.string.conrmTitle); //
// OK,
builder.setPositiveButton(R.string.erase,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int button)
{
clearButtons(); //
// SharedPreferences.Editor
SharedPreferences.Editor preferencesEditor =
savedSearches.edit();
preferencesEditor.clear(); // /
preferencesEditor.apply(); //
}
// onClick
}
//
); // setPositiveButton
builder.setCancelable(true);
builder.setNegativeButton(R.string.cancel, null);
//
builder.setMessage(R.string.conrmMessage);
// AlertDialog AlertDialog.Builder

5.5.
210
211
212
213
214

197

AlertDialog conrmDialog = builder.create();


conrmDialog.show(); //
} // onClick
}; // OnClickListener

185201 AlertDialog .
. 191 clearButtons (. 5.12),
Button, . SharedPreferences.Editor savedSearches ( 194195), / clear SharedPreferences.Editor ( 192).
( 198). 203 ,
. , ,
Back (). 204
. 5.13, .
207211 ,
, .

,
OnClickListener , newTagButton
216234 5.15
queryButtonListener, OnClickListener. 113
newTagButton.
218233 onClick OnClickListener. 222
Button, , 223 savedSearches.
226 getString Activity.
searchURL, URL . URL-.
5.15. , OnClickListener
queryButton
215
216
217
218
219
220
221
222
223
224
225
226

// -
public OnClickListener queryButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
//
String buttonText = ((Button)v).getText().toString();
String query = savedSearches.getString(buttonText, null);
// URL-,
String urlString = getString(R.string.searchURL) + query;


198

Favorite Twitter Searches

5.15 ()
227
228
229
230
231
232
233
234
235

// -
Intent getURL = new Intent(Intent.ACTION_VIEW,
Uri.parse(urlString));
startActivity(getURL); //
}
// onClick
}; // OnClickListener

229230 Intent (), - . Intent ,


. , Intent,
, .
Intent.ACTION_VIEW, . Intent ,
, , . ( 230) Uri
(uniform resource identifier, ).
, . parse
Uri , URL- (uniform resource locator,
), Uri-.
232 Intent startActivity ( Context), Activity,
.
URI-, Intent - -.
, .
Intent. , -,
,
.
, startActivity, ,
.
, startActivity
ActivityNotFoundException.
. , Android,
, ,
-.
Intents, Activity, .
-:


openintents.org;

developer.android.com/guide/appendix/g-app-intents.html.

5.6. AndroidManifest.xml

199

,
OnClickListener editButton
237253 5.16
editButtonListener, OnClickListener. 118 newEditButton.
239252 onClick OnClickListener. , , editButton ( 243).
editButton, Button ID, R.id.
newTagButton ( 244245). . 247 searchButton, 250
tagEditText. , 251
savedSearches queryEditText.
5.16. , OnClickListener
editButton
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 }

//
public OnClickListener editButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// GUI
TableRow buttonTableRow = (TableRow) v.getParent();
Button searchButton =
(Button) buttonTableRow.ndViewById(R.id.newTagButton);
String tag = searchButton.getText().toString();
// EditTexts
tagEditText.setText(tag);
queryEditText.setText(savedSearches.getString(tag, null));
} // onClick
}; // OnClickListener
// FavoriteTwitterSearches

5.6. AndroidManifest.xml
Android Eclipse ADT Plugin
AndroidManifest.xml (
). . ( 5.17), ,
.
.
-: developer.android.com/guide/topics/manifest/
manifest-intro.html.

200

Favorite Twitter Searches

5.17. AndroidManifest.xml Favorite Twitter Searches


1 <?xml version="1.0" encoding="utf-8"?>
2 <manifest xmlns:android=http://schemas.android.com/apk/res/android
3
package="com.deitel.favoritetwittersearches"
4
android:versionCode="1" android:versionName="1.0">
5
<application android:icon="@drawable/icon"
6
android:label="@string/app_name">
7
<activity android:name=".FavoriteTwitterSearches"
8
android:label="@string/app_name"
9
android:windowSoftInputMode="stateAlwaysHidden">
10
<intent-lter>
11
<action android:name="android.intent.action.MAIN" />
12
<category android:name="android.intent.category.LAUNCHER" />
13
</intent-lter>
14
</activity>
15
</application>
16
<uses-sdk android:targetSdkVersion="10" android:minSdkVersion="8"/>
17 </manifest>

manifest ( 217) AndroidManifest.xml. package ( 3) , . android:versionCode ( 4)


,
. android:versionName ( 4)
, ,
.
manifest nested application ( 515)
uses-sdk ( 16). application .
android:icon drawable, . ,
ADT Plugin . res/drawable.
android:label . uses-sdk
SDK, (10 Android
SDK 2.3.3), SDK (8 2.2). Android 2.2 .
application activity ( 714),
Activity .
Activity,
activity. android:name ( 7)
Activity. (.),
, manifest. android:label ( 8) , Activity. , . 9 android:windowSoftInputMode.
stateAlwaysHidden ,
Activity. , XML-
, AndroidManifest.xml ,

5.6. AndroidManifest.xml

201

. . 5.8 Application (), .


. android:windowSoftInputMode,
.FavoriteTwitterSearches Application Nodes ( )
( ). activity. Window soft input mode (
) Select ().
. stateAlwaysHidden OK.

. 5.8. Application

activity intent-filter ( 1013),


, Activity.
action. 11 , .

202

Favorite Twitter Searches

category ( 12) Android,


. "android.intent.category.LAUNCHER"
,
.

5.7.
Favorite Twitter Searches.
(GUI).
ScrollView, ViewGroup, , .
.
(Button),
- . ,
New Android XML File, colors.xml
, dimen.xml, ,
, .
XML- Android R.color.
/ SharedPreferences,
. ,
. SharedPreferences.Editor
, ,
SharedPreferences. Uri -
startActivity Context.
AlertDialog.Builder
AlertDialogs, . GUI
XML-.
GUI . TableRow, Button
(), , .
TableRows TableLayout ScrollView,
.
, AndroidManifest.xml , ,
.
6 Flag Quiz Game,
,
3, 6 9 .
,
.

Flag
Quiz Game

P, AssetManager,
, ,











strings.xml.
.
AssetManager.
.
ImageView Drawable.
Handler.
- ArrayList
HashMap .
Menu MenuItems,
, onCreateOptionsMenu
Activity.
Android
.

6.1.
Flag Quiz Game
(. 6.1). . ,

204

Flag Quiz Game

.
, ( 10 )
TextView, .

. 6.1. Flag Quiz Game

. 6.2.

6.1.

205


, Button.
, Buttons,
, ,
(. 6.2). Buttons,
.


, Button, ,
Incorrect! (. 6.3).
, .

. 6.3. Flag Quiz Game

10
10 AlertDialog,

(. 6.4). Reset Quiz,
, .


.
Select Number

206

Flag Quiz Game

. 6.4.

. 6.5. Flag Quiz Game: ,


Select Number of Choices;
AlertDialog

of Choices ( ) Select Regions ( ).


Select Number of Choices AlertDialog,
3, 6 9 , -

6.2. Flag Quiz Game

207

. 6.6. Flag Quiz Game:


, Select Regions;
AlertDialog

(. 6.5).
(
).

,
Select Regions,
AlertDialog,
, ,
, (. 6.6).
. Reset Quiz ,
, .

6.2. Flag Quiz Game



Eclipse Flag Quiz Game.
:
1. Import. Import,
FileImport ().

208

Flag Quiz Game

2. FlagQuiz Game. Import General () Existing Projects into Workspace


( ). Next >
( >) Import Projects ( ). Browse (). Browse
For Folder ( ) FlagQuizGame
OK. Finish ()
Eclipse. Package Explorer,
Eclipse.
3. FlagQuiz Game. Eclipse FlagQuizGame, Package Explorer,
Run AsAndroid Application (
 Android).


Menu ( ) ,
. , (. . 6.5), Select
Number of Choices.
.
, 6.
Select Regions, ,
(. . 6.6). , ,
. Africa () Oceania
(), . ,
, .
, Reset Quiz ( ).


,
Africa Oceania. , ( )
. , ,
. , 10 , ,
AlertDialog,
(. . 6.4). , Reset Quiz.

6.3.
assets
1.
. assets
1

- www.free-country-flags.com.

6.3.

209

( , ). images/FlagQuizGameImages,
. drawable, , assets
,
( , ,
). , assets,
AssetManager ( android.content.res),
assets,
.
, , AssetManager
InputStream ( java.io) .
createFromStream Drawable, Drawable.
Drawable ( android.graphics.drawable)
ImageView, setImageDrawable
ImageView.



, , Menu (
android.view).
onCreateOptionsMenu Activity, Menu,
.
onOptionsItemSelected Activity. AlertDialogs .

Handler
Runnable
Handler ( android.os).
Runnable.
postDelayed Handler Runnable ( )
, .


,
Animation ( android.view.animation) ImageView. loadAnimation
AnimationUtils XML-, .
setRepeatCount Animation. ImageView startAnimation View (
Animation) ImageView.

210

Flag Quiz Game

Log.e
( )
Android . .
Android Log ( android.util),
, .
Android logcat. LogCat
Android DDMS (Dalvik Debug Monitor Server) Eclipse.
- developer.android.com/reference/
android/util/Log.html/.

Java

java.util. ArrayList<String>. shuffle
Collections
ArrayList<String> . ArrayList<String>
, 10
. HashMap<String, Boolean>
, . ArrayList<String> HashMap<String, Boolean> , List<String> Map<String,
Boolean> . Java,
,
. , HashMap Set<String>.

6.4.


Flag Quiz Game. XML-, Buttons ,
. XML ,
.

6.4.1. main.xml LinearLayout


LinearLayout
main.xml. . 6.7
. Id XML- Java
GUI.

6.4.

211

titleTextView
questionNumberTextView

flagImageView

guessCountryTextView
buttonTableLayout

answerTextView

. 6.7. GUI Flag Quiz Game,


Id. buttonTableLayout tableRow0,
tableRow1 tableRow2, Buttons

6.4.2.
Android FlagQuizGame.
New Android Project ( Android) ,
Finish ().


Build Target ( ): Android 2.3.3.

Application name ( ): FlagQuizGame.

Package name ( ): com.deitel.flagquizgame.

Create Activity ( ): FlagQuizGame.

Min SDK Version ( SDK): 8.

6.4.3.
, colors.xml dimen.
xml, .
, :
1. Package Explorer NewOther (), New
() Android Android XML File (XML- Android).
New Android XML File ( XML-
Android).
2. File () colors.xml.

212

Flag Quiz Game

3. What type of resource would you like to create? (


) Values ().
res/values .
4. Finish ().
5. dimen.xml.
colors.xml dimen.xml 6.16.2. main.
xml. .
6.1. , colors.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <resources>
3
<color name="text_color">#000000</color>
4
<color name="background_color">#FFFFCC</color>
5
<color name="correct_answer">#00CC00</color>
6
<color name="incorrect_answer">#FF0000</color>
7 </resources>

6.2. , dimen.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <resources>
3
<dimen name="title_size">25sp</dimen>
4
<dimen name="ag_width">227dp</dimen>
5
<dimen name="ag_height">150dp</dimen>
6
<dimen name="answer_size">40sp</dimen>
7
<dimen name="text_size">20sp</dimen>
8 </resources>

strings.xml
,
strings.xml ( 6.3).
strings.xml. ( 18
25) Buttons, ( 2630) .
XML string-array item ( 6.3).
6.3. , strings.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <resources>
3
<string name="app_name">FlagQuizGame</string>
4
<string name="choices">Select Number of Choices</string>
5
<string name="correct">correct</string>
6
<string name="guess_country">Guess the Country</string>
7
<string name="guesses">guesses</string>
8
<string name="incorrect_answer">Incorrect!</string>
9
<string name="more_regions_title">More Regions Required</string>
10
<string name="more_regions_message">There are not enough countries in

6.4.

213

11
the selected regions. Please select more regions.</string>
12
<string name="of">of</string>
13
<string name="ok">OK</string>
14
<string name="question">Question</string>
15
<string name="quiz_title">Ten Question Flag Quiz</string>
16
<string name="regions">Select Regions</string>
17
<string name="reset_quiz">Reset Quiz</string>
18
<string-array name="regionsList">
19
<item>Africa</item>
20
<item>Asia</item>
21
<item>Europe</item>
22
<item>North_America</item>
23
<item>Oceania</item>
24
<item>South_America</item>
25
</string-array>
26
<string-array name="guessesList">
27
<item>3</item>
28
<item>6</item>
29
<item>9</item>
30
</string-array>
31 </resources>


. :
1. Add (), String Array ( )
OK.
2. Name (), ,
.
3. , Add (), OK,
Item .
4. 3 .
5. Item Value
() .

6.4.4. LinearLayout
, , , . 6.7.
,
. strings.xml
(. 6.3), colors.xml (. 6.1) dimen.xml (. 6.2).
GUI .
GUI, XML- ,
.

214

Flag Quiz Game

1. LinearLayout
Outline () LinearLayout .


Background (): @color/background_color.

Gravity (): center_horizontal.


Id (): @+id/linearLayout.

Layout width Layout height, fill_


parent ( ) match_parent.

2.
. 6.7 , TextViews, ImageView
TableLayout linearLayout . Id Text
. XML- main.xml ( 6.4), .
. Buttons TableRows,
.
6.4. XML- (main.xml) FlagQuizGame
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

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


<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/linearLayout" android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:background="@color/background_color">
<TextView android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/quiz_title"
android:layout_marginBottom="10dp"
android:textSize="@dimen/title_size"
android:textColor="@color/text_color" android:gravity="center">
</TextView>
<TextView android:id="@+id/questionNumberTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:textColor="@color/text_color"
android:textSize="@dimen/text_size"
android:layout_gravity="center"
android:gravity="center"></TextView>
<ImageView android:id="@+id/agImageView"

6.4.
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

215

android:adjustViewBounds="false"
android:layout_width="@dimen/ag_width"
android:layout_height= "@dimen/ag_height"></ImageView>
<TextView android:id="@+id/guessCountryTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:text="@string/guess_country"
android:textColor="@color/text_color"
android:textSize= "@dimen/text_size"></TextView>
<TableLayout android:id="@+id/buttonTableLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" android:stretchColumns="0,1,2">
<TableRow android:id="@+id/tableRow0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"></TableRow>
<TableRow android:id="@+id/tableRow1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"></TableRow>
<TableRow android:id="@+id/tableRow2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"></TableRow>
</TableLayout>
<TextView android:id="@+id/answerTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/answer_size"
android:layout_gravity="center" android:textStyle="bold"
android:gravity="center"></TextView>
</LinearLayout>

main.xml
27 android:adjustViewBounds ImageView, ImageView Drawable.
false, .
42 android:layout_weight buttonTableLayout
1. buttonTableLayout (
) linearLayout
. android:layout_weight
buttonTableLayout,

216

Flag Quiz Game

, ,
. android:stretchColumns buttonTableLayout
0,1,2, TableRow,
.

6.4.5.
XML- Button.
XML-, Button . 6.5 Buttons TableRow.
XML-, :
1.
NewOther (...)
New ().
2. Android Android XML File (XML- Android)
Next > ( >) New Android XML File
( XML- Android).
3. File () guess_button.xml.
4. What type of resource would you like to create? ( )
Layout (). guess_button.xml
res/layout.
5.
. Button.
6. Finish (). XML.
7. Button ( 6.5).
6.5. newGuessButton
(guess_button.xml)
1
2
3
4
5

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


<Button xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/newGuessButton" android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>

6.4.6.
XML-, 6.6, , . , XML-,
6.5.

6.4.

217

6.6. (incorrect_shake.xml),

1
2
3
4
5
6
7
8
9
10
11
12
13
14

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


<set xmlns:android=http://schemas.android.com/apk/res/android
android:interpolator="@android:anim/decelerate_interpolator">
<translate android:fromXDelta="0" android:toXDelta="-5%p"
android:duration="100"/>
<translate android:fromXDelta="-5%p" android:toXDelta="5%p"
android:duration="100" android:startOffset="100"/>
<translate android:fromXDelta="5%p" android:toXDelta="-5%p"
android:duration="100" android:startOffset="200"/>
</set>

:
1.
NewOther (...),
New ().
2. Android Android XML File (XML- Android)
Next > ( >) New Android XML File
( XML- Android).
3. File () incorrect_shake.xml.
4. What type of resource would you like to create? ( )
Animation (). incorrect_
shake.xml res/anim.
5. .
6. Finish (). XML.
7. (. 6.6).
View
. , ( 314).
, . alpha (), scale (
), translate () rotate.
translate. translate View .
3.0, Android ,
.
SpotOn Game 8.
translate ( 67) View . android:fromXDelta
View ( ),

218

Flag Quiz Game

android:toXDelta View . :


( );
View;
 View.


android:fromXDelta 0.
android:toXDelta -5%p, , View
( ) 5 %
( p). ,
5 % View, p .
android:duration ( ). , , 67, View
5 % 100 .
( 910) ,
. View ,
-5%p, , %5p, 100 .
, , ,
android:startOffset ( )
.
.
100 . ( 1213)
,
200 .

6.5.
6.76.15 Flag Quiz Game
FlagQuizGame, Activity.

package import
6.7 package import FlagQuizGame.java. package 3 , com.deitel.
flagquizgame ( ). 535 Java Android, , . ,
, 6.3.
6.7. package import FlagQuizGames
1
2
3
4
5
6
7

// FlagQuizGame.java
// Activity Flag Quiz Game App
package com.deitel.agquizgame;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

6.5.
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

import
import
import
import
import
import

java.util.Collections;
java.util.HashMap;
java.util.List;
java.util.Map;
java.util.Random;
java.util.Set;

import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.content.Context;
android.content.DialogInterface;
android.content.res.AssetManager;
android.graphics.drawable.Drawable;
android.os.Bundle;
android.os.Handler;
android.util.Log;
android.view.LayoutInater;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.view.View.OnClickListener;
android.view.animation.Animation;
android.view.animation.AnimationUtils;
android.widget.Button;
android.widget.ImageView;
android.widget.TableLayout;
android.widget.TableRow;
android.widget.TextView;

219


6.8 FlagQuizGame. 40
static final String TAG,
Log (. 6.10). Activity ,
.
List<String> fileNameList , , . List<String>
quizCountriesList 10 ,
. Map<String, Boolean> regionsMap , .
String correctAnswer . int totalGuesses
, .
int correctAnswers ,
. 10
. int guessRows
three-Button, .

220

Flag Quiz Game

Random random ,
, , , Button, . Handler handler
.
Animation shakeAnimation
, , . 5356 ,
GUI.
6.8. FlagQuizGame
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

public class FlagQuizGame extends Activity


{
// ,
private static nal String TAG = "FlagQuizGame Activity";
private
private
private
private
private
private
private
private
private

List<String> leNameList; //
List<String> quizCountriesList; //
Map<String, Boolean> regionsMap; //
String correctAnswer; //
int totalGuesses; //
int correctAnswers; //
int guessRows; // ,
Random random; //
Handler handler; //
//
private Animation shakeAnimation; //
private TextView answerTextView; // Correct!
// Incorrect!
private TextView questionNumberTextView; // #
private ImageView agImageView; //
private TableLayout buttonTableLayout; // Buttons

OnCreate Activity
onCreate (. 6.9) GUI
Activity. , onCreate ( 62),
Activity ( 63).
6.9. onCreate Activity
58
59
60
61
62
63

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

6.5.
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

221

leNameList = new ArrayList<String>(); //


quizCountriesList = new ArrayList<String>(); //
regionsMap = new HashMap<String, Boolean>(); // -
guessRows = 1; //
random = new Random(); //
handler = new Handler(); //
// ,
//
shakeAnimation =
AnimationUtils.loadAnimation(this, R.anim.incorrect_shake);
shakeAnimation.setRepeatCount(3); //
// strings.xml
String[] regionNames =
getResources().getStringArray(R.array.regionsList);
//
for (String region : regionNames)
regionsMap.put(region, true);
// GUI
questionNumberTextView =
(TextView) ndViewById(R.id.questionNumberTextView);
agImageView = (ImageView) ndViewById(R.id.agImageView);
buttonTableLayout =
(TableLayout) ndViewById(R.id.buttonTableLayout);
answerTextView = (TextView) ndViewById(R.id.answerTextView);
// questionNumberTextView
questionNumberTextView.setText(
getResources().getString(R.string.question) + " 1 " +
getResources().getString(R.string.of) + " 10");
resetQuiz(); //
} // onCreate

6566 ArrayList<String>,

, . 67 HashMap<String, Boolean>, ,
.
guessRows 1,
Buttons, .
, (
) ( ).
69 Random random,
, , ,

222

Flag Quiz Game

Button . 70
Handler handler,
,
.
7374
, , ,
. loadAnimation AnimationUtils
XML-, R.anim.incorrect_shake.
Context ( FlagQuizGame),
, . 75
, setRepeatCount
Animation.
7879
regionNames. getResources ( ContextWrapper)
Resources ( android.content.res),
Activity. getStringArray
, R.array.regionsList strings.xml.
8283 put
HashMap. true ().
(. 6.136.14).
8691 GUI,
. 9496 , questionNumberTextView. questionNumberTextView. 7.4.3
. 98
resetQuiz FlagQuizGame, .

resetQuiz FlagQuizGame ( )
resetQuiz (. 6.11) .
,
assets.
AssetManager ( 106).
getAssets ( ContextWrapper).
107 fileNameList,
, . keySet ( 111) HashMap , regionsMap.
, Set<String>.
( 114124). list
AssetManager ( 119) ,
, paths. 121122
.png ,
fileNameList.

6.5.

223

6.11. resetQuiz FlagQuizGame


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145

//
private void resetQuiz()
{
// AssetManager
//
AssetManager assets = getAssets(); // AssetManager
//
leNameList.clear(); //
try
{
Set<String> regions = regionsMap.keySet(); //
//
//
for (String region : regions)
{
if (regionsMap.get(region)) //
{
//
String[] paths = assets.list(region);
for (String path : paths)
leNameList.add(path.replace(".png", ""));
}
// if
}
// for
}
// try
catch (IOException e)
{
Log.e(TAG, "Error loading image le names", e);
}
// catch
correctAnswers = 0; //
totalGuesses = 0;
//
//
quizCountriesList.clear();
//
//
// 10 quizCountriesList
int agCounter = 1;
int numberOfFlags = leNameList.size(); //
while (agCounter <= 10)
{
int randomIndex = random.nextInt(numberOfFlags); //
//
//
String leName = leNameList.get(randomIndex);


224

Flag Quiz Game

6.11 ()
146
147
148
149
150
151
152
153
154
155
156

// ,
if (!quizCountriesList.contains(leName))
{
quizCountriesList.add(leName); //
++agCounter;
}
// if
// while

loadNextFlag(); //
// resetQuiz

131133
, (correctAnswers),
, (totalGuesses),
quizCountriesList.
136152 10
quizCountriesList. ,
0 , , .
fileNamesList. quizCountriesList , ,
flagCounter. , 10
. 154 loadNextFlag (.
6.11) .

loadNextFlag, getTableRow getCountryName


FlagQuizGame
loadNextFlag (. 6.11)
Buttons.
, quizCountriesList, , .png:
regionName-countryName

regionName countryName ,
(_).
6.11. loadNextFlag FlagQuizGame
157
158
159
160
161
162
163

//
//
private void loadNextFlag()
{
//
//
String nextImageName = quizCountriesList.remove(0);
correctAnswer = nextImageName; //

6.5.
164
165
166
167
168
169
170
171
172
173
174
175
176

answerTextView.setText(""); // answerTextView
//
questionNumberTextView.setText(
getResources().getString(R.string.question) + " " +
(correctAnswers + 1) + " " +
getResources().getString(R.string.of) + " 10");
//
String region =
nextImageName.substring(0, nextImageName.indexOf('-'));
// AssetManager
// assets
AssetManager assets = getAssets(); // AssetManager
//
InputStream stream; //

177
178
179
180
181
182
183
184
185

try
{
// InputStream ,
stream = assets.open(region + "/" + nextImageName + ".png");
// Drawable
// agImageView
Drawable ag = Drawable.createFromStream
(stream, nextImageName);
agImageView.setImageDrawable(ag);
} // try
catch (IOException e)

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

225

{
Log.e(TAG, "Error loading " + nextImageName, e);
} // catch
// Buttons TableRows
for (int row = 0; row < buttonTableLayout.getChildCount(); ++row)
((TableRow) buttonTableLayout.getChildAt(row)).removeAllViews();
Collections.shufe(leNameList); //
// leNameList
int correct = leNameList.indexOf(correctAnswer);
leNameList.add(leNameList.remove(correct));
// LayoutInater
LayoutInater inater = (LayoutInater) getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// 3, 6 9 Buttons
// guessRows
for (int row = 0; row < guessRows; row++)

226

Flag Quiz Game

6.11 ()
210
211
212
213
214
215
216
217
218
219
220

{
TableRow currentTableRow = getTableRow(row);
// Buttons currentTableRow
for (int column = 0; column < 3; column++)
{
// guess_button.xml Button
Button newGuessButton =
(Button) inater.inate(R.layout.guess_button, null);
//
// newGuessButton
String leName = leNameList.get((row * 3) + column);
newGuessButton.setText(getCountryName(leName));

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

// answerButtonListener
//
newGuessButton.setOnClickListener(guessButtonListener);
currentTableRow.addView(newGuessButton);
} // for
// for

// Button
int row = random.nextInt(guessRows); //
int column = random.nextInt(3); //
TableRow randomTableRow = getTableRow(row); // TableRow
String countryName = getCountryName(correctAnswer);
((Button)randomTableRow.getChildAt(column)).setText(countryName);
} // loadNextFlag
// TableRow
private TableRow getTableRow(int row)
{
return (TableRow) buttonTableLayout.getChildAt(row);
} // getTableRow
//
private String getCountryName(String name)
{
return name.substring(name.indexOf('-') + 1).replace('_', ' ');
} // getCountryName

161 quizCountriesList,
nextImageName.
correctAnswer . answerTextView
questionNumerTextView

6.5.

227

( 164170).
, 7.
173174 nextImageName , assets, . AssetManager, try InputStream,
, .
createFromStream
Drawable, Drawable.
Drawable flagImageView
setImageDrawable. try
( 180188),
Android ( ). , .
e ( ,
).
,
- developer.android.com/reference/android/util/Log.html.
195196 Buttons
TableRows buttonTableLayout. 198
fileNameList, 201202
correctAnswer fileNameList. answer Buttons.
205206 LayoutInflater,
Button, , guess_button.xml. 209228
buttonTableLayout ( guessRows).
Button :


217218 Button guess_button.xml;

221 ;

222 Button ;

225 OnClickListener Button;

226 Button
TableRow.

231235 (
guessRows) buttonTableLayout,
Button, ,
.
211 233 loadNextFlag
getTableRow ( 239242) TableRow
buttonTableLayout. 222 234 getCountryName
( 245248) .

228

Flag Quiz Game

submitGuess disableButtons FlagQuizGame


submitGuess (. 6.12)
Button, , .
guessButton Button, . Button ( 253)
( 254), totalGuesses.
6.12. submitGuess FlagQuizGame
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290

// ,
private void submitGuess(Button guessButton)
{
String guess = guessButton.getText().toString();
String answer = getCountryName(correctAnswer);
++totalGuesses; //
//
if (guess.equals(answer))
{
++correctAnswers; // 1
// "Correct!"
answerTextView.setText(answer + "!");
answerTextView.setTextColor(
getResources().getColor(R.color.correct_answer));
disableButtons(); // Buttons
// 10
if (correctAnswers == 10)
{
// AlertDialog Builder
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.reset_quiz); //
// AlertDialog,
//
builder.setMessage(String.format("%d %s, %.02f%% %s",
totalGuesses, getResources().getString(R.string.guesses),
(1000 / (double) totalGuesses),
getResources().getString(R.string.correct)));
builder.setCancelable(false);
// Button "Reset Quiz"
builder.setPositiveButton(R.string.reset_quiz,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{

6.5.
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337

};

229

resetQuiz();
} // onClick
}
//
// setPositiveButton

// AlertDialog Builder
AlertDialog resetDialog = builder.create();
resetDialog.show(); //
}
// if
else // ,
{
//
handler.postDelayed(
new Runnable()
{
@Override
public void run()
{
loadNextFlag();
}
}, 1000); // 1000 ( )
} // else
}
// if
else //
{
//
agImageView.startAnimation(shakeAnimation);

// "Incorrect!"
answerTextView.setText(R.string.incorrect_answer);
answerTextView.setTextColor(
getResources().getColor(R.color.incorrect_answer)
guessButton.setEnabled(false); //
//
}
// else
// submitGuess

// , Buttons
private void disableButtons()
{
for (int row = 0; row < buttonTableLayout.getChildCount(); ++row)
{
TableRow tableRow = (TableRow) buttonTableLayout.getChildAt(row);
for (int i = 0; i < tableRow.getChildCount(); ++i)
tableRow.getChildAt(i).setEnabled(false);
}
// for
}
// disableButtons

( 258) , correctAnswers.

230

Flag Quiz Game

answerTextView , ,
R.color.correct_answer. disableButtons
( 328336), buttonTableLayout Buttons.
correctAnswers 10 ( 270), . 273299 AlertDialog.Builder,
,
. AlertDialog, .
Reset Quiz, resetQuiz,
.
correctAnswers 10, 303311
postDelayed Handler handler.
, Runnable.
(loadNextFlag),
. , (1000).
, 317
startAnimation flagImageView, shakeAnimation, onCreate.
answerTextView Incorrect!, ( 320322), setEnabled guessButton false
( 323), Button,
.

onCreateOptionsMenu Activity
OnCreateOptionsMenu Activity (. 6.13)
Activity.
Menu, .
,
Select Number of Choices ( ) Select Regions (
). Select Number of Choices 3, 6 9, .
Select Regions ,
.
6.13. onCreateOptionsMenu Activity
338
339
340
341
342
343
344
345
346
347

//
private nal int CHOICES_MENU_ID = Menu.FIRST;
private nal int REGIONS_MENU_ID = Menu.FIRST + 1;
// ,
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);

6.5.
348
349
350
351
352
353
354

231

// "Choices" "Regions"
menu.add(Menu.NONE, CHOICES_MENU_ID, Menu.NONE, R.string.choices);
menu.add(Menu.NONE, REGIONS_MENU_ID, Menu.NONE, R.string.regions);
return true; //
// onCreateOptionsMenu

339340 ID . Menu.FIRST
, Menu.
ID. onCreateOptionsMenu
onCreateOptionsMenu . add Menu,
MenuItems Menu ( 349350). ID MenuItem
MenuItems, (, ). Menu.NONE, MenuItem .
ID MenuItem. ,
MenuItem. Menu.NONE,
MenuItems .
String, .
true ( 352).

onOptionsItemSelected Activity
onOptionsItemSelected (. 6.14) ,

MenuItem. switch . switch getItemId item,
( 360),
, MenuItem .
6.14. onOptionsItemSelected Activity
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// ,
//
switch (item.getItemId())
{
case CHOICES_MENU_ID:
//
nal String[] possibleChoices =
getResources().getStringArray(R.array.guessesList);
// AlertDialog Builder
AlertDialog.Builder choicesBuilder =
new AlertDialog.Builder(this);
choicesBuilder.setTitle(R.string.choices);

232

Flag Quiz Game

6.11 ()
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417

// possibleChoices
// ,
//
choicesBuilder.setItems(R.array.guessesList,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int item)
{
// guessRows,
//
guessRows = Integer.parseInt(
possibleChoices[item].toString()) / 3;
resetQuiz(); //
}
// onClick
}
//
); // setItems
// AlertDialog Builder
AlertDialog choicesDialog = choicesBuilder.create();
choicesDialog.show(); //
return true;
case REGIONS_MENU_ID:
//
nal String[] regionNames =
regionsMap.keySet().toArray(new String[regionsMap.size()]);
// ,
boolean[] regionsEnabled = new boolean[regionsMap.size()];
for (int i = 0; i < regionsEnabled.length; ++i)
regionsEnabled[i] = regionsMap.get(regionNames[i]);
// AlertDialog Builder
//
AlertDialog.Builder regionsBuilder =
new AlertDialog.Builder(this);
regionsBuilder.setTitle(R.string.regions);
// _
String[] displayNames = new String[regionNames.length];
for (int i = 0; i < regionNames.length; ++i)
displayNames[i] = regionNames[i].replace('_', ' ');
// displayNames
//
regionsBuilder.setMultiChoiceItems(
displayNames, regionsEnabled,
new DialogInterface.OnMultiChoiceClickListener()
{

6.5.
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

);

233

@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked)
{
// /
// ,
regionsMap.put(
regionNames[which].toString(), isChecked);
}
// onClick
}
//
// setMultiChoiceItems

// "Reset Quiz"
regionsBuilder.setPositiveButton(R.string.reset_quiz,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int button)
{
resetQuiz(); //
} // onClick
}
//
); // setPositiveButton
// Builder
AlertDialog regionsDialog = regionsBuilder.create();
regionsDialog.show(); //
return true;
// switch

return super.onOptionsItemSelected(item);
// onOptionsItemSelected

Select Number of Choices,


, 362390. 364365
guessesList possibleChoices.
AlertDialog.Builder, ( 368370).
AlertDialogs , Buttons. possibleChoice ,
, . ,
setItems AlertDialog.Builder ( 374385).
,
( ). DialogInterface.OnClickListener
. onClick listener , . possibleChoices,

234

Flag Quiz Game

String int, 3
guessRows. resetQuiz
Buttons. 388389 .
Select Regions, ,
392445, AlertDialog, , .
-, regionNames , regionsMap
( 394395). 398400 boolean,
. 403405
AlertDialog.Builder . 408410 displayNames, ,
.
setMultiChoiceItems AlertDialog.Builder,
. (. . 6.6). ,
,
boolean, .
,
. DialogInterface.
OnMultiChoiceClickListener,
. ( 416427) onClick (listener), , . ,
.
. regionsMap.
431440 Button .
, resetQuiz,
, . Back () ,
, . , 443444
.

, OnClickListener
, Buttons

guessButtonListener
OnClickListener, , Button. 225 guessButtonListener -
newGuessButton. onClick Button
submitGuess (. 6.15).

6.6. AndroidManifest.xml

235

6.15. , OnClickListener
answerButton
451
452
453
454
455
456
457

// ,
private OnClickListener guessButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
submitGuess((Button) v); //
// Button submitGuess
458
} // onClick
459
}; // answerButtonListener
460 } // FlagQuizGame

6.6. AndroidManifest.xml
5.6 . , , ( 6.16). 7 android:theme
application .
,
GUI.
, .
- developer.android.com/reference/
android/R.style.html.
-
developer.android.com/guide/topics/ui/themes.html.
, Application () . , 7,
Theme ().
activity ( 10) android:screenOrientation
( ). ,
Application .
Application.
Screen orientation ( )
portrait ().
.
6.16. AndroidManifest.xml Flag Quiz Game
1
2
3
4
5
6
7

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.agquizgame" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar">

236

Flag Quiz Game

6.16 ()
8
9
10
11
12
13
14
15
16
17
18

<activity android:name=".FlagQuizGame"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
</application>
<uses-sdk android:targetSdkVersion="10" android:minSdkVersion="8"/>
</manifest>

6.7.
Flag Quiz Game,
. String strings.xml.
String colors.xml strings.xml Resources
Activity.
, , AssetManager. ,
.
createFromStream Drawable
Drawable, ImageView
setImageDrawable ImageView.
, Menu .
Menu, onCreateOptionsMenu
Activity. ,
onOptionsItemSelected Activity.

postDelayed Handler
Runnable , 1000 .
,
ImageView.
loadAnimation AnimationUtils XML-, .
setRepeatCount Animation,
startAnimation (
Animation) View ImageView.
( )
Android.
.

6.7.

237

java.util.
7 Cannon Game. , ,

. .


Cannon Game

,
, , ,
, SurfaceView SurfaceHolder












, .
SurfaceView .
Paints Canvas.
onTouchEvent Activity , , .
GestureDetector
, .
.
SoundPooll AudioManager.

Activity.

7.1.
Cannon Game
10 , (. 7.1). , , , , . , .

7.2. Cannon Game

239

, .
. AlertDialog, ,
, (. 7.2).

. 7.1. Cannon Game

, 10 .
, .
,
. ,
.
. .
, . ,
, . .
. .

7.2. Cannon Game



Eclipse Cannon Game app.
:
1. Import. FileImport
().

240

Cannon Game

. 7.2. AlertDialogs Cannon Game


, :
AlertDialog;
, AlertDialog

2. Canon Game. Import


General () Existing Projects into Workspace ( ). Next > ( >)
Import Projects ( ).
Browse (). Browse For Folder
( ) CanonGame
OK. Finish ()
Eclipse. Package Explorer,
Eclipse.
3. Canon Game. Eclipse
CanonGame, Package Explorer,
Run AsAndroid Application ( 
Android).


, .
, . , .
AVD,

7.3.

241

. .
, .

7.3.
, Cannon Game.
, .

strings.xml
String,
. getString (
format String) Resource. , ( 1)

.
.
strings.xml , .
, ,
7.4.3.


View
. CannonView ( 7.5.3), SurfaceView ( ).
XML-, XML-,
. 7.4.4.

raw
(, ), Cannon Game,
res/raw. 7.4.5. .

onPause onDestroy Activity


Activity.
onPause Activity , .
. onPause .
, .
Activity , onDestroy. .
7.5.2.

242

Cannon Game

onTouchEvent Activity
,
. ,
( ) .
Activity, , onTouchEvent
Activity ( 7.5.2), MotionEvent
( android.view)
.

GestureDetector SimpleOnGestureListener
, ,
, GestureDetector
( android.view). ,
MotionEvents. GestureDetector
, , , . GestureDetector.OnGestureListener GestureDetector.OnDoubleTapListener.
GestureDetector.SimpleOnGestureListener ,
. (),
. 7.5.2
GestureDetector SimpleOnGestureListener,
, .

SoundPool AudioManager
SoundPool
( android.media). , .
Android,
, DTMF, , ,
, . Android .
setVolumeControlStream Activity
,
.
AudioManager ( android.media).

, SurfaceView SurfaceHolder

, .
Thread run, CannonView
, . , GUI. Android

7.4.

243

, GUI, , GUI
ANR (Application Not Responding, ).
, , .
Android SurfaceView View,
. SurfaceView
SurfaceHolder,
Canvas, . SurfaceHolder
, Canvas , SurfaceView .
SurfaceView SurfaceHolder.Callback, , , ( )
SurfaceView.


CannonView - CannonView, .
7.5.3.

, ,
.

Paint Canvas
Canvas ( android.graphics) ,
. Canvas Bitmap View.
Canvas Paint ( android.
graphics) , , ,
.
drawGameElements, 7.5.3.
, Paint, - developer.android.com/reference/android/graphics/
Paint.html.

7.4.

main.xml.

7.4.1.
Android Cannon Game.
New Android Project ( Android) ,
Finish ():

244


Cannon Game

Build Target ( ): Android 2.3.3.

Application name ( ): Cannon Game.


 Package name ( ): com.deitel.cannongame.
 Create Activity ( ): CannonGame.
 Min SDK Version ( SDK): 8.

7.4.2. AndroidManifest.xml
7.1 XML- AndroidManifest.xml.
6.6, android:screenOrientation "portrait" ( 9),
.
7.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.cannongame" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name" android:debuggable="true">
<activity android:name=".CannonGame"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10"/>
</manifest>

7.4.3. strings.xml
strings.xml, ,
( 7.2, 45 910). 7.3, ,
,
(, ). 1$ %1$.1f
( 5) , %1$d. , %2$.1f ,

%2$.1f. d ,
, f
. strings.xml %1$d %2$.1f
. format Resourcesmethod getString String

7.4.

245

%1$d, %2$.1f ( ,
).
7.2. , strings.xml
1
2
3
4
5
6
7
8
9
10
11

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


<resources>
<string name="app_name">Cannon Game</string>
<string name="results_format">
Shots red: %1$d\nTotal time: %2$.1f</string>
<string name="reset_game">Reset Game</string>
<string name="win">You win!</string>
<string name="lose">You lose!</string>
<string name="time_remaining_format">
Time remaining: %.1f seconds</string>
</resources>

7.4.4. main.xml
main.xml, , FrameLayout. View CannonView
7.5.3. 7.3 main.xml,
XML-, 27. ,
CannonView ,
. 7.3 View XML, 2
CannonView com.deitel.cannongame.CannonView.
7.3. XML- (main.xml) Cannon Game
1
2
3
4
5
6
7

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


<com.deitel.cannongame.CannonView
xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/cannonView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"/>

7.4.5.
, res/raw.
: blocker_hit.wav,
target_hit.wav cannon_fire.wav. sounds .
, :
1. res
NewFolder ().
2. raw Finish (), .
3. res/raw.

246

Cannon Game

7.5.
Line ( 7.4), CannonGame (
Activity; 7.57.8) CannonView ( 7.97.21).

7.5.1. Line
Line (. 7.4) Points,
(Point). . Line ,
:
1. src Package Explorer.
2. (com.deitel.cannongame)
NewClass ()
New Java Class ( Java).
3. Name (), , Line,
Finish ().
4. , 7.4, Line.java.
7.4. Line
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18

// Line.java
// Line,
// .
package com.deitel.cannongame;
import android.graphics.Point;
public class Line
{
public Point start; //
public Point end;
//

// ,
// Point ()
// (0, 0)
public Line()
{
start = new Point(0, 0); //
end = new Point(0, 0);
//
} // Line
// Line

7.5.2. CannonGame Activity


CannonGame (. 7.57.8) Activity
Cannon Game.

7.5.

247

package, import
7.3 ,
CannonGame. 7.5.
15 cannonView,
CannonGame CannonView.
7.5. package, import
CannonGame
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// CannonGame.java
// Activity Cannon Game.
package com.deitel.cannongame;
import
import
import
import
import
import

android.app.Activity;
android.os.Bundle;
android.media.AudioManager;
android.view.GestureDetector;
android.view.MotionEvent;
android.view.GestureDetector.SimpleOnGestureListener

public class CannonGame extends Activity


{
private GestureDetector gestureDetector; //
//
private CannonView cannonView; //
//

16

onCreate, onPause onDestroy Activity


7.6 onCreate ( 1832), onPause
( 3540) onDestroy ( 4348) Activity. onCreate main.xml , CannonView ( 25).
28 GestureDetector,
gestureListener, 7.8. , 31,

Android.
7.6. onCreate, onPause onDestroy Activity
17
18
19
20
21
22
23
24
25

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); // onCreate super
setContentView(R.layout.main);
//
// CannonView
cannonView = (CannonView) ndViewById(R.id.cannonView);


248

Cannon Game

7.6 ()
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

// GestureDetector
gestureDetector = new GestureDetector(this, gestureListener);

//
//
setVolumeControlStream(AudioManager.STREAM_MUSIC);
// onCreate

// ,
@Override
public void onPause()
{
super.onPause();
// super
cannonView.stopGame(); //
}
// onPause
//
@Override
protected void onDestroy()
{
super.onDestroy();
cannonView.releaseResources();
} // onDestroy

onPause ( 3540) , CannonGame


, . , ,
.
. onPause
39 stopGame cannonView (. 7.19).

.
, onDestroy ( 4346) releaseResources cannonView (. 7.19),
.

onTouchEvent Activity
onTouchEvent (. 7.7)
. MotionEvent . 55
getAction MotionEvent
. 5859
(MotionEvent.ACTION_DOWN) (MotionEvent.
ACTION_MOVE). 61 alignCannon
cannonView (. 7.16),

7.5.

249

. 65 MotionEvent
onTouchEvent gestureDetector , .
7.7. onTouchEvent Activity
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

// ,
//
@Override
public Boolean OnTouchEvent(MotionEvent event)
{
// int, ,
int action = event.getAction();
//
if (action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_MOVE)
{
cannonView.alignCannon(event); //
} // end if

// onTouchEvent GestureDetector
return gestureDetector.onTouchEvent(event);
// onTouchEvent

,
SimpleOnGestureListener
7.8 SimpleOnGestureListener gestureListener,
28 GestureDetector. SimpleOnGestureListener OnGestureListener
OnDoubleTapListener. false,
, . onDoubleTap
( 7176), .
74 fireCannonBall CannonView (. 7.15), . fireCannonBall
MotionEvent.
. 75 true,
, .
7.8. , SimpleOnGestureListener
67
68
69
70
71
72
73

// , ,
// GestureDetector
SimpleOnGestureListener gestureListener =
new SimpleOnGestureListener()
{
//
@Override
public boolean onDoubleTap(MotionEvent e)
{

250

Cannon Game

7.8 ()
74
75
76
77
78

cannonView.reCannonball(e); //
return true; //
} // onDoubleTap
}; // gestureListener
// CannonGame

7.5.3. CannonView View


CannonView (. 7.97.21) View,
Cannon Game .
, :
1. src Package Explorer.
2. (com.deitel.cannongame)
NewClass () New Java
Class ( Java).
3. Name () CannonView, Superclass
() android.view.View, Finish ().
4. , 7.97.19, CannonView.java.

package import
7.9 package import CannonView. 7.3 ,
CannonView.
7.9. package import CannonView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// CannonView.java
// Cannon Game
package com.deitel.cannongame;
import java.util.HashMap;
import java.util.Map;
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.content.Context;
android.content.DialogInterface;
android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.graphics.Point;
android.media.AudioManager;
android.media.SoundPool;
android.util.AttributeSet;
android.view.MotionEvent;

7.5.
20
21
22

251

import android.view.SurfaceHolder;
import android.view.SurfaceView;

CannonView
7.10 CannonView.
,
.
7.10. CannonView
23
24
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

public class CannonView extends SurfaceView


implements SurfaceHolder.Callback
{
private CannonThread cannonThread; //
private Activity activity;
//
// Game Over GUI
private boolean dialogIsDisplayed = false;
// ,
public static nal int TARGET_PIECES = 7;
public static nal int MISS_PENALTY = 2;
public static nal int HIT_REWARD = 3;

//
//
//
//
//


,

,

// ,
private boolean gameOver; // ?
private double timeLeft; //
private int shotsFired; //
private double totalTimeElapsed; //
// ,
private Line blocker; //
private int blockerDistance; //
private int blockerBeginning; //
private int blockerEnd; //
private int initialBlockerVelocity; //
//
private oat blockerVelocity; //
//
private
private
private
private
private
private

Line target; //
int targetDistance; //
int targetBeginning; //
double pieceLength; //
int targetEnd; //
int initialTargetVelocity; //
//

252

Cannon Game

7.10 ()
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

private oat targetVelocity;

//
//

private int lineWidth; //


private boolean[] hitStates; // ?
private int targetPiecesHit; //
// ( 7)
//
private Point cannonball; //
private int cannonballVelocityX; //
private int cannonballVelocityY; //
private boolean cannonballOnScreen; //
private int cannonballRadius; //
private int cannonballSpeed; //
private int cannonBaseRadius; //
private int cannonLength; //
private Point barrelEnd; //
private int screenWidth; //
private int screenHeight; //
// ,
private static nal int TARGET_SOUND_ID = 0;
private static nal int CANNON_SOUND_ID = 1;
private static nal int BLOCKER_SOUND_ID = 2;
private SoundPool soundPool; //
private Map<Integer, Integer> soundMap; // ID
// SoundPool
// Paint,
private Paint textPaint; // Paint,
private Paint cannonballPaint; // Paint,
//
private Paint cannonPaint;
// Paint,
//
private Paint blockerPaint;
// Paint,
//
private Paint targetPaint;
// Paint,
//
private Paint backgroundPaint; // Paint,
//

88

CannonView
7.11 CannonView.
View , Context
AttributeSet. Context Activity (CannonGame), CannonView, AttributeSet ( android.util)
, XML-.

7.5.

253

( 92)
View
View, XML.
93 Activity, AlertDialog Activity GUI.
96 ( CannonView) ,
SurfaceHolder.Callback , , SurfaceView , . getHolder
SurfaceView SurfaceHolder,
SurfaceView, addCallback SurfaceHolder,
, SurfaceHolder.Callback.
7.11. CannonView
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

//
public CannonView(Context context, AttributeSet attrs)
{
super(context, attrs); // super
activity = (Activity) context;
// SurfaceHolder.Callback
getHolder().addCallback(this);
// ,
blocker = new Line();
//
target = new Line();
//
cannonball = new Point(); //
// hitStates
hitStates = new boolean[TARGET_PIECES];
// SoundPool,
//
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
//
soundMap = new HashMap<Integer, Integer>(); //
// HashMap
soundMap.put(TARGET_SOUND_ID,
soundPool.load(context, R.raw.target_hit, 1));
soundMap.put(CANNON_SOUND_ID,
soundPool.load(context, R.raw.cannon_re, 1));
soundMap.put(BLOCKER_SOUND_ID,
soundPool.load(context, R.raw.blocker_hit, 1));
// Paints , ,
// ;
// onSizeChanged
textPaint = new Paint();
// Paint
cannonPaint = new Paint();
// Paint


254

Cannon Game

7.11 ()
122
123
124
125
126
127

cannonballPaint = new Paint(); //


blockerPaint = new Paint();
//
targetPaint = new Paint();
//
backgroundPaint = new Paint(); //
// CannonView

Paint
Paint
Paint
Paint

99101 Lines
Point. hitStates, (
).
107116 , .
SoundPool,
, .
, .
, 1.
,
. ,
AudioManager. , SoundPool (AudioManager.STREAM_MUSIC)
. ,
,
( 0).
110 HashMap ( soundMap). 111116 , ( 7577).
load
SoundPool, ID,
( ) . load SoundPool
(Context) , (ID),
, (priority) . , ,
1.
120125 Paint,
. onSizeChanged, Paint ,
.

Overriding View Method onSizeChanged


7.12 onSizeChanged View,
View,
View View .
, onSizeChanged
, onCreate GUI.
View. ( ) 0.

7.5.

255

, , ( , ).
. 173 newGame (. 7.13).
7.12. onSizeChanged
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

// ,
//
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenWidth = w; //
screenHeight = h; //
cannonBaseRadius = h / 18; // ,
// 1/18
cannonLength = w / 8; // 1/8
cannonballRadius = w / 36; // 1/36
cannonballSpeed = w * 3 / 2; //
lineWidth = w / 24; // 1/24
//
// ,
blockerDistance = w * 5 / 8; //
// 5/8
blockerBeginning = h / 8; //
// 1/8
blockerEnd = h * 3 / 8; // 3/8
//
initialBlockerVelocity = h / 2; //
//
blocker.start = new Point(blockerDistance, blockerBeginning);
blocker.end = new Point(blockerDistance, blockerEnd);
// ,
targetDistance = w * 7 / 8; //
// 7/8
targetBeginning = h / 8; //
// 1/8
targetEnd = h * 7 / 8; // 7/8
pieceLength = (targetEnd targetBeginning) / TARGET_PIECES;
initialTargetVelocity = -h / 4; //
//
target.start = new Point(targetDistance, targetBeginning);
target.end = new Point(targetDistance, targetEnd);


256

Cannon Game

7.12 ()
162
163
164
165
166
167
168
169
170
171
172
173
174
175

// ( )
barrelEnd = new Point(cannonLength, h / 2);
// Paint
textPaint.setTextSize(w / 20); // 1/20
//
textPaint.setAntiAlias(true); //
cannonPaint.setStrokeWidth(lineWidth * 1.5f); //
//
blockerPaint.setStrokeWidth(lineWidth); //
//
targetPaint.setStrokeWidth(lineWidth); //
backgroundPaint.setColor(Color.WHITE); //
newGame(); //
} // end method onSizeChanged

newGame CannonView
newGame (. 7.13) , . gameOver true,
, 197
gameOver, 198199
CannonThread .
7.13. newGame CannonView
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

//
public void newGame()
{
// hitStates
// false --
for (int i = 0; i < TARGET_PIECES; ++i)
hitStates[i] = false;
targetPiecesHit = 0; //
blockerVelocity = initialBlockerVelocity; //
//
targetVelocity = initialTargetVelocity;
//
//
timeLeft = 10; // 10
cannonballOnScreen = false; //
shotsFired = 0; //
totalElapsedTime = 0.0; //
blocker.start.set(blockerDistance, blockerBeginning);
blocker.end.set(blockerDistance, blockerEnd);
target.start.set(targetDistance, targetBeginning);
target.end.set(targetDistance, targetEnd);

7.5.
195
196
197
198
199
200
201
202

257

if (gameOver)
{
gameOver = false; //
cannonThread = new CannonThread(getHolder());
cannonThread.start();
} // if
// newGame

updatePositions CannonView
updatePositions (. 7.14) run CannonThread
(. 7.21),
.
( ), .
, (
). (. 7.21).
7.14. updatePositions CannonView
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227

// CannonThread
//
private void updatePositions(double elapsedTimeMS)
{
double interval = elapsedTimeMS / 1000.0; //
//
if (cannonballOnScreen) //
{
//
cannonball.x += interval * cannonballVelocityX;
cannonball.y += interval * cannonballVelocityY;
//
if (cannonball.x + cannonballRadius > blockerDistance &&
cannonball.x cannonballRadius < blockerDistance &&
cannonball.y + cannonballRadius > blocker.start.y &&
cannonball.y cannonballRadius < blocker.end.y)
{
cannonballVelocityX *= -1; //
//
timeLeft -= MISS_PENALTY; //

//
soundPool.play(soundMap.get(BLOCKER_SOUND_ID),
1, 1, 1, 0, 1f)
// if

//

258

Cannon Game

7.14 ()
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273

else if (cannonball.x + cannonballRadius > screenWidth ||


cannonball.x cannonballRadius < 0)
cannonballOnScreen = false; // remove
//
//
else if (cannonball.y + cannonballRadius > screenHeight ||
cannonball.y cannonballRadius < 0)
cannonballOnScreen = false; //
//
//
else if (cannonball.x + cannonballRadius > targetDistance &&
cannonball.x cannonballRadius < targetDistance &&
cannonball.y + cannonballRadius > target.start.y &&
cannonball.y cannonballRadius < target.end.y)
{
// (0 )
int section =
(int) ((cannonball.y target.start.y) / pieceLength);
//
if ((section >= 0 && section <
!hitStates[section])
{
hitStates[section] = true;
cannonballOnScreen = false;
timeLeft += HIT_REWARD;

TARGET_PIECES) &&

//
//
//
//

//
soundPool.play(soundMap.get(TARGET_SOUND_ID), 1,
1, 1, 0, 1f);
//
if (++targetPiecesHit == TARGET_PIECES)
{
cannonThread.setRunning(false);
showGameOverDialog(R.string.win); //
//
gameOver = true; //
}
// if
}
// if
}
// else if
} // if
//
double blockerUpdate = interval * blockerVelocity;
blocker.start.y += blockerUpdate;
blocker.end.y += blockerUpdate;

7.5.
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299

259

//
double targetUpdate = interval * targetVelocity;
target.start.y += targetUpdate;
target.end.y += targetUpdate;
//
//
if (blocker.start.y < 0 || blocker.end.y > screenHeight)
blockerVelocity *= -1;
//
//
if (target.start.y < 0 || target.end.y > screenHeight)
targetVelocity *= -1;
timeLeft -= interval; //
//
if (timeLeft <= 0)
{
timeLeft = 0.0;
gameOver = true; //
cannonThread.setRunning(false);
showGameOverDialog(R.string.lose); //
} // end if
} // updatePositions

206 ,
( ).
.
208 , . , , .

( 211212). 215218 , .
, .
:


x
, (blockerDistance), 215. , , ,
.
 x
, ( 216). , .
 , ( 217).
 , ( 218).

260

Cannon Game

,
( 220), MISS_PENALTY timeLeft, play
soundPool , .
soundMap BLOCKER_SOUND_ID (ID)
SoundPool.
.
228230 , ,
, . 233235 , .
( 238241).
, .
, , .
244245 , ,
.
0 , 6
. ,
hitStates ( 249). ,
hitStates true, .
HIT_REWARD timeLeft,
,
(TARGET_SOUND_ID). targetPiecesHit, , TARGET_PIECES ( 260).
, . CannonThread
setRunning false, showGameOverDialog
(ID) , gameOver true.
, , . 271273
blockerVelocity
,
x y. 276278 . , -1 ( 281282). 285286 ,
, , .
timeLeft ,
. timeLeft
, . timeLeft 0.0 , .
, .
gameOver true,
CannonThread setRunning false,
showGameOverDialog ID , .

7.5.

261

reCannonball CannonView
, (. 7.8) fireCannonball (. 7.15),
.
, . (
) . 306 alignCannon,
,
. 309310 ( ). 313 316
. cannonballOnScreen true,
drawGameElements (. 7.17)
shotsFired.
(CANNON_SOUND_ID).
7.15. reCannonball CannonView
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

//
public void reCannonball(MotionEvent event)
{
if (cannonballOnScreen) //
return; //
double angle = alignCannon(event); //
//
cannonball.x = cannonballRadius; // x-
cannonball.y = screenHeight / 2; //
// x-
cannonballVelocityX = (int) (cannonballSpeed * Math.sin(angle));
// y-
cannonballVelocityY = (int) (-cannonballSpeed * Math.cos(angle));
cannonballOnScreen = true; //
++shotsFired; // shotsFired

//
soundPool.play(soundMap.get(CANNON_SOUND_ID), 1, 1, 1, 0, 1f);
// end method reCannonball

alignCannon CannonView
alignCannon (. 7.16)
. 328 x y
( MotionEvent).
. ,
( 338).

262

Cannon Game

, Math.
PI ( 342). cannonLength angle
x y . , , .
7.16. alignCannon CannonView
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

//
public double alignCannon(MotionEvent event)
{
//
Point touchPoint = new Point((int) event.getX(),
(int) event.getY());
//
// y
double centerMinusY = (screenHeight / 2 touchPoint.y);
double angle = 0; // 0
//
if (centerMinusY != 0) // 0
angle = Math.atan((double) touchPoint.x / centerMinusY);
//
if (touchPoint.y > screenHeight / 2)
angle += Math.PI; //
//
barrelEnd.x = (int) (cannonLength * Math.sin(angle));
barrelEnd.y =
(int) (-cannonLength * Math.cos(angle) + screenHeight / 2);

return angle; //
// alignCannon


drawGameElements (. 7.17) , , SurfaceView. Canvas,
CannonThread SurfaceHolder SurfaceView.
7.17. drawGameElements CannonView
352
353
354
355
356
357
358

// Canvas
public void drawGameElements(Canvas canvas)
{
//
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(),
backgroundPaint);

7.5.
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
}
405 } //
406

263

//
canvas.drawText(getResources().getString(
R.string.time_remaining_format, timeLeft), 30, 50, textPaint);
// ,
if (cannonballOnScreen)
canvas.drawCircle(cannonball.x, cannonball.y, cannonballRadius,
cannonballPaint);
//
canvas.drawLine(0, screenHeight / 2, barrelEnd.x, barrelEnd.y,
cannonPaint);
//
canvas.drawCircle(0, (int) screenHeight / 2,
(int) cannonBaseRadius, cannonPaint);
//
canvas.drawLine(blocker.start.x, blocker.start.y, blocker.end.x,
blocker.end.y, blockerPaint);
Point currentPoint = new Point(); //
// curPoint
currentPoint.x = target.start.x;
currentPoint.y = target.start.y;
//
for (int i = 1; i <= TARGET_PIECES; ++i)
{
// ,
if (!hitStates[i 1])
{
//
if (i % 2 == 0)
targetPaint.setColor(Color.YELLOW);
else
targetPaint.setColor(Color.BLUE);
canvas.drawLine(currentPoint.x, currentPoint.y, target.end.x,
(int) (currentPoint.y + pieceLength), targetPaint);
}
// curPoint
currentPoint.y += pieceLength;
// for
drawGameElements

drawRect Canvas ( 356357),


Canvas.

264

Cannon Game

. ,
Paint, (,
backgroundPaint ). drawText
Canvas ( 360361) .
, x y textPaint ( 166167),
( , ).
, 365366
drawCircle Canvas . .
. Paint,
, .
drawLine Canvas ( 369
370), ( 377378) ( 398399).
x-y
, Paint, ,
.
373374 drawCircle Canvas . ,
.
,
SurfaceView.
380404 .
, (
). , .

showGameOverDialog CannonView
showGameOverDialog (. 7.18) AlertDialog, ,
, .
419430 setPositiveButton Builder, . onClick
, , newGame
. GUI,
432440 runOnUiThread Activity
, Runnable. run
Runnable .
7.18. showGameOverDialog CannonView
407
408
409
410
411

// AlertDialog
private void showGameOverDialog(int messageId)
{
// ,
nal AlertDialog.Builder dialogBuilder =

7.5.
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

265

new AlertDialog.Builder(getContext());
dialogBuilder.setTitle(getResources().getString(messageId));
dialogBuilder.setCancelable(false);
//
dialogBuilder.setMessage(getResources().getString(
R.string.results_format, shotsFired, totalElapsedTime));
dialogBuilder.setPositiveButton(R.string.reset_game,
new DialogInterface.OnClickListener()
{
// "Reset Game"
@Override
public void onClick(DialogInterface dialog, int which)
{
dialogIsDisplayed = false;
newGame(); //
} // onClick
}
//
); // setPositiveButton
activity.runOnUiThread(
new Runnable() {
public void run()
{
dialogIsDisplayed = true;
dialogBuilder.show(); //
} // run
}
// Runnable
); // runOnUiThread
}
// showGameOverDialog

stopGame releaseResources CannonView


onPause onDestroy (. 7.6) CannonGame ( Activity) stopGame releaseResources CannonView (. 7.19) . stopGame ( 444448) Activity
onPause Activity. . releaseResources
( 451455) release SoundPool, , SoundPool.
7.19. stopGame releaseResources CannonView
443
444
445
446
447
448
449

//
public void stopGame()
{
if (cannonThread != null)
cannonThread.setRunning(false);
} // stopGame


266

Cannon Game

7.19 ()
450
451
452
453
454
455
456

// ; onDestroy
// CannonGame
public void releaseResources()
{
soundPool.release(); // ,
// SoundPool
soundPool = null;
} // releaseResources

SurfaceHolder.Callback
7.20 surfaceChanged, surfaceCreated surfaceDestroyed
SurfaceHolder.Callback. surfaceChanged
, .
SurfaceView . surfaceCreated ( 465471) SurfaceView
( , ). surfaceCreated
CannonThread . surfaceDestroyed ( 474
492) SurfaceView (
). CannonThread .
479 setRunning CannonThread
false, , . 481491
. ,
surfaceDestroyed
SurfaceView.
7.20. SurfaceHolder.Callback
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475

//
@Override
public void surfaceChanged (SurfaceHolder holder, int format,
int width, int height)
{
}
// surfaceChanged
//
@Override
public void SurfaceCreated (SurfaceHolder holder)
{
cannonThread = new CannonThread(holder);
cannonThread.setRunning(true);
cannonThread.start(); //
} // surfaceCreated
//
@Override
public void surfaceDestroyed (SurfaceHolder holder)

7.5.
476
{
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
}
492 } //
493

267

//
boolean retry = true;
cannonThread.setRunning(false);
while (retry)
{
try
{
cannonThread.join();
retry = false;
} // try
catch (InterruptedException e)
{
} // catch
// while
surfaceDestroyed

CannonThread:
7.21 Thread .
SurfaceHolder SurfaceView ( 497), , . run
( 514543) (
).
, .
518 , ,
. 520542 ,
threadIsRunning false.
7.21. Runnable, ,
TIME_INTERVAL ( )
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511

// Thread,
private class CannonThread extends Thread
{
private SurfaceHolder surfaceHolder; // canvas
private boolean threadIsRunning = true; //
//
public CannonThread(SurfaceHolder holder)
{
surfaceHolder = holder;
setName("CannonThread");
} //
//
public void setRunning(boolean running)
{
threadIsRunning = running;
} // setRunning

268

Cannon Game

7.21 ()
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545

//
@Override
public void run()
{
Canvas canvas = null; //
long previousFrameTime = System.currentTimeMillis();
while (threadIsRunning)
{
try
{
canvas = surfaceHolder.lockCanvas(null);
// surfaceHolder
synchronized(surfaceHolder)
{
long currentTime = System.currentTimeMillis();
double elapsedTimeMS = currentTime previousFrameTime;
totalElapsedTime += elapsedTimeMS / 1000.0;
updatePositions(elapsedTimeMS); //
//
drawGameElements(canvas); //
previousFrameTime = currentTime; //
//
} //
} // try
nally
{
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
} // nally
} // while
} // run
} // CannonThread
} // CannonView

Canvas, , SurfaceView
lockCanvas SurfaceHolder ( 524). SurfaceView
,
SurfaceHolder.
( ),
.
. 532
updatePositions, ,
. ,
. .

7.6.

269

( ),
.
( ),
. , 533 Canvas
SurfaceView, 534 currentTime
previousFrameTime .

7.6.
Cannon Game,
,
, 10 .
. .
String , getString Resource
format String, .
SurfaceView. ,
XML, .
Activity,
onPause Activity , onDestroy,
.
onTouchEvent Activity
. ,
, GestureDetector.
SimpleOnGestureListener, onDoubleTap.
res/raw
SoundPool, AudioManager
.

SurfaceView . Thread run,
Canvas. SurfaceHolder SurfaceView Canvas, ,
, .
.
SpotOn game Android 3.x. SpotOn Android 3.x
View, .
, , .

SpotOn

,
ViewPropertyAnimator,
AnimatorListener, -
, SharedPreferences,





, .
ViewPropertyAnimator ,
ImageViews.
 AnimatorListener.
 ImageViews .
 ConcurrentLinkedQueue
java.util.concurrent .


8.1.
SpotOn , (. 8.1).
,
. . 10
. ,

8.2. SpotOn Game

271

. 8.1. SpotOn game

. , . (
10). , ,
, 15 . (),
.
, , ,
.
( ). ,
, (. 8.2).

8.2. SpotOn Game



Eclipse SpotOn app.
:
1. Import, FileImport
().

272

SpotOn

. 8.2. ,
Reset Game

2. SpotOn app. Import


General () Existing Projects into Workspace ( ). Next > ( >)
Import Projects ( ). Select root
directory ( ) Browse ().
Browse For Folder ( ) SponOn
OK. Finish ()
Eclipse. Package Explorer, Eclipse.
3. SpotOn. Eclipse
SpotOn, Package Explorer,
Run AsAndroid Application ( 
Android).


(
AVD). ,
. ,
, .

Android 3.1.
AVD Android 3.0
.
Android 3.1.

8.3.

273

8.3.
Android 3.x
, Android 3.0+. ,
, Android 3.0, ImageViews.
Android 3.0 :


Tweened View ( ) View, ,


.
 Frame View ( ) .
, (. 7). , View
.
Button , , Button
.
(package android.animation)
( View). Button
Button ,
Button
.

. :






, ( ), ;
( );
;
, , ;
().

ValueAnimator ObjectAnimator. ValueAnimator .


AnimatorUpdateListener,
. , ,
. ValueAnimator ObjectAnimator
, .
Android 3.1 ViewPropertyAnimator, View
. View ,
ViewPropertyAnimator, , .

274

SpotOn

, . .
Android :



android-developers.blogspot.com/2011/02/animation-in-honeycomb.html;
android-developers.blogspot.com/2011/05/introducing-viewpropertyanimator.html.


AnimatorListener
, ,
, , .
,
AnimatorListenerAdapter () .


7
onTouchEvent Activity. SpotOn
.
OnClickListeners (
ImageView), onTouchEvent.

ConcurrentLinkedQueue Queue
ConcurrentLinkedQueue ( java.util.concurrent)
Queue ,
.

8.4.


SpotOn game.
strings.xml. ,
Eclipse.

8.4.1. AndroidManifest.xml
8.1 AndroidManifest.xml. uses-sdk
android:minSdkVersion "12" ( 5), Android 3.1 SDK. Android 3.1+
AVD. 7 android:hardwareAccelerated true.
( ) . 9 android:screenOrientation,
() .

8.4.

275

8.1. AndroidManifest.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <manifest xmlns:android=http://schemas.android.com/apk/res/android
3
android:versionCode="1" android:versionName="1.0"
4
package="com.deitel.spoton">
5
<uses-sdk android:minSdkVersion="12"/>
6
<application android:icon="@drawable/icon"
7
android:hardwareAccelerated="true" android:label="@string/app_name">
8
<activity android:name=".SpotOn" android:label="@string/app_name"
9
android:screenOrientation="landscape">
10
<intent-lter>
11
<action android:name="android.intent.action.MAIN" />
12
<category android:name="android.intent.category.LAUNCHER"/>
13
</intent-lter>
14
</activity>
15
</application>
16 </manifest>

8.4.2. main.xml RelativeLayout


main.xml ( 8.2) RelativeLayout,
TextView, , ,
LinearLayout,
. GUI
, . GUI . 8.3.
8.2. main.xml SpotOn
1 <?xml version="1.0" encoding="utf-8"?>
2 <RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android
3
android:id="@+id/relativeLayout" android:layout_width="match_parent"
4
android:layout_height="match_parent"
5
android:background="@android:color/white">
6
<TextView android:id="@+id/highScoreTextView"
7
android:layout_width="wrap_content"
8
android:layout_height="wrap_content"
9
android:layout_marginTop="10dp"
10
android:layout_marginLeft="10dp"
11
android:textColor="@android:color/black" android:textSize="25sp"
12
android:text="@string/high_score"></TextView>
13
<TextView android:id="@+id/levelTextView"
14
android:layout_toRightOf="@id/highScoreTextView"
15
android:layout_width="wrap_content"
16
android:layout_height="wrap_content"
17
android:layout_marginTop="10dp"
18
android:layout_marginRight="10dp"
19
android:gravity="right"
20
android:layout_alignParentRight="true"
21
android:textColor="@android:color/black" android:textSize="25sp"


276

SpotOn

8.2 ()
22
23
24
25
26
27
28
29
30
31
32
33
34
35

android:text="@string/level"></TextView>
<TextView android:id="@+id/scoreTextView"
android:layout_below="@id/highScoreTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textColor="@android:color/black" android:textSize="25sp"
android:text="@string/score"></TextView>
<LinearLayout android:id="@+id/lifeLinearLayout"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"></LinearLayout>
</RelativeLayout >

. 8.3. GUI SpotOn

8.4.3. untouched.xml
ImageView
untouched.xml ( 8.3) ImageView, .

8.5.

277

8.3. untouched.xml ImageView SpotOn



1
2
3

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


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

8.4.4. life.xml ImageView


life.xml ( 8.4) ImageView,
() .
8.4. life.xml SpotOn
1
2
3

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


<ImageView xmlns:android=http://schemas.android.com/apk/res/android
android:src="@drawable/life"></ImageView>

8.5.
SpotOn SpotOn ( 8.5.1),
Activity , SpotOnView ( 8.5.2),
.

8.5.1. SpotOn Activity


SpotOn ( 8.5) onCreate, GUI. 2425 SpotOnView, 26
RelativeLayout ( 0). . SpotOnView Context, GUI ( Activity),
SharedPreferences RelativeLayout (
SpotOnView GUI). 5 , SharedPreferences.
,
Activity. getPreferences Activity.
8.5. SpotOn Activity
1
2
3
4
5
6
7
8
9
10

// SpotOn.java
// Activity SpotOn
package com.deitel.spoton;
import
import
import
import

android.app.Activity;
android.content.Context;
android.os.Bundle;
android.widget.RelativeLayout;

public class SpotOn extends Activity

278

SpotOn

8.5 ()
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

{
private SpotOnView view; //
// Activity
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// SpotOnView
// RelativeLayout
RelativeLayout layout =
(RelativeLayout) ndViewById(R.id.relativeLayout);
view = new SpotOnView(this, getPreferences(Context.MODE_PRIVATE),
layout);
layout.addView(view, 0); //
// onCreate

// Activity
@Override
public void onPause()
{
super.onPause();
view.pause(); // ,
} // onPause
// Activity
@Override
public void onResume()
{
super.onResume();
view.resume(this); // ,
// onPause
} // onResume
} // SpotOn

onPause onResume Activity


pause resume SpotOnView . onPause
Activity, pause SpotOnView SoundPool, , . ,
Activity onCreate.
onStart onResume Activity. onResume
, Activity .
onResume Activity , resume
SpotOnView SoundPool . , .

8.5.

279

8.5.2. SpotOnView View


SpotOnView ( 8.68.18) .

package import
8.3 ,
SpotOnView. . 8.6.
8.6. package import SpotOnView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// SpotOnView.java
// View,
package com.deitel.spoton;
import
import
import
import
import

java.util.HashMap;
java.util.Map;
java.util.Random;
java.util.concurrent.ConcurrentLinkedQueue;
java.util.Queue;

import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.animation.Animator;
android.animation.AnimatorListenerAdapter;
android.app.AlertDialog;
android.app.AlertDialog.Builder;
android.content.Context;
android.content.DialogInterface;
android.content.SharedPreferences;
android.content.res.Resources;
android.media.AudioManager;
android.media.SoundPool;
android.os.Handler;
android.view.LayoutInater;
android.view.MotionEvent;
android.view.View;
android.widget.ImageView;
android.widget.LinearLayout;
android.widget.RelativeLayout;
android.widget.TextView;


8.7 SpotOnView, . 3334
SharedPreferences, () SharedPreferences
Activity. 3773 ,
.
. 7684 ,

280

SpotOn

. 7 .
8.7. SpotOnView
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

public class SpotOnView extends View


{
// ,
// SharedPreference
private static nal String HIGH_SCORE = "HIGH_SCORE";
private SharedPreferences preferences; // stores the high score
// ,
private int spotsTouched; // ,
//
private int score; //
private int level; //
private int viewWidth; //
private int viewHeight; //
private long animationTime; //
//
private boolean gameOver;
//
private boolean gamePaused; //
private boolean dialogDisplayed; //
private int highScore; //
// (ImageViews) Animators
private nal Queue<ImageView> spots =
new ConcurrentLinkedQueue<ImageView>();
private nal Queue<Animator> animators =
new ConcurrentLinkedQueue<Animator>();
private
private
private
private

TextView highScoreTextView;
//
TextView currentScoreTextView; //
TextView levelTextView; //
LinearLayout livesLinearLayout; //
//
private RelativeLayout relativeLayout; //
private Resources resources; //
private LayoutInater layoutInater;
//
// GUI
// ( ) ,
//
private static nal int INITIAL_ANIMATION_DURATION = 6000;
private static nal Random random = new Random(); //
//
private static nal int SPOT_DIAMETER = 100; //
//
private static nal oat SCALE_X = 0.25f;
//
// x

8.5.
67

private static nal oat SCALE_Y = 0.25f;

68

private

69
70
71
72

private
private
private
private

73
74
75
76
77
78
79
80
81
82
83
84
85

private

281

//
// y
static nal int INITIAL_SPOTS = 5; //
//
static nal int SPOT_DELAY = 500; //
static nal int LIVES = 3; //
static nal int MAX_LIVES = 7;
//
static nal int NEW_LEVEL = 10; // ,
//
//
Handler spotHandler; //

// ,
private static nal int HIT_SOUND_ID = 1;
private static nal int MISS_SOUND_ID = 2;
private static nal int DISAPPEAR_SOUND_ID = 3;
private static nal int SOUND_PRIORITY = 1;
private static nal int SOUND_QUALITY = 100;
private static nal int MAX_STREAMS = 4;
private SoundPool soundPool; //
private int volume; //
private Map<Integer, Integer> soundMap; // ID soundpool

SpotOnView
SpotOnView (. . 8.8) . 93
SharedPreferences SpotOn, Activity. 94 . ,
getInt 0, HIGH_SCORE . 97
context Resources
Activity. String,
,
. 100101 LayoutInflater,
ImageView . 104
RelativeLayout SpotOn Activity. 105112
LinearLayout,
, TextView,
, . 114 Handler,
resetGame (. 8.11) .
8.8. SpotOnView
86
87
88
89
90

// SpotOnView
public SpotOnView(Context context, SharedPreferences sharedPreferences,
RelativeLayout parentLayout)
{
super(context);

282

SpotOn

8.8 ()
91
92 //
93 preferences = sharedPreferences;
94 highScore = preferences.getInt(HIGH_SCORE, 0);
95
96 // ,
97 resources = context.getResources();
98
99 // LayoutInater
100 layoutInater = (LayoutInater) context.getSystemService(
101 Context.LAYOUT_INFLATER_SERVICE);
102
103 // GUI
104 relativeLayout = parentLayout;
105 livesLinearLayout = (LinearLayout) relativeLayout.ndViewById(
106
R.id.lifeLinearLayout);
107 highScoreTextView = (TextView) relativeLayout.ndViewById(
108
R.id.highScoreTextView);
109 currentScoreTextView = (TextView) relativeLayout.ndViewById(
110
R.id.scoreTextView);
111 levelTextView = (TextView) relativeLayout.ndViewById(
112
R.id.levelTextView);
113
114
spotHandler = new Handler(); //
115 } // SpotOnView
116

onSizeChanged View

SpotOnView.
,
View.
. ,
onSizeChanged View (. 8.9),
View View .
8.9. onSizeChanged View
117
118
119
120
121
122
123
124

// width/height SpotOnView
@Override
protected void onSizeChanged(int width, int height,
int oldw, int oldh)
{
viewWidth = width;
//
viewHeight = height; //
} // onSizeChanged

8.5.

283

pause, cancelAnimations resume


pause, cancelAnimations resume (. 8.10) ,
.
8.10. pause, cancelAnimations resume SpotOnView
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

// SpotOn Activity onPause


public void pause()
{
gamePaused = true;
soundPool.release(); //
soundPool = null;
cancelAnimations(); //
} // pause
// ImageViews,
private void cancelAnimations()
{
//
for (Animator animator : animators)
animator.cancel();
//
for (ImageView view : spots)
relativeLayout.removeView(view);

spotHandler.removeCallbacks(addSpotRunnable);
animators.clear();
spots.clear();
// cancelAnimations

// SpotOn Activity
// onResume
public void resume(Context context)
{
gamePaused = false;
initializeSoundEffects(context); // SoundPool
//

if (!dialogDisplayed)
resetGame(); //
// resume

onPause Activity pause ( 126


132), SoundPool, , cancelAnimations. gamePaused (. 8.15)
missedSpot
.

284

SpotOn

cancelAnimations ( 135148) cancel Animator.


onAnimationCancel
onAnimationEnd AnimationListener.
onResume Activity resume ( 151158) SoundPool initalizeSoundEffects (. 8.12). dialogDisplayed true, - . ,
Reset Game ( ), .
, 157 resetGame
(. 8.11), .

resetGame
resetGame (. 8.11) ,
() . 163164
, 165 removeAllViews ViewGroup
ImageView, ,
livesLinearLayout. 167171
, :






animationTime 5 %
;
spotsTouched
, NEW_LEVEL;
score ;
level ;
gameOver , .

8.11. resetGame SpotOnView


160
161
162
163
164
165
166
167
168
169
170
171

//
public void resetGame()
{
spots.clear();
//
animators.clear(); //
livesLinearLayout.removeAllViews(); //
animationTime = INITIAL_ANIMATION_DURATION; //
//
spotsTouched = 0; // ,
//
score = 0; //
level = 1; //
gameOver = false; //

8.5.
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

displayScores();

285

//

//
for (int i = 0; i < LIVES; i++)
{
//
livesLinearLayout.addView(
(ImageView) layoutInater.inate(R.layout.life, null));
} // for
// INITIAL_SPOTS
// SPOT_DELAY,
for (int i = 1; i <= INITIAL_SPOTS; ++i)
spotHandler.postDelayed(addSpotRunnable, i * SPOT_DELAY);
} // resetGame

172 displayScores (. 8.13), TextView . 175180


life.xml livesLinearLayout ImageView. , 183184
spotHandler
, SPOT_DELAY ( ).

Method initializeSoundEffects
initializeSoundEffects (. 8.12) , Cannon Game ( 7.5.3),
. ,
:


R.raw.hit ;

R.raw.miss , , ;
 R.raw.disappear ,
.
MP3- .
8.12. initializeSoundEffects SpotOnView
187
188
189
190
191
192
193
194
195

// SoundPool
private void initializeSoundEffects(Context context)
{
// SoundPool
// ,
soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC,
SOUND_QUALITY);
//
AudioManager manager =

286

SpotOn

8.12 ()
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

(AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
volume = manager.getStreamVolume(AudioManager.STREAM_MUSIC);
//
soundMap = new HashMap<Integer, Integer>(); // HashMap
// SoundPool
soundMap.put(HIT_SOUND_ID,
soundPool.load(context, R.raw.hit, SOUND_PRIORITY));
soundMap.put(MISS_SOUND_ID,
soundPool.load(context, R.raw.miss, SOUND_PRIORITY));
soundMap.put(DISAPPEAR_SOUND_ID,
soundPool.load(context, R.raw.disappear, SOUND_PRIORITY));
} // initializeSoundEffect

displayScores
displayScores (. 8.13) TextView
, .
strings.xml getString resources.
8.13. displayScores SpotOnView
211
212
213
214
215
216
217
218
219
220
221
222

//
private void displayScores()
{
// ,
highScoreTextView.setText(
resources.getString(R.string.high_score) + " " + highScore);
currentScoreTextView.setText(
resources.getString(R.string.score) + " " + score);
levelTextView.setText(
resources.getString(R.string.level) + " " + level);
}
// displayScores

Runnable AddSpotRunnable
resetGame (. 8.14) spotHandler ,
postDelayed spotHandler addSpotRunnable (.
8.14). run Runnable addNewSpot (. 8.15).
8.14. Runnable addSpotRunnable

223

// Runnable
//

8.5.
224
225
226
227
228
229
230
231

287

private Runnable addSpotRunnable = new Runnable()


{
public void run()
{
addNewSpot(); //
} // run
}; // Runnable

addNewSpot
addNewSpot (. 8.15) .
,
()
, .
236239 SpotOnView
. 242250 ImageView
. 245246
ImageView setLayoutParams RelativeLayout.
LayoutParams. 247248
setImageResource ImageView . 249250
. 251259 OnClickListener ImageView
touchedSpot (. 8.17)
ImageView. relativeLayout,
.
8.15. addNewSpot SpotOnView
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

//
//
public void addNewSpot()
{
//
int x = random.nextInt(viewWidth SPOT_DIAMETER);
int y = random.nextInt(viewHeight SPOT_DIAMETER);
int x2 = random.nextInt(viewWidth SPOT_DIAMETER);
int y2 = random.nextInt(viewHeight SPOT_DIAMETER);
//
nal ImageView spot =
(ImageView) layoutInater.inate(R.layout.untouched, null);
spots.add(spot); //
spot.setLayoutParams(new RelativeLayout.LayoutParams(
SPOT_DIAMETER, SPOT_DIAMETER));
spot.setImageResource(random.nextInt(2) == 0 ?
R.drawable.green_spot : R.drawable.red_spot);
spot.setX(x); // x
spot.setY(y); // y

288

SpotOn

8.15 ()
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

spot.setOnClickListener( // ,
//
new OnClickListener()
{
public void onClick(View v)
{
touchedSpot(spot); // ,
//
} // onClick
} // OnClickListener
); // setOnClickListener
relativeLayout.addView(spot); //
//
spot.animate().x(x2).y(y2).scaleX(SCALE_X).scaleY(SCALE_Y)
setDuration(animationTime).setListener(
new AnimatorListenerAdapter()
{
@Override
public void onAnimationStart(Animator animation)
{
animators.add(animation); //
} // onAnimationStart
public void onAnimationEnd(Animator animation)
{
animators.remove(animation); // ,
if (!gamePaused && spots.contains(spot)) //
{
missedSpot(spot); //
} // if
} // onAnimationEnd
} // AnimatorListenerAdapter
); // setListener
} // addNewSpot

263283 ViewPropertyAnimator, animate View. ViewPropertyAnimator


View (), , , (
) . ViewPropertyAnimator ,
, AnimatorListener ( )
TimeInterpolator ( ). ,
ViewPropertyAnimator. :


X x View.

Y y View.

8.5.

289

scaleX View (
).

scaleY View ( ).

setDuration ( ).

setListener AnimatorListener.

(
setListener), . TimeInterpolator,
LinearInterpolator.
.
- developer.android.com/reference/android/animation/TimeInterpolator.
html.
AnimatorListener ,
AnimatorListenerAdapter,
AnimatorListener.
onAnimationStart onAnimationEnd.
onAnimationStart.
Animator,
. Animator
. onPause SpotOn Activity Animator
.

onAnimationEnd. C Animator animators
( ).
, missedSpot (. 8.18),
, . , .

onTouchEvent View
onTouchEvent View (. 8.16)
, . , , 15. ,
, .
8.16. onTouchEvent View
286
287
288
289
290
291
292
293

// , ,
@Override
public boolean onTouchEvent(MotionEvent event)
{
//
if (soundPool != null)
soundPool.play(MISS_SOUND_ID, volume, volume,
SOUND_PRIORITY, 0, 1f);

290

SpotOn

8.16 ()
294
295
296
297
298
299
300

score -= 15 * level; //
score = Math.max(score, 0); //
displayScores(); // /
return true;
// onTouchEvent

touchedSpot
touchedSpot (. 8.17) ImageView, . ,
.
,
(), (
, ). , , , ,
.
8.17. touchedSpot SpotOnView
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

//
private void touchedSpot(ImageView spot)
{
relativeLayout.removeView(spot); //
//
spots.remove(spot); //
++spotsTouched; //
//
score += 10 * level; //
//
if (soundPool != null)
soundPool.play(HIT_SOUND_ID, volume, volume,
SOUND_PRIORITY, 0, 1f);
// 10
//
if (spotsTouched % 10 == 0)
{
++level; //
animationTime *= 0.95; // 5%
//
//
if (livesLinearLayout.getChildCount() < MAX_LIVES)
{
ImageView life =
(ImageView) layoutInater.inate(R.layout.life, null);

8.5.
326
327
328
329
330
331
332
333
334
335

291

livesLinearLayout.addView(life); //
//
} // if
// if

displayScores(); // /
if (!gameOver)
addNewSpot(); //
} // touchedSpot

Method missedSpot
missedSpot (. 8.18) ,
. ,
, . . ,
. , ,
, ( 356362).
,
.
(), 385390
.
8.18. missedSpot SpotOnView
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358

// ,
//
public void missedSpot(ImageView spot)
{
spots.remove(spot); //
relativeLayout.removeView(spot); //
if (gameOver) // ,
return;
//
if (soundPool != null)
soundPool.play(DISAPPEAR_SOUND_ID, volume, volume,
SOUND_PRIORITY, 0, 1f);
//
if (livesLinearLayout.getChildCount() == 0)
{
gameOver = true; //
//
if (score > highScore)
{
SharedPreferences.Editor editor = preferences.edit();

292

SpotOn

8.18 ()
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392

editor.putInt(HIGH_SCORE, score);
editor.commit(); //
highScore = score;
// if

cancelAnimations();
//
Builder dialogBuilder = new AlertDialog.Builder(getContext());
dialogBuilder.setTitle(R.string.game_over);
dialogBuilder.setMessage(resources.getString(R.string.score) +
" " + score);
dialogBuilder.setPositiveButton(R.string.reset_game,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
displayScores(); //
dialogDisplayed = false;
resetGame(); //
} // onClick
} // DialogInterface
); // dialogBuilder.setPositiveButton
dialogDisplayed = true;
dialogBuilder.show(); //
} // if
else //
{
livesLinearLayout.removeViewAt( //
livesLinearLayout.getChildCount() 1);
addNewSpot(); //
} // else
} // missedSpot
} // SpotOnView

8.6.
SpotOn,
, , .
, , Android 3.0
. ,
ImageView , Android 3.0.
, Android, 3.0,
. View , View,
View, .

8.6.

293

, View
View .
,
.
. :
, (), ,
.
Android 3.0 ValueAnimator ObjectAnimator, Android 3.1 ViewPropertyAnimator,
API
View ( ).
animate View
ViewPropertyAnimator View, , .
.
AnimatorUpdateListener,
, , ,
.
, AnimatorListenerAdapter.
, ConcurrentLinkedQueue java.util.concurrent Queue ,
.
9 Doodlz,
Android, .

Doodlz

,
SensorManager, -
Toast










, ,
.
, .
SensorManager , , .
AtomicBoolean,
.
Paint .
Path
Canvas.
Toast.

9.1.
Doodlz
(. 9.1).
.

9.3.

295

. Choose Color ( ),
. 9.2, , SeekBar (), - (), ,
, ARGB. SeekBar , SeekBar,
. Choose Line Width ( ),
. 9.2, , SeekBar, . (. 9.3)
(Erase), (Clear)
(Save Image).
, .

. 9.1. Doodlz

9.2. Doodlz
1.11 1,
.

9.3.
, Doodlz , .

296

Doodlz

. 9.2. Doodlz:
Choose Color;
Choose Line Width

. 9.3. Doodlz

9.3.

297


Android 3.0
Android 3.0,
Android 3.0 SDK ( 9.4.2).
GUI
Android 3.0, (
).
Android. ,
Android 3.0, ,
.

SensorManager

, ,
. Android ,
. Android , , ,
, , , ,
. , ,
- developer.android.com/reference/android/hardware/Sensor.html.

SensorManager ( 9.5.1),
. SensorManager , ,
SensorEventListener , . , ,
android.hardware.


, ,
AlertDialog Button.
AlertDialog String Button.
Dialog ( android.app),
( 9.5.1).
. Dialog XML-
( 9.4 9.5).

AtomicBoolean
Android , , , GUI. , ,

298

Doodlz

, ,
. ,
AtomicBoolean ( import java.util.concurrent.atomic),
, .
AtomicBoolean .
.
AtomicBoolean true,
.


Color,
( 9.5.1) . Color: , , SeekBar Dialog.
0 255.
Color, 0 , 255 . Color ,
Color ( ).
Color ( SeekBar Choose Color).


Bitmap ( android.graphics). Canvas
Bitmap, Canvas Bitmap, ( 9.5.1 9.5.2). Bitmap .

Save Image ( ).


.
Path ( android.graphics),
, . ,
,
OnTouchEvent View ( 9.5.2). MotionEvent (
android.view), , ID () ( ), . ID
, Path. ,
,
.


Save Image ( ),

9.4.

299

, . ContentResolver ( android.content)
, . OutputStream ( 9.5.2),
, JPEG.

Toast

Toast ( android.widget) ,
.
, , ,
. ( 9.5.2)
.

9.4.

GUI Doodlz.

9.4.1.
Android Doodlz.
New Android Project ( Android) , Finish ().


Build Target ( ): Android 2.3.3.

Application name ( ): Doodlz.

Package name ( ): com.deitel.doodlz.

Create Activity ( ): Doodlz.

Min SDK Version ( SDK): 8.

9.4.2. AndroidManifest.xml
9.1 AndroidManifest.xml, .
android:targetSdkVersion uses-sdk
"11" ( 15), Android 3.0 SDK.
Android 3.0 ,
GUI
Android 3.0, , . android:targetSdkVersion 11 ,
Android. SDK 11
, Android.

300

Doodlz

, ,
Android 3.0 .
9.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
android:versionCode="1" android:versionName="1.0"
package="com.deitel.doodlz">
<application android:icon="@drawable/icon"
android:label="@string/app_name" android:debuggable="true">
<activity android:label="@string/app_name"
android:name=".Doodlz"
android:screenOrientation="portrait">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name=
"android.intent.category.LAUNCHER"/>
</intent-lter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11" />
</manifest>

9.4.3. strings.xml
9.2 String, .
9.2. Strings, strings.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

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


<resources>
<string name="app_name">Doodlz</string>
<string name="button_erase">Erase</string>
<string name="button_cancel">Cancel</string>
<string name="button_set_color">Set Color</string>
<string name="button_set_line_width">Set Line Width</string>
<string name="label_alpha">Alpha</string>
<string name="label_red">Red</string>
<string name="label_green">Green</string>
<string name="label_blue">Blue</string>
<string name="menuitem_clear">Clear</string>
<string name="menuitem_color">Color</string>
<string name="menuitem_erase">Erase</string>
<string name="menuitem_line_width">Line Width</string>
<string name="menuitem_save_image">Save Image</string>
<string name="message_erase">Erase the drawing?</string>
<string name="message_error_saving">
There was an error saving the image</string>
<string name="message_saved">
Your painting has been saved to the Gallery</string>
<string name="title_color_dialog">Choose Color</string>

9.4.
23
24

301

<string name="title_line_width_dialog">Choose Line Width</string>


</resources>

9.4.4. main.xml
main.xml . ,
View, DoodleView. 9.3 main.xml, XML-
( 25). , DoodleView
ADT, .
9.3. XML- Doodlz (main.xml)
1
2
3
4
5

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


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

9.4.5. color_dialog.xml
9.4 color_dialog.xml, ,
, , ,
. LinearLayout ( 6167) View ( 6466), ,
SeekBar. 0 ( )
255 ( ). alphaSeekBar
,
View. SeekBar,
android:thumb SeekBar drawable, .
9.4. XML-,
Choose Color
1
2
3
4
5
6
7
8
9
10
11
12

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


<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/colorDialogLinearLayout"
android:layout_width="match_parent" android:minWidth="300dp"
android:layout_height="match_parent" android:orientation="vertical">
<TableLayout android:id="@+id/tableLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_margin="10dp"
android:stretchColumns="1">
<TableRow android:orientation="horizontal"
android:layout_width="match_parent"

302

Doodlz

9.4 ()
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_alpha" android:gravity="right"
android:layout_gravity="center_vertical"/>
<SeekBar android:id="@+id/alphaSeekBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:max="255"
android:paddingLeft="10dp" android:paddingRight="10dp"/>
</TableRow>
<TableRow android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_red" android:gravity="right"
android:layout_gravity="center_vertical"/>
<SeekBar android:id="@+id/redSeekBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:max="255"
android:paddingLeft="10dp" android:paddingRight="10dp"/>
</TableRow>
<TableRow android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_green" android:gravity="right"
android:layout_gravity="center_vertical"/>
<SeekBar android:id="@+id/greenSeekBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:max="255"
android:paddingLeft="10dp" android:paddingRight="10dp"/>
</TableRow>
<TableRow android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/label_blue" android:gravity="right"
android:layout_gravity="center_vertical"/>
<SeekBar android:id="@+id/blueSeekBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:max="255"
android:paddingLeft="10dp" android:paddingRight="10dp"/>
</TableRow>
</TableLayout>
<LinearLayout android:background="@android:color/white"
android:layout_width="match_parent"

9.5.

303

63
android:layout_height="wrap_content" android:layout_margin="10dp">
64
<View android:id="@+id/colorView"
65
android:layout_width="match_parent"
66
android:layout_height="30dp"/>
67 </LinearLayout>
68
69 <Button android:id="@+id/setColorButton"
70
android:layout_width="wrap_content"
71
android:layout_height="wrap_content"
72
android:layout_gravity="center_horizontal"
73
android:text="@string/button_set_color"/>
74 </LinearLayout>

9.4.6. width_dialog.xml
9.5 width_dialog.xml,
,
. widthSeekBar
ImageView ( 68)
.
9.5. XML- Choose Line Width
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

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


<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/widthDialogLinearLayout"
android:layout_width="match_parent" android:minWidth="300dp"
android:layout_height="match_parent" android:orientation="vertical">
<ImageView android:id="@+id/widthImageView"
android:layout_width="match_parent" android:layout_height="50dp"
android:layout_margin="10dp"/>
<SeekBar android:layout_height="wrap_content" android:max="50"
android:id="@+id/widthSeekBar" android:layout_width="match_parent"
android:layout_margin="20dp" android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_gravity="center_horizontal"/>
<Button android:id="@+id/widthDialogDoneButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/button_set_line_width"/>
</LinearLayout>

9.5.
Doodlz ( Activity; 9.89.17) DoodleView ( 9.189.26).

304

Doodlz

9.5.1. Doodlz Activity


Doodlz ( 9.69.17) Activity Doodlz.
,
.

package import
9.3 ,
Doodlz. 9.6.
9.6. package import Doodlz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

// Doodlz.java
// View,
// .
package com.deitel.doodlz;
import java.util.concurrent.atomic.AtomicBoolean;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.app.Dialog;
android.content.Context;
android.content.DialogInterface;
android.graphics.Bitmap;
android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.hardware.Sensor;
android.hardware.SensorEvent;
android.hardware.SensorEventListener;
android.hardware.SensorManager;
android.os.Bundle;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
android.widget.ImageView;
android.widget.SeekBar;
android.widget.SeekBar.OnSeekBarChangeListener;


9.7 Doodlz.
doodleView DoodleView ( 32) . sensorManager ,
. float,
3436,

9.5.

305

, ( , ). , 47, . ,
. 37
AtomicBoolean ( false),
.
. 4044 int
. currentDialog Dialog
Choose Color Choose Line Width ( 50).
.
9.7. Doodlz
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

public class Doodlz extends Activity


{
private
private
private
private
private
private

DoodleView doodleView; // View


SensorManager sensorManager; //
oat acceleration;
//
oat currentAcceleration;
//
oat lastAcceleration;
//
AtomicBoolean dialogIsDisplayed = new AtomicBoolean();
//

//
private static nal int COLOR_MENU_ID = Menu.FIRST;
private static nal int WIDTH_MENU_ID = Menu.FIRST + 1;
private static nal int ERASE_MENU_ID = Menu.FIRST + 2;
private static nal int CLEAR_MENU_ID = Menu.FIRST + 3;
private static nal int SAVE_MENU_ID = Menu.FIRST + 4;
// ,
private static nal int ACCELERATION_THRESHOLD = 15000;
// , Choose Color
// Choose Line Width
private Dialog currentDialog;

onCreate onPause Activity


onCreate Doodlz (. 9.8) DoodleView,
,
. currentAcceleration lastAcceleration
GRAVITY_EARTH SensorManager,
. SensorManager
, , . . developer.android.com/reference/android/hardware/SensorManager.html.

306

Doodlz

67 enableAccelerometerListening (. 9.9),
SensorManager
. onPause Doodlz ( 7176)
disableAccelerometerListening (. 9.9), .
9.8. onCreate onPause Activity
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

// Activity
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main); //
// DoodleView
doodleView = (DoodleView) ndViewById(R.id.doodleView);
//
acceleration = 0.00f;
currentAcceleration = SensorManager.GRAVITY_EARTH;
lastAcceleration = SensorManager.GRAVITY_EARTH;

enableAccelerometerListening(); //
// onCreate

// ,
//
@Override
protected void onPause()
{
super.onPause();
disableAccelerometerListening(); //
} // onPause

enableAccelerometerListening disableAccelerometerListening
enableAccelerometerListening (. 9.9; 7987)
SensorManager. 8283 getSystemService
Activity SensorManager,
.
. registerListener
SensorManager, :



SensorEventListener, ;
Sensor, ,
. getDefaultSensor
SensorManager, Sensor-type ( Sensor.TYPE_
ACCELEROMETER );

9.5.


307

, .
, SENSOR_DELAY_NORMAL.
, .

disableAccelerometerListening (. 9.9; 90101),


onPause, unregisterListener SensorManager
. ,
,
sensorManager null.
9.9. enableAccelerometerListening disableAccelerometerListening
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

//
private void enableAccelerometerListening()
{
// SensorManager
sensorManager =
(SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensorManager.registerListener(sensorEventListener,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_NORMAL);
} // enableAccelerometerListening
//
private void disableAccelerometerListening()
{
//
if (sensorManager != null)
{
sensorManager.unregisterListener(
sensorEventListener,
sensorManager.getDefaultSensor(
SensorManager.SENSOR_ACCELEROMETER));
sensorManager = null;
} // if
} // disableAccelerometerListening

,
SensorEventListener,

9.10 onSensorChanged SensorEventListener
( 108168), .
,
. , 133165
AlertDialog,
. SensorEventListener

308

Doodlz

onAccuracyChanged ( 171174). , .
9.10. ,
SensorEventListener
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

//
private SensorEventListener sensorEventListener =
new SensorEventListener()
{
//
//
@Override
public void onSensorChanged(SensorEvent event)
{
//
if (!dialogIsVisible.get())
{
// x, y z SensorEvent
oat x = event.values[0];
oat y = event.values[1];
oat z = event.values[2];
//
lastAcceleration = currentAcceleration;
//
currentAcceleration = x * x + y * y + z * z;
//
acceleration = currentAcceleration *
(currentAcceleration lastAcceleration);
//
if (acceleration > ACCELERATION_THRESHOLD)
{
// AlertDialog Builder
AlertDialog.Builder builder =
new AlertDialog.Builder(Doodlz.this);
// AlertDialog
builder.setMessage(R.string.message_erase);
builder.setCancelable(true);
// Erase
builder.setPositiveButton(R.string.button_erase,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialogIsVisible.set(false);

9.5.
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

309

doodleView.clear(); //
} // onClick
} //
); // setPositiveButton
// Cancel
builder.setNegativeButton(R.string.button_cancel,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialogIsVisible.set(false);
dialog.cancel(); //
} // onClick
} //
); // setNegativeButton
dialogIsVisible.set(true); // ,
//
builder.show(); //
} // if
} // if
} // onSensorChanged
// "" SensorEventListener
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
} // onAccuracyChanged
}; //

,
. onSensorChanged
, . get
dialogIsVisible ( 110).
, ,
.

.
SensorEvent
. ,
( /2) x (/), y (/) z (/).
, API SensorEvent, : developer.android.com/reference/android/hardware/SensorEvent.html.
, x, y z
SensorEvent .
115117, currentAcceleration 120. 123

310

Doodlz

x, y z, currentAcceleration.
currentAcceleration lastAcceleration (), ACCELERATION_THRESHOLD.
, , , . shakeDetected
true, AlertDialog,

. shakeDetected true,
onSensorChanged , . ,
147 clear DoodleView (. 9.20).

,
.

onCreateOptionsMenu onOptionsItemSelected
9.11 onCreateOptionsMenu Activity, Activity. add
( 184193). ,
,
. - ,
NONE Menu.
( , 40
44).
.
, NONE Menu.
Android , .
String, .
9.11. onCreateOptionsMenu onOptionsItemSelected
Activity
177
178
179
180
181
182
183
184
185
186
187

//
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu); //
//
menu.add(Menu.NONE, COLOR_MENU_ID, Menu.NONE,
R.string.menuitem_color);
menu.add(Menu.NONE, WIDTH_MENU_ID, Menu.NONE,
R.string.menuitem_line_width);

9.5.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

311

menu.add(Menu.NONE, ERASE_MENU_ID, Menu.NONE,


R.string.menuitem_erase);
menu.add(Menu.NONE, CLEAR_MENU_ID, Menu.NONE,
R.string.menuitem_clear);
menu.add(Menu.NONE, SAVE_MENU_ID, Menu.NONE,
R.string.menuitem_save_image);

return true; //
// onCreateOptionsMenu

//
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// switch, MenuItem id
switch (item.getItemId())
{
case COLOR_MENU_ID:
showColorDialog(); //
return true; //
case WIDTH_MENU_ID:
showLineWidthDialog(); //
//
return true; //
case ERASE_MENU_ID:
doodleView.setDrawingColor(Color.WHITE); //
return true; //
case CLEAR_MENU_ID:
doodleView.clear(); // doodleView
return true; //
case SAVE_MENU_ID:
doodleView.saveImage(); //
return true; //
} // switch

return super.onOptionsItemSelected(item); //
//
// onOptionsItemSelected

199223 onOptionItemSelected Activity, . ID


MenuItem ( 203) ,
. :


Color. 206 showColorDialog (. 9.12),

.
 Width. 209 showLineWidthDialog (. 9.15), .
 Erase. 212 doodleView,
.

312

Doodlz

Clear. 215 clear doodleView,


.
 Save. 218 saveImage doodleView
.

showColorDialog
showColorDialog (. 9.12) Dialog
setContentView,
color_dialog.xml ( 229230).

Back , .
235242 SeekBar ,
256248 OnSeekBarChangeListener SeekBar
colorSeekBarChanged (. 9.13). 251255 doodleView,
SeekBar. ARGB
alpha, red, green blue Color.
setProgress SeekBar .
258260 setColorButton setColorButtonListener ( 9.14). true set DialigVisible
( 262). , 263 show . ,
Set Color ( ) .
9.12.
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245

//
private void showColorDialog()
{
//
currentDialog = new Dialog(this);
currentDialog.setContentView(R.layout.color_dialog);
currentDialog.setTitle(R.string.title_color_dialog);
currentDialog.setCancelable(true);
// SeekBar
// onChange
nal SeekBar alphaSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.alphaSeekBar);
nal SeekBar redSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.redSeekBar);
nal SeekBar greenSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.greenSeekBar);
nal SeekBar blueSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.blueSeekBar);
// SeekBar
alphaSeekBar.setOnSeekBarChangeListener(colorSeekBarChanged);

9.5.
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

313

redSeekBar.setOnSeekBarChangeListener(colorSeekBarChanged);
greenSeekBar.setOnSeekBarChangeListener(colorSeekBarChanged);
blueSeekBar.setOnSeekBarChangeListener(colorSeekBarChanged);
// SeekBar
nal int color = doodleView.getDrawingColor();
alphaSeekBar.setProgress(Color.alpha(color));
redSeekBar.setProgress(Color.red(color));
greenSeekBar.setProgress(Color.green(color));
blueSeekBar.setProgress(Color.blue(color));
// onClickListeneset Color
Button setColorButton = (Button) currentDialog.ndViewById(
R.id.setColorButton);
setColorButton.setOnClickListener(setColorButtonListener);

dialogIsVisible.set(true); //
currentDialog.show();
//
// showColorDialog

,
OnSeekBarChangeListener
alpha, red, green blue SeekBar
9.13 ,
OnSeekBarChangeListener , SeekBar Choose Color. SeekBar (. 9.12, 246249).
SeekBar onProgressChanged
( 270290). currentDialog SeekBar,
View, ( 275284).
setBackgroundColor View,
colorView Color,
SeekBar ( 287289). argb Color
SeekBar Color.

onProgressChanged ,
SeekBar. , GUI ,
, onProgressChanged.
9.13. , OnSeekbarChangeListener SeekBar Choose Color
266
267
268

// OnSeekBarChangeListener
// SeekBar
private OnSeekBarChangeListener colorSeekBarChanged =
new OnSeekBarChangeListener()

314

Doodlz

9.13 ()
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

{
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
// SeekBar colorView LinearLayout
SeekBar alphaSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.alphaSeekBar);
SeekBar redSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.redSeekBar);
SeekBar greenSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.greenSeekBar);
SeekBar blueSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.blueSeekBar);
View colorView =
(View) currentDialog.ndViewById(R.id.colorView);
//
colorView.setBackgroundColor(Color.argb(
alphaSeekBar.getProgress(), redSeekBar.getProgress(),
greenSeekBar.getProgress(), blueSeekBar.getProgress()));
} // onProgressChanged
// OnSeekBarChangeListener
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
// onStartTrackingTouch
// , OnSeekBarChangeListener
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
} // onStopTrackingTouch
}; // colorSeekBarChanged

,
OnClickListener,
9.14 ,
OnClickListener.
Set Color Choose Color.
Button 9.12 ( 261). onClick SeekBar,
322324 SeekBar
. 325 set isDialigVisible false, , . 326
dismiss Dialog, .

9.5.

315

9.14. , OnClickListener,
Set Color
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

// OnClickListener,
// Set Color
private OnClickListener setColorButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// SeekBar
SeekBar alphaSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.alphaSeekBar);
SeekBar redSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.redSeekBar);
SeekBar greenSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.greenSeekBar);
SeekBar blueSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.blueSeekBar);
//
doodleView.setDrawingColor(Color.argb(
alphaSeekBar.getProgress(), redSeekBar.getProgress(),
greenSeekBar.getProgress(), blueSeekBar.getProgress()));
dialogIsVisible.set(false); //
currentDialog.dismiss();
//
currentDialog = null;
//
} // onClick
}; // setColorButtonListener

showLineWidthDialog
showLineWidthDialog (. 9.15)
.
setContentView, width_dialog.xml ( 335336).
, . 341344 SeekBar ,
OnSeekBarChangeListener widthSeekBarChanged listener
(. 9.16), . 347349 Button , OnClickListener
setLineWidthButtonListener (. 9.17). 351 ,
set isDialigVisible
true. , 352 . ,
Set Line Width ( ) .
9.15. showLineWidthDialog ,

331
332

// ,
private void showLineWidthDialog()

316

Doodlz

9.15 ()
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

{
//
currentDialog = new Dialog(this);
currentDialog.setContentView(R.layout.width_dialog);
currentDialog.setTitle(R.string.title_line_width_dialog);
currentDialog.setCancelable(true);
// widthSeekBar
SeekBar widthSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.widthSeekBar);
widthSeekBar.setOnSeekBarChangeListener(widthSeekBarChanged);
widthSeekBar.setProgress(doodleView.getLineWidth());
// onClickListener Set Line Width
Button setLineWidthButton =
(Button) currentDialog.ndViewById(R.id.widthDialogDoneButton);
setLineWidthButton.setOnClickListener(setLineWidthButtonListener);

dialogIsVisible.set(true); //
//
currentDialog.show(); //
// showLineWidthDialog

,
OnSeekBarChangeListener widthSeekBar
9.16 widthSeekBarChanged OnSeekBarChangeListener, SeekBar
Choose Line Width. 359360 Bitmap,
. 361
Canvas, Bitmap. onProgressChanged
( 364381) ,
SeekBar. 368369 ImageView,
. 372375 Paint,
. setStrokeCap Paint ( 374)
( Paint.Cap.ROUND). 378
( ) eraseColor Bitmap. . , 380 widthImageView setImageBitmap ImageView.
9.16. ,
OnSeekbarChangeListener SeekBar
Choose Line Width
355
356

// OnSeekBarChangeListener
// SeekBar width
private OnSeekBarChangeListener widthSeekBarChanged =

9.5.
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395

317

new OnSeekBarChangeListener()
{
Bitmap bitmap = Bitmap.createBitmap( // Bitmap
400, 100, Bitmap.Cong.ARGB_8888);
Canvas canvas = new Canvas(bitmap); //
// Canvas
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
// ImageView
ImageView widthImageView = (ImageView)
currentDialog.ndViewById(R.id.widthImageView);
// Paint SeekBar
Paint p = new Paint();
p.setColor(doodleView.getDrawingColor());
p.setStrokeCap(Paint.Cap.ROUND);
p.setStrokeWidth(progress);

//
bitmap.eraseColor(Color.WHITE);
canvas.drawLine(30, 50, 370, 50, p);
widthImageView.setImageBitmap(bitmap);
// onProgressChanged

// , OnSeekBarChangeListener
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
} // onStartTrackingTouch
// , OnSeekBarChangeListener
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
} // onStopTrackingTouch
}; // widthSeekBarChanged

,
OnClickListener Set Line Width
9.17 ,
OnClickListener.
Set Line Width ( )
Choose Line Width ( ). Button 9.15 ( 349). onClick SeekBar ,
SeekBar. 409

318

Doodlz

set isDialigVisible
false. 410 dismiss Dialog, .
9.17. , OnClickListener,
Set Line Width
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

// OnClickListener,
// Set Line Width
private OnClickListener setLineWidthButtonListener =
new OnClickListener()
{
@Override
public void onClick(View v)
{
// SeekBar
SeekBar widthSeekBar =
(SeekBar) currentDialog.ndViewById(R.id.widthSeekBar);
//
doodleView.setLineWidth(widthSeekBar.getProgress());
dialogIsVisible.set(false); //
currentDialog.dismiss();
//
currentDialog = null;
//
} // onClick
};// setColorButtonListener
} // Doodlz

9.5.2. DoodleView View


DoodleView (. 9.189.26)
.

DoodleView Doodlz:

9.18 package import,
DoodleView Doodlz.
9.3 .
9.18. package import DoodleView
1
2
3
4
5
6
7
8
9

// DoodleView.java
// Doodlz.
package com.deitel.doodlz;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import android.content.ContentValues;

9.5.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

import
import
import
import
import
import
import
import
import
import
import
import
import
import

319

android.content.Context;
android.graphics.Bitmap;
android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.graphics.Path;
android.graphics.Point;
android.net.Uri;
android.provider.MediaStore.Images;
android.util.AttributeSet;
android.view.Gravity;
android.view.MotionEvent;
android.view.View;
android.widget.Toast;

, onSizeChanged DoodleView
DoodleView (. 9.19, 2936)
, , . ( 3954) . 43
paintScreen Paint,
. 46 paintLine Paint,
(), . 4751 paintLine. setAntiAlias
Paint true,
. style Paint Paint.Style.STROKE
( setStyle Paint). style
STROKE, FILL FILL_AND_STROKE , . Paint.
Style.FILL. setStrokeWidth Paint .
, .
setStrokeCap Paint Paint.Cap.ROUND. 52 pathMap, ( )
Path , . 53 previousPointMap,
, .
9.19. , onSizeChanged
DoodleView
25
26
27
28
29
30
31

//
public class DoodleView extends View
{
// , ,
//
private static nal oat TOUCH_TOLERANCE = 10;
private Bitmap bitmap; //


320

Doodlz

9.19 ()
32
33
34
35
36
37
38
39
40
41
42
43

private
private
private
private
private

// DoodleView DoodleView
public DoodleView(Context context, AttributeSet attrs)
{
super(context, attrs); // View
paintScreen = new Paint(); //
//

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

Canvas bitmapCanvas; //
Paint paintScreen;
//
Paint paintLine;
//
HashMap<Integer, Path> pathMap; // Paths
HashMap<Integer, Point> previousPointMap; // Points

//
//
paintLine = new Paint();
paintLine.setAntiAlias(true); //
//
paintLine.setColor(Color.BLACK); //
paintLine.setStyle(Paint.Style.STROKE); //
paintLine.setStrokeWidth(5); //
//
paintLine.setStrokeCap(Paint.Cap.ROUND); //
//
pathMap = new HashMap<Integer, Path>();
previousPointMap = new HashMap<Integer, Point>();
// DoodleView

// onSizeChanged BitMap Canvas


//
@Override
public void onSizeChanged(int w, int h, int oldW, int oldH)
{
bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Cong.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
bitmap.eraseColor(Color.WHITE); // BitMap
}
// onSizeChanged

DoodleView ,
View Activity Doodlz.
Bitmap, , onCreate. ,
5864 onSizeChanged View,
DoodleView. , ,
View Activity
. onSizeChanged

9.5.

321

, DoodleView View Activity


Doodlz,
(. 9.1). createBitmap Bitmap ,
.
Bitmap DoodleView.
createBitmap encoding () Bitmap, , . Bitmap.Config.
ARGB_8888 ,
( , , ).
Canvas, Bitmap. , eraseColor Bitmap
( ).

clear, setDrawingColor, getDrawingColor,


setLineWidth getLineWidth DoodleView
9.20 clear ( 6773), setDrawingColor ( 7679), getDrawingColor ( 8285), setLineWidth ( 8891) getLineWidth
( 9497), Activity Doodlz. clear
pathMap previousPointMap,
, invalidate View,
, View. onDraw View. setDrawingColor
paintLine Paint.
setColor Paint int, ARGB. getDrawingColor ,
Choose Color. setLineWidth
paintLine, . getLineWidth ,
Choose Line Width ( ).
9.20. clear, setDrawingColor, getDrawingColor, setLineWidth getLineWidth
DoodleView
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

//
public void clear()
{
pathMap.clear(); //
previousPointMap.clear(); //
bitmap.eraseColor(Color.WHITE); //
invalidate(); //
} // clear
//
public void setDrawingColor(int color)
{
paintLine.setColor(color);
} // setDrawingColor


322

Doodlz

9.20 ()
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

//
public int getDrawingColor()
{
return paintLine.getColor();
} // getDrawingColor
//
public void setLineWidth(int width)
{
paintLine.setStrokeWidth(width);
} // setLineWidth
//
public int getLineWidth()
{
return (int) paintLine.getStrokeWidth();
} // getLineWidth

OnDraw View
View , onDraw. 9.21
onDraw, ( Bitmap,
), DoodleView.
drawBitmap Canvas. , . x-y
, View.
Paint, .
107108 HashMap pathMap.
Path drawPath
Canvas. paintLine,
.
9.21. onDraw DoodleView
99
100
101
102
103
104
105
106
107
108
109
110

//
@Override
protected void onDraw(Canvas canvas)
{
//
canvas.drawBitmap(bitmap, 0, 0, paintScreen);
//
for (Integer key : pathMap.keySet())
canvas.drawPath(pathMap.get(key), paintLine); //
} // onDraw

9.5.

323

onTouchEvent View
OnTouchEvent (. 9.22) , View
. Android -, , .

. ,
, ,
. Path, . pathMap.
9.22. DoodleView onTouchEvent
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

//
@Override
public boolean onTouchEvent(MotionEvent event)
{
// ,
//
int action = event.getActionMasked();
//
int actionIndex = event.getActionIndex(); // ()
// MotionEvent
// ,
if (action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_DOWN)
{
touchStarted(event.getX(actionIndex),
event.getY(actionIndex),
event.getPointerId(actionIndex));
} // if
else if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_POINTER_UP)
{
touchEnded(event.getPointerId(actionIndex));
} // else if
else
{
touchMoved(event);
} // else

invalidate(); //
return true; //
// onTouchEvent

getActionMasked MotionEvent ( 116)


int, MotionEvent. MotionEvent .
getActionIndex MotionEvent ,

324

Doodlz

, .
, ,
MotionEvent. ,
MotionEvent ,
, getPointerID MotionEvent ( 125 130),
.
MotionEvent.ACTION_DOWN MotionEvent.ACTION_POINTER_DOWN
( 121122) , .
, , MotionEvent.ACTION_DOWN,
MotionEvent. ACTION_POINTER_DOWN.
touchStarted (. 9.23), . MotionEvent.ACTION_UP MotionEvent.
ACTION_POINTER_UP, , .
touchEnded (. 9.25),
, Path.
touchMoved (. 9.24), . 137
invalidate View, , 138
true, , .

touchStarted DoodleView
touchStarted (. 9.23) ,
. .
Path ( 148), reset Path , Path
. Path,
pathMap, Point previousPointMap. 163165
moveTo Path, Path x y Point.
9.23. touchStarted DoodleView
141
142
143
144
145
146
147
148
149
150
151
152
153

//
private void touchStarted(oat x, oat y, int lineID)
{
Path path; //
//
Point point; //
// lineID
if (pathMap.containsKey(lineID))
{
path = pathMap.get(lineID); //
path.reset(); // -
//
point = previousPointMap.get(lineID); //
} // f

9.5.
154
155
156
157
158
159

else
{

160
161
162
163
164
165
166
167

325

path = new Path(); //


pathMap.put(lineID, path); //
point = new Point(); //
previousPointMap.put(lineID, point);
//
//
// else

//
path.moveTo(x, y);
point.x = (int) x;
point.y = (int) y;
// touchStarted

touchMoved DoodleView
touchMoved (. 9.24) , . MotionEvent, onTouchEvent, ,
. getPointerCount MotionEvent (
172) , MotionEvent.
pointerID ( 175),
MotionEvent ( 176)
pointerIndex. (Path) pathMap
HashMap ( 179). , getX getY
MotionEvent
pointerIndex. (Point)
pointerID HashMap, . Path ,
, , TOUCH_TOLERANCE.
, , MotionEvents

. ,
TOUCH_TOLERANCE, quadTo Path ( 198199) ( ),
.
9.24. touchMoved DoodleView
168
169
170
171
172
173
174
175
176

//
private void touchMoved(MotionEvent event)
{
// MotionEvent
for (int i = 0; i < event.getPointerCount(); i++)
{
//
int pointerID = event.getPointerId(i);
int pointerIndex = event.ndPointerIndex(pointerID);

326

Doodlz

9.24 ()
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
}
207 } //
208

// ,
if (pathMap.containsKey(pointerID))
{
//
oat newX = event.getX(pointerIndex);
oat newY = event.getY(pointerIndex);
// ,
//
Path path = pathMap.get(pointerID);
Point point = previousPointMap.get(pointerID);
//
oat deltaX = Math.abs(newX point.x);
oat deltaY = Math.abs(newY point.y);
//
if (deltaX >= TOUCH_TOLERANCE || deltaY >= TOUCH_TOLERANCE)
{
//
path.quadTo(point.x, point.y, (newX + point.x) / 2,
(newY + point.y) / 2);
//
point.x = (int) newX;
point.y = (int) newY;
}
// if
} // if
// for
touchMoved

touchEnded DoodleView
touchEnded (. 9.25) , .
(lineID) . 212 .
213 drawPath bitmapCanvas. Bitmap, , reset Path,
.
,
, . , , .
9.25. touchEnded DoodleView
209
210
211

//
private void touchEnded(int lineID)
{

9.5.
212
213
214
215
216

327

Path path = pathMap.get(lineID); //


//
bitmapCanvas.drawPath(path, paintLine); // bitmapCanvas
path.reset(); //
// touchEnded

saveImage
saveImage (. 9.26) ,
.

, . ,
Android , , , . . AVD AVD Dev Tools
Media Scanner ( ),
.
9.26. saveImage DoodleView
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244

//
public void saveImage()
{
// "Doodlz"
//
String leName = "Doodlz" + System.currentTimeMillis();
// ContentValues
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, leName);
values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
values.put(Images.Media.MIME_TYPE, "image/jpg");
// Uri ,
Uri uri = getContext().getContentResolver().insert(
Images.Media.EXTERNAL_CONTENT_URI, values);
try
{
// OutputStream uri
OutputStream outStream =
getContext().getContentResolver().openOutputStream(uri);
// OutputStream
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
// OutputStream
outStream.ush(); //
outStream.close(); //

328

Doodlz

9.26 ()
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

//
Toast message = Toast.makeText(getContext(),
R.string.message_saved, Toast.LENGTH_SHORT);
message.setGravity(Gravity.CENTER, message.getXOffset() / 2,
message.getYOffset() / 2);
message.show(); // Toast
}
// try
catch (IOException ex)
{
//
Toast message = Toast.makeText(getContext(),
R.string.message_error_saving, Toast.LENGTH_SHORT);
message.setGravity(Gravity.CENTER, message.getXOffset() / 2,
message.getYOffset() / 2);
message.show();
// Toast
261
} // catch
262 } // saveImage
263 } // DoodleView

"Doodlz",
. 224 ContentValues,
ContentResolver (
), MIME ( "image/jpg"). MIME
-: www.w3schools.com/media/media_mimeref.asp.
ContentValues - ContentValues.
Images.Media.TITLE ( 225) fileName . Images.Media.DATE_ADDED ( 226)
. Images.Media.
MIME_TYPE ( 227) MIME
JPEG.
230231 ContentResolver ,
insert Uri,
. Images.Media.EXTERNAL_CONTENT_URI
,
( SD). ContentValues
, MIME.
, , Uri. OutputStream,
, Uri ( 236237). compress Bitmap, ,
(Bitmap.CompressFormat.JPEG).
( 100 ).
OutputStream , .
243244 OutputStream .

9.6.

329

, Toast
( 247251). , Toast ( 256260). makeText
Toast Context, . setGravity Toast
Toast. Gravity.CENTER , Toast
,
. Toast show
Toast.

9.6.

. 11 SDK,
, , Android 3.0,
Android 3.0. Android 3.0
Android 3.0.
, , SensorEventListener SensorManager.
Dialog .
AtomicBoolean.
ARGB,
, , , . Canvas, .
.

Path.
onTouchEvent View
MotionEvent ,
, .

OutputStream ContentResolver. , Toast
, .
10 Address Book, .
,
. , , ,
. ,
.


Address Book

10

ListActivity, AdapterViews,
, ,
SQLite, GUI,
MenuInater










ListActivity ,
ListView.
Activity, ,
Intents.
SQLite SQLiteOpenHelper,
, SQLite
SQLiteDatabase.
SimpleCursorAdapter
ListView.
Cursor.
GUI .

10.2. Address Book

331

, GUI,
GUI.

XML, MenuInflater.

10.1.
Address Book (. 10.1) .
. ,
. ,
, Edit Contact ( ) Delete
Contact ( ), . 10.2.
, Activity, , EditTexts (. . 10.2).
, , (. 10.3).
, , Add Contact
( ). Activity, (. 10.4). Save Contact
( ) ,
.

10.2. Address Book



Eclipse Address Book app.
:
1. FileImport ()
Import ().
2. Import General ()
Existing Projects into Workspace ( ).
Next > ( >).
3. Select root directory ( )
Browse (). AddressBook.
4. Finish () .
Eclipse , Package Explorer, Run
AsAndroid Application (  Android).

332

10

Address Book

. 10.1. ,

. 10.2.

10.2. Address Book

. 10.3.

. 10.4.

333

334

10

Address Book


.
, Add Contact ( ). ,
.
Save Contact ( ) . , Back ()
. .


, ,
.


,
Edit Contact ( ).
EditTexts, .
, Save Contact ( )
.


, Delete Contact ( ).
,
. , .

Android 2.3
Android 2.3, ,
, . (
)
.
.

10.3.
,
Address Book ( ).


Activity
AndroidManifest.xml .
Activity.
Activity. Activity
( 10.4.2).

GUI
-
GUI XML ( 10.4.3).

10.3.

335

, ( 10.4.6).
style. , ,
GUI, .

TextView
TextView . ,
Drawable android:background TextView. Drawable ,
Drawable, XML- ( 10.4.4). XML-, Drawable,
drawable, res .

ListView
ListView ( android.widget)
, ,
. ( 10.4.5), ListView.

XML- MenuInater
, , MenuItems . MenuItems
XML, ( 10.5.1 10.5.2).
MenuInflater Activity ( android.view),
LayoutInflater.
Android.

ListActivity Activity,
ListView
Activity
, ListActivity ( android.app, 10.5.1),
ListView, (
). ListView AdapterView ( android.widget)
GUI, Adapter (
android.widget). CursorAdapter ( android.
widget) ListView.
Adapter AdapterView.
Android
- developer.android.com/guide/topics/ui/binding.html.


Activity
Activity
, .

Activity. 5

336

10

Address Book

Intent URL- -
. 10.5.1 10.5.2
Intent Activity
Activity .
10.5.3 , Activity,
Activity.

SQLite
SQLite. SQLite (www.sqlite.org)
. Activity, ,
SQLite DatabaseConnector ( 10.5.4).
SQLiteOpenHelper ( android.
database.sqlite),
SQLiteDatabase ( android.database.sqlite)
. Cursor
( android.database).


GUI
GUI ( ).
Activity Not Responding (ANR,
). , Android , GUI
.
GUI, AsyncTask ( android.os)
GUI.
AsyncTask, AsyncTask GUI.

10.4.

Address Book GUI.
strings.xml ViewContact Activity (view_contact.xml)
AddEditContact (add_contact.xml). ,
Eclipse.

10.4.1.
Android AddresBook.
New Android Project ( Android) ,
Finish ():


Build Target ( ): Android 2.3.3.

Application name ( ): Address Book.

10.4.

337

Package name ( ): com.deitel.adressbook.


Create Activity ( ): AdressBook.
 Min SDK Version ( SDK): 8.

10.4.2. AndroidManifest.xml
10.1 AndroidManifest.xml ,
activity Activity . 1415
activity AddEditContact. 1617
activity ViewContact.
10.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.addressbook" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".AddressBook"
android:label="@string/app_name">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
<activity android:name=".AddEditContact"
android:label="@string/app_name"></activity>
<activity android:name=".ViewContact"
android:label="@string/app_name"></activity>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>

10.4.3. styles.xml
10.2 , view_
contact.xml ( 10.4.6). XML-, , XML-, style, res/values . style name, . 3,
GUI,
item (. 4),
XML .
10.2. , styles.xml
res/values
1
2

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


<resources>

338

10

Address Book

10.2 ()
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

<style name="ContactLabelTextView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">right</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:layout_marginLeft">5dp</item>
<item name="android:layout_marginRight">5dp</item>
<item name="android:layout_marginTop">5dp</item>
</style>
<style name="ContactTextView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">16sp</item>
<item name="android:textColor">@android:color/white</item>
<item name="android:layout_margin">5dp</item>
<item name="android:background">@drawable/textview_border</item>
</style>
</resources>

10.4.4. textview_border.xml
ContactTextView, 10.2 ( 1320),
TextView,
ViewContact Activity. 19 Drawable
android:background TextView.
Drawable ( textview_border) XML shape (. 10.3)
res/drawable. shape android:shape ( 3) "rectangle" ( ), "oval", "line"
"ring". corners ( 4) .
stroke ( 5) .
padding ( 67) ,
Drawable. , , .
XML -: developer.android.com/guide/topics/resources/ drawable-resource.html#Shape.
10.3. XML Drawable,
TextView
1
2
3
4
5
6
7
8

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


<shape xmlns:android=http://schemas.android.com/apk/res/android
android:shape="rectangle" >
<corners android:radius="5dp"/>
<stroke android:width="1dp" android:color="#555"/>
<padding android:top="10dp" android:left="10dp" android:bottom="10dp"
android:right="10dp"/>
</shape>

10.4.

339

10.4.5. AddressBook
Activity: contact_list_item.xml
AddressBook Activity ListActivity Activity.
ListActivity ListView, .
Activity. ListActivity, XML- ,
ListView, android:id "@android:id/list".
12,
Slideshow.
ListView
, contact_
list_item.xml ( 10.4). , TextView,
. ListView , ( 5).
android:id TextView. 6 listPreferredItemHeight
( Android). 7
center_vertical.
, list-item ,
android:id.
android:id 10.5.1. . 10.1
list-item.
10.4. ListView
AddressBook ListActivity
1
2
3
4
5
6
7

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


<TextView xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/contactTextView" android:layout_width="match_parent"
android:layout_height="wrap_content" android:padding="8dp"
android:textSize="20sp" android:textColor="@android:color/white">
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"></TextView>

10.4.6. ViewContact
Activity: view_contact.xml
AddressBook Activity,
ViewContact Activity (. 10.5). Activity ( view_contact.xml)
ScrollView, TableLayout,
TableRow TextView.
, TextView , 10.6. , 1115
:

340

10

Address Book

<TextView android:id="@+id/nameLabelTextView"
style="@style/ContactLabelTextView"
android:text="@string/label_name"></TextView>
<TextView android:id="@+id/nameTextView"
style="@style/ContactTextView"></TextView>

TextViews TableRow.
TextView style .
@style/styleName.

nameLabelTextView

nameTextView

phoneLabelTextView

phoneTextView

emailLabelTextView

emailTextView

streetLabelTextView
cityLabelTextView

streetTextView
cityTextView

. 10.5. GUI ViewContact, Activity,


id.
GUI ScrollView,
TableLayout TableRows

10.4.7. AddEditContact
Activity: add_contact.xml
Add Contact AddressBook
Activity Edit Contact ViewContact Activity, AddEditContact Activity (. 10.6). Activity
ScrollView, LinearLayout.
Activity AddressBook, Activity, EditText , ( 12,
17, 22, 33 38 XML- ). EditTexts , AddEditContact Activity
ViewContact Activity. EditText android:inputType
android:imeOptions. , ,
android:inputType ( 13, 18, 23, 34 39 XML- )

10.4.

341

,
EditText. ,
EditText. 5, android:imeOptions
Next () nameEditText, emailEditText,
phoneEditText streetEditText. ,
Button
EditText. cityEditText , , Done () .

nameEditText
phoneEditText
emailEditText
addressTextView
streetEditText
cityEditText

saveContactButton

. 10.6. GUI AddEditContact Activity,


id. GUI
ScrollView, LinearLayout

10.4.8. MenuItems
, XML-
10.5 10.6 AddressBook Activity ViewContact
Activity . , , res/menu ( )
( 3.5). New
Android XML File ( XML- Android) Menu ()
. XML- root menu,
item, MenuItem. 10.5.1 10.5.2 , .

342

10

Address Book

10.5. AddressBook Activity


1
2
3
4
5
6
7
8

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


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/addContactItem"
android:title="@string/menuitem_add_contact"
android:icon="@android:drawable/ic_menu_add"
android:titleCondensed="@string/menuitem_add_contact"
android:alphabeticShortcut="e"></item>
</menu>

10.6. ViewContact Activity


1
2
3
4
5
6
7
8
9
10
11
12
13

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


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/editItem"
android:title="@string/menuitem_edit_contact"
android:orderInCategory="1" android:alphabeticShortcut="e"
android:titleCondensed="@string/menuitem_edit_contact"
android:icon="@android:drawable/ic_menu_edit"></item>
<item android:id="@+id/deleteItem"
android:title="@string/menuitem_delete_contact"
android:orderInCategory="2" android:alphabeticShortcut="d"
android:titleCondensed="@string/menuitem_delete_contact"
android:icon="@android:drawable/ic_delete"></item>
</menu>

android:id, MenuItem
.
:


android:title android:titleCondensed. , MenuItem.


, .
 android:icon. Drawable, MenuItem .
MenuItems ,
Android SDK. SDK platforms
data/res/drawable-hdpi, .
XML,
@android:drawable/_ (. 10.5, 5, 10.6,
7 12).
 android:alphabeticShortcut. ,
,
.
 android:orderInCategory. ,
MenuItems. 10.5, MenuItem.

10.5.

343

, -
developer.android.com/guide/topics/resources/menu-resource.html.

10.5.
AddressBook ( ListActivity,
10.710.12), ViewContact ( 10.1310.17), AddEditContact
( 10.1810.21) DatabaseConnector ( 10.2210.25).
, Activity ( AddressBook) , , ListActivity. Activity DatabaseConnector
src/com.deitel.addressbook.

10.5.1. AddressBook ListActivity


AddressBook (. 10.710.12) Activity, . ,
ListActivity, Activity,
Activity ListView,
.

package, import
10.7 package, import
AddressBook. , ,
10.3. ROW_ID
, ( 10.12).
contactListView
AddressBook ListView, . contactAdapter CursorAdapter, ListView AddressBook.
10.7. package, import
AddressBook
1
2
3
4
5
6
7
8
9
10
11
12
13
14

// AddressBook.java
// Activity Address Book.
package com.deitel.addressbook;
import
import
import
import
import
import
import
import
import
import

android.app.ListActivity;
android.content.Intent;
android.database.Cursor;
android.os.AsyncTask;
android.os.Bundle;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.view.View;
android.widget.AdapterView;

344

10

Address Book

10.7 ()
15
16
17
18
19
20
21
22

import
import
import
import

android.widget.AdapterView.OnItemClickListener;
android.widget.CursorAdapter;
android.widget.ListView;
android.widget.SimpleCursorAdapter;

public class AddressBook extends ListActivity


{
public static nal String ROW_ID = "row_id";

//
// Intent
private ListView contactListView;
// ListView
// ListActivity
private CursorAdapter contactAdapter; // ListView

23
24
25

onCreate Activity
onCreate (. 10.8, 2632) Activity.
, ListActivity ListView,
Activity. GUI setContentView, .
31 getListView ListActivity ListView. 32 OnItemClickListener
ListView viewContactListener (. 10.12)
ListView.
10.8. onCreate Activity
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); // onCreate
//
contactListView = getListView(); // ListView
contactListView.setOnItemClickListener(viewContactListener);

// TextView
// ListView
String[] from = new String[] { "name" };
int[] to = new int[] { R.id.contactTextView };
CursorAdapter contactAdapter = new SimpleCursorAdapter(
AddressBook.this, R.layout.contact_list_item, null, from, to);
setListAdapter(contactAdapter); // contactView
// onCreate

Cursor ListView, CursorAdapter ( 3538).


Cursor , ListView. SimpleCursorAdapter CursorAdapter,

10.5.

345

Cursor TextViews
ImagesView, XML-. SimpleCursorAdapter
, , GUI. ID GUI,
. 35 String,
, . 36 int, ID
GUI ( , R.id.contactTextView). 3738
SimpleCursorAdapter.
:


Context, ListView ( AddressBook


Activity);
 ID ,
ListView;
 Cursor, , null, Cursor ;
 String, ;


int, ID
GUI.

39 setListAdapter ListActivity
ListView CursorAdapter. ListView
.

onResume onStop Activity


8.5.1, onResume (. 10.9, 4249)
Activity ,
Activity. onResume AsyncTask ( 48) GetContactsTask ( 10.10),
Cursor
contactAdapter , ListView
AddressBook. AsyncTask .
execute , - .
, , ,
doInBackground. 48
GetContactsTask. , AsyncTask
.
10.9. onResume onStop Activity
42
43
44
45
46

@Override
protected void onResume()
{
super.onResume(); // onResume


346

10

Address Book

10.9 ()
47
48
49
50
51
52
53
54

@Override
protected void onStop()
{
Cursor cursor = contactAdapter.getCursor(); //
// Cursor

55
56
57
58
59
60
61
62

// GetContactsTask
new GetContactsTask().execute((Object[]) null);
// onResume

if (cursor != null)
cursor.deactivate(); //

contactAdapter.changeCursor(null); //
// Cursor
super.onStop();
// onStop

onStop Activity (. 10.9, 5161) ,


Activity , , Activity.
Cursor, ListView, .
54 getCursor CursorAdapter, Cursor contactAdapter. 57
deactivate Cursor, ,
Cursor. 59 changeCursor CursorAdapter
null Cursor CursorAdapter.

GetContactsTask AsyncTask
GetContactsTask (. 10.10) AsyncTask.

GUI Activity
ListView. AsyncTask , :



doInBackground AsyncTask ( 5057). execute
AsyncTask, doInBackground
. doInBackground
, Object
null execute
AsyncTask, doInBackground.

onProgressUpdate AsyncTask. GUI
. , Object .

10.5.


347

, onPostExecute AsyncTask ( 8085).


GUI Activity
AsyncTask.

10.10. GetContactsTask AsyncTask


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

// GUI
private class GetContactsTask extends AsyncTask<Object, Object, Cursor>
{
DatabaseConnector databaseConnector =
new DatabaseConnector(AddressBook.this);
//
@Override
protected Cursor doInBackground(Object... params)
{
databaseConnector.open();

// ,
return databaseConnector.getAllContacts();
// doInBackground

// Cursor, doInBackground
@Override
protected void onPostExecute(Cursor result)
{
contactAdapter.changeCursor(result); // Cursor
databaseConnector.close();
} // onPostExecute
// GetContactsTask

AsyncTask ,
,
, .
6667 DatabaseConnector, Context (AddressBook.this). (
DatabaseConnector 10.5.4.)
doInBackground ( 7077) databaseConnector .
Cursor, getAllContacts, onPostExecute ( 8086).
Cursor, ,
changeCursor CursorAdapter. ListView Activity
.


Activity Cursor CursorAdapter. Activity

348

10

Address Book

. startManagingCursor Activity Activity


Cursor Activity.
Activity , deactivate
. Activity,
. Activity ,
close, ,
. Cursor ,
, Cursor
Activity, Cursor Activity.
Activity Cursor, , Cursor
, .

onCreateOptionsMenu
onOptionsItemSelected Activity
Activity, onCreateOptionsMenu (. 10.11, 8996) MenuInflater
addressbook_menu.xml, Add Contact MenuItem. MenuInflater
getMenuInflater Activity. MenuItem onOptionsItemSelected ( 99107)
AddEditContact Activity ( 10.5.3). 103104
Intent, Activity. Intent Context,
Activity, , Activity
(AddEditContact.class). Intent startActivity
Activity, Activity.
10.11. onCreateOptionsMenu
onOptionsItemSelected Activity
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

// Activity XML-
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
MenuInater inater = getMenuInater();
inater.inate(R.menu.addressbook_menu, menu);
return true;
} // onCreateOptionsMenu
// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Intent
// AddEditContact Activity
Intent addNewContact =
new Intent(AddressBook.this, AddEditContact.class);
startActivity(addNewContact); // start the AddEditContact Activity

10.5.
106
107
108

349

return super.onOptionsItemSelected(item); //
//
// onOptionsItemSelected

,
OnItemClickListener ListView
viewContactListener OnItemClickListener (. 10.12)
ViewContact Activity .
onItemClick :


AdapterView, (
ListView);
 View
;
 ListView;

ID long ID Cursor.

10.12. OnItemClickListener viewContactListener,


, ListView
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

// ,
// ListView
OnItemClickListener viewContactListener = new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3)
{
// Intent ViewContact Activity
Intent viewContact =
new Intent(AddressBook.this, ViewContact.class);

// ID
// Intent
viewContact.putExtra(ROW_ID, arg3);
startActivity(viewContact); // ViewContact Activity
} // onItemClick
}; // viewContactListener
// AddressBook

118119 Intent,
ViewContact Activity.
ViewContact Activity ,
.
(extra) Intent putExtra Intent ( 122).
Bundle,

350

10

Address Book

Intent.
, .

10.5.2. ViewContact Activity


ViewContact Activity (. 10.1310.17) , ,
.

package, import
10.13 package, import
ViewContact. import 10.3. rowID
. TextView ( 2024)
.
10.13. package, import
ViewContact
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

// ViewContact.java
// Activity, .
package com.deitel.addressbook;
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.content.DialogInterface;
android.content.Intent;
android.database.Cursor;
android.os.AsyncTask;
android.os.Bundle;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.widget.TextView;

public class ViewContact extends Activity


{
private long rowID; //
private TextView nameTextView;
//
private TextView phoneTextView; //
private TextView emailTextView; // e_mail
private TextView streetTextView; //
private TextView cityTextView;
// //

onCreate onResume Activity


onCreate (. 10.14, 2743) TextView Activity, ID .
getIntent Activity Intent, Activity.

10.5.

351

getExtras Intent,
Bundle, ,
Intent . null,
. getLong Bundle long, ID .

, ,
null ( 41). , Bundle, . ,
null , . , , Activity
nish.

onResume ( 4653) AsyncTask LoadContactTask (. 10.15),


.
10.14. onCreate Activity
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.view_contact);
// EditTexts
nameTextView = (TextView) ndViewById(R.id.nameTextView);
phoneTextView = (TextView) ndViewById(R.id.phoneTextView);
emailTextView = (TextView) ndViewById(R.id.emailTextView);
streetTextView = (TextView) ndViewById(R.id.streetTextView);
cityTextView = (TextView) ndViewById(R.id.cityTextView);

// ID
Bundle extras = getIntent().getExtras();
rowID = extras.getLong("row_id");
// onCreate

//
@Override
protected void onResume()
{
super.onResume();

// LoadContactTask
new LoadContactTask().execute(rowID);
// onResume

352

10

Address Book

GetContactsTask AsyncTask
GetContactsTask (. 10.15) AsyncTask
, .
, :


Long ,
doInBackground AsyncTask. ID ,
.
 Object ,
onProgressUpdate AsyncTask. .
 Cursor ,
onPostExecute AsyncTask.
10.15. loadContact ViewContact
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

// GUI
private class LoadContactTask extends AsyncTask<Long, Object, Cursor>
{
DatabaseConnector databaseConnector =
new DatabaseConnector(ViewContact.this);
//
@Override
protected Cursor doInBackground(Long... params)
{
databaseConnector.open();

// ,
return databaseConnector.getOneContact(params[0]);
// doInBackground

// Cursor,
// doInBackground
@Override
protected void onPostExecute(Cursor result)
{
super.onPostExecute(result);
result.moveToFirst(); //
//
int nameIndex = result.getColumnIndex("name");
int phoneIndex = result.getColumnIndex("phone");
int emailIndex = result.getColumnIndex("email");
int streetIndex = result.getColumnIndex("street");
int cityIndex = result.getColumnIndex("city");
// TextViews

10.5.
87
88
89
90
91
92
93
94
95
96
97

353

nameTextView.setText(result.getString(nameIndex));
phoneTextView.setText(result.getString(phoneIndex));
emailTextView.setText(result.getString(emailIndex));
streetTextView.setText(result.getString(streetIndex));
cityTextView.setText(result.getString(cityIndex));

result.close(); //
databaseConnector.close(); //
} // onPostExecute
// LoadContactTask

5859 DatabaseConnector ( 10.5.4).


doInBackground ( 6269) getOneContact DatabaseConnector,
rowID,
execute AsyncTask. doInBackground rowID
params[0].
Cursor onPostExecute ( 7295).
Cursor . . Cursor
moveToFirst ( 77).

, moveToFirst Cursor true , Cursor.


Cursor.

getColumnIndex Cursor . (
, String
, ROW_ID AddressBook.)
-1, . Cursor
getColumnIndexOrThrow,
, . 8791 getString Cursor String
Cursor
TextView. 9394 Cursor Activity, . ,
,
.

onCreateOptionsMenu
onOptionsItemSelected Activity
ViewContact Activity ,
. onCreateOptionsMenu (. 10.16, 99106) MenuInflater view_contact.

354

10

Address Book

xml, MenuItems Edit Contact Delete Contact. onOptionsItemSelected ( 109134) ID MenuItem . Edit Contact,
116126 Intent AddEditContact
Activity ( 10.5.3), Intent,
, EditTexts AddEditContact
Activity, Activity. Delete Contact,
129 deleteContact (. 10.17).
10.16. onCreateOptionsMenu
onOptionsItemSelected
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

Activity XML-
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
MenuInater inater = getMenuInater();
inater.inate(R.menu.view_contact_menu, menu);
return true;
} // onCreateOptionsMenu
// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId()) //
// ID MenuItem
{
case R.id.editItem:
// Intent AddEditContact Activity
Intent addEditContact =
new Intent(this, AddEditContact.class);
//
// Intent
addEditContact.putExtra("row_id", rowID);
addEditContact.putExtra("name", nameTextView.getText());
addEditContact.putExtra("phone", phoneTextView.getText());
addEditContact.putExtra("email", emailTextView.getText());
addEditContact.putExtra("street", streetTextView.getText());
addEditContact.putExtra("city", cityTextView.getText());
startActivity(addEditContact); // start the Activity
return true;
case R.id.deleteItem:
deleteContact(); //
return true;
default:
return super.onOptionsItemSelected(item);
} // switch
} // onOptionsItemSelected

10.5.

355

deleteContact
deleteContact (. 10.17) AlertDialog,
.
AsyncTask SQLite.
Delete Button , 153154
DatabaseConnector. 158173 AsyncTask,
( 176) Long, ID
, doInBackground. . 164
deleteContact DatabaseConnector, .
doInBackground , 171 finish Activity Activity,
ViewContact, Activity.
AddressBook Activity.
10.17. deleteContact ViewContact
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

//
private void deleteContact()
{
// AlertDialog Builder
AlertDialog.Builder builder =
new AlertDialog.Builder(ViewContact.this);
builder.setTitle(R.string.conrmTitle);
//
builder.setMessage(R.string.conrmMessage); //
//
// OK,
builder.setPositiveButton(R.string.button_delete,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int button)
{
nal DatabaseConnector databaseConnector =
new DatabaseConnector(ViewContact.this);
// AsyncTask,
// , nish
AsyncTask<Long, Object, Object> deleteTask =
new AsyncTask<Long, Object, Object>()
{
@Override
protected Object doInBackground(Long... params)
{
databaseConnector.deleteContact(params[0]);
return null;
} // doInBackground
@Override

356

10

Address Book

10.17 ()
169
protected void onPostExecute(Object result)
170
{
171
nish(); // AddressBook Activity
172
} // onPostExecute
173
}; // AsyncTask
174
175
// AsyncTask rowID
176
deleteTask.execute(new Long[] { rowID });
177
} // onClick
178
} //
179
); // setPositiveButton
180
181
builder.setNegativeButton(R.string.button_cancel, null);
182
builder.show(); //
183
} // deleteContact
184 } // ViewContact

10.5.3. AddEditContact Activity


AddEditContact Activity (. 10.1810.21)
.

package, import
10.18 package, import
AddEditContact. Activity .
databaseConnector
Activity . rowID
, , Activity
, .
2024
EditTexts Activity.
10.18. package, import
AddEditContact
1
2
3
4
5
6
7
8
9
10
11
12
13

// AddEditContact.java
// Activity,
// .
package com.deitel.addressbook;
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.AlertDialog;
android.os.AsyncTask;
android.os.Bundle;
android.view.View;
android.view.View.OnClickListener;
android.widget.Button;
android.widget.EditText;

10.5.
14
15
16
17
18
19
20
21
22
23
24
25

357

public class AddEditContact extends Activity


{
private long rowID; // id
// EditTexts,
private EditText nameEditText;
private EditText phoneEditText;
private EditText emailEditText;
private EditText streetEditText;
private EditText cityEditText;

onCreate Activity
onCreate (. 10.19) AddEditContact Activity.
3337 EditText Activity.
getIntent Activity Intent,
Activity, getExtras Intent. Bundle, Intent. AddEditContact Activity AddressBook Activity
- Intent,
. getExtras null.
Bundle ( 42), , Activity
ViewContact Activity, . 4449 Bundle getLong
( 44) getString, String
EditText. 5355 Save
Contact Button Activity.
10.19. onCreate onPause Activity
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

// Activity
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); // onCreate
setContentView(R.layout.add_contact); // UI
nameEditText = (EditText) ndViewById(R.id.nameEditText);
emailEditText = (EditText) ndViewById(R.id.emailEditText);
phoneEditText = (EditText) ndViewById(R.id.phoneEditText);
streetEditText = (EditText) ndViewById(R.id.streetEditText);
cityEditText = (EditText) ndViewById(R.id.cityEditText);
Bundle extras = getIntent().getExtras(); //
// EditTexts
if (extras != null)
{

358

10

Address Book

10.19 ()
44
45
46
47
48
49
50
51
52
53
54
55
56
57

rowID = extras.getLong("row_id");
nameEditText.setText(extras.getString("name"));
emailEditText.setText(extras.getString("email"));
phoneEditText.setText(extras.getString("phone"));
streetEditText.setText(extras.getString("street"));
cityEditText.setText(extras.getString("city"));
// if

// Save Contact Button


Button saveContactButton =
(Button) ndViewById(R.id.saveContactButton);
saveContactButton.setOnClickListener(saveContactButtonClicked);
// onCreate

OnClickListener,
Save Contact Button
Save Contact Button AddEditContact
Activity, saveContactButtonClicked OnClickListener (.
10.20). , , ,
. onClick ,
0 ( 64), , AsyncTask
. doInBackground ( 6974)
saveContact (. 10.21), . onPostExecute ( 7680) finish,
Activity Activity (AddressBook
ViewContact). nameEditText , 8996
AlertDialog, .
10.20. OnClickListener doneButtonClicked,
doneButton
58
59
60
61
62
63
64
65
66
67
68
69
70
71

// ,
// Done Button
OnClickListener saveContactButtonClicked = new OnClickListener()
{
@Override
public void onClick(View v)
{
if (nameEditText.getText().length() != 0)
{
AsyncTask<Object, Object, Object> saveContactTask =
new AsyncTask<Object, Object, Object>()
{
@Override
protected Object doInBackground(Object... params)
{

10.5.
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

};

359

saveContact(); //
return null;
// doInBackground

@Override
protected void onPostExecute(Object result)
{
nish(); // Activity
} // onPostExecute
// AsyncTask

//
saveContactTask.execute((Object[]) null);
} // if
else
{
// AlertDialog Builder
AlertDialog.Builder builder =
new AlertDialog.Builder(AddEditContact.this);
//
// ,
builder.setTitle(R.string.errorTitle);
builder.setMessage(R.string.errorMessage);
builder.setPositiveButton(R.string.errorButton, null);
builder.show(); // display the Dialog
} // else
} // onClick
};// OnClickListener saveContactButtonClicked

saveContact
saveContact (. 10.21) EditText,
Activity. 105
DatabaseConnectort, , - Intent,
Activity. , ,
, 110115 String
EditText Activity, insertContact
DatabaseConnector .
Intent, Activity, . String EditText
Activity, updateContact DatabaseConnector.
rowID .
insertContact updateContact DatabaseConnector
.
10.21. saveContact AddEditContact
101
102

//
private void saveContact()

360

10

Address Book

10.21 ()
103
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
}
127 } //

// DatabaseConnector
// SQLite
DatabaseConnector databaseConnector = new DatabaseConnector(this);
if (getIntent().getExtras() == null)
{
//
databaseConnector.insertContact(
nameEditText.getText().toString(),
emailEditText.getText().toString(),
phoneEditText.getText().toString(),
streetEditText.getText().toString(),
cityEditText.getText().toString());
} // if
else
{
databaseConnector.updateContact(rowID,
nameEditText.getText().toString(),
emailEditText.getText().toString(),
phoneEditText.getText().toString(),
streetEditText.getText().toString(),
cityEditText.getText().toString());
} // else
// saveContact
AddEditContact

10.5.4. DatabaseConnector
DatabaseConnector (. 10.2210.25)
SQLite.
UserContacts ,
.

package, import
10.22 package, import DatabaseConnector.
import , 10.3, . DATABASE_NAME ( 16)
, . .
, . SQLiteDatabase ( 17) / SQLite. DatabaseOpenHelper
( 18) ,
SQLiteOpenHelper. , ,
(
). SQLOpenHelper 10.25.

10.5.

361

10.22. package, import


DatabaseConnector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// DatabaseConnector.java
// UserContacts
package com.deitel.addressbook;
import
import
import
import
import
import
import

android.content.ContentValues;
android.content.Context;
android.database.Cursor;
android.database.SQLException;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteOpenHelper;
android.database.sqlite.SQLiteDatabase.CursorFactory;

public class DatabaseConnector


{
//
private static nal String DATABASE_NAME = "UserContacts";
private SQLiteDatabase database; //
private DatabaseOpenHelper databaseOpenHelper; //
//

19

open close DatabaseConnector


DatabaseConnection (. 10.23, 2126)
DatabaseOpenHelper (. 10.25),
. DatabaseOpenHelper
10.25. open ( 2933) SQLException, . getWritableDatabase ( 32),
SQLiteOpenHelper, SQLiteDatabase. ,
. , .
,
.
close ( 3640) close
SQLiteOpenHelper.
10.23. , open close
20
21
22
23
24
25
26
27
28
29

// DatabaseConnector
public DatabaseConnector(Context context)
{
// DatabaseOpenHelper
databaseOpenHelper =
new DatabaseOpenHelper(context, DATABASE_NAME, null, 1);
} // DatabaseConnector
//
public void open() throws SQLException

362

10

Address Book

10.23 ()
30
31
32
33
34
35
36
37
38
39
40
41

// /
database = databaseOpenHelper.getWritableDatabase();
// open

//
public void close()
{
if (database != null)
database.close(); //
} // close

insertContact, updateContact, getAllContacts,


getOneContact deleteContact
insertContact (. 10.24, 4356) .
ContentValues ( 4651),
( ).
. 5355 ,
, . insert SQLiteDatabase ( 54)
ContentValues ,
, .
, ,
nullColumnHack. , SQLite

ContentValues insert.
ContentValues , nullColumnHack , NULL.
10.24. insertContact, updateContact, getAllContacts, getOneContact
deleteContact
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

//
public void insertContact(String name, String email, String phone,
String state, String city)
{
ContentValues newContact = new ContentValues();
newContact.put("name", name);
newContact.put("email", email);
newContact.put("phone", phone);
newContact.put("street", state);
newContact.put("city", city);

open(); //
database.insert("contacts", null, newContact);
close(); //
// insertContact

10.5.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

363

//
public void updateContact(long id, String name, String email,
String phone, String state, String city)
{
ContentValues editContact = new ContentValues();
editContact.put("name", name);
editContact.put("email", email);
editContact.put("phone", phone);
editContact.put("street", state);
editContact.put("city", city);

open(); //
database.update("contacts", editContact, "_id=" + id, null);
close(); //
// updateContact

// Cursor
//
public Cursor getAllContacts()
{
return database.query("contacts", new String[] {"_id", "name"},
null, null, null, null, "name");
}
// getAllContacts
// Cursor,
//
public Cursor getOneContact(long id)
{
return database.query(
"contacts", null, "_id='" + id, null, null, null, null);
} // getOnContact
// , String
public void deleteContact(long id)
{
open(); //
database.delete("contacts", "_id=" + id, null);
close(); //
} // deleteContact

updateContact ( 5972) insertContact


, update SQLiteDatabase ( 70)
. update
SQL WHERE ( WHERE), .
ID , .
getAllContacts ( 7579) query SqLiteDatabase
( 7778) Cursor,
ID . :

364

10

Address Book

, .

String ( _id ). null,


. ,
, . , .

SQL WHERE ( WHERE) null, .

String, WHERE.
? . WHERE null.

SQL GROUP BY ( GROUP BY) null,


.

SQL HAVING ( HAVING), GROUP BY, . GROUP BY null, null.

SQL ORDER BY ( ORDER BY) . null,


.

Cursor, query, ,
, . Cursor
, move
Cursor Cursor .
getOneContact ( 8387) query
SqLiteDatabase .
ID.
deleteContact ( 9095) delete SqLiteDatabase
( 93) .
, ID.
, , WHERE ( WHERE) , WHERE
, String , WHERE
( null).

DatabaseOpenHelper,
LiteOpenHelper
DatabaseOpenHelper (. 10.25) SQLiteOpenHelper,
. ( 100104)
, :


Context, ,
;

10.5.

365

null, , ;
 CursorFactory null, ,
SQLite CursorFactory ( );
 ( 1).
10.25. DatabaseOpenHelper SQLiteOpenHelper
97 private class DatabaseOpenHelper extends SQLiteOpenHelper
98 {
99
//
100
public DatabaseOpenHelper(Context context, String name,
101
CursorFactory factory, int version)
102
{
103
super(context, name, factory, version);
104
} // DatabaseOpenHelper
105
106
//
107
@Override
108
public void onCreate(SQLiteDatabase db)
109
{
110
//
111
String createQuery = "CREATE TABLE contacts" +
112
"(_id integer primary key autoincrement," +
113
"name TEXT, email TEXT, phone TEXT," +
114
"street TEXT, city TEXT);";
115
116
db.execSQL(createQuery); //
117
} // onCreate
118
119
@Override
120
public void onUpgrade(SQLiteDatabase db, int oldVersion,
121
int newVersion)
122
{
123
} // onUpgrade
124
} // DatabaseOpenHelper
125 } // DatabaseConnector

onCreate onUpgrade. , onCreate


DatabaseOpenHelper, . , ,
, onUpgrade DatabaseOpenHelper.
(
).
onCreate ( 107117) ,
SQL CREATE TABLE, String ( 111114).
(_id),
,

366

10

Address Book

. 116 execSQL SQLiteDatabase,


CREATE TABLE. , onUpgrade .
Android 3.0, SQLiteOpenHelper onDowngrade, ,
, , SQLiteOpenHelper.
,
, .
.
SQLiteDatabase, DatabaseConnector,
, ,
. , -1
(, insertOrThrow
insert). , , .

10.6.
Address Book,
, ,
, SQLite. Activity
, AndroidManifest.xml.
GUI XML style. ,
style. TextView Drawable
android:background TextView, Drawable
XML- .
XML- MenuItems ,
MenuInflater Activity.
Android .
, Activity
. ListActivity, Activity,
ListView .
, . ,
ListView AdapterView, . CursorAdapter
ListView
Activity.
Intents , , ,
. ,
finish Activity.
SQLiteOpenHelper
SQLiteDatabase,

10.6.

367

. Cursor . AsyncTask
GUI GUI,
Android
.
11 Route Tracker, GPS
, . MapView - Google
Maps . Overlay
. GPS Android.


Route Tracker

11

Google Maps API, GPS, LocationManager,


MapActivity, MapView Overlay

, GPS- , Android Emulator, Eclipse DDMS


GPS .
Maps API, MapActivity
MapView Google Maps, -
Google.
Google Maps API, .
LocationManager
.
Overlay,
MapView, GPS,
Location.
.

PowerManager





11.1.
Android Route
Tracker .
. , Start Tracking ToggleButton ( ),
. 11.1, . ( , (

11.1.

369

).) ToggleButton Stop


Tracking ( ) .
,
(. 11.1, ). ,
10 GPS, (.
. 11.1, ). Android, , ( ), , ,
. , , ,
Android, .
Map () Satellite (), . 11.2, .
. Map
Google Maps ( ,
). Satellite () , (.
. 11.2, ). Stop Tracking ToggleButton. , (. 11.3) (
), ( / /).

. 11.1. Route Tracker


Start Tracker: Start Tracking
;
Stop Tracking

370

11

Route Tracker

. 11.2.
; Satellite
:
Map Satellite; Satellite
Satellite

. 11.3.
Stop Tracking

11.2. Route Tracker

371

11.2. Route Tracker



Eclipse Route Tracker app.
:
1. FileImport ()
Import ().
2. Import General ()
Existing Projects into Workspace ( ).
Next > ( >).
3. Select root directory ( )
Browse (). Route Tracker.
4. Finish () .

Google Maps API


Route Tracker
, Google Maps API,
API Google. , Google ,
. 2.7,
.
ADT Plugin
, . ,
Google ( MD5 Fingerprint),
. API,
, . MD5 encryption MD5 fingerprints
-:



en.wikipedia.org/wiki/Md5;
en.wikipedia.org/wiki/Public_key_fingerprint.

, - code.google.com/android/
add-ons/google-apis/mapkey.html Getting the MD5 Fingerprint of the SDK Debug Certificate. fingerprint,
- code.google.com/android/maps-api-signup.html
Google Maps API. , , Getting the MD5
Fingerprint of Your Signing Certificate Google Maps
API. - .

, String google_
maps_api_key strings.xml Google Maps
API. ,
, .

372

11

Route Tracker

Android
Android,
( ). ,
( ),
.
Package Explorer, Eclipse,
Run AsAndroid Application (  Android).
Android Device Chooser ( Android), OK
.
GPS
. , GPS .
Route Tracker .
GPS Toast, . Start
Tracking -.

. , , . , .
Satellite
.
, Map.
Stop Tracking. AlertDialog,
. OK .
, ( );
/ .
Start Tracking .

AVD
AVD
AVD , Google API
Android. :
1. Android SDK and AVD Manager.
2. Android AVD,
(
NexusS), Edit ().
3. Edit Android Virtual Device (AVD) ( Android
(AVD)) Target () Google APIs

11.2. Route Tracker

373

(Google Inc.) API Level #, Edit AVD ( AVD).


# (level) API, . AVD Android API
Google API API (, API 10
Android 2.3.3). AVD,
AVD, ,
.
4. Android SDK and AVD Manager AVD .
Package Explorer
Eclipse, Run AsAndroid Application (  Android). Android Device Chooser
AVD, OK
AVD.

GPS- AVD GPX-


Android GPS- AVD,
, ,
Android. ,
GPS- GPS Exchange Format.
.gpx GPX-. GPX- ( GPXfiles), DDMS ADT. GPS-
AVD. GPX
GPSLogger, Google
Play: play.google.com/store/apps/details?id=com.mendhak.gpslogger.
GPS, GPX-, . GPSLogger
GPX 1.0, Android GPX 1.1.
,
GPS. , - www.gpsbabel.org.

GPX 1.1.
GPS- GPX AVD,
:
1. AVD Eclipse
WindowOpen PerspectiveDDMS ( DDMS),
DDMS.
2. Devices () AVD.
3. Emulator Control ( ) GPX.
4. Load GPX ( GPX),
GPX-, GPXFiles, , ,
, Open ().

374

11

Route Tracker

5. GPX
. GPS-
AVD.
AVD Start Tracking , GPS-.
Stop Tracking , .

11.3.
,
Route Tracker ( ).

, AndroidManifest.xml
, ( 11.4):


,
Android API (, Google Maps API),
. uses-library,
application.
 , , .
Android,
android:theme activity. . Android
- developer.android.com/reference/android/R.style.html.
Android .
, ,
,
.
.
uses-permission, manifest.
,
, .
, . - developer.android.com/reference/android/
Manifest.permission.html.

ToggleButton
ToggleButton ( android.widget) onoff (-). ToggleButton Start Tracking
, ,
off. ,

11.3.

375

. ToggleButton Stop Tracking,


, ,
on (), .
, off
( Start Tracking), . ToggleButton
CompoundButton. CompoundButton CompoundButton.OnCheckedChangeListener.

MapActivity, MapView Overlay


com.google.android.maps ,
Google Maps API. RouteTracker ( 11.5.1)
MapActivity , MapView ( 11.5.2).
, Google Maps API.
MapView , .
. MapView,
, ,
Overlay ( 11.5.3) draw.
GeoPoints ( 11.5.1 11.5.3) GPS- ,

.


android.location ( 11.5.1) , . LocationManager
.
Intent ,
.
.
LocationManager
, Criteria. ,
Criteria, , , , , .
,
, LocationListener. Location, , ,
( )
(
). GPS,
GPS ,
, GpsStatus.Listener.

376

11

Route Tracker

PowerManager WakeLock
PowerManager ( android.os) Android. , ,
, PowerManager.
, , , ,
, . PowerManager WakeLock,
,
GPS- ( 11.5.1).


Display ( android.view) .
( 11.5.2) ,

.

11.4.

Route Tracker.
strings.xml
. , Eclipse.

11.4.1.
Android RouteTracker.
New Android Project ( Android) ,
Finish ():


Build Target ( ). , Google API


2.3.3 ( ). ADT Plugin Android API Google API Android 2.3.3 (
). Google API Google Maps.

Application name ( ): Route Tracker.

Package name ( ): com.deitel.routetracker.

Create Activity ( ): RouteTracker.

Min SDK Version ( SDK): 8.

11.4.2. AndroidManifest.xml
11.1 AndroidManifest.xml .
.

11.4.

377

11.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.routetracker" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name" android:debuggable="true">
<uses-library android:name="com.google.android.maps" />
<activity android:name=".RouteTracker"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:screenOrientation="portrait">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
</application>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>


7 Google Maps API
uses-library, application.


10 android:theme activity Activity
Theme.Black.NoTitleBar Android,
Activity.


uses-permission 2126 , :


android.permission.INTERNET.
.
 android.permission.ACCESS_FINE_LOCATION. .

378

11

Route Tracker

android.permission.ACCESS_MOCK_LOCATION. ( 11.2).
.

android.permission.WAKE_LOCK. PowerManager
.

Android
- developer.android.com/guide/topics/security/security.html.

11.4.3. Route Tracker:


main.xml
XML- Route Tracker ( 11.2) FrameLayout
( android.widget) , ().
.
FrameLayout,
gravity.
ToggleButton .
BearingFrameLayout, MapView,
. android:textOn android:textOff ( 910)
ToggleButton ,
.
11.2. RouteTracker MapActivity
1
2
3
4
5
6
7
8
9
10
11
12

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


<FrameLayout xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/mainLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ToggleButton android:id="@+id/trackingToggleButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="@string/button_stop_tracking"
android:textOff="@string/button_start_tracking"
android:layout_gravity="bottom|right"></ToggleButton>
</FrameLayout>

11.5.
RouteTracker ( MapActivity;
11.311.11), BearingFrameLayout ( 11.1211.16) RouteOverlay ( 11.17
11.20). , Activity
RouteTracker ,
MapActivity .
src/com.deitel.routetracker .

11.5.

379

11.5.1. RouteTracker MapActivity


RouteTracker (. 11.311.11) Activity . , MapActivity, Activity MapView, ,
, Google Map. ListActivity, MapActivity
View.
MapActivity .

package, import RouteTracker


11.3 package, import, RouteTracker.
import , 11.3, 11.5.1. .
11.3. package, import
RouteTracker MapActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

// RouteTracker.java
// MapActivity RouteTracker.
package com.deitel.routetracker;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.AlertDialog;
android.content.Context;
android.location.Criteria;
android.location.GpsStatus;
android.location.Location;
android.location.LocationListener;
android.location.LocationManager;
android.os.Bundle;
android.os.PowerManager;
android.view.Gravity;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.widget.CompoundButton;
android.widget.CompoundButton.OnCheckedChangeListener;
android.widget.FrameLayout;
android.widget.Toast;
android.widget.ToggleButton;

import
import
import
import

com.google.android.maps.GeoPoint;
com.google.android.maps.MapActivity;
com.google.android.maps.MapController;
com.google.android.maps.MapView;

public class RouteTracker extends MapActivity


{
private LocationManager locationManager; //
private MapView mapView; // Google

380

11

Route Tracker

11.3 ()
33
34
35
36
37
38
39
40
41
42
43
44
45
46

private MapController mapController; // /


//
private Location previousLocation; //
private RouteOverlay routeOverlay; //
private long distanceTraveled; //
private BearingFrameLayout bearingFrameLayout; // MapView
private boolean tracking; // ?
private long startTime; // ( )
private PowerManager.WakeLock wakeLock; //
//
private boolean gpsFix; // GPS?
private static nal double MILLISECONDS_PER_HOUR = 1000 * 60 * 60;
private static nal double MILES_PER_KILOMETER = 0.621371192;
private static nal int MAP_ZOOM = 18; // Google Maps supports 121

onCreate Activity
11.4 onCreate Activity. 5556
bearingFrameLayout BearingFrameLayout ( 11.5.2), MapView
. ( Android). 64 MapView
BearingFrameLayout mapView. 65
getController MapController mapView.
MapController ,
, MapView. 66 setZoom MapController
( ). 1
( ) 21 ( ).
.
,
Google.
11.4. onCreate Activity
47
48
49
50
51
52
53
54
55
56

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// MapView Google Maps API
bearingFrameLayout = new BearingFrameLayout(this,
getResources().getString(R.string.google_maps_api_key));

11.5.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

381

// bearingFrameLayout mainLayout
FrameLayout mainLayout =
(FrameLayout) ndViewById(R.id.mainLayout);
mainLayout.addView(bearingFrameLayout, 0);
// MapView MapController
mapView = bearingFrameLayout.getMapview();
mapController = mapView.getController(); // MapController
mapController.setZoom(MAP_ZOOM); //
//
routeOverlay = new RouteOverlay();
// RouteOverlay
mapView.getOverlays().add(routeOverlay);
distanceTraveled = 0; // distanceTraveled 0

// trackingToggleButton
ToggleButton trackingToggleButton =
(ToggleButton) ndViewById(R.id.trackingToggleButton);
trackingToggleButton.setOnCheckedChangeListener(
trackingToggleButtonListener);
// onCreate

69 routeOverlay
Overlay RouteOverlay ( 11.5.3), MapView. 72
Overlay mapView
routeOverlay . Overlay, , .
74 distanceTraveled 0. distanceTraveled GPS. 7780
trackingToggleButton
trackingToggleButtonListener (. 11.11) OnCheckedChangeListener
.

onStart onStop Activity


11.5 onStart onStop Activity. onStart ( 84121) Criteria,
.
9195 Criteria,
:


setAccuracy. Criteria.ACCURACY_FINE , GPS-,

382

11

Route Tracker

. GPS . , Criteria.ACCURACY_
COARSE. Android 2.3, : Criteria.
ACCURACY_HIGH, Criteria.ACCURACY_MEDIUM Criteria.ACCURACY_LOW.
setBearingRequired. true, , .
,
.
setCostAllowed. true, ,
(, ), .
,
.
setPowerRequirement.
. Criteria.POWER_LOW
, . Criteria.
NO_REQUIREMENT, Criteria.POWER_HIGH Criteria.POWER_MEDIUM.
setAltitudeRequired. false, ,
.

11.5. onStart onStop Activity


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

// , Activity
@Override
public void onStart()
{
super.onStart(); // onStart
// Criteria,
//
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE); //
//
criteria.setBearingRequired(true); //
//
criteria.setCostAllowed(true); //
criteria.setPowerRequirement(Criteria.POWER_LOW); //
//
criteria.setAltitudeRequired(false); //
// LocationManager
locationManager =
(LocationManager) getSystemService(LOCATION_SERVICE);
// , GPS

11.5.
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

383

locationManager.addGpsStatusListener(gpsStatusListener);
// Criteria
String provider = locationManager.getBestProvider(criteria, true);
//
locationManager.requestLocationUpdates(provider, 0, 0,
locationListener);
//
PowerManager powerManager =
(PowerManager) getSystemService(Context.POWER_SERVICE);
//
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "No sleep");
wakeLock.acquire(); //

bearingFrameLayout.invalidate(); //
// BearingFrameLayout
// onStart

// , Activity
@Override
public void onStop()
{
super.onStop(); //
wakeLock.release(); //
} // onStop

9899 LocationManager,
locationManager. 102 gpsStatusListener (. 11.8) GpsStatus.Lisener
LocationManager. GPS.
GPS,
.
getBestProvider LocationManager ( 105)
String, , , Criteria.
true , .
requestLocationUpdates LocationManager
locationListener (. 11.7), , .
0 ( , ),
( , , ), ,

384

11

Route Tracker

( ).
,
. GPS
. GPS-
getLastKnownLocation LocationManager ,
GPS (,
).
,
.
112113 PowerManager.
newWakeLock PowerManager WakeLock ( 116117).
acquire WakeLock ( 118) ,
, WakeLock ( )
, release,
. PowerManager.
PARTIAL_WAKE_LOCK, ,
CPU,
.
. ,
ToggleButton Stop Tracking. WakeLocks
- developer.android.com/reference/android/os/PowerManager.html.
onStop ( 124130) release WakeLock, .
.

updateLocation
updateLocation (. 11.6), LocationListener (.
11.7), Location
. GPS,
:


addPoint routeOverlay, .
 previousLocation, distanceTo Location
( 143)
previousLocation distanceTraveled,
.
11.6. updateLocation RouteTracker
131
132
133
134
135

//
public void updateLocation(Location location)
{
if (location != null && gpsFix) // ; GPS
{

11.5.
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164


385

//
routeOverlay.addPoint(location);
//
if (previousLocation != null)
{
// distanceTraveled
distanceTraveled += location.distanceTo(previousLocation);
} // if
//
Double latitude = location.getLatitude() * 1E6;
Double longitude = location.getLongitude() * 1E6;
// GeoPoint,
GeoPoint point =
new GeoPoint(latitude.intValue(), longitude.intValue());
//
mapController.animateTo(point);

//
bearingFrameLayout.setBearing(location.getBearing());
bearingFrameLayout.invalidate(); //
//
// if

previousLocation = location;
// updateLocation

GeoPoint ( 147152). GeoPoint , ( ). getLatitude


getLongitude Location .
,
1E6. latitude longitude
.
GeoPoint .
 animateTo MapController ( 155) GeoPoint .
, Message Runnable.
 getBearing Location ( 158)
.
,
. setBearing bearingFrameLayout,
, . invalidate
bearingFrameLayout .

386

11

Route Tracker

bearingTo
Location, .
AVD.

, , , .

LocationListener,
LocationManager
11.7 LocationListener. LocationListener LocationManager,
. requestLocationUpdates (. 11.5, 108109).
onLocationChanged ( 170176) ,
Location. gpsFix true , Location
GPS
. ,
updateLocation (. 11.6),
Location. ,
(onProviderDisabled,
onProviderEnabled onStatusChanged). , .
11.7. LocationListener, LocationManager
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

// LocationManager
private nal LocationListener locationListener =
new LocationListener()
{
//
public void onLocationChanged(Location location)
{
gpsFix = true; // GPS

if (tracking) //
updateLocation(location); //
// onLocationChanged

public void onProviderDisabled(String provider)


{
} // onProviderDisabled
public void onProviderEnabled(String provider)
{
} // onProviderEnabled

11.5.
185
186
187
188
189
190
191

387

public void onStatusChanged(String provider,


int status, Bundle extras)
{
} // onStatusChanged
}; // locationListener

, GpsStatus.
Listener, GpsStatus
11.8 ,
GpsStatus.Listener.
GPS.
GPS (), . 197 GpsStatus.GPS_EVENT_FIRST_FIX ,
. , gpsFix
true, Toast , GPS, . , GPS
, .
172 gpsFix true.
11.8. , GpsStatus.Listener
GPS,
GPS-
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

// GPS
GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener()
{
public void onGpsStatusChanged(int event)
{
if (event == GpsStatus.GPS_EVENT_FIRST_FIX)
{
gpsFix = true;
Toast results = Toast.makeText(RouteTracker.this,
getResources().getString(R.string.toast_signal_acquired),
Toast.LENGTH_SHORT);
// Toast
results.setGravity(Gravity.CENTER,
results.getXOffset() / 2, results.getYOffset() / 2);
results.show(); //
} // if
} // GpsStatusChanged
}; //

388

11

Route Tracker

isRouteDisplayed MapActivity
11.9 isRouteDisplayed MapActivity,
false. , , Google (Terms of Use)
true.
API (code.google.com/android/add-ons/google-apis/mapkey.html).
11.9. isRouteDisplayed MapActivity
212
213
214
215
216
217
218
219

// Google
// true, ,
//
@Override
protected boolean isRouteDisplayed()
{
return false; //
} // isRouteDisplayed

onCreateOptionsMenu
onOptionsItemSelected Activity
11.10 onCreateOptionsMenu onOptionsItemSelected
Activity. onCreateOptionsMenu MenuInflater
route_tracker_menu.xml.
, onOptionsItemSelected
. MenuItem Map,
238 setSatellite MapView false,
.
MenuItem Satellite, 241 setSatellite true,
.
11.10. onCreateOptionsMenu onOptionsItemSelected
Activity
220
221
222
223
224
225
226
227
228
229
230
231
232
233

// Activity XML-
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
MenuInater inater = getMenuInater();
inater.inate(R.menu.route_tracker_menu, menu);
return true;
} // onCreateOptionsMenu
// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{

11.5.
234
235
236
237
238
239
240
241
242
243
244
245
246
247

389

//
switch (item.getItemId())
{
case R.id.mapItem: // "Map"
mapView.setSatellite(false); //
return true;
case R.id.satelliteItem: // "Satellite"
mapView.setSatellite(true); //
return true;
default:
return super.onOptionsItemSelected(item);
} // switch
// onOptionsItemSelected

,
OnCheckedChangeListener, trackingToggleButton
11.11 trackingToggleButtonListener OnCheckedChangeListener, trackingToggleButton .
11.11. trackingToggleButtonListener,
trackingToggleButton
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

// trackingToggleButton
OnCheckedChangeListener trackingToggleButtonListener =
new OnCheckedChangeListener()
{
// ,
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked)
{
//
if (!isChecked)
{
tracking = false; //
//
long milliseconds = System.currentTimeMillis() startTime;
double totalHours = milliseconds / MILLISECONDS_PER_HOUR;
//
AlertDialog.Builder dialogBuilder =
new AlertDialog.Builder(RouteTracker.this);
dialogBuilder.setTitle(R.string.results);
double distanceKM = distanceTraveled / 1000.0;

390

11

Route Tracker

11.11 ()
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294

double speedKM = distanceKM / totalHours;


double distanceMI = distanceKM * MILES_PER_KILOMETER;
double speedMI = distanceMI / totalHours;
// distanceTraveled
//
dialogBuilder.setMessage(String.format(
getResources().getString(R.string.results_format),
distanceKM, distanceMI, speedKM, speedMI));
dialogBuilder.setPositiveButton(
R.string.button_ok, null);
dialogBuilder.show(); // display the dialog
} // if
else
{
tracking = true; //
startTime = System.currentTimeMillis(); //
routeOverlay.reset(); //
bearingFrameLayout.invalidate(); //
previousLocation = null; //
} // else
} // onCheckChanged
}; //
} // RouteTracker

trackingToggleButton,
onCheckedChanged, . ( 258),
, 260282
. 263264 totalHours,
, . . distanceTraveled ( ).
1000.0 ( 271) .
272 (/). 273274 , , /.
trackingToggleButton , . 286290
, , routeOverlay, invalidate bearingFrameLayout (
), previousLocation
null. Stop Tracking tracking
false ( 282), .
totalMilliseconds startTime
, System.currentMillis.

11.5.

391

11.5.2. BearingFrameLayout FrameLayout


BearingFrameLayout (. 11.1211.16) MapView
,
.

package, import
11.12 package, import
BearingFrameLayout. scale
MapView .
.
11.12. package, import
BearingFrameLayout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// BearingFrameLayout.java
// MapView .
package com.deitel.routetracker;
import com.google.android.maps.MapView;
import
import
import
import
import

android.app.Activity;
android.content.Context;
android.graphics.Canvas;
android.view.Display;
android.widget.FrameLayout;

public class BearingFrameLayout extends FrameLayout


{
private int scale = 0;
//
private MapView mapView;
// Google
private oat bearing = 0f; //

getChildLayoutParams
11.13 getChildLayoutParams,
LayoutParams.
View . LayoutParams Views ViewGroups. , LinearLayouts
LayoutParams, ,
RelativeLayouts. View LayoutParams, .
, XML,
, match_parent wrap_content width / height
View .

392

11

Route Tracker

11.13. getChildLayoutParams BearingFrameLayout


19
20
21
22
23
24
25
26
27
28
29
30

// MapView
public LayoutParams getChildLayoutParams()
{
Display display =
((Activity) getContext()).getWindowManager().getDefaultDisplay();
int w = display.getWidth();
int h = display.getHeight();
scale = (int) Math.sqrt((w * w) + (h * h));

return new LayoutParams(scale, scale);


// getChildLayoutParams

2223 Display, . Display ,


. getWidth getHeight . BearingMapView ,
,
MapView .
MapView ,
, 26.
, MapView
(- ).

11.14 BearingFrameLayout.
, .
MapView apiKey Google Maps. 3743
MapView :


setClickable. true,
MapView, . MapView.

setEnabled. true MapView


. , , .
 setSatellite. false,
Google, .
 setBuiltInZoomControls. true, MapView.
 setLayoutParams. LayoutParams , MapView .
MapView.
44 mapView BearingFrameLayout.

11.5.

393

11.14. BearingFrameLayout
31
32
33
34
35
36
37

// BearingFrameLayout
public BearingFrameLayout(Context context, String apiKey)
{
super(context); //
mapView = new MapView(context, apiKey); // MapView
mapView.setClickable(true); //
//
mapView.setEnabled(true); //
// MapView
mapView.setSatellite(false); //
mapView.setBuiltInZoomControls(true); //
//

38
39
40
41
42
43
44
45
46

// MapView
mapView.setLayoutParams(getChildLayoutParams());
addView(mapView); // MapView
// BearingFrameLayout

dispatchDraw View
11.15 dispatchDraw View. draw View
View.
View. View .
11.15. dispatchDraw View
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

//
@Override
protected void dispatchDraw(Canvas canvas)
{
if (bearing >= 0) // bearing 0
{
//
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
//
int width = scale;
int height = scale;
//
int centerXScaled = width / 2;
int centerYScaled = height / 2;
//
int centerX = canvasWidth / 2;

394

11

Route Tracker

11.15 ()
67
68
69

int centerY = canvasHeight / 2;


//
//
canvas.translate(-(centerXScaled centerX),
-(centerYScaled centerY));

70
71
72
73
74
75
76
77
78
79

//
canvas.rotate(-bearing, centerXScaled, centerYScaled);
} // if

super.dispatchDraw(canvas); //
//
// dispatchDraw

5455 ,
( Canvas).
getLayoutParams ( 5867).

Google
.
API , .

,
. ( View, 7071.) Canvas bearing (74).
, bearing
( ) . ,
-,
, .
, .
.
Canvas dispatchDraw View,
Overlay, .
, .
77 View.

setBearing getMapView
11.16 setBearing getMapView BearingFrameLayout.
setBearing ,

11.5.

395

getMapView MapView.
RouteTracker.
11.16. setBearing MapView BearingFrameLayout
80
81
82
83
84
85
86
87
88
89
90
91 }

//
public void setBearing(oat bearing)
{
this.bearing = bearing;
} // setBearing
// MapView
public MapView getMapView()
{
return mapView;
} // getMapView
// BearingFrameLayout

11.5.3. RouteOverlay Overlay


Overlay RouteOverlay (. 11.1711.20) Location .

package, import
11.17 package, import
RouteOverlay. POSITION_MARKER
.
11.17. package, import
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// RouteOverlay.java
// MapView.
package com.deitel.routetracker;
import java.util.ArrayList;
import java.util.List;
import
import
import
import
import
import

android.graphics.Canvas;
android.graphics.Color;
android.graphics.Paint;
android.graphics.Path;
android.graphics.Point;
android.location.Location;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
public class RouteOverlay extends Overlay
{

396

11

Route Tracker

11.17 ()
21
22
23
24
25

private
private
private
private

List<Location> locations; // Location


Paint pathPaint; // Paint Path
Paint positionPaint; // Paint
nal int POSITION_MARKER = 10; //

RouteOverlay
11.18 RouteOverlay. 2933 Paint, ,
. setAntiAlias Paint
, . ,
STROKE , 5. ArrayList<Location>, ( 34), Locations .
3739 Paint,
POSITION_MARKER, .
11.18. RouteOverlay
26
27
28

public RouteOverlay()
{
// Paint, Path
// 5
pathPaint = new Paint();
pathPaint.setAntiAlias(true);
pathPaint.setColor(Color.RED);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeWidth(5);
locations = new ArrayList<Location>(); //

29
30
31
32
33
34
35
36
37
38
39
40
41

// Paint,
// , POSITION_MARKER
positionPaint = new Paint();
positionPaint.setAntiAlias(true);
positionPaint.setStyle(Paint.Style.FILL);
// RouteOverlay

addPoint reset
11.19 addPoint reset. , RouteTracker
, Location addPoint.
ArrayList<Location>. reset RouteTracker

.
11.19. addPoint reset RouteOverlay
42
43

//
public void addPoint(Location location)

11.5.
44
45
46
47
48
49
50
51
52
53

397

{
}

locations.add(location);
// addPoint

// Overlay
public void reset()
{
locations.clear(); //
} // reset

Overlay
11.20 draw Overlay, MapView.
Canvas (), MapView (mapView) shadow.
draw . true. Overlay shadow,
false overlay. shadow
, ,
Google Google Maps.
11.20. draw View
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

// Overlay MapView
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow)
{
super.draw(canvas, mapView, shadow); //
// draw
Path newPath = new Path(); // Path
Location previous = null; //
//
for (int i = 0; i < locations.size(); ++i)
{
Location location = locations.get(i);
// Location GeoPoint
Double newLatitude = location.getLatitude() * 1E6;
Double newLongitude = location.getLongitude() * 1E6;
GeoPoint newPoint = new GeoPoint(newLatitude.intValue(),
newLongitude.intValue());
// GeoPoint
Point newScreenPoints = new Point();
mapView.getProjection().toPixels(newPoint, newScreenPoints);
if (previous != null) //
{
// GeoPoint
Double oldLatitude = previous.getLatitude() * 1E6;

398

11

Route Tracker

11.20 ()
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

Double oldLongitude = previous.getLongitude() * 1E6;


GeoPoint oldPoint = new GeoPoint(oldLatitude.intValue(),
oldLongitude.intValue());
// GeoPoint
Point oldScreenPoints = new Point();
mapView.getProjection().toPixels(oldPoint, oldScreenPoints);
// Path
newPath.quadTo(oldScreenPoints.x, oldScreenPoints.y,
(newScreenPoints.x + oldScreenPoints.x) / 2,
(newScreenPoints.y + oldScreenPoints.y) / 2);
//
if ((i % POSITION_MARKER) == 0)
canvas.drawCircle(newScreenPoints.x, newScreenPoints.y, 10,
positionPaint);
} // if
else
{
//
newPath.moveTo(newScreenPoints.x, newScreenPoints.y);
} // else

previous = location; //
// for

canvas.drawPath(newPath, pathPaint); //
} // draw
// RouteOverlay

Path, 59 Path. Location


null, Path draw.
Location ArrayList<Location>
:


Location ( 65).
GeoPoint Location ( 6871)
, 11.6.
 GeoPoint Location ,
( 7475). getProjection MapView Projection,
.
Projection, MapView
Projection. toPixels Projection
GeoPoint Point. ,
GeoPoint Point.


11.6.

399

Location previous null,


:


8087 GeoPoint previous Location,


.

9092 quadTo Path


Path ( ).

9597 , Location (i)


POSITION_MARKER.

previous null, Location


, 102 moveTo Path Point, newScreenPoints. for
105 ( previous),
. Location
newPath .

11.6.
Route Tracker, ,
Google Map. ,
. Google Maps API,
uses-library.
Activity Activity
android:theme activity, uses-permission ,
.
ToggleButton onoff, .
ToggleButton CompoundButton.
OnCheckedChangeListener.
Google Maps API
com.google.android.maps. MapActivity,
Activity, MapView.
MapView Overlay
draw. GeoPoints GPS- , ,
.
,
android.location. LocationManager , , Criteria.
, LocationListener.
Location,

400

11

Route Tracker

. GPS
GpsStatus.Listener.
PowerManager , ,
. Display
,
. .
12 Slideshow, - .
,
Android. - ,
, -.


Slideshow

12

Gallery
Media,
Content Providers, MediaPlayer,
,
Custom
ListActivity View-Holder

Intents
.

Intents, .

MediaPlayer
-.

ListActivity.

view holder
, ListView.


AlertDialog, .

BitmapFactory.

TransitionDrawable
BitmapDrawable, .

402

12

Slideshow

12.1.
Slideshow - . ,
. . 12.1 , -. ListView - Button. -
Play Button. ,
( ). - .
Edit Button () - Activity,
. Delete Button (), - . -
Enhanced Slideshow,
13.

. 12.1. -,

, - .
New Slideshow ( ) (. 12.2, ). Set
Slideshow Name ( -) (. 12.2, ),
-. Set Name ( )
, - Slideshow Editor
Activity (. 12.3).
Add Picture ( ) Gallery (. 12.4, ).

12.1.

403

. 12.2. - :
New Slideshow;
New Slideshow
Set Slideshow Name
( -
Set Name)

. 12.3. Slideshow Editor Activity


- -

. -, . . 12.4,
Slideshow Editor Activity
-. ,
ListView, , ,
. Delete Button,

404

12

Slideshow

. 12.4. Gallery, ,
Slideshow Editor Activity :
Add Picture
Gallery .
; Slideshow Editor Activity
-

,
-.
Add Music Button ( )
Android , Music (). Android ,
(. 12.5), Select music track ( ) Sound Recorder ( ). Select music track
, .
Sound Recorder Sound Recorder,
,
-. ,

. - . Play Slideshow Editor (
-). . 12.6 -.

12.2. Slideshow App

405

. 12.5. , Android,

(Select music track) (Sound Recorder)

. 12.6. , -

12.2. Slideshow App



Eclipse Slideshow app.
:
1. FileImport ()
Import ().
2. General () Existing Projects into Workspace
( ). Next >
( >).
3. Select root directory (
) Browse (). Browse For
Folder ( ) Slideshow OK.
4. Finish () Eclipse.
Eclipse , Package Explorer, Run
AsAndroid Application (  Android).

406

12

Slideshow

AVD
Slideshow AVD. SD- AVD,
AVD. :
1. AVD Android SDK AVD
Manager.
2. Eclipse DDMS, WindowOpen
Perspective ( ).
3. DDMS Devices () AVD.
4. DDMS File Explorer ( ) AVD.
5. /mnt/sdcard, .
6. AVD , Launch from
snapshot checked. AVD SD
/ .
images . - , ( MP3).

-
New Slideshow Button,
Set Slideshow Name. -,
Set Name - Slideshow
Editor.

-
Add Picture Button Gallery.
Gallery, -. , . Back () ,
Slideshow Editor .
, Delete Button, .
Add Music Button ( ).
Select music track Sound Recorder.
Select music track Sound Recorder .
Slideshow Editor.

-
- :

12.3.

407

Slideshow Editor Play Button.


 Done Button () Slideshow Editor,
-, Play Button (), -, .
-
-,
. .
-, . ,
. (
<Ctrl + F11> <Ctrl + F12>.) Back
- -
, -.

-
-, Edit Button.
, .
. - , Delete.

12.3.
,
Slideshow.

Intents,

Android ,
. () , ,
.
9 , Doodlz
Gallery .
Android , (, , ,
). android.provider,
developer.android.com/reference/
android/provider/package-summary.html.
, -. Intent,
MIME , ( 12.5.3). Android
Activity, ,
Activity, Activity. , . 12.4, Activity,

408

12

Slideshow

Gallery . . 12.5
Activity,
Sound Recorder.
- developer.android.com/guide/topics/providers/content-providers.html.


AlertDialog
AlertDialog View .
Slideshow - ,
EditText, AlertDialog (
12.4.6 12.5.2).

ListActivity
Address Book 10
ListActivity ListView. ListActivity ListView. SlideshowEditor ListActivity
( 12.4.7). ListActivity ListView android:id
"@android:id/list".

Intent,
Intent ( Favorite Twitter Searches, 5),
Activity, ( Address Book, 10). startActivity Activity,
Activity, Intent. Favorite
Twitter Searches ,
Back . Address Book
Activity
Activity .
srartActivityForResult Activity, Activity
Activity, Activity.
:


ListView Activity Slideshow


- ;

ListView Activity SlideshowEditor


-;

, -.

12.3.

409

ArrayAdapter ListView
10, ListView . SimpleCursorAdapter
ListView. ArrayAdapter ( android.widget)
, ListViews ( )
( 12.5.2 12.5.3).

View-Holder
ListView ,
, , list-item. ListView
. Android
, .
GUI ListView.
, view-holder. (Object) View setTag View.
Object getTag View.
, ( ) ,
View ( GUI).
View , view-holder
( ).
ListView ListView . ,
GUI , GUI , call ViewHolder.
setTag ViewHolder ListView.
,
getTag, ViewHolder,
ListView. , ViewHolder,
GUI, ViewHolder.

ListView
ArrayAdapter notifyDataSetChanged ( 12.5.2 12.5.3),
Adapter, ListView.

GUI,

Slideshow SlideshowEditor ( 12.5.2 12.5.3) setTag
getTag, GUI, . Slideshow
Play Edit, -,

410

12

Slideshow

. SlideshowInfo, Delete Button, , List of


SlideshowInfo, -.

MediaPlayer
MediaPlayer ( android.media, 12.5.4)
- , . MediaPlayer
( ), -.

BitmapFactory
BitmapFactory ( android.graphics) Bitmap.
,
( 12.5.2 12.5.3),
- ( 12.5.4).
BitmapFactory.Options
Bitmap, BitmapFactory. , .
out-of-memory ( ), Bitmap.

,
TransitionDrawable BitmapDrawable
-
. TransitionDrawable ( 12.5.4),
Drawable. TransitionDrawable Drawable , Drawable,
ImageView.
Bitmap, BitmapDrawables, . TransitionDrawable BitmapDrawable
android.graphics.drawable.

12.4.

GUI- Slideshow.
GUI , , String .

strings.xml. , GUI,
. ,
Eclipse.

12.4.

411

12.4.1.
Android Slideshow.
New Android Project ( Android) ,
Finish ():


Build Target ( ): Android 2.3.3.

Application name ( ): Slideshow.


 Package name ( ): com.deitel.slideshow.
 Create Activity ( ): Slideshow.
 Min SDK Version ( SDK): 8.

12.4.2.
Android
10 , Android,
.
SDKs platforms data/res/drawable-hdpi
. , ,
, ,
Android. ,
res/drawable-hdpi .
Eclipse, .

12.4.3. AndroidManifest.xml
12.1 AndroidManifest.xml . , . ,
Slideshow SlideshowEditor activity
Activity ( 10 20). Slideshow
SlideshowPlayer ( 11 24), .
,
-.
12.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.slideshow" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".Slideshow"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Light">
<intent-lter>

412

12

Slideshow

12.1 ()
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

<action android:name="android.intent.action.MAIN" />


<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
<activity android:name=".SlideshowEditor"
android:label="@string/slideshow_editor"
android:screenOrientation="portrait"></activity>
<activity android:name=".SlideshowPlayer"
android:label="@string/app_name"
android:theme="@android:style/Theme.Light.NoTitleBar"></activity>
</application>
<uses-sdk android:minSdkVersion="8" />
</manifest>

12.4.4. ListView
ListActivity Slideshow
. 12.7 ListView,
ListActivity Slideshow. , slideshow_list_item.
xml, LinearLayout, TextView
LinearLayout. LinearLayout ImageView Button. Button
, android:drawableTop
Drawable Button.
Android. , XML- playButton
: android:drawableTop="@drawable/ic_menu_play_clip",
ic_menu_play_clip.png
Button. android:drawableLeft, android:drawableRight
android:drawableBottom ,
.
nameTextView

playButton
editButton

slideshowImageView

deleteButton

. 12.7. slideshow_list_item.xml,
ListView ListActivity Slideshow

12.4.5. ListActivity Slideshow


12.2 Slideshow ListActivity. ic_menu_slideshow.
png ( 5).

12.4.

413

12.2. ListActivity Slideshow,


slideshow_menu.xml
1
2
3
4
5
6
7
8

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


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/newSlideshowItem"
android:title="@string/menuitem_new_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:titleCondensed="@string/menuitem_new_slideshow"
android:alphabeticShortcut="n"></item>
</menu>

12.4.6. EditText,
Set Slideshow Name
. 12.8 Set Slideshow Name,
- EditText. nameEditText
LinearLayout,
android:layout_marginLeft android:layout_marginRight . android:singleLine true,
- .

nameEditText

. 12.8. AlertDialog

- Set Name Button

12.4.7. ListActivity SlideshowEditor


. 12.11 ListActivity SlideshowEditor. ListActivity ( slideshow_list_item.xml), ListView
android:id "@android:id/list".
ListView getListView ListActivity.
slideshow_editor.xml LinearLayout,
LinearLayout ListView. LinearLayout,
, Button.

414

12

Slideshow

doneButton
addPictureButton

playButton
addMusicButton

ListView

. 12.9. ListActivity SlideshowEditor,


slideshow_editor.xml

12.4.8. ListView SlideshowEditor


. 12.10 ListView, ListActivity SlideshowEditor. ,
slideshow_edit_item.xml, LinearLayout,
ImageView Button.

slideshowImageView

deleteButton

. 12.10. ListView ListActivity SlideshowEditor,


slideshow_edit_item

12.4.9. Activity SlideshowPlayer


. 12.11 Activity SlideshowPlayer.
slideshow_edit_item.xml LinearLayout, ImageView, LinearLayout
.

12.5.
SlideshowInfo ( 12.3), Slideshow (
ListActivity, 12.412.13), SlideshowEditor ( ListActivity,
12.1412.22) SlideshowPlayer ( 12.2312.28). Activity
, Slideshow, , ListActivity,
src/com.deitel.slideshow.

12.5.

415

imageView

. 12.11. ListActivity SlideshowPlayer,


slideshow_player.xml

12.5.1. SlideshowInfo
SlideshowInfo (. 12.3) -,
:


name ( 10) -, ;
 imageList ( 11) , ;
 musicPath ( 12)
( ),
-.
imageList ArrayList<String>.
12.3. -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

// SlideshowInfo.java
// -.
package com.deitel.slideshow;
import java.util.ArrayList;
import java.util.List;
public class SlideshowInfo
{
private String name; // -
private List<String> imageList; // -
private String musicPath; //
//
public SlideshowInfo(String slideshowName)
{
name = slideshowName; // -

416

12

Slideshow

12.3 ()
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

imageList = new ArrayList<String>();


musicPath = null; // -
// SlideshowInfo

// -
public String getName()
{
return name;
} // getName
// , -
public List<String> getImageList()
{
return imageList;
} // getImageList
//
public void addImage(String path)
{
imageList.add(path);
} // addImage
// ,
public String getImageAt(int index)
{
if (index >= 0 && index < imageList.size())
return imageList.get(index);
else
return null;
} // getImageAt
// -
public String getMusicPath()
{
return musicPath;
} // getMusicPath
// -
public void setMusicPath(String path)
{
musicPath = path;
} // setMusicPath

// / -
public int size()
{
return imageList.size();
} // size
// SlideshowInfo

12.5.

417

12.5.2. Slideshow ListActivity


Slideshow (. 12.412.12) Activity. ListActivity, Activity
ListView.

package, import Slideshow


Slideshow ListActivity (. 12.4) Activity
. ListView
-. import
, 12.3 .
List of SlideshowInfo ( 41) -. List static,
. SlideshowAdapter ( 43)
ArrayAdapter, SlideshowInfo ListView.
12.4. package, import Slideshow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

// Slideshow.java
// Activity Slideshow.
package com.deitel.slideshow;
import java.util.ArrayList;
import java.util.List;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.AlertDialog;
android.app.ListActivity;
android.content.ContentResolver;
android.content.Context;
android.content.DialogInterface;
android.content.Intent;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.provider.MediaStore;
android.view.Gravity;
android.view.LayoutInater;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.view.View;
android.view.View.OnClickListener;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.Button;
android.widget.EditText;

418

12

Slideshow

12.4 ()
31
32
33
34
35
36
37
38
39
40
41
42
43
44

import
import
import
import

android.widget.ImageView;
android.widget.ListView;
android.widget.TextView;
android.widget.Toast;

public class Slideshow extends ListActivity


{
// -
// Intent
public static nal String NAME_EXTRA = "NAME";
static List<SlideshowInfo> slideshowList; // -
private ListView slideshowListView; // ListView ListActivity
private SlideshowAdapter slideshowAdapter; // ListView

onCreate Activity
onCreate Slideshow (. 12.5) ListView,
- ( 50),
slideshowList slideshowAdapter, slideshowListView
slideshowAdapter. slideshowListView -,
, Button Play, Edit Delete.
, slideshow_list_item.xml ( 12.4.4). 5862
AlertDialog, .
12.5. onCreate Activity
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
slideshowListView = getListView(); // ListView
// ListView
slideshowList = new ArrayList<SlideshowInfo>();
slideshowAdapter = new SlideshowAdapter(this, slideshowList);
slideshowListView.setAdapter(slideshowAdapter);

// (Buider)
// AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.welcome_message_title);
builder.setMessage(R.string.welcome_message);
builder.setPositiveButton(R.string.button_ok, null);
builder.show();
// onCreate

12.5.

419

onCreateOptionsMenu,
onOptionsItemSelected onActivityResult Activity
onCreateOptionsMenu (. 12.6, 6673)
Activity slideshow_menu.xml ( 12.4.5). New Slideshow onOptionsItemSelected ( 79132)
,
-. EditText slideshow_name_edittext.xml ( 87) View ( 93).
OK onClick ( 99124)
EditText, SlideshowInfo - slideshowList. 110112 Intent,
Activity SlideshowEditor. 113
Intent startActivityForResult.
Intent, -Activity.
, Activity,
.
onActivityResult ( 135141), , -Activity. Activity .
Activity ,
onActivityResult -Activity,
.
-Activity Activity 0 (
EDIT_ID 76) . ,
startActivityForResult , startActivity.
Activity Intent, startActivityForResult
ActivityNotFoundException.

startActivity startActivityForResult
try, ,
Activity, Intent.
12.6. onCreateOptionsMenu, onOptionsItemSelected
onActivityResult Activity
65
66
67
68
69
70
71
72
73
74

// Activity XML-
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
MenuInater inater = getMenuInater();
inater.inate(R.menu.slideshow_menu, menu);
return true;
} // onCreateOptionsMenu


420

12

Slideshow

12.6 ()
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

// SlideshowEditor startActivityForResult
private static nal int EDIT_ID = 0;
// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// LayoutInater
LayoutInater inater = (LayoutInater) getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// slideshow_name_edittext.xml EditText
View view = inater.inate(R.layout.slideshow_name_edittext, null);
nal EditText nameEditText =
(EditText) view.ndViewById(R.id.nameEditText);
// -
AlertDialog.Builder inputDialog = new AlertDialog.Builder(this);
inputDialog.setView(view); //
//
inputDialog.setTitle(R.string.dialog_set_name_title);
inputDialog.setPositiveButton(R.string.button_set_slideshow_name,
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton)
{
// SlideshowInfo -
String name = nameEditText.getText().toString().trim();
if (name.length() != 0)
{
slideshowList.add(new SlideshowInfo(name));

// Intent SlideshowEditor Activity,


// -
// Activity
Intent editSlideshowIntent =
new Intent(Slideshow.this, SlideshowEditor.class);
editSlideshowIntent.putExtra("NAME_EXTRA", name);
startActivityForResult(editSlideshowIntent, 0);
// if
else
{
//
// -
Toast message = Toast.makeText(Slideshow.this,
R.string.message_name, Toast.LENGTH_SHORT);
message.setGravity(Gravity.CENTER,
message.getXOffset() / 2, message.getYOffset() / 2);

12.5.
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

421

message.show(); // Toast
} // else
} // onClick
} //
);// setPositiveButton
inputDialog.setNegativeButton(R.string.button_cancel, null);
inputDialog.show();
return super.onOptionsItemSelected(item); //
} // onOptionsItemSelected
// ListView -
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
slideshowAdapter.notifyDataSetChanged(); //
} // onActivityResult

onActivityResult Activity ( 135141) , Activity . requestCode , startActivityForResult


Activity. resultCode :



RESULT_OK, Activity ;
RESULT_CANCELED , Activity , , Activity setResult
RESULT_CANCELED.

Intent, ( ),
Activity.
, Activity SlideshowEditor,
ListView -. notifyDataSetChanged
SlideshowAdapter
ListView.

SlideshowAdapter:
View-Holder ListView
12.7 ViewHolder SlideshowAdapter.
ViewHolder package-access, SlideshowAdapter
ViewHolder. ListView, ViewHolder ListView.
ListView ,
ViewHolder, .

422

12

Slideshow

12.7. SlideshowAdapter,
ListView
143
144
145
146
147
148
149
150
151
152
153
154
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

// , "ViewHolder pattern"
// ListView
private static class ViewHolder
{
TextView nameTextView; // TextView ListView
ImageView imageView;
// ImageView ListView
Button playButton;
// Play ListView
Button editButton;
// Edit ListView
Button deleteButton;
// Delete ListView
} // ViewHolder
// ArrayAdapter, -,
// "Play", "Edit" "Delete"
private class SlideshowAdapter extends ArrayAdapter<SlideshowInfo>
{
private List<SlideshowInfo> items;
private LayoutInater inater;
// SlideshowAdapter
public SlideshowAdapter(Context context, List<SlideshowInfo> items)
{
//
super(context, -1, items);
this.items = items;
inater = (LayoutInater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} // SlideshowAdapter
// View
@Override
public View getView(int position, View convertView,
ViewGroup parent)
{
ViewHolder viewHolder; // GUI
// convertView null, "" GUI
// ViewHolder ViewHolder
if (convertView == null)
{
convertView =
inater.inate(R.layout.slideshow_list_item, null);
// ViewHolder ListView
viewHolder = new ViewHolder();
viewHolder.nameTextView = (TextView)
convertView.ndViewById(R.id.nameTextView);
viewHolder.imageView = (ImageView)
convertView.ndViewById(R.id.slideshowImageView);

12.5.
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230

423

viewHolder.playButton =
(Button) convertView.ndViewById(R.id.playButton);
viewHolder.editButton =
(Button) convertView.ndViewById(R.id.editButton);
viewHolder.deleteButton =
(Button) convertView.ndViewById(R.id.deleteButton);
convertView.setTag(viewHolder); // View
} // if
else // ViewHolder convertView
viewHolder = (ViewHolder) convertView.getTag();
// -, nameTextView
SlideshowInfo slideshowInfo = items.get(position);
viewHolder.nameTextView.setText(slideshowInfo.getName());
// -
if (slideshowInfo.size() > a)
{
//
// -
String rstItem = slideshowInfo.getImageAt(0);
new LoadThumbnailTask().execute(viewHolder.imageView,
Uri.parse(rstItem));
} // if
// OnClickListener "Play"
viewHolder.playButton.setTag(slideshowInfo);
viewHolder.playButton.setOnClickListener(playButtonListener);
// OnClickListener "Edit"
viewHolder.editButton.setTag(slideshowInfo);
viewHolder.editButton.setOnClickListener(editButtonListener);
// OnClickListener "Delete"
viewHolder.deleteButton.setTag(slideshowInfo);
viewHolder.deleteButton.setOnClickListener(deleteButtonListener);

return convertView; // View


} // getView
// SlideshowAdapter

AddressBook SimpleCursorAdapter,
String ( ), .
, ,
String TextView ImageView .
ListView, , .
( -), ( -)
Button (Play, Edit Delete). - ListView, ArrayAdapter ,
getView,

424

12

Slideshow

ListView. ( 162169) , List of SlideshowInfo LayoutInflater


getView.
ID , TextView,
ListView. ,
-1.
getView ( 172228)
ListView. ListView, View (convertView), ListView, ListView. convertView
ListView. convertView null,
182196 slideshow_list_item.xml ListView, convertView. ViewHolder
GUI, ViewHolder. 197
ViewHolder ListView. convertView
null, ListView ListView,
. 200 ListView,
ViewHolder. 203
SlideshowInfo, ListView.
204 nameTextView viewHolder . - , 210212
, LoadThumbnailTask AsyncTask
( 12.8),
imageView viewHolder.
216225 Play, Edit Delete
ListView. setTag Button ( Object), (, SlideshowInfo, -). playButton editButton
Intent, SlideshowPlayer
SlideshowEditor , - . deleteButton SlideshowInfo,
List of SlideshowInfo.

LoadThumbnailTask
LoadThumbnailTask (. 12.8) , GUI. doInBackground getThumbnail
static . onPostExecute
Bitmap ImageView.
12.8. LoadThumbnailTask
231
232
233

//
private class LoadThumbnailTask extends AsyncTask<Object,Object,Bitmap>
{

12.5.
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

425

ImageView imageView; //
// : ImageView Uri
@Override
protected Bitmap doInBackground(Object... params)
{
imageView = (ImageView) params[0];

return Slideshow.getThumbnail((Uri) params[1],


getContentResolver(), new BitmapFactory.Options());
// doInBackground

// ListView
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
imageView.setImageBitmap(result);
} // onPostExecute
// LoadThumbnailTask

playButtonListener OnClickListener,
playButton -
playButtonLIstener OnClickListener (. 12.9)
playButton. Intent, Activity
SlideshowPlayer - Intent (
262265). ,
( -). 265
getTag View ,
setTag ( -) 216. 266 Intent.
12.9. , click
playButton
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

// , "Play"
OnClickListener playButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// intent Activity SlideshowPlayer
Intent playSlideshow =
new Intent(Slideshow.this, SlideshowPlayer.class);
playSlideshow.putExtra(
NAME_EXTRA, ((SlideshowInfo) v.getTag()).getName());
startActivity(playSlideshow); // launch SlideshowPlayer Activity
} // onClick
}; // playButtonListener

426

12

Slideshow

editButtonListener OnClickListener,
editButton -
editButtonLIstener OnClickListener (. 12.10) editButton. Intent,
Activity SlideshowEditor, - Intent ( 277280). 280 getTag View
, setTag (
-) 220. 281 Intent startActivityForResult. ListView Activity
onActivityResult (
- ).
12.10. click editButton
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

// , "Edit"
private OnClickListener editButtonListener = new OnClickListener()
{
@Override
public void onClick(View v)
{
// intent,
// Activity SlideshowEditor
Intent editSlideshow =
new Intent(Slideshow.this, SlideshowEditor.class);
editSlideshow.putExtra(
NAME_EXTRA, ((SlideshowInfo) v.getTag()).getName());
startActivityForResult(editSlideshow, 0);
} // onClick
}; // playButtonListener

deleteButtonListener OnClickListener,
deleteButton -
deleteButtonLIstener OnClickListener (. 12.11) , deleteButton. -
. , getTag
View SlideshowInfo,
setTag 224. slideshowList.
304 ListView notifyDataSetChanged
slideshowAdapter.
12.11. ,
deleteButton
285
286
287
288

// , "Delete"
private OnClickListener deleteButtonListener = new OnClickListener()
{
@Override

12.5.
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312

427

public void onClick(nal View v)


{
// AlertDialog
AlertDialog.Builder builder =
new AlertDialog.Builder(Slideshow.this);
builder.setTitle(R.string.dialog_conrm_delete);
builder.setMessage(R.string.dialog_conrm_delete_message);
builder.setPositiveButton(R.string.button_ok,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
Slideshow.slideshowList.remove(
(SlideshowInfo) v.getTag());
slideshowAdapter.notifyDataSetChanged();
//
} // onClick
} //
); // setPositiveButton
builder.setNegativeButton(R.string.button_cancel, null);
builder.show();
} // onClick
}; // playButtonListener

getSlideshowInfo
12.12 getSlideshowInfo, SlideshowInfo. List of
SlideshowInfo , . SlideshowInfo , 319 .
, 321 null.
12.12. getSlideshowInfo, SlideshowInfo
-
313
314
315
316
317
318
319
320
321
322
323

// , SlideshowInfo
// -
public static SlideshowInfo getSlideshowInfo(String name)
{
// SlideshowInfo
for (SlideshowInfo slideshowInfo : slideshowList)
if (slideshowInfo.getName().equals(name))
return slideshowInfo;

return null; //
// getSlideshowInfo

428

12

Slideshow

getThumbnail
12.13 getThumbnail,
, Uri, , ContentResolver,
,
BitmapFactory.Options, Bitmap. 328
Uri id , .
330331 Android MediaStore
. MediaStore.Images.Thumbnails getThumbnail, .
ContentResolver,
, id , BitmapFactory.
Options Bitmap. 333
Bitmap.
12.13. getThumbnail Bitmap
Uri
324

// , Bitmap
//
public static Bitmap getThumbnail(Uri uri, ContentResolver cr,
BitmapFactory.Options options)
{
int id = Integer.parseInt(uri.getLastPathSegment());

325
326
327
328
329
330
Bitmap bitmap = MediaStore.Images.Thumbnails.getThumbnail(cr, id,
331
MediaStore.Images.Thumbnails.MICRO_KIND, options);
332
333
return bitmap;
334
} // getThumbnail
335 } // Slideshow

12.5.3. SlideshowEditor ListActivity


SlideshowEditor (. 12.1412.22) , -.
ListActivity, Activity
ListView -.
12.4.7, ListActivity .

package, import
SlideshowEditor
12.14 SlideShowEditor. import , 12.3
. SlideshowEditorAdapter ( 26) ArrayAdapter, -,
ListView Activity.
- ListView, Delete Button

12.5.

429

(), -.
- SlideshowInfo, 27.
12.14. package, import
SlideshowEditor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// SlideshowEditor.java
// Activity, -.
package com.deitel.slideshow;
import java.util.List;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.ListActivity;
android.content.Context;
android.content.Intent;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.view.LayoutInater;
android.view.View;
android.view.View.OnClickListener;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.Button;
android.widget.ImageView;

public class SlideshowEditor extends ListActivity


{
// slideshowEditorAdapter, - ListView
private SlideshowEditorAdapter slideshowEditorAdapter;
private SlideshowInfo slideshow; // -

onCreate Activity
12.15 onCreate, Activity. 34 ListActivity ,
slideshow_editor.xml. 37 Intent,
Activity, String Slideshow.NAME_EXTRA, Bundle Intent. 38
getSlideshowInfo Slideshow (. 12.12)
SlideshowInfo -, .
4152 Button GUI
. 5556 SlideshowEditorAdapter (.
12.22), - list-item, slideshow_edit_item.xml.
ListView SlideshowEditorAdapter.

430

12

Slideshow

12.15. onCreate Activity


29
30
31
32
33
34
35
36
37
38
39
40

//
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.slideshow_editor);
// -
String name = getIntent().getStringExtra(Slideshow.NAME_EXTRA);
slideshow = Slideshow.getSlideshowInfo(name);
// OnClickListeners
// Button
Button doneButton = (Button) ndViewById(R.id.doneButton);
doneButton.setOnClickListener(doneButtonListener);

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

Button addPictureButton =
(Button) ndViewById(R.id.addPictureButton);
addPictureButton.setOnClickListener(addPictureButtonListener);
Button addMusicButton = (Button) ndViewById(R.id.addMusicButton);
addMusicButton.setOnClickListener(addMusicButtonListener);
Button playButton = (Button) ndViewById(R.id.playButton);
playButton.setOnClickListener(playButtonListener);

// ListView
//
SlideshowEditorAdapter =
new SlideshowEditorAdapter(this, slideshow.getImageList());
getListView().setAdapter(slideshowEditorAdapter);
// onCreate

onActivityResult Activity
12.5.2, onActivityResult (. 12.16)
, subActivity, startActivityForResult,
. , SlideshowEditor
Activity, ,
, Activity,
. subActivity, 6162, , subActivity, onActivityResult.
Activity startActivityForResult,
onActivityResult . resultCode
RESULT_OK ( 69) , Activity
. ,

12.5.

431

. Intent Activity. 71
getData Intent Uri, , .
onActivityResult ( 74), 77
-. 80 , SlideshowEditorAdapter ,
ListView SlideshowEditor . onActivityResult ( 82), 83
-.
12.16. onActivityResult Activity
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

// ID
//
private static nal int PICTURE_ID = 1;
private static nal int MUSIC_ID = 2;
// , Activity,
// Activity
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (resultCode == RESULT_OK) //
{
Uri selectedUri = data.getData();
// Activity
if (requestCode == PICTURE_ID)
{
// -
slideshow.addImage(selectedUri.toString());

// ListView
slideshowEditorAdapter.notifyDataSetChanged();
} // end if
else if (requestCode == MUSIC_ID) // Activity
//
slideshow.setMusicPath(selectedUri.toString());
} // if
// onActivityResult

doneButtonListener OnClickListener,
doneButton
doneButton doneButtonListener (. 12.17) finish Activity ( 94),
Activity .

432

12

Slideshow

12.17. backButtonListener OnClickListener


backButton
87
88
89
90
91
92
93
94
95
96
97

// "Done"
private OnClickListener doneButtonListener = new OnClickListener()
{
// Activity
@Override
public void onClick(View v)
{
nish();
} // onClick
}; // doneButtonListener OnClickListener

addPictureButtonListener OnClickListener,

addPictureButton
addPictureButtonListener (. 12.18) Activity, (, Gallery)
addPictureButton. 105 Intent,
ACTION_GET_CONTENT, Intent, . setType Intent
String, MIME ,
. (*) MIME
. createChooser
Intent Intent android.intent.
action.CHOOSER. Activity, Activity, (
Activity).
Activity, . ,
Gallery.
createChooser ,
Activity.
12.18. addPictureButtonListener OnClickListener
addPictureButton
98
99
100
101
102
103
104
105
106

// "Add Picture" Button


private OnClickListener addPictureButtonListener =
new OnClickListener()
{
//
@Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");

12.5.
107
108
109
110

433

startActivityForResult(Intent.createChooser(intent,
getResources().getText(R.string.chooser_music)), MUSIC_ID);
} // onClick
}; // addPictureButtonListener
// OnClickListener

111

addMusicButtonListener OnClickListener,
addMusicButton
addMusicButtonListener OnClickListener (. 12.19)
Activity, , -. , 12.18, ,
Intent MIME- "audio/*",
. ,
Intent, , . 12.12.
( Select music track)
( Sound Recorder).
12.19. addMusicButtonListener OnClickListener
addMusicButton
112
113
114
115
116
117
118
119
120
121
122
123
124

// "Add Music" Button


private OnClickListener addMusicButtonListener = new OnClickListener()
{
//
@Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("audio/*");
startActivityForResult(Intent.createChooser(intent,
getResources().getText(R.string.chooser_music)), MUSIC_ID);
} // onClick
}; // addMusicButtonListener
// OnClickListener

125

. 12.12.

434

12

Slideshow

playButtonListener OnClickListener,
PlayButton
playButtonListener OnClickListener (. 12.20)
Activity SlideshowPlayer Play Button. 137142 Intent SlideshowPlayer,
- Intent, Intent.
12.20. playButtonListener OnClickListener,
playButton
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

// , "Play" Button
private OnClickListener playButtonListener = new OnClickListener()
{
// -
@Override
public void onClick(View v)
{
// Intent SlideshowPlayer Activity
Intent playSlideshow =
new Intent(SlideshowEditor.this, SlideshowPlayer.class);
// -
playSlideshow.putExtra(
Slideshow.NAME_EXTRA, slideshow.getName());
startActivity(playSlideshow); // Activity
} // onClick
}; // playButtonListener

deleteButtonListener OnClickListener,
deleteButton
OnClickListener deleteImage (. 12.21) Delete Button, .
Delete . 152 , remove slideshowEditorAdapter.
ListView SlideshowEditor, .
12.21. deleteButtonListener OnClickListener, deleteButton,

144
145
146
147
148
149
150

// "Delete" Button,
// ImageView
private OnClickListener deleteButtonListener = new OnClickListener()
{
//
@Override
public void onClick(View v)

12.5.
151
152
153
154
155

435

{
slideshowEditorAdapter.remove((String) v.getTag());
} // onClick
}; // OnClickListener deleteButtonListener

ViewHolder SlideshowEditorAdaptor:
- View-Holder
12.7, ListView SlideshowEditor
view-holder. ViewHolder (. 12.22, 158162)
GUI, ListView.
SlideshowEditorAdapter ( 165212) ArrayAdapter
- ListView SlideshowEditor. List, , Strings,
-. SlideshowEditorAdapter
SlideshowAdapter, 12.7, slideshow_edit_item.xml ListView.
12.7.
12.22. SlideshowEditorAdapter - ListView SlideshowEditor
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182

// , " ViewHolder pattern",


// ListView
private static class ViewHolder
{
ImageView slideImageView; // ImageView ListView
Button deleteButton; // Button ListView
} // ViewHolder
// ArrayAdapter, Slideshow
private class SlideshowEditorAdapter extends ArrayAdapter<String>
{
private List<String> items; // Uri
private LayoutInater inater;
public SlideshowEditorAdapter(Context context, List<String> items)
{
super(context, -1, items);
this.items = items;
inater = (LayoutInater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} // SlideshoweditorAdapter
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder; // GUI


436

12

Slideshow

12.22 ()
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

// convertView null, ViewHolder;


// ViewHolder
if (convertView == null)
{
convertView =
inater.inate(R.layout.slideshow_edit_item, null);
// ViewHolder ListView
viewHolder = new ViewHolder();
viewHolder.slideImageView = (ImageView)
convertView.ndViewById(R.id.slideshowImageView);
viewHolder.deleteButton =
(Button) convertView.ndViewById(R.id.deleteButton);
convertView.setTag(viewHolder); // View
} // if
else // ViewHolder convertView
viewHolder = (ViewHolder) convertView.getTag();
// Bitmap
String item = items.get(position); //
//
new LoadThumbnailTask().execute(viewHolder.slideImageView,
Uri.parse(item));
// "Delete" Button
viewHolder.deleteButton.setTag(item);
viewHolder.deleteButton.setOnClickListener(deleteButtonListener);
return convertView;
} // getView
} // SlideshowEditorAdapter

LoadThumbnailTask
LoadThumbnailTask (. 12.23) ,
GUI. doInBackground getThumbnail
Slideshow . onPostExecute
(Bitmap) ImageView.
12.23. LoadThumbnailTask

214
215
216
217
218

//
private class LoadThumbnailTask extends AsyncTask<Object,Object,Bitmap>
{
ImageView imageView; //

12.5.
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

437

// ImageView, MediaType Uri


@Override
protected Bitmap doInBackground(Object... params)
{
imageView = (ImageView) params[0];

return Slideshow.getThumbnail((Uri) params[1],


getContentResolver(), new BitmapFactory.Options());
// doInBackground

// ListView
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
imageView.setImageBitmap(result);
} // onPostExecute
} // LoadThumbnailTask
// SlideshowEditor

12.5.4. SlideshowPlayer ListActivity


SlideshowPlayer Activity (. 12.2412.28) , Intent, Activity.

package, import SlideshowPlayer


12.24 SlideShowPlayer.
import , 12.3
. String, 25, , -. String,
2830,
onSaveInstanceState onCreate ,
Activity . int 32
-. 3340 ,
-.
12.24. package, import SlideshowPlayer
1
2
3
4
5
6
7
8

// SlideshowPlayer.java
// -,
// Intent
package com.deitel.slideshow;
import java.io.FileNotFoundException;
import java.io.InputStream;
import android.app.Activity;

438

12

Slideshow

12.24 ()
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

import
import
import
import
import
import
import
import
import
import
import
import
import

android.content.ContentResolver;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.graphics.drawable.BitmapDrawable;
android.graphics.drawable.Drawable;
android.graphics.drawable.TransitionDrawable;
android.media.MediaPlayer;
android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.os.Handler;
android.util.Log;
android.widget.ImageView;

public class SlideshowPlayer extends Activity


{
private static nal String TAG = "SLIDESHOW";

// ,
//
private static nal String
private static nal String
private static nal String
private
private
private
private
private

37
38

private
private

39

private

40
41

private

//
//

-
MEDIA_TIME = "MEDIA_TIME";
IMAGE_INDEX = "IMAGE_INDEX";
SLIDESHOW_NAME = "SLIDESHOW_NAME";

static nal int DURATION = 5000; // 5


ImageView imageView; //
String slideshowName; // -
SlideshowInfo slideshow; // -
BitmapFactory.Options options; //
//
Handler handler; // -
int nextItemIndex; //
//
int mediaTime; // ( )
//
MediaPlayer mediaPlayer; //

onCreate Activity
12.25 onCreate Activity
SlideshowPlayer. 49 ImageView SlideshowPlayer. 5168
, Activity ( savedInstanceState
Bundle null ( 51)), Activity ( ). Activity , 54
- Intent, Activity. 55 mediaTime 0, . 56 nextItemIndex 0, -

12.5.

439

. Activity , 6167
, savedInstanceState Bundle.
12.25. onCreate Activity
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

// Activi SlideshowPlayer
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.slideshow_player);
imageView = (ImageView) ndViewById(R.id.imageView);
if (savedInstanceState == null)
{
// - Intent
slideshowName = getIntent().getStringExtra(Slideshow.NAME_EXTRA);
mediaTime = 0;
//
nextItemIndex = 0; //
} // if
else // Activity
{
// ,
//
mediaTime = savedInstanceState.getInt(MEDIA_TIME);
// ,
//
nextItemIndex = savedInstanceState.getInt(IMAGE_INDEX);

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

// -,
//
slideshowName = savedInstanceState.getString(SLIDESHOW_NAME);
// else

// SlideshowInfo -
slideshow = Slideshow.getSlideshowInfo(slideshowName);
// BitmapFactory.Options
options = new BitmapFactory.Options();
options.inSampleSize = 4; // ,
// 1/4
//
if (slideshow.getMusicPath() != null)
{
// try MediaPlayer,
try
{
mediaPlayer = new MediaPlayer();

440

12

Slideshow

12.25 ()
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

mediaPlayer.setDataSource(
this, Uri.parse(slideshow.getMusicPath()));
mediaPlayer.prepare(); // MediaPlayer
//
mediaPlayer.setLooping(true); //
//
mediaPlayer.seekTo(mediaTime); // mediaTime
} // end try
catch (Exception e)
{
Log.v(TAG, e.toString());
} // catch
// if

handler = new Handler(); //


// -
// onCreate

71 SlideshowInfo, , 7475 BitmapFactory.Options,


, -.
- , 83
MediaPlayer, . 84
85 setDataSource MediaPlayer Uri,
. prepare MediaPlayer ( 86)
MediaPlayer .
, MediaPlayer .
, . ,
prepareAsync, . prepare
, . prepare , MediaPlayer
(, ).

( 92). MediaPlayer
- developer.android.com/reference/android/media/MediaPlayer.html.
87 setLooping MediaPlayer true. , ,
-, . 88
seekTo MediaPlayer,
, . 0
, Activity ;
. , 96
Handler, -.

12.5.

441

onStart, onPause,
onResume, onStop onDestroy Activity
12.26 onStart, onPause, onResume, onStop onDestroy
Activity. onStart ( 100105) Runnable
updateSlideshow (. 12.28) . onPause (
108115) pause
MediaPlayer. ,
Activity . onResume ( 118125)
start MediaPlayer,
, . onStop
( 128135) removeCallbacks,
Runnables updateSlideshow ,
Activity . onDestroy ( 138145) release
MediaPlayer, , MediaPlayer.
12.26. onStart, onPause, onResume, onStop
onDestroy Activity
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

// onCreate onStop
@Override
protected void onStart()
{
super.onStart();
handler.post(updateSlideshow); // updateSlideshow
//
} // onStart
// , Activity
@Override
protected void onPause()
{
super.onPause();

if (mediaPlayer != null)
mediaPlayer.pause(); //
// onPause

// onStart onPause
@Override
protected void onResume()
{
super.onResume();

if (mediaPlayer != null)
mediaPlayer.start(); //
// onResume

// , Activity
@Override

442

12

Slideshow

12.26 ()
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146

protected void onStop()


{
super.onStop();

// -
handler.removeCallbacks(updateSlideshow);
// onStop

// Activity
@Override
protected void onDestroy()
{
super.onDestroy();

if (mediaPlayer != null)
mediaPlayer.release(); // MediaPlayer
// onDestroy

onSaveInstanceState Activity
2.27 onSaveInstanceState ,
Activity -, ( , nextItemIndex ), - Bundle outState
.
onCreate -
.
12.27. onSaveInstanceState Activity
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161

// -,
// onCreate
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
// mediaPlayer,
if (mediaPlayer != null)
outState.putInt(MEDIA_TIME, mediaPlayer.getCurrentPosition());

// nextItemIndex slideshowName
outState.putInt(IMAGE_INDEX, nextItemIndex 1);
outState.putString(SLIDESHOW_NAME, slideshowName);
// onSaveInstanceState

12.5.

443

updateSlideshow Runnable
12.28 Runnable, . - ( 168),
171172 MediaPlayer,
. 173 finish Activity,
Activity Activity, SlideshowPlayer.
12.28. updateSlideshow Runnable - 5
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

// , Runnable
// -
private Runnable updateSlideshow = new Runnable()
{
@Override
public void run()
{
if (nextItemIndex >= slideshow.size())
{
//
if (mediaPlayer != null && mediaPlayer.isPlaying())
mediaPlayer.reset(); // - ,
// mediaPlayer
nish(); // Activity
} // if
else
{
String item = slideshow.getImageAt(nextItemIndex);
new LoadImageTask().execute(Uri.parse(item));
++nextItemIndex;
} // else
} // run
//
class LoadImageTask extends AsyncTask<Uri, Object, Bitmap>
{
//
@Override
protected Bitmap doInBackground(Uri... params)
{
return getBitmap(params[0], getContentResolver(), options);
} // doInBackground
// ListView
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
BitmapDrawable next = new BitmapDrawable(result);
next.setGravity(android.view.Gravity.CENTER);

444

12

Slideshow

12.28 ()
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

Drawable previous = imageView.getDrawable();


// TransitionDrawable,
// Drawable
if (previous instanceof TransitionDrawable)
previous = ((TransitionDrawable) previous).getDrawable(1);
if (previous == null)
imageView.setImageDrawable(next);
else
{
Drawable[] drawables = { previous, next };
TransitionDrawable transition =
new TransitionDrawable(drawables);
imageView.setImageDrawable(transition);
transition.startTransition(1000);
} // else

handler.postDelayed(updateSlideshow, DURATION);
} // onPostExecute
// LoadImageTask

// , Bitmap Uri
public Bitmap getBitmap(Uri uri, ContentResolver cr,
BitmapFactory.Options options)
{
Bitmap bitmap = null;
//
try
{
InputStream input = cr.openInputStream(uri);
bitmap = BitmapFactory.decodeStream(input, null, options);
} // try
catch (FileNotFoundException e)
{
Log.v(TAG, e.toString());
} // catch

return bitmap;
} // getBitmap
}; // updateSlideshow Runnable
// SlieshowPlayer

, 177 , 178 LoadImageTask, . LoadImageTask ( 184220) .


doInBackground getBitmap ( 223240) . , onPostExecute

12.6.

445

. 198199 BitmapDrawable
Bitmap (), gravity
center. ImageView.
200 Drawable. TransitionDrawable,
BitmapDrawable TransitionDrawable. ( ,
TransitionDrawable, , .) Drawable , 208
BitmapDrawable. ,
211215 TransitionDrawable Drawable ImageView. 214 TransitionDrawable
setImageDrawable ImageView currentImageView.
TransitionDrawable ,
. startTransition TransitionDrawable ( 215)
(1000 ). , Drawable drawables.
218 updateSlideshow,
5 .
getBitmap ( 223240) ContentResolver (InputSteam) . 232
decodeStream BitmapFactory
Bitmap . InputStream,
, Rect
( null ) BitmapFactory.
Options, .

12.6.
Slideshow,
- . Android, , .
,
, .
, Intent
MIME . Android Activity,
Activity, Activity.
, ,
AlertDialog View. ListActivity
, ListView, android:id
"@android:id/list". ArrayAdapter , ListViews .
ArrayAdapter, notifyDataSetChanged,

446

12

Slideshow

ListView. view-holder
ListViews .
Intent Activity, , Activity.
setTag View Object View, .
MediaPlayer ,
. BitmapFactory
Bitmap , BitmapFactory.Options. ,
TransitionDrawable,
ImageView.
13 Enhanced Slideshow,
, -
- .


Enhanced
Slideshow App

13

,

Camera

VideoView










Intent .
,
-.
SurfaceView, SurfaceHolder Camera
.
VideoView.
- Serializable.
- ObjectOutputStream FileOutputStream.
- ObjectInputStream
FileInputStream.

13.1.
Enhanced Slideshow
12 Slideshow.

448

13

Enhanced Slideshow App

- , . -
. -
( ; . . 13.1).
, , (. . 13.2, ). ,
(. . 13.2, ) , -. SlideshowPlayer Activity
(. . 13.2), VideoView - .

- , Android.
Android , .

. 13.1. ,

13.2. Enhanced Slideshow App



Eclipse Enhanced Slideshow app.
:

13.2. Enhanced Slideshow App

449

. 13.2. :
;

. 13.3. VideoView

1. FileImport ()
Import ().
2. General () Existing Projects into Workspace
( ). Next >
( >).
3. Select root directory (
) Browse (). Browse For
Folder ( ) EnhancedSlideshow
OK.
4. Finish () Eclipse.

450

13

Enhanced Slideshow App

Eclipse , Package Explorer, Run


AsAndroid Application (  Android).

AVD
12.2 AVD,
.

, , .

-
12, , New
Slideshow ( -) Set Slideshow Name
( -). -, Set
Name - Slideshow Editor.
- (. 12).
. - Done
Slideshow Activity, - .

-
-. - Slideshow
Editor.

-
SlideshowPlayer ,
VideoView, - .

13.3.
, Enhanced Slideshow.


-, . ,
. SlideshowInfo ( 13.5.3).

13.3.

451

,
.
java.io. , , Serializable, .
. ,
Serializable, Serializable. , , Serializable, Serializable. ObjectOutputStream
Serializable OutputSteam
FileOutputStream. Serializable , ObjectOutputStream Serializable.
List of SlideshowInfo
List writeObject ObjectOutputStream.
graph, List.
SlideshowInfo, , SlideshowInfo, . graph ,
Serializable, NotSerializableException. ,
. ,
Serializable .
. ,
, graph .
ObjectInputStream, InputStream, FileInputStream.
readObject ObjectOutputStream ,
Object. ,
List<SlideshowInfo>.

,
Gallery
Enhanced Slideshow , Gallery -. 13.5.5
Camera ( android.hardware) SurfaceView ( android.view)
.
, PictureTaker Activity
Camera, .
Camera.PictureCallback , .
, Gallery,
Uri SlideshowEditor Activity, . PictureTaker Activity ,
Camera.
,

452

13

Enhanced Slideshow App

, -, . Camera.Parameters,
Camera. ,
Activity, ,
.
Activity,
:
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intput.putExtra(MediaStore.EXTRA_OUTPUT, storageURI);
startActivityForResult(intent, requestCode);

storageURI .
onActivityResult, requestCode
, .

, -
Slideshow Intent Activity,
Gallery. ( 13.5.4) .
MIME , .

VideoView
SlideshowPlayer Activity Enhanced Slideshow ( 13.5.6) VideoView -.
URI VideoView , MediaController ( android.widget),
.
VideoView MediaPlayer, . MediaPlayer.OnCompletionListener ,
-, .

13.4.

, GUI
Slideshow ( 12). Enhanced
Slideshow. Eclipse.

13.4.1.
Slideshow ( 12)
:
1. Slideshow EnhancedSlideshow.

13.4.

453

2. EnhancedSlideshow Eclipse.
3. src.
4. com.deitel.slideshow RefactorRename ().
5. Rename Package ( ) com.deitel.
enhancedslideshow, Preview> (>).
6. OK, .
7. strings.xml app_name
"Enhanced Slideshow".

13.4.2. AndroidManifest.xml
13.1 AndroidManifest.xml. activity PictureTaker Activity ( 2629), ,
WRITE_EXTERNAL_STORAGE CAMERA.
13.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.enhancedslideshow" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".Slideshow"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Light">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
<activity android:name=".SlideshowEditor"
android:label="@string/slideshow_editor"
android:screenOrientation="portrait"></activity>
<activity android:name=".SlideshowPlayer"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar"></activity>
<activity android:name=".PictureTaker"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenOrientation="landscape"></activity>
</application>
<uses-sdk android:minSdkVersion="8" />

454

13

Enhanced Slideshow App

13.1 ()
32
33
34
35
36
37

<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE">
</uses-permission>
<uses-permission android:name="android.permission.CAMERA">
</uses-permission>
</manifest>

13.4.3. SlideshowEditor ListActivity


. 13.4 ListActivity SlideshowEditor,
Buttons TableLayout, .

doneButton

takePictureButton

addPictureButton
addMusicButton
addVideoButton
playButton

. 13.4. ListActivity SlideshowEditor,


slideshow_editor.xml

13.4.4. PictureTaker Activity


13.2 XML- PictureTaker Activity (camera_preview.xml), SurfaceView, .
SurfaceView ,
.
13.2. PictureTaker Activity,
camera_preview.xml
1
2
3
4
5

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


urfaceView xmlns:android=http://schemas.android.com/apk/res/android
android:id="@+id/cameraSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</SurfaceView>

13.4.5. SlideshowPlayer Activity


13.3 slideshow_player.xml,
XML- SlideshowPlayer Activity. , , ImageView VideoView ( - ).

13.5.

455

FrameLayout, ImageView VideoView,


. View.
13.3. SlideshowPlayer Activity,
slideshow_editor.xml
1
2
3
4
5
6
7
8
9
10
11
12

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


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

android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:id="@+id/imageView" android:scaleType="centerInside"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"></ImageView>
<VideoView android:id="@+id/videoView" android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"></VideoView>
</FrameLayout>

13.5.
MediaItem ( 13.4), SlideshowInfo ( 13.5),
Slideshow ( 13.613.11), SlideshowEditor ( 13.1213.14), PictureTaker ( 13.1513.20) SlideshowPlayer ( 13.2113.23). 12,
, .

13.5.1. MediaItem
Slideshow
List<String>, ListActivity Slideshow.
,
MediaItem (. 13.1), MediaType String. enum MediaType ( 12) ,
, MediaItem: . SlideshowInfo
( 13.5.2) List of MediaItems, , -. Enhanced
Slideshow SlideshowInfo, ,
MediaItem Serializable.
13.4. MediaItem,
-
1
2
3
4
5

// MediaItem.java
// -.
package com.deitel.enhancedslideshow;
import java.io.Serializable;

456

13

Enhanced Slideshow App

13.4 ()
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

public class MediaItem implements Serializable


{
private static nal long serialVersionUID = 1L; // #
// ,
public static enum MediaType { IMAGE, VIDEO }
private nal MediaType type;
private nal String path;

// MediaItem
// IMAGE VIDEO
// MediaItem

//
public MediaItem(MediaType mediaType, String location)
{
type = mediaType;
path = location;
} //
// MediaType
public MediaType getType()
{
return type;
} // MediaType

//
public String getPath()
{
return path;
} // getDescription
// MediaItem

13.5.2. SlideshowInfo
SlideshowInfo (. 13.5) List<MediaItem> ( 13),
, (
List<String> ). getImageList,
addImage getImageAt getMediaItemList ( 31), addMediaItem ( 37) getMediaItemAt ( 43) .
MediaItems, Strings.
SlideshowInfo Serializable ( 9).
13.5. SlideshowInfo List of MediaItems
1
2
3
4

// SlideshowInfo.java
// -.
package com.deitel.enhancedslideshow;

13.5.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

457

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class SlideshowInfo implements Serializable
{
private static nal long serialVersionUID = 1L; // #
private String name; // -
private List<MediaItem> mediaItemList; // -
private String musicPath; //
//
public SlideshowInfo(String slideshowName)
{
name = slideshowName; // -
mediaItemList = new ArrayList<MediaItem>();
musicPath = null; // -
} // SlideshowInfo
// -
public String getName()
{
return name;
} // getName
// List of MediaItems, -
public List<MediaItem> getMediaItemList()
{
return mediaItemList;
} // getMediaItemList
// MediaItem
public void addMediaItem(MediaItem.MediaType type, String path)
{
mediaItemList.add(new MediaItem(type, path));
} // addMediaItem
// MediaItem
public MediaItem getMediaItemAt(int index)
{
if (index >= 0 && index < mediaItemList.size())
return mediaItemList.get(index);
else
return null;
} // getMediaItemAt
// -
public String getMusicPath()
{
return musicPath;
} // getMusicPath

458

13

Enhanced Slideshow App

13.5 ()
56
57
58
59
60
61
62
63
64
65
66
67
68

// -
public void setMusicPath(String path)
{
musicPath = path;
} // setMusicPath

// / -
public int size()
{
return mediaItemList.size();
} // size
// SlideshowInfo

13.5.3. Slideshow
- . 13.3,
- . Slideshow (. 13.613.11), Activity , ,
List<SlideshowInfo>.
Slideshow.

package, import Slideshow


Slideshow ListActivity (. 13.6)
import .
. 59 24 import,
.
slideshowFile ( 53)
.
13.6. package, import Slideshow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// Slideshow.java
// Activity Slideshow.
package com.deitel.enhancedslideshow;
import
import
import
import
import
import
import

java.io.File;
java.io.FileInputStream;
java.io.FileOutputStream;
java.io.ObjectInputStream;
java.io.ObjectOutputStream;
java.util.ArrayList;
java.util.List;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.ContentResolver;

13.5.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

459

android.content.Context;
android.content.DialogInterface;
android.content.Intent;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.provider.MediaStore;
android.util.Log;
android.view.Gravity;
android.view.LayoutInater;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.view.View;
android.view.View.OnClickListener;
android.view.ViewGroup;
android.widget.ArrayAdapter;
android.widget.Button;
android.widget.EditText;
android.widget.ImageView;
android.widget.ListView;
android.widget.TextView;
android.widget.Toast;

public class Slideshow extends ListActivity


{
private static nal String TAG = "SLIDESHOW";//
// -
// Intent
public static nal String NAME_EXTRA = "NAME";
static List<SlideshowInfo> slideshowList; // -
private ListView slideshowListView;
// ListView
// ListActivity
private SlideshowAdapter slideshowAdapter; // ListView
private File slideshowFile; // ,
// -

54

onCreate Activity
onCreate Slideshow (. 13.7) File ( 6365),
Android,
-. Context ,
. getExternalFilesDir File,
, .
SD,

460

13

Enhanced Slideshow App


. , , ,
. File getAbsolutePath,
/EnhancedSlideshowData.ser ,
-. (, , ,
,
SD.) 66 AsyncTask
LoadSlideshowsTask (. 13.8),
execute, - (
-). - , execute null.
13.7. onCreate Activity Slideshow
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

// activity
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
slideshowListView = getListView(); // ListView
//
// -
slideshowFile = new File(
getExternalFilesDir(null).getAbsolutePath() +
"/EnhancedSlideshowData.ser");
new LoadSlideshowsTask().execute((Object[]) null);

// AlertDialog Builder
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.welcome_message_title);
builder.setMessage(R.string.welcome_message);
builder.setPositiveButton(R.string.button_ok, null);
builder.show();
// onCreate

LoadSlideshowsTask AsyncTask
doInBackground LoadSlideshowsTask (. 13.8) ,
EnhancedSlideshowData.ser ( 84). ,
ObjectInputStream ( 8889). 90 readObject
ObjectInputStream, List<SlideshowInfo>
slideshowFile. , 115 List<SlideshowInfo>.
94110 runOnUiThread Activity Toast UI, .

13.5.

461

onPostExecute ( 121130) UI, ListView Slideshow.


13.8. LoadSlideshowsTask List<SlideshowInfo>
,
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

// , List<SlideshowInfo>
//
private class LoadSlideshowsTask extends AsyncTask<Object,Object,Object>
{
// , GUI
@Override
protected Object doInBackground(Object... arg0)
{
// - , -
if (slideshowFile.exists())
{
try
{
ObjectInputStream input = new ObjectInputStream(
new FileInputStream(slideshowFile));
slideshowList = (List<SlideshowInfo>) input.readObject();
} // try
catch (nal Exception e)
{
runOnUiThread(
new Runnable()
{
public void run()
{
//
Toast message = Toast.makeText(Slideshow.this,
R.string.message_error_reading,
Toast.LENGTH_LONG);
message.setGravity(Gravity.CENTER,
message.getXOffset() / 2,
message.getYOffset() / 2);
message.show(); // Toast
Log.v(TAG, e.toString());
} // run
} // Runnable
); // runOnUiThread
} // catch
} // if
if (slideshowList == null) // null,
slideshowList = new ArrayList<SlideshowInfo>();

return (Object) null; //


//
// doInBackground

462

13

Enhanced Slideshow App

13.8 ()
119
120
121
122
123
124
125
126
127
128
129
130
131
132

// ListView GUI
@Override
protected void onPostExecute(Object result)
{
super.onPostExecute(result);

// ListView
slideshowAdapter =
new SlideshowAdapter(Slideshow.this, slideshowList);
slideshowListView.setAdapter(slideshowAdapter);
} // onPostEecute
// LoadSlideshowsTask

SaveSlideshowsTask AsyncTask
doInBackground SaveSlideshowsTask (. 13.9) , EnhancedSlideshowData.ser ( 143).
, . 147148 ObjectOutputStream. 149 writeObject ObjectOutputStream,
List<SlideshowInfo> slideshowFile. ( 154169) runOnUiThread
Toast UI, .
13.9. SaveSlideshowsTask List<SlideshowInfo>

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

// ,
// List<SlideshowInfo>
private class SaveSlideshowsTask extends AsyncTask<Object,Object,Object>
{
// , GUI
@Override
protected Object doInBackground(Object... arg0)
{
try
{
// ,
if (!slideshowFile.exists())
slideshowFile.createNewFile();

// ObjectOutputStream,
// slideshowList
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream(slideshowFile));
output.writeObject(slideshowList);
output.close();
// try

13.5.
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

463

catch (nal Exception e)


{
runOnUiThread(
new Runnable()
{
public void run()
{
//
Toast message = Toast.makeText(Slideshow.this,
R.string.message_error_writing, Toast.LENGTH_LONG);
message.setGravity(Gravity.CENTER,
message.getXOffset() / 2,
message.getYOffset() / 2);
message.show(); // Toast
Log.v(TAG, e.toString());
} // run
} // Runnable
); // runOnUiThread
// catch

return (Object) null; //


//
} // doInBackground
// SaveSlideshowsTask

onActivityResult Activity
onActivityResult (. 13.10)
List<SlideshowInfo> -.
251 AsyncTask
SaveSlideshowsTask (. 13.9), execute
.
13.10. onCreateOptionsMenu, onOptionsItemSelected
onActivityResult Activity
245
246
247
248
249
250
251
252
253
254

// ListView
// -
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
new SaveSlideshowsTask().execute((Object[]) null); //
// -
slideshowAdapter.notifyDataSetChanged(); //
} // onActivityResult

464

13

Enhanced Slideshow App

getThumbnail
getThumbnail (. 13.11) ( 439453).
13.11. getThumbnail,

438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453

// , Bitmap,
//
public static Bitmap getThumbnail(MediaItem.MediaType type, Uri uri,
ContentResolver cr, BitmapFactory.Options options)
{
Bitmap bitmap = null;
int id = Integer.parseInt(uri.getLastPathSegment());
if (type == MediaItem.MediaType.IMAGE) //
bitmap = MediaStore.Images.Thumbnails.getThumbnail(cr, id,
MediaStore.Images.Thumbnails.MICRO_KIND, options);
else if (type == MediaItem.MediaType.VIDEO) //
bitmap = MediaStore.Video.Thumbnails.getThumbnail(cr, id,
MediaStore.Video.Thumbnails.MICRO_KIND, options);

return bitmap;
// getThumbnail

13.5.4. SlideshowEditor
SlideshowEditor (. 13.1213.14), , ,
-. ,
.

onActivityResult Activity
SlideshowEditor ,
.
7172 (. 13.12),
startActivityForResult Activity. onActivityResult Activity, . onActivityResult ,
Uri, .
13.12. onActivityResult
68
69
70
71

// ID
//
private static nal int PICTURE_ID = 1;
private static nal int MUSIC_ID = 2;
private static nal int VIDEO_ID = 3;

13.5.
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

465

private static nal int TAKE_PICTURE_ID = 4;


// , Activity,
// Activity
@Override
protected nal void onActivityResult(int requestCode, int resultCode,
Intent data)
{
if (resultCode == RESULT_OK) //
{
Uri selectedUri = data.getData();
// Activity
if (requestCode == PICTURE_ID ||
requestCode == TAKE_PICTURE_ID || requestCode == VIDEO_ID )
{
//
MediaItem.MediaType type = (requestCode == VIDEO_ID ?
MediaItem.MediaType.VIDEO : MediaItem.MediaType.IMAGE);
// MediaItem -
slideshow.addMediaItem(type, selectedUri.toString());

// ListView
slideshowEditorAdapter.notifyDataSetChanged();
} // if
else if (requestCode == MUSIC_ID) // Activity
slideshow.setMusicPath(selectedUri.toString());
} // if
// onActivityResult

takePictureButton addVideoButton
13.13 takePictureButton
( 128141) addVideoButton ( 144155). , addVideoButtonListener , 12.18,
MIME "video/*".
, .
13.13. takePictureButton addVideoButton
127

// , "Take Picture"

128
129
130
131
132
133
134
135

private OnClickListener takePictureButtonListener =


new OnClickListener()
{
// ,
@Override
public void onClick(View v)
{
// Intent,
// Slideshowplayer Activity

466

13

Enhanced Slideshow App

13.13 ()
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

Intent takePicture =
new Intent(SlideshowEditor.this, PictureTaker.class);
startActivityForResult(takePicture, TAKE_PICTURE_ID);
// onClick
// OnClickListener takePictureButtonListener

}
};

// , "Add Picture"
private OnClickListener addVideoButtonListener = new OnClickListener()
{
// ,
@Override
public void onClick(View v)
{
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("video/*");
startActivityForResult(Intent.createChooser(intent,
getResources().getText(R.string.chooser_video)), VIDEO_ID);
} // onClick
}; // OnClickListener addVideoButtonListener

LoadThumbnailTask AsyncTask
LoadThumbnailTask (. 13.14)
MediaItem getThumbnail Slideshow, Bitmap .
13.14. LoadThumbnailTask

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

//
private class LoadThumbnailTask extends AsyncTask<Object,Object,Bitmap>
{
ImageView imageView; //
// : ImageView, MediaType
// Uri
@Override
protected Bitmap doInBackground(Object... params)
{
imageView = (ImageView) params[0];

return Slideshow.getThumbnail((MediaItem.MediaType)params[1],
(Uri) params[2], getContentResolver(),
new BitmapFactory.Options());
// doInBackground

// ListView
@Override
protected void onPostExecute(Bitmap result)

13.5.
278
279
280
281
282

467

super.onPostExecute(result);
imageView.setImageBitmap(result);
} // onPostExecute
// LoadThumbnailTask

13.5.5. PictureTaker Activity


PictureTaker (. 13.1513.20) , -. ,
.

package, import
SlideshowEditor
13.15 PictureTaker.
import , 13. 3132
SurfaceView,
, SurfaceHolder, SurfaceView. 35 Camera,
. List<String> effects ( 36)
. ,
(, black and white
(-), sepia () . .). List<Camera.Size> sizes ( 37)
, . .
, String effect EFFECT_NONE Camera.Parameter.
13.15. package, import PictureTaker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// PictureTaker.java
// Activity,
//
package com.deitel.enhancedslideshow;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore.Images;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;

468

13

Enhanced Slideshow App

13.15 ()
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

import
import
import
import
import
import
import

android.view.MenuItem;
android.view.MotionEvent;
android.view.SurfaceHolder;
android.view.SurfaceView;
android.view.View;
android.view.View.OnTouchListener;
android.widget.Toast;

public class PictureTaker extends Activity


{
private static nal String TAG = "PICTURE_TAKER"; //
//
private SurfaceView surfaceView; //
private SurfaceHolder surfaceHolder; //
// SurfaceView
private boolean isPreviewing; //
// ?
private Camera camera; //
private List<String> effects; //
//
private List<Camera.Size> sizes; //
//
private String effect = Camera.Parameters.EFFECT_NONE; // ,
//

39

onCreate Activity
onCreate (. 13.16) ,
,
Camera Android.
SurfaceView ,
. PictureTaker Activity
(gallery) . SurfaceHolder
, Callback , SurfaceView. 56
Android, Android 3.0. setType SurfaceHolder - Android 3.0
.
13.16. onCreate Activity PictureTaker
40
41
42
43
44

//
@Override
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);

13.5.
45
46
47
48
49
50
51

setContentView(R.layout.camera_preview); //
// surfaceView
surfaceView = (SurfaceView) ndViewById(R.id.cameraSurfaceView);
surfaceView.setOnTouchListener(touchListener);
// surfaceHolder
//
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(surfaceCallback);

52
53
54
55
56
57
58

469

//
// , Android 3.0
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// onCreate

onCreateOptionsMenu
onOptionsItemSelected Activity
onCreateOptionsMenu (. 13.17, 6070) .
onOptionsItemSelected Camera.Parameter ( 76), setColorEffect .
78 setParameters .
.
13.17. onCreateOptionsMenu onOptionsItemSelected
Activity
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

// Activity
//
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
//
for (String effect : effects)
menu.add(effect);

return true;
// onCreateOptionsMenu

// ,
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
Camera.Parameters p = camera.getParameters(); //
p.setColorEffect(item.getTitle().toString()); //
//


470

13

Enhanced Slideshow App

13.17 ()
78
79
80
81

camera.setParameters(p); //
return true;
// onOptionsItemSelected

Callbacks SurfaceHolder
, SurfaceView Callback SurfaceHolder. 13.18
, SurfaceHolder.Callback.
13.18. ,
Surface.Callback
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116

// SurfaceHolder.Callback
private SurfaceHolder.Callback surfaceCallback =
new SurfaceHolder.Callback()
{
// SurfaceView
@Override
public void surfaceDestroyed(SurfaceHolder arg0)
{
camera.stopPreview(); // Camera
isPreviewing = false;
camera.release(); // Camera
} // surfaceDestroyed
// SurfaceView
@Override
public void surfaceCreated(SurfaceHolder arg0)
{
//
// /
camera = Camera.open(); //
effects = camera.getParameters().getSupportedColorEffects();
sizes = camera.getParameters().getSupportedPreviewSizes();
} // surfaceCreated
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height)
{
if (isPreviewing) //
camera.stopPreview(); //
//
Camera.Parameters p = camera.getParameters();
p.setPreviewSize(sizes.get(0).width, sizes.get(0).height);
p.setColorEffect(effect); //
camera.setParameters(p); //

13.5.
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

471

try
{
camera.setPreviewDisplay(holder); // ,
//
} // try
catch (IOException e)
{
Log.v(TAG, e.toString());
} // catch
camera.startPreview(); //
isPreviewing = true;
} // surfaceChanged
}; // SurfaceHolder.Callback

surfaceDestroyed ( 8893) SurfaceHolder.Callback ,


Camera. surfaceCreated SurfaceHolder.Callback ( 96103)
Camera . open Camera Camera,
. Parameters Camera List<String>, , , List<Camera.Size>,
.

RuntimeException
open, .

surfaceChanged ( 105129) SurfaceHolder.Callback


SurfaceView (
SurfaceView). (
Activity.) 109 , , , , ,
stopPreview Camera .
Parameters Camera setPreviewSize.

( List<Camera.
Size> ). setColorEffect
( ). Camera
setParameters, . 120 SurfaceHolder
setPreviewDisplay Camera.
SurfaceView. 127
startPreview Camera.

472

13

Enhanced Slideshow App

PictureCallbacks Camera
13.19 Camera.PictureCallback, . onPictureTaken
byte, Camera,
. imageData
JPEG.
imageData ( 154158). 161163
Intent, setData Uri , Activity.
setResult ( 163) Activity , returnIntent. Activity SlideshowEditor
Intent -
.
13.19. Camera.PictureCallback,

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

// Camera
Camera.PictureCallback pictureCallback = new Camera.PictureCallback()
{
//
public void onPictureTaken(byte[] imageData, Camera c)
{
//
// "Slideshow_" + ( )
String leName = "Slideshow_" + System.currentTimeMillis();
// ContentValues
//
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, leName);
values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
values.put(Images.Media.MIME_TYPE, "image/jpg");
// Uri ,
//
Uri uri = getContentResolver().insert(
Images.Media.EXTERNAL_CONTENT_URI, values);
try
{
// OutputStream uri
OutputStream outStream =
getContentResolver().openOutputStream(uri);
outStream.write(imageData); //
outStream.ush(); //
outStream.close(); //
// Intent, SlideshowEditor

13.5.
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

Intent returnIntent = new Intent();


returnIntent.setData(uri);
//
//
setResult(RESULT_OK, returnIntent); //
//

473

Uri
SlideshowEditor

//
Toast message = Toast.makeText(PictureTaker.this,
R.string.message_saved, Toast.LENGTH_SHORT);
message.setGravity(Gravity.CENTER, message.getXOffset() / 2,
message.getYOffset() / 2);
message.show(); // Toast
nish(); // SlideshowEditor
} // try
catch (IOException ex)
{
setResult(RESULT_CANCELED); //
//
Toast message = Toast.makeText(PictureTaker.this,
R.string.message_error_saving, Toast.LENGTH_SHORT);
message.setGravity(Gravity.CENTER, message.getXOffset() / 2,
message.getYOffset() / 2);
message.show(); // Toast
} // catch
} // onPictureTaken
}; // pictureCallback

SurfaceView
onTouch (. 13.20)
. takePicture Camera ( 195)
. .
Camera.ShutterCallback, .
.
, null.
Camera.PictureCallback,

RAW ( ) JPEG . RAW,
takePicture null. pictureCallback
(. 13.19) JPEG.

474

13

Enhanced Slideshow App

13.20. OnTouchListener,

188
189
190
191
192
193
194
195
196
197
198
199 }

//
private OnTouchListener touchListener = new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
//
camera.takePicture(null, null, pictureCallback);
return false;
} // onTouch
}; // touchListener
// PictureTaker

13.5.6. SlideshowPlayer
Activity SlideshowPlayer (. 13.2113.23) -, . SlideshowPlayer
, -.
.

package, import
SlideshowEditor
13.21 SlideshowPlayer.
import, ,
13.3. videoView VideoView,
.
13.21. package, import SlideshowPlayer
1

// SlideshowPlayer.java

// -,
// Intent
package com.deitel.enhancedslideshow;

3
4
5
6
7
8
9
10
11
12
13
14
15
16

import java.io.FileNotFoundException;
import java.io.InputStream;
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.content.ContentResolver;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.graphics.drawable.BitmapDrawable;
android.graphics.drawable.Drawable;
android.graphics.drawable.TransitionDrawable;
android.media.MediaPlayer;
android.media.MediaPlayer.OnCompletionListener;

13.5.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

import
import
import
import
import
import
import
import
import

475

android.net.Uri;
android.os.AsyncTask;
android.os.Bundle;
android.os.Handler;
android.util.Log;
android.view.View;
android.widget.ImageView;
android.widget.MediaController;
android.widget.VideoView;

public class SlideshowPlayer extends Activity


{
private static nal String TAG = "SLIDESHOW";

//
//

// , -
//
private static nal String MEDIA_TIME = "MEDIA_TIME";
private static nal String IMAGE_INDEX = "IMAGE_INDEX";
private static nal String SLIDESHOW_NAME = "SLIDESHOW_NAME";

32
33
34
35
36
37
38
39
40
41

private
private
private
private
private
private

42
43
44

private
private
private

45
46

private

static nal int DURATION = 5000; // 5


ImageView imageView;
//
VideoView videoView;
//
String slideshowName;
// -
SlideshowInfo slideshow; // -
BitmapFactory.Options options; //
//
Handler handler; // -
int nextItemIndex; //
int mediaTime; // ( )
//
MediaPlayer mediaPlayer; // ( )

onCreate Activity
5565 onCreate (. 13.22). 55
VideoView, 5665 OnCompletionListener,
VideoView. onCompletion postUpdate Handler
updateSlideshow Runnable
-.
13.22. onCreate Activity
SlideshowPlayer
47
48
49
50

// SlideshowPlayer Activity
@Override
public void onCreate(Bundle savedInstanceState)
{

476

13

Enhanced Slideshow App

13.22 ()
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

super.onCreate(savedInstanceState);
setContentView(R.layout.slideshow_player);
imageView = (ImageView) ndViewById(R.id.imageView);
videoView = (VideoView) ndViewById(R.id.videoView);
videoView.setOnCompletionListener( //
//
new OnCompletionListener()
{
@Override
public void onCompletion(MediaPlayer mp)
{
handler.post(updateSlideshow); // -
} // onCompletion
} //
); // OnCompletionListener
if (savedInstanceState == null) // Activity
{
// - Intent
slideshowName = getIntent().getStringExtra(Slideshow.NAME_EXTRA);
mediaTime = 0;
//
nextItemIndex = 0; //
} // if
else // Activity
{
// ,
//
mediaTime = savedInstanceState.getInt(MEDIA_TIME);
// ,
//
nextItemIndex = savedInstanceState.getInt(IMAGE_INDEX);

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

// -,
//
slideshowName = savedInstanceState.getString(SLIDESHOW_NAME);
// else

// SlideshowInfo -
slideshow = Slideshow.getSlideshowInfo(slideshowName);
// BitmapFactory.Options
options = new BitmapFactory.Options();
options.inSampleSize = 4; // 1/4 /
//
if (slideshow.getMusicPath() != null)
{
// MediaPlayer

13.5.
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

477

try
{
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(
this, Uri.parse(slideshow.getMusicPath()));
mediaPlayer.prepare(); //
// MediaPlayer
mediaPlayer.setLooping(true); //
//
mediaPlayer.seekTo(mediaTime); // mediaTime
} // try
catch (Exception e)
{
Log.v(TAG, e.toString());
} // catch
} // if

handler = new Handler(); //


// -
// onCreate

, updateSlideshow Runnable
updateSlideshow Runnable (. 13.23) . - , run 193208 , - .
, 197198
ImageView, videoView . 199 AsyncTask
LoadImageTask ( 213249),
. , 203204
ImageView videoView, 205
playVideo ( 272279). playVideo , Uri. 275 setVideoUri
VideoView,
. 276277 MediaController VideoView,
.
278 start
VideoView.
13.23. Runnable,

178
179
180
181
182
183

// , Runnable
// -
private Runnable updateSlideshow = new Runnable()
{
@Override
public void run()
{

478

13

Enhanced Slideshow App

13.23 ()
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

if (nextItemIndex >= slideshow.size())


{
//
if (mediaPlayer != null && mediaPlayer.isPlaying())
mediaPlayer.reset(); // - ,
// MediaPlayer
nish(); // Activity
}
// if
else
{
MediaItem item = slideshow.getMediaItemAt(nextItemIndex);
if (item.getType() == MediaItem.MediaType.IMAGE)
{
imageView.setVisibility(View.VISIBLE);// ImageView
videoView.setVisibility(View.INVISIBLE); // videoView
new LoadImageTask().execute(Uri.parse(item.getPath()));
} // if
else
{
imageView.setVisibility(View.INVISIBLE); // ImageView
videoView.setVisibility(View.VISIBLE); // videoView
playVideo(Uri.parse(item.getPath()));//
} // else

++nextItemIndex;
} // else
// run

//
class LoadImageTask extends AsyncTask<Uri, Object, Bitmap>
{
//
@Override
protected Bitmap doInBackground(Uri... params)
{
return getBitmap(params[0], getContentResolver(), options);
} // doInBackground
// , ListView
@Override
protected void onPostExecute(Bitmap result)
{
super.onPostExecute(result);
BitmapDrawable next = new BitmapDrawable(result);
next.setGravity(android.view.Gravity.CENTER);
Drawable previous = imageView.getDrawable();
// TransitionDrawable,
// Drawable

13.5.
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

479

if (previous instanceof TransitionDrawable)


previous = ((TransitionDrawable) previous).getDrawable(1);
if (previous == null)
imageView.setImageDrawable(next);
else
{
Drawable[] drawables = { previous, next };
TransitionDrawable transition =
new TransitionDrawable(drawables);
imageView.setImageDrawable(transition);
transition.startTransition(1000);
} // else

handler.postDelayed(updateSlideshow, DURATION);
} // onPostExecute
// LoadImageTask

// , Bitmap Uri
public Bitmap getBitmap(Uri uri, ContentResolver cr,
BitmapFactory.Options options)
{
Bitmap bitmap = v;
//
try
{
InputStream input = cr.openInputStream(uri);
bitmap = BitmapFactory.decodeStream(input, null, options);
} // try
catch (FileNotFoundException e)
{
Log.v(TAG, e.toString());
} // catch

return bitmap;
// getBitmap

//
private void playVideo(Uri videoUri)
{
//
videoView.setVideoURI(videoUri);
videoView.setMediaController(
new MediaController(SlideshowPlayer.this));
videoView.start(); //
} // playVideo
}; // Runnable updateSlideshow
} // SlideshowPlayer

480

13

Enhanced Slideshow App

13.6.
java.io - .

Serializable. writeObject ObjectOutputStream
graph .
readObject ObjectInputStream.

, Gallery -. Camera.
SurfaceView .
Camera ,
Camera.PictureCallback ,
. Camera
.
Slideshow Intent Activity,
Gallery.
,
MIME, .
VideoView -.
URI , VideoView, MediaController. MediaPlayer.OnCompletionListener
.
, Android 3.x. WeatherBug Weather Viewer.


Weather Viewer

14

-, JSON,
, ListFragment,
DialogFragment, ActionBar,
,
, Broadcast Intents
BroadcastReceivers






- WeatherBug
, Android 3.x JsonReader.
,
.
Android 3.x ActionBar.
,
.

.

14.1.
Weather Viewer app (. 14.1) - WeatherBug

482

14

Weather Viewer

. 14.1. Weather Viewer,


,

.
Boston.
Android,
, Android 3.x.
Android 3.x JsonReader , - WeatherBug. JSON ( JavaScript Object Notation, JavaScript).

. 14.2. Add City,


Set as preferred city

14.1.

483

Android 3.x, ,
.
, Add New City ( ), . (. 14.2),
.
(. 14.3)
(Current Conditions Five Day Forecast,
. 14.1).

. 14.3. Weather Viewer


Sudbury,

, , , , Android 3.x,
Activity. ,
Activity . ListFragment Fragment, ListView.

DialogFragment, (
). ,
Add New City , DialogFragment.
Fragment.
(. 14.4),
.
Android. Android 3.x

484

14

Weather Viewer

. 14.4. Weather Viewer,


. Weather Viewer
.

14.2. Weather Viewer



Eclipse Weather Viewer app. :
1. FileImport ()
Import ().
2. Import General ()
Existing Projects into Workspace ( ).
Next > ( >).
3. Select root directory ( )
Browse (). WeatherViewer.
4. Finish () .
- WeatherBug.
WeatherBug API - weather.weatherbug.com/desktop-weather/api.html.
API, YOUR_API_KEY
62 ReadLocationTask, 66 ReadForecastTask, 53
ReadFiveDayForecastTask. API,
, Package Explorer,
Run AsAndroid Application (  Android).



;
Five Day Forecast (
), . ,
( )

14.3.

485

. , Current Conditions ( ), .


, Add New City ( ), . Add City ( ). (ZIP code) .
, Set as preferred
city ( ). Add City ( ),
.



, ,
, Set as Preferred City (
), Delete () Cancel (). ,
. , .


, , home ,
, .
, Weather Viewer. , ,
. ,
.
, , ,
Remove (), . , ,
. Android
, .

14.3.
Android 3.x Fragment, ListFragment and DialogFragment
, Android 3.x.
Activity , .
.
,

486

14

Weather Viewer

. , .
Fragment ( android.app). . ,
ListView, ListFragment. DialogFragments.
Fragment.
Android 3.x,
Android . - http://developer.android.com/
sdk/compatibility-library.html.


Activity, Fragment . Fragment .
Fragments Activity,
. Fragments Activity Activity
WeatherViewerActivity. Activity FragmentManager ( android.app). FragmentTransaction
( android.app), FragmentManager, Activity ,
.


Activity, Fragment , XML-, .
Fragment, , ,
.
.
Configuration Activity ( android.
content.res). .

Android 3.x
Android 3.x , , ,
Android. .
,
( ) GUI.
, Fragment,
.
,
ListFragment, .

14.3.

487

, ( ).
android:showAsAction, .


ListFragment AdapterView.OnItemLongClickListener (
android.widget), , , .


, Weather
Viewer . ,
.
, ,
AppWidgetProvider
( android.appwidget), BroadcastReceiver (
android.content).

PendingIntent,
Activity
, . PendingIntent
( android.app)
.

JsonReader -
JsonReader ( android.util),
JSON, .
URL URL-, -
WeatherBug RESTful, JSON. URL- InputStream, -. JsonReader
InputStream.

Intents Receivers
Weather Viewer
, .
. , Intent .
BroadcastReceiver ( android.content) ,
.

488

14

Weather Viewer

14.4.

Weather Viewer.
strings.xml,
XML.

14.4.1. AndroidManifest.xml
14.1 AndroidManifest.xml .
Android 3.1 SDK android:minSdkVersion
uses-sdk "12" ( 5).
Android 3.1+ AVD. 67
, . receiver
( 1930) WeatherProvider (
) BroadcastReceiver, XML- , Intent WeatherProvider.
32 WeatherService
WeatherProvider, .
WeatherService .
,
.
14.1. AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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


<manifest xmlns:android=http://schemas.android.com/apk/res/android
package="com.deitel.weatherviewer" android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="12" />
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".WeatherViewerActivity"
android:label="@string/app_name">
<intent-lter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-lter>
</activity>
<receiver android:name=".WeatherProvider">
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/weather_widget_provider_info" />

14.4.
22
23
24
25
26
27
28
29
30
31
32
33
34

489

<intent-lter>
<action android:name=
"android.appwidget.action.APPWIDGET_UPDATE" />
</intent-lter>
<intent-lter>
<action android:name=
"com.deitel.weatherviewer.UPDATE_WIDGET" />
</intent-lter>
</receiver>
<service android:name=".WeatherProvider$WeatherService" />
</application>
</manifest>

14.4.2. WeatherViewerActivity,
main.xml
main.xml ( 14.2) WeatherViewerActivity. fragment CitiesFragment
LinearLayout. CitiesFragment WeatherViewerActivity.
-, ForecastFragments,
forecast_replacer FrameLayout. - Activity,
ForecastFragments. WeatherViewerActivity
ForecastFragments FragmentTransactions.
14.2. main.xml WeatherViewerActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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


<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.deitel.weatherviewer.CitiesFragment"
android:id="@+id/cities" android:layout_weight="3"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<FrameLayout android:layout_width="8dp"
android:layout_height="match_parent"
android:background="@android:color/black"/>
<FrameLayout android:id="@+id/forecast_replacer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" android:background="@android:color/white"/>
</LinearLayout>

490

14

Weather Viewer

14.4.3. arrays.xml


arrays.xml ( 14.3).
String (
). String WeatherViewerActivity
getStringArray Resources.
14.3. arrays.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

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


<resources>
<string-array name="default_city_names">
<item>Boston</item>
<item>Chicago</item>
<item>Dallas</item>
<item>Denver</item>
<item>New York</item>
<item>San Diego</item>
<item>San Francisco</item>
<item>Seattle</item>
</string-array>
<string-array name="default_city_zipcodes">
<item>02115</item>
<item>60611</item>
<item>75254</item>
<item>80202</item>
<item>10024</item>
<item>92104</item>
<item>94112</item>
<item>98101</item>
</string-array>
</resources>

14.4.4. WeatherViewerActivity,
actionmenu.xml
actionmenu.xml ( 14.4) ActionBar.
, Android.
android:showAsAction, ,
ActionBar.
ifRoom ActionBar
. always, ActionBar,
.
withText android:title String
.

14.4.

491

14.4. actionmenu.xml WeatherViewerActivity


1
2
3
4
5
6
7

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


<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/add_city_item"
android:icon="@android:drawable/ic_input_add"
android:title="@string/add_new_city"
android:showAsAction="ifRoom|withText"/>
</menu>

14.4.5.
WeatherProvider
weather_widget_provider_info.xml ( 14.5)
AppWidgetProvider WeatherViewer. minWidth
minHeight .
. Android
, - http://developer.android.com/guide/practices/
ui_guidelines/widget_design.html#sizes.
minWidth minHeight
.
initialLayout. updatePeriodMillis ,
AppWidgetProvider Intent
ACTION_APPWIDGET_UPDATE. Intent WeatherProvider
( 14.5.11) WeatherService, . , 30 . , AlarmManager.
android:resizeMode Android 3.1 ,
.
14.5. WeatherProvider
1
2
3
4
5
6
7

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


<appwidget-provider
xmlns:android=http://schemas.android.com/apk/res/android
android:minWidth="212dp" android:minHeight="148dp"
android:initialLayout="@layout/weather_app_widget_layout"
android:updatePeriodMillis="3600000"
android:resizeMode="horizontal|vertical"/>

weather_app_widget_layout.xml,
LinearLayout.
LinearLayout Google, - http://developer.android.com/guide/
practices/ui_guidelines/widget_design.html#frames.

492

14

Weather Viewer

14.5.
11 ,
14.5.114.5.11. .


WeatherViewerActivity ( 14.5.1) Activity


. Activity AddCityDialogFragment ( 14.5.3) . Activity
CitiesFragment ( 14.5.2), . WeatherViewerActivity
ForecastFragments ( 14.5.4, 14.5.5 14.5.8),
. Activity ActionBar ,
.
 ReadLocationTask ( 14.5.6)
(ZIP code) - WeatherBug.
WeatherViewerActivity, ForecastFragment
.
 SingleForecastFragment ( 14.5.5) Fragment, .
AsyncTask ReadForecastTask ( 14.5.7).
 FiveDayForecastFragment ( 14.5.8) SingleForecastFragment,
, , AsyncTask
ReadFiveDayForecastTask ( 14.5.9). DailyForecast ( 14.5.10) .
, ReadFiveDayForecast.


WeatherProvider ( 14.5.11) . , ,
WeatherViewerActivity .

14.5.1. WeatherViewerActivity
WeatherViewerActivity ( 14.6)
import ( ).
DialogFinishedListener ( 14.29),
. ( ).
14.6. package, import
WeatherViewerActivity
1
2
3
4

// WeatherViewerActivity.java
// Activity Weather Viewer.
package com.deitel.weatherviewer;

14.5.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

493

import java.util.HashMap;
import java.util.Map;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.ActionBar;
android.app.ActionBar.Tab;
android.app.ActionBar.TabListener;
android.app.Activity;
android.app.FragmentManager;
android.app.FragmentTransaction;
android.content.Intent;
android.content.SharedPreferences;
android.content.SharedPreferences.Editor;
android.os.Bundle;
android.os.Handler;
android.view.Gravity;
android.view.Menu;
android.view.MenuInater;
android.view.MenuItem;
android.widget.Toast;

import com.deitel.weatherviewer.AddCityDialogFragment.
DialogFinishedListener;
import com.deitel.weatherviewer.CitiesFragment.CitiesListChangeListener;
import com.deitel.weatherviewer.ReadLocationTask.LocationLoadedListener;
public class WeatherViewerActivity extends Activity implements
DialogFinishedListener
{
public static nal String WIDGET_UPDATE_BROADCAST_ACTION =
"com.deitel.weatherviewer.UPDATE_WIDGET";
private static nal int BROADCAST_DELAY = 10000;
private static nal int CURRENT_CONDITIONS_TAB = 0;
public static nal String PREFERRED_CITY_NAME_KEY =
"preferred_city_name";
public static nal String PREFERRED_CITY_ZIPCODE_KEY =
"preferred_city_zipcode";
public static nal String SHARED_PREFERENCES_NAME =
"weather_viewer_shared_preferences";
private static nal String CURRENT_TAB_KEY = "current_tab";
private static nal String LAST_SELECTED_KEY = "last_selected";
private int currentTab; //
private String lastSelectedCity; // ,
//
private SharedPreferences weatherSharedPreferences;
//
private Map<String, String> favoriteCitiesMap;

494

14

Weather Viewer

14.6 ()
54
55
56

private CitiesFragment listCitiesFragment;


private Handler weatherHandler;

onCreate WeatherViewerActivity
onCreate ( 14.7) WeatherViewerActivity. getFragmentManager Activity ( 66), FragmentManager, Activity.
CitiesFragment. Activity FragmentManager.

setupTabs ( 14.22)
ActionBar Activity.
14.7. onCreate WeatherViewerActivity
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

// Activity /
// xml-
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); //
// Bundle
setContentView(R.layout.main);
// main.xml
// CitiesFragment
listCitiesFragment = (CitiesFragment)
getFragmentManager().ndFragmentById(R.id.cities);
// CitiesListChangeListener
listCitiesFragment.setCitiesListChangeListener(
citiesListChangeListener);
// HashMap
//
favoriteCitiesMap = new HashMap<String, String>();
weatherHandler = new Handler();
weatherSharedPreferences = getSharedPreferences(
SHARED_PREFERENCES_NAME, MODE_PRIVATE);

setupTabs(); // ActionBar
// onCreate

14.5.

495

onSaveInstanceState onRestoreInstanceState
WeatherViewerActivity
onSaveInstanceState ( 14.8, 8492)
.
Bundle putInt Bundle.
onRestoreInstanceState ( 95104),
Activity
.
14.8. onSaveInstanceState onRestoreInstanceState
WeatherViewerActivity
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

// Activity
@Override
public void onSaveInstanceState(Bundle savedInstanceStateBundle)
{
//
savedInstanceStateBundle.putInt(CURRENT_TAB_KEY, currentTab);
savedInstanceStateBundle.putString(LAST_SELECTED_KEY,
lastSelectedCity); //
super.onSaveInstanceState(savedInstanceStateBundle);
} // onSaveInstanceState
// Activity
@Override
public void onRestoreInstanceState(Bundle savedInstanceStateBundle)
{
super.onRestoreInstanceState(savedInstanceStateBundle);

//
currentTab = savedInstanceStateBundle.getInt(CURRENT_TAB_KEY);
lastSelectedCity = savedInstanceStateBundle.getString(
LAST_SELECTED_KEY); //
// onRestoreInstanceState

onResume WeatherViewerActivity
onResume Activity ( 14.9). favoriteCitiesMap ,
SharedPreferences loadSavedCities
( 14.13). SharedPreferences , favoriteCitiesMap
. addSampleCities ( 14.18), , XML-.
ActionBar selectTab ( 124),
loadSelectedForecast ( 14.11).

496

14

Weather Viewer

14.9. onResume WeatherViewerActivity


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

// Activity
@Override
public void onResume()
{
super.onResume();
if (favoriteCitiesMap.isEmpty()) //
{
loadSavedCities(); //
} // if
//
if (favoriteCitiesMap.isEmpty())
{
addSampleCities(); //
} // if

//
getActionBar().selectTab(getActionBar().getTabAt(currentTab));
loadSelectedForecast();
// onResume

CitiesListChangeListener
CitiesListChangeListener ( 14.10)
CitiesFragment, . onSelectedCityChanged ( 133138) .
selectForecast WeatherViewerActivity ( 14.16) ,
, ForecastFragment.
,
onPreferredCityChanged ( 141146).
setPreferred WeatherViewerActivity (. 14.12)
SharedPreferences .
14.10. CitiesListChangeListener
128
129
130
131
132
133
134
135
136
137

// CitiesFragment
private CitiesListChangeListener citiesListChangeListener =
new CitiesListChangeListener()
{
//
@Override
public void onSelectedCityChanged(String cityNameString)
{
//
selectForecast(cityNameString);

14.5.
138
139
140
141
142
143
144
145
146
147
148

497

// onSelectedCityChanged

//
@Override
public void onPreferredCityChanged(String cityNameString)
{
//
// SharedPreferences
setPreferred(cityNameString);
} // onPreferredCityChanged
}; // CitiesListChangeListener

loadSelectedForecast WeatherViewerActivity
loadSelectedForecast ( 14.11) selectForecast ( 14.16)
,
CitiesFragment. , .
14.11. loadSelectedForecast WeatherViewerActivity
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166

//
private void loadSelectedForecast()
{
//
if (lastSelectedCity != null)
{
selectForecast(lastSelectedCity); //
} // if
else
{
//
String cityNameString = weatherSharedPreferences.getString(
PREFERRED_CITY_NAME_KEY, getResources().getString(
R.string.default_zipcode));
selectForecast(cityNameString); //
//
} // else
} // loadSelectedForecast

setPreferred WeatherViewerActivity
setPreferred ( 14.12) , , SharedPreferences.
, Editor edit
SharedPreferences. putString Editor.
apply SharedPreferences.
loadSelectedForecast (. 14.11)

498

14

Weather Viewer

. Intent WIDGET_UPDATE_
BROADCAST_ACTION, sendBroadcast
Activity. ,
WeatherProvider ( 14.5.11)
, .
-, WeatherBug,
. , Handler,
. -
.
14.12. setPreferred WeatherViewerActivity
167
168
169
170
171
172
173
174
175
176
177
178
179

//
public void setPreferred(String cityNameString)
{
//
String cityZipcodeString = favoriteCitiesMap.get(cityNameString);
Editor preferredCityEditor = weatherSharedPreferences.edit();
preferredCityEditor.putString(PREFERRED_CITY_NAME_KEY,
cityNameString);
preferredCityEditor.putString(PREFERRED_CITY_ZIPCODE_KEY,
cityZipcodeString);
preferredCityEditor.apply(); //
lastSelectedCity = null; //
loadSelectedForecast(); //
//

180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

//
//
nal Intent updateWidgetIntent = new Intent(
WIDGET_UPDATE_BROADCAST_ACTION);

//
weatherHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
sendBroadcast(updateWidgetIntent); //
}
}, BROADCAST_DELAY);
// setPreferred

loadSavedCities WeatherViewerActivity
loadSavedCities ( 14.13)
SharedPreferences . ,
, getAll SharedPreferences.

14.5.

499

addCity
WeatherViewerActivity ( 14.14).
14.13. loadSavedCities WeatherViewerActivity
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211

//
// SharedPreferences
private void loadSavedCities()
{
Map<String, ?> citiesMap = weatherSharedPreferences.getAll();

for (String cityString : citiesMap.keySet())


{
//
if (!(cityString.equals(PREFERRED_CITY_NAME_KEY) ||
cityString.equals(PREFERRED_CITY_ZIPCODE_KEY)))
{
addCity(cityString, (String) citiesMap.get(cityString),
false);
} // if
} // for
// loadSavedCities

addSampleCities WeatherViewerActivity
addSampleCities (. 14.14)
arrays.xml. getStringArray
Resource ( 216217 220221) ,
. addCity ( 14.15).
setPreferred WeatherViewerActivity
(. 14.12).
14.14. addSampleCities WeatherViewerActivity
212
213
214
215
216
217
218
219
220
221
222
223
224
225

//
private void addSampleCities()
{
//
String[] sampleCityNamesArray = getResources().getStringArray(
R.array.default_city_names);
//
String[] sampleCityZipcodesArray = getResources().getStringArray(
R.array.default_city_zipcodes);
//
for (int i = 0; i < sampleCityNamesArray.length; i++)
{

500

14

Weather Viewer

14.14 ()
226
227
228
229
230
231
232
233
234
235
236
237

//
//
if (i == 0)
{
setPreferred(sampleCityNamesArray[i]);
} // if
//
addCity(sampleCityNamesArray[i], sampleCityZipcodesArray[i],
false);
} // for
} // addSampleCities

addCity WeatherViewerActivity
CitiesFragment ( 14.5.2)
addCity ( 14.15). -
favoriteCitiesMap, addCity
CitiesFragment. SharedPreferences,
apply .
14.15. addCity WeatherViewerActivity
238
239
240
241
242
243
244
245
246
247

// CitiesFragment ListFragment
public void addCity(String city, String zipcode, boolean select)
{
favoriteCitiesMap.put(city, zipcode);
//
// HashMap of cities
listCitiesFragment.addCity(city, select); //
// Fragment
Editor preferenceEditor = weatherSharedPreferences.edit();
preferenceEditor.putString(city, zipcode);
preferenceEditor.apply();
} // addCity

selectForecast WeatherViewerActivity
selectForecast ( 14.16) . findFragmentById Fragment , . ID
FrameLayout Activity.
null. FragmentManager Fragment, , FrameLayout Fragment
FragmentTransaction. ActionBar
Current Conditions, ForecastFragment ( 270271). Five
Day Forecast, FiveDayForecastFragment (

14.5.

501

276277). beginTransaction FragmentManager


FragmentTransaction ( 281282). FragmentTransaction
, Fragments . Fragment,
Activity, Fragment. setTransition TRANSIT_FRAGMENT_FADE FragmentTransaction (
285286),
. replace ForecastFragment
( 290291), ID Fragment. commit FragmentTransaction ( 293)
.
14.16. selectForecast WeatherViewerActivity
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

//
public void selectForecast(String name)
{
lastSelectedCity = name; //
String zipcodeString = favoriteCitiesMap.get(name);
if (zipcodeString == null) //
{
return; //
} // if
// ForecastFragment
ForecastFragment currentForecastFragment = (ForecastFragment)
getFragmentManager().ndFragmentById(R.id.forecast_replacer);
if (currentForecastFragment == null ||
!(currentForecastFragment.getZipcode().equals(zipcodeString) &&
correctTab(currentForecastFragment)))
{
// "Current Conditions"
if (currentTab == CURRENT_CONDITIONS_TAB)
{
// ForecastFragment
currentForecastFragment = SingleForecastFragment.newInstance(
zipcodeString);
} // if
else
{
// ForecastFragment
currentForecastFragment = FiveDayForecastFragment.newInstance(
zipcodeString);
} // else
// FragmentTransaction
FragmentTransaction forecastFragmentTransaction =
getFragmentManager().beginTransaction();


502

14

Weather Viewer

14.16 ()
284
285
286
287
288
289
290
291
292
293
294
295
296

//
forecastFragmentTransaction.setTransition(
FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// Fragment ( View) id
//
forecastFragmentTransaction.replace(R.id.forecast_replacer,
currentForecastFragment);

forecastFragmentTransaction.commit(); //
} // if
// selectForecast

correctTab selectTab WeatherViewerActivity


correctTab ( 14.17, 298313) true, ForecastFragment
( , Current Conditions SingleForecastFragment, Five Day Forecast
FiveDayForecastFragment).
selectForecast
ForecastFragment. selectTab ( 316320) . currentTab, loadSelectedForecast (. 14.11).
14.17. correctTab selectTab WeatherViewerActivity
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317

// ForecastFragment ?
private boolean correctTab(ForecastFragment forecastFragment)
{
// "Current Conditions"
if (currentTab == CURRENT_CONDITIONS_TAB)
{
// true, ForecastFragment
// SingleForecastFragment
return (forecastFragment instanceof SingleForecastFragment);
} // if
else // "Five Day Forecast"
{
// true, ForecastFragment
// FiveDayForecastFragment
return (forecastFragment instanceof FiveDayForecastFragment);
} // else
} // correctTab
//
private void selectTab(int position)
{

14.5.
318
319
320
321

503

currentTab = position; //
loadSelectedForecast();
// selectTab

onCreateOptionsMenu
onOptionsItemSelected Activity
onCreateOptionsMenu ( 14.18, 323332)
Add New City ActionBar. getMenuInflator Activity MenuInflator. ,
actionmenu.xml,
Menu. onOptionsItemSelected ( 335346) ,
Add New City ActionBar. ,
MenuItem ID , showAddCityDialog
( 14.19) AddCityDialogFragment ( 14.5.3). true, ,
.
14.18. onCreateOptionsMenu onOptionsItemSelected
Activity
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

// Activities
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
super.onCreateOptionsMenu(menu);
MenuInater inater = getMenuInater();// MenuInator

// , actionmenu.xml
inater.inate(R.menu.actionmenu, menu);
return true; // true
// onCreateOptionsMenu

//
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// "Add City"
If (item.getItemId() == R.id.add_city_item)
{
showAddCityDialog(); //
//
return true; // true
} // if

return false; //
// onOptionsItemSelected

504

14

Weather Viewer

showAddCityDialog onDialogFinished
WeatherViewerActivity
showAddCityDialog (. 14.19, 349364)
DialogFragment, .
AddCityDialogFragment FragmentManager
Activity ( 356). FragmentTransaction beginTransaction FragmentManager. FragmentTransaction
show DialogFragment Activity.
FragmentDialog View Activity (
). onDialogFinished ( 367372)
AddCityDialog. zipcodeString ,
. preferred boolean true,
Set as preferred city.
getCityNameFromZipcode ( 14.20).
14.19. showAddCityDialog onDialogFinished
WeatherViewerActivity
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373

// FragmentDialog,
//
private void showAddCityDialog()
{
// AddCityDialogFragment
AddCityDialogFragment newAddCityDialogFragment =
new AddCityDialogFragment();
// FragmentManager
FragmentManager thisFragmentManager = getFragmentManager();
// FragmentTransaction
FragmentTransaction addCityFragmentTransition =
thisFragmentManager.beginTransaction();

// DialogFragment
newAddCityDialogFragment.show(addCityFragmentTransition, "");
// showAddCityDialog

// , FragmentDialog
@Override
public void onDialogFinished(String zipcodeString, boolean preferred)
{
//
getCityNameFromZipcode(zipcodeString, preferred);
} // onDialogFinished

14.5.

505

getCityNameFromZipcode
getCityNameFromZipcode (. 14.20) ReadLocationTask
( 14.5.6) .
, AsyncTask
Toast, ,
.
14.20. getCityNameFromZipcode WeatherViewerActivity
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396

//
private void getCityNameFromZipcode(String zipcodeString,
boolean preferred)
{
//
if (favoriteCitiesMap.containsValue(zipcodeString))
{
// Toast,
Toast errorToast = Toast.makeText(WeatherViewerActivity.this,
WeatherViewerActivity.this.getResources().getString(
R.string.duplicate_zipcode_error), Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0);
errorToast.show(); // Toast
} // if
else
{
//
new ReadLocationTask(zipcodeString, this,
new CityNameLocationLoadedListener(zipcodeString, preferred)).
execute();
} // else
} // getCityNameFromZipcode

LocationLoadedListener
CityNameLocationLoadedListener ( 14.21)
ReadLocationTask. LocationLoadedListener
, ,
preferred, boolean.
,
addCity WeatherViewerActivity.
. , setPreferred.
14.21. LocationLoadedListener
397
398
399

// ,
private class CityNameLocationLoadedListener implements
LocationLoadedListener

506

14

Weather Viewer

14.21 ()
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419

{
private String zipcodeString; //
private boolean preferred;
// CityNameLocationLoadedListener
public CityNameLocationLoadedListener(String zipcodeString,
boolean preferred)
{
this.zipcodeString = zipcodeString;
this.preferred = preferred;
} // CityNameLocationLoadedListener
@Override
public void onLocationLoaded(String cityString, String stateString,
String countryString)
{
//
if (cityString != null)
{
addCity(cityString, zipcodeString, !preferred);
//

420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439

if (preferred) //
//
{
// SharedPreferences
setPreferred(cityString);
} // if
} // if
else
{
// ,
//
Toast zipcodeToast = Toast.makeText(WeatherViewerActivity.this,
WeatherViewerActivity.this.getResources().getString(
R.string.invalid_zipcode_error), Toast.LENGTH_LONG);
zipcodeToast.setGravity(Gravity.CENTER, 0, 0);
zipcodeToast.show(); // Toast
} // else
} // onLocationLoaded
// CityNameLocationLoadedListener

setupTabs WeatherViewerActivity
ActionBar
setupTabs ( 14.22). getActionBar Activity
ActionBar. ActionBar
Android
,

14.5.

507

. NAVIGATION_MODE_TABS
ActionBar setNavigationMode,
( Tabs). newTab
ActionBar Tab ( 449 460), .
Tab TabListener
(weatherTabListener, 14.19). 457 464
Tabs ActionBar addTab ActionBar.
, Tabs, Current Conditions Five Day Forecast.
14.22. setupTabs WeatherViewerActivity
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469

// ActionBar
private void setupTabs()
{
ActionBar weatherActionBar = getActionBar(); // ActionBar
// ActionBar,
weatherActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// "Current Conditions"
Tab currentConditionsTab = weatherActionBar.newTab();
// Tab
currentConditionsTab.setText(getResources().getString(
R.string.current_conditions));
// Tab
currentConditionsTab.setTabListener(weatherTabListener);
weatherActionBar.addTab(currentConditionsTab); //
// "Five Day Forecast"
Tab veDayForecastTab = weatherActionBar.newTab();
veDayForecastTab.setText(getResources().getString(
R.string.ve_day_forecast));
veDayForecastTab.setTabListener(weatherTabListener);
weatherActionBar.addTab(veDayForecastTab);

// "Current Conditions"
currentTab = CURRENT_CONDITIONS_TAB;
// setupTabs

TabListener
14.23 TabListener,
, , 14.21. onTabSelected ( 480485) selectTab (.
14.17) Tab
.

508

14

Weather Viewer

14.23. TabListener
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493

// , Tabs ActionBar
TabListener weatherTabListener = new TabListener()
{
// ,
@Override
public void onTabReselected(Tab arg0, FragmentTransaction arg1)
{
} // onTabReselected
// , Tab
@Override
public void onTabSelected(Tab tab, FragmentTransaction arg1)
{
// , Tab
selectTab(tab.getPosition());
} // onTabSelected
// ,
@Override
public void onTabUnselected(Tab arg0, FragmentTransaction arg1)
{
} // onTabSelected
}; // WeatherTabListener
} // WeatherViewerActivity

14.5.2. CitiesFragment
CitiesFragment ListFragment,
. View WeatherViewerActivity CitiesFragment,
Activity .

package, import CitiesFragment,


CitiesListChangeListener
14.24 CitiesFragment. Fragment
Activity,
CitiesListChangeListener ( 4047; 14.10). onSelectedCityChanged ,
. onPreferredCityChanged
.
14.24. package, import CitiesFragment,
CitiesListChangeListener
1
2
3

// CitiesFragment.java
// Fragment, .
package com.deitel.weatherviewer;

14.5.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

509

import java.util.ArrayList;
import java.util.List;
import
Import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.AlertDialog;
android.app.ListFragment;
android.content.Context;
android.content.DialogInterface;
android.content.SharedPreferences;
android.content.SharedPreferences.Editor;
android.content.res.Resources;
android.graphics.Color;
android.os.Bundle;
android.view.Gravity;
android.view.View;
android.view.ViewGroup;
android.widget.AdapterView;
android.widget.AdapterView.OnItemLongClickListener;
android.widget.ArrayAdapter;
android.widget.ListView;
android.widget.TextView;
android.widget.Toast;

public class CitiesFragment extends ListFragment


{
private int currentCityIndex; //
// , Bundle
private static nal String CURRENT_CITY_KEY = "current_city";
public ArrayList<String> citiesArrayList; //
private CitiesListChangeListener citiesListChangeListener;
private ArrayAdapter<String> citiesArrayAdapter;
// ,
//
public interface CitiesListChangeListener
{
//
public void onSelectedCityChanged(String cityNameString);

//
public void onPreferredCityChanged(String cityNameString);
// CitiesListChangeListener

onActivityCreated setCitiesListChangeListener
CitiesFragment
onActivityCreated ( 14.25, 5078) ListView ListFragment. ,

510

14

Weather Viewer

Bundle null. ,
getInt Bundle.
. ,
, list city_list_item.xml
ArrayList, ListAdapter, CitiesArrayAdapter (
14.26). ListView
OnLongItemClickListener, .
setCitiesListChangeListener ( 8185) Activity CitiesListChangeListener CitiesFragment. CitiesFragment WeatherViewerActivity.
14.25. onActivityCreated setCitiesListChangeListener
CitiesFragment
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

// Activity
@Override
public void onActivityCreated(Bundle savedInstanceStateBundle)
{
super.onActivityCreated(savedInstanceStateBundle);
// Bundle
if (savedInstanceStateBundle != null)
{
// Bundle
currentCityIndex = savedInstanceStateBundle.getInt(
CURRENT_CITY_KEY);
} // if
// ArrayList,
//
citiesArrayList = new ArrayList<String>();
// ListView Fragment
setListAdapter(new CitiesArrayAdapter<String>(getActivity(),
R.layout.city_list_item, citiesArrayList));
ListView thisListView = getListView(); // ListView
// Fragment
citiesArrayAdapter = (ArrayAdapter<String>)getListAdapter();
//
thisListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
thisListView.setBackgroundColor(Color.WHITE); //
thisListView.setOnItemLongClickListener(
citiesOnItemLongClickListener);
} // onActivityCreated
// CitiesListChangeListener
public void setCitiesListChangeListener(

14.5.
82
83
84
85
86

511

CitiesListChangeListener listener)
{
citiesListChangeListener = listener;
// setCitiesChangeListener

CitiesArrayAdapter CitiesFragment
CitiesArrayAdapter ( 14.26) ArrayAdapter,
. . getView ( 101122),
ListView Fragment,
View. getView , View (
).
isPreferredCity ( 125136). , setCompoundDrawables TextView.
,
. isPreferredCity true,
String . Context
Activity , String .
14.26. CitiesArrayAdapter CitiesFragment
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

// ArrayAdapter ListView CitiesFragment


private class CitiesArrayAdapter<T> extends ArrayAdapter<String>
{
private Context context; // Context Activity
// Fragment
// CitiesArrayAdapter
public CitiesArrayAdapter(Context context, int textViewResourceId,
List<String> objects)
{
super(context, textViewResourceId, objects);
this.context = context;
} // CitiesArrayAdapter
// ListView
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TextView,
// getView ArrayAdapter
TextView listItemTextView = (TextView)
super.getView(position, convertView, parent);
//
if (isPreferredCity(listItemTextView.getText().toString()))


512

14

Weather Viewer

14.24 ()
110
111

{
//
// TextView
listItemTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
android.R.drawable.btn_star_big_on, 0);
} // if
else
{
//
// TextView
listItemTextView.setCompoundDrawablesWithIntrinsicBounds(0, 0,
0, 0);
} // else
return listItemTextView;
} //

112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

// ?
private boolean isPreferredCity(String cityString)
{
// SharedPreferences
SharedPreferences preferredCitySharedPreferences =
context.getSharedPreferences(
WeatherViewerActivity.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);

// true,
//
return cityString.equals(preferredCitySharedPreferences.getString(
WeatherViewerActivity.PREFERRED_CITY_NAME_KEY, null));
} // isPreferredCity
// CitiesArrayAdapter

OnItemLongClickListener
citiesOnItemLongClickListener ( 14.27) ListView Fragment.
AlertDialog, . setPositiveButton AlertDialog.Builder
Set Preferred. onClick OnClickListener
Button ( 172177)
onPreferredCityChanged CitiesListChangeListener. notifyDataSetChanged
ArrayAdapter ListView.
Button Delete, . onClick ( 185233) ,
, getCount
ArrayAdapter. , , ,
Toast. ,
remove ArrayAdapter.

14.5.

513

. , .
, WeatherViewerActivity,
onSelectedCityChanged CitiesListChangeListener.
14.27. OnItemLongClickListener
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180

// ,
// ListView
private OnItemLongClickListener citiesOnItemLongClickListener =
new OnItemLongClickListener()
{
// ListView
@Override
public boolean onItemLongClick(AdapterView<?> listView, View view,
int arg2, long arg3)
{
// View
nal Context context = view.getContext();
// xml
nal Resources resources = context.getResources();
//
nal String cityNameString =
((TextView) view).getText().toString();
// AlertDialog
AlertDialog.Builder builder = new AlertDialog.Builder(context);
// AlertDialog
builder.setMessage(resources.getString(
R.string.city_dialog_message_prex) + cityNameString +
resources.getString(R.string.city_dialog_message_postx));
// "+" AlertDialog
builder.setPositiveButton(resources.getString(
R.string.city_dialog_preferred),
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
citiesListChangeListener.onPreferredCityChanged(
cityNameString);
citiesArrayAdapter.notifyDataSetChanged();
} // onClick
});// DialogInterface.OnClickListener
// AlertDialog
builder.setNeutralButton(resources.getString(

514

14

Weather Viewer

14.27 ()
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

R.string.city_dialog_delete),
new DialogInterface.OnClickListener()
{
// "Delete"
public void onClick(DialogInterface dialog, int id)
{
//
if (citiesArrayAdapter.getCount() == 1)
{
//
//
Toast lastCityToast =
Toast.makeText(context, resources.getString(
R.string.last_city_warning), Toast.LENGTH_LONG);
lastCityToast.setGravity(Gravity.CENTER, 0, 0);
lastCityToast.show(); // Toast
return; //
} // if
//
citiesArrayAdapter.remove(cityNameString);
//
SharedPreferences sharedPreferences =
context.getSharedPreferences(
WeatherViewerActivity.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
//
// SharedPreferences
Editor preferencesEditor = sharedPreferences.edit();
preferencesEditor.remove(cityNameString);
preferencesEditor.apply();
//
String preferredCityString =
sharedPreferences.getString(
WeatherViewerActivity.PREFERRED_CITY_NAME_KEY,
resources.getString(R.string.default_zipcode));
//
if (cityNameString.equals(preferredCityString))
{
//
citiesListChangeListener.onPreferredCityChanged(
citiesArrayList.get(0));
} // if
else if (cityNameString.equals(citiesArrayList.get(
currentCityIndex)))
{

14.5.
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

515

//
citiesListChangeListener.onSelectedCityChanged(
preferredCityString);
} // else if
} // onClick
});// OnClickListener
// AlertDialog
builder.setNegativeButton(resources.getString(
R.string.city_dialog_cancel),
new DialogInterface.OnClickListener()
{
// "No"
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel(); // AlertDialog
} // onClick
});// OnClickListener
builder.create().show(); // display the AlertDialog
return true;
} // citiesOnItemLongClickListener
}; // OnItemLongClickListener

onSaveInstanceState, addCity
onListItemClick CitiesFragment
onSaveInstanceState ( 14.28) CitiesFragment. addCity ( 263273) WeatherViewerActivity
ListView.
String ArrayAdapter, Adapter .
select, boolean, true,
onSelectedCityChanged CitiesListChangeListener,
WeatherViewerActivity .
onListItemClick ( 276283)
ListView. onSelectedCityChanged
CitiesListChangeListener, WeatherViewerActivity
.
currentCityIndex.
14.28. onSaveInstanceState, addCity onListItemClick
CitiesFragment
252
253
254
255
256
257

// Fragment
@Override
public void onSaveInstanceState(Bundle outStateBundle)
{
super.onSaveInstanceState(outStateBundle);


516

14

Weather Viewer

14.28 ()
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284

// Bundle
outStateBundle.putInt(CURRENT_CITY_KEY, currentCityIndex);
// onSaveInstanceState

//
public void addCity(String cityNameString, boolean select)
{
citiesArrayAdapter.add(cityNameString);
citiesArrayAdapter.sort(String.CASE_INSENSITIVE_ORDER);

if (select) //
{
// CitiesListChangeListener
citiesListChangeListener.onSelectedCityChanged(cityNameString);
} // if
// addCity

// ListView
@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
// Activity ForecastFragment
citiesListChangeListener.onSelectedCityChanged(((TextView)v).
getText().toString());
currentCityIndex = position; //
//
} // onListItemClick
} // CitiesFragment

14.5.3. AddCityDialogFragment
AddCityDialogFragment (. 14.29)

. DialogFinishedListener ( 1923)
WeatherViewerActivity ( 14.19), Activity , AddCityDialogFragment. Fragment
Activity. DialogFragment EditText,
, CheckBox,
.
14.29. AddCityDialogFragment
1
2
3
4
5

// AddCityDialogFragment.java
// DialogFragment,
// .
package com.deitel.weatherviewer;
import android.app.DialogFragment;

14.5.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

import
import
import
import
import
import
import
import

517

android.os.Bundle;
android.view.LayoutInater;
android.view.View;
android.view.View.OnClickListener;
android.view.ViewGroup;
android.widget.Button;
android.widget.CheckBox;
android.widget.EditText;

public class AddCityDialogFragment extends DialogFragment


implements OnClickListener
{
// , AddCityDialog
public interface DialogFinishedListener
{
// , AddCityDialog
void onDialogFinished(String zipcodeString, boolean preferred);
} // DialogFinishedListener
EditText addCityEditText; // EditText DialogFragment
CheckBox addCityCheckBox; // CheckBox DialogFragment
// DialogFragment
@Override
public void onCreate(Bundle bundle)
{
super.onCreate(bundle);

// back
this.setCancelable(true);
// onCreate

// "" DialogFragment
@Override
public View onCreateView(LayoutInater inater, ViewGroup container,
Bundle argumentsBundle)
{
// "" , add_city_dialog.xml
View rootView = inater.inate(R.layout.add_city_dialog, container,
false);
// EditText
addCityEditText = (EditText) rootView.ndViewById(
R.id.add_city_edit_text);
// CheckBox
addCityCheckBox = (CheckBox) rootView.ndViewById(
R.id.add_city_checkbox);
if (argumentsBundle != null) // Bundle


518

14

Weather Viewer

14.29 ()
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

addCityEditText.setText(argumentsBundle.getString(
getResources().getString(
R.string.add_city_dialog_bundle_key)));
// if

// DialogFragment
getDialog().setTitle(R.string.add_city_dialog_title);

// "+"
Button okButton = (Button) rootView.ndViewById(
R.id.add_city_button);
okButton.setOnClickListener(this);
return rootView; // View Fragment
// onCreateView

// DialogFragment
@Override
public void onSaveInstanceState(Bundle argumentsBundle)
{
// EditText Bundle
argumentsBundle.putCharSequence(getResources().getString(
R.string.add_city_dialog_bundle_key),
addCityEditText.getText().toString());
super.onSaveInstanceState(argumentsBundle);
} // onSaveInstanceState

// Add City Button


@Override
public void onClick(View clickedView)
{
if (clickedView.getId() == R.id.add_city_button)
{
DialogFinishedListener listener =
(DialogFinishedListener) getActivity();
listener.onDialogFinished(addCityEditText.getText().toString(),
addCityCheckBox.isChecked() );
dismiss(); // DialogFragment
} // if
} // onClick
// AddCityDialogFragment

onCreate
onCreate ( 2936) setCancelable
DialogFragment.
DialogFragment, back.

14.5.

519

onCreateView
DialogFragment onCreateView
( 3970). 4453 ,
add_city_dialog.xml, EditText Checkbox
DialogFragment.
, argumentsBundle , EditText. DialogFragment
EditText.

onCreate
onSaveInstanceState ( 7381) EditText,
Fragment .
Bundle, putCharSequence argumentBundle.

onCreate
onClick ( 8495) AddCityDialogFragment Button Fragment.
onDialogFinished DialogFinishedListener EditText CheckBox. Fragment
Activity dismiss DialogFragment.

14.5.4. ForecastFragment
ForecastFragment ( 14.30) Fragment
getZipcode,
(ZIP code) String. WeatherViewerActivity
ForecastFragment, SingleForecastFragment ( 14.5.5)
FiveDayForecastFragment ( 14.5.8), . WeatherViewerActivity
getZipcode ,
ForecastFragment.
14.30. ForecastFragment
1
2
3
4
5
6
7
8
9
10

// ForecastFragment.java
// , Fragment
// .
package com.deitel.weatherviewer;
import android.app.Fragment;
public abstract class ForecastFragment extends Fragment
{
public abstract String getZipcode();
} // ForecastFragment

520

14

Weather Viewer

14.5.5. SingleForecastFragment
SingleForecastFragment Fragment
.

package, import
SingleForecastFragment
14.31 SingleForecastFragment
. 2530 String,
SingleForecastFragment
.
14.31. package, import
SingleForecastFragment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

// SingleForecastFragment.java
// .
package com.deitel.weatherviewer;
import
import
import
import
import
import
import
import
import
import
import

android.content.Context;
android.content.res.Resources;
android.graphics.Bitmap;
android.os.Bundle;
android.view.Gravity;
android.view.LayoutInater;
android.view.View;
android.view.ViewGroup;
android.widget.ImageView;
android.widget.TextView;
android.widget.Toast;

import com.deitel.weatherviewer.ReadForecastTask.ForecastListener;
import com.deitel.weatherviewer.ReadLocationTask.LocationLoadedListener;
public class SingleForecastFragment extends ForecastFragment
{
private String zipcodeString; //
//
private static nal
private static nal
private static nal
private static nal
private static nal
private static nal

Fragment
String LOCATION_KEY = "location";
String TEMPERATURE_KEY = "temperature";
String FEELS_LIKE_KEY = "feels_like";
String HUMIDITY_KEY = "humidity";
String PRECIPITATION_KEY = "chance_precipitation";
String IMAGE_KEY = "image";

// Bundle
private static nal String ZIP_CODE_KEY = "id_key";
private View forecastView; //

14.5.
36
37
38
39
40
41
42
43
44
45
46
47
48

private TextView temperatureTextView; //


//
private TextView feelsLikeTextView;
//
//
private TextView humidityTextView;
//

521


" "

private TextView locationTextView;


//
private TextView chanceOfPrecipitationTextView;
private ImageView conditionImageView; //
private TextView loadingTextView;
private Context context;
private Bitmap conditionBitmap;

newInstance SingleForecastFragment
newInstance SingleForecastFragment
Fragment .
( 14.32, 5064) SingleForecastFragment,
Bundle,
setArguments Fragment. onCreate Fragment. newInstance
Bundle ( 6772),
Bundle newInstance, String.
14.32. newInstance SingleForecastFragment
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

// ForecastFragment
public static SingleForecastFragment newInstance(String zipcodeString)
{
// ForecastFragment
SingleForecastFragment newForecastFragment =
new SingleForecastFragment();
Bundle argumentsBundle = new Bundle(); // Bundle
// Bundle
argumentsBundle.putString(ZIP_CODE_KEY, zipcodeString);
// Fragement
newForecastFragment.setArguments(argumentsBundle);
return newForecastFragment; // return the completed ForecastFragment
} // newInstance
// ForecastFragment Bundle
public static SingleForecastFragment newInstance(Bundle argumentsBundle)


522

14

Weather Viewer

14.32 ()
68
69
70
71
72
73

// Bundle
String zipcodeString = argumentsBundle.getString(ZIP_CODE_KEY);
return newInstance(zipcodeString);// ForecastFragment
// newInstance

onCreate, onSaveInstanceState getZipcode


SingleForecastFragment
onCreate ( 14.33, 7582) ,
String, Bundle
zipcodeString SingleForecastFragment.
onSaveInstanceState ( 85102) , Fragment.
AsyncTask .
, TextView, Bundle
putString Bundle. Bitmap,
, putParcelable
Bundle. getZipcode ForecastFragment ( 105108)
String, ,
SingleForecastFragment.
14.33. onCreate, onSaveInstanceState getZipcode
SingleForecastFragment
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

// Fragment Bundle
@Override
public void onCreate(Bundle argumentsBundle)
{
super.onCreate(argumentsBundle);

// Bundle
this.zipcodeString = getArguments().getString(ZIP_CODE_KEY);
// onCreate

// Fragment
@Override
public void onSaveInstanceState(Bundle savedInstanceStateBundle)
{
super.onSaveInstanceState(savedInstanceStateBundle);
// View Bundle
savedInstanceStateBundle.putString(LOCATION_KEY,
locationTextView.getText().toString());
savedInstanceStateBundle.putString(TEMPERATURE_KEY,
temperatureTextView.getText().toString());
savedInstanceStateBundle.putString(FEELS_LIKE_KEY,

14.5.
96
97
98
99
100
101
102
103
104
105
106
107
108
109

523

feelsLikeTextView.getText().toString());
savedInstanceStateBundle.putString(HUMIDITY_KEY,
humidityTextView.getText().toString());
savedInstanceStateBundle.putString(PRECIPITATION_KEY,
chanceOfPrecipitationTextView.getText().toString());
savedInstanceStateBundle.putParcelable(IMAGE_KEY, conditionBitmap);
} // onSaveInstanceState
// ,
// Fragment
public String getZipcode()
{
return zipcodeString; // String,
//
} // getZIP code

onCreateView
onCreateView ( 14.34)
View ForecastFragment. , forecast_fragment_layout.
xml, LayoutInflator. inflate LayoutInflator
null . ViewGroup, View.
, View Fragment
ViewGroup onCreateView. , Fragment.
findViewById View View
Fragment, View .
14.34. onCreateView
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

// "" Fragement xml-


@Override
public View onCreateView(LayoutInater inater, ViewGroup container,
Bundle savedInstanceState)
{
// LayoutInator ""
// , forecast_fragment_layout.xml
View rootView = inater.inate(R.layout.forecast_fragment_layout,
null);
// TextView Fragment
forecastView = rootView.ndViewById(R.id.forecast_layout);
loadingTextView = (TextView) rootView.ndViewById(
R.id.loading_message);
locationTextView = (TextView) rootView.ndViewById(R.id.location);
temperatureTextView = (TextView) rootView.ndViewById(
R.id.temperature);
feelsLikeTextView = (TextView) rootView.ndViewById(

524

14

Weather Viewer

14.34 ()
128
129
130
131
132
133
134
135
136
137
138
139
140

R.id.feels_like);
humidityTextView = (TextView) rootView.ndViewById(
R.id.humidity);
chanceOfPrecipitationTextView = (TextView) rootView.ndViewById(
R.id.chance_of_precipitation);
conditionImageView = (ImageView) rootView.ndViewById(
R.id.forecast_image);
context = rootView.getContext(); // Context

return rootView; // "" View


// onCreateView

onActivityCreated
onActivityCreated ( 14.35) Activity View Fragment. ,
Bundle. ,
View, , .
ReadLocationTask,
Fragment. Bundle null, , Bundle, onSaveInstanceState (. 14.33).
View Fragment.
14.35. onActivityCreated
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

// Activity
@Override
public void onActivityCreated(Bundle savedInstanceStateBundle)
{
super.onActivityCreated(savedInstanceStateBundle);
//
if (savedInstanceStateBundle == null)
{
//
forecastView.setVisibility(View.GONE);
loadingTextView.setVisibility(View.VISIBLE);
//
new ReadLocationTask(zipcodeString, context,
new WeatherLocationLoadedListener(zipcodeString)).execute();
} // if
else
{
// Bundle
// View Fragment
conditionImageView.setImageBitmap(

14.5.
163
164
165
166
167
168
169
170
171
172
173
174
175
176

525

(Bitmap) savedInstanceStateBundle.getParcelable(IMAGE_KEY));
locationTextView.setText(savedInstanceStateBundle.getString(
LOCATION_KEY));
temperatureTextView.setText(savedInstanceStateBundle.getString(
TEMPERATURE_KEY));
feelsLikeTextView.setText(savedInstanceStateBundle.getString(
FEELS_LIKE_KEY));
humidityTextView.setText(savedInstanceStateBundle.getString(
HUMIDITY_KEY));
chanceOfPrecipitationTextView.setText(
savedInstanceStateBundle.getString(PRECIPITATION_KEY));
} // else
// onActivityCreated

ForecastListener
weatherForecastListener ( 14.36) ReadForecastTask ( 14.5.7). ,
Fragment WeatherViewerActivity. isAdded
Fragment. ,
Fragment ReadForecastTask (
- ). ,
View Fragment.
14.36. ForecastListener
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

// AsyncTask
ForecastListener weatherForecastListener = new ForecastListener()
{
//
@Override
public void onForecastLoaded(Bitmap imageBitmap,
String temperatureString, String feelsLikeString,
String humidityString, String precipitationString)
{
// Fragment
if (!SingleForecastFragment.this.isAdded())
{
return; //
} // if
else if (imageBitmap == null)
{
Toast errorToast = Toast.makeText(context,
context.getResources().getString(
R.string.null_data_toast), Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0);
errorToast.show(); // show the Toast
return; //
} // if


526

14

Weather Viewer

14.36 ()
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217

Resources resources = SingleForecastFragment.this.getResources();


//
conditionImageView.setImageBitmap(imageBitmap);
conditionBitmap = imageBitmap;
temperatureTextView.setText(temperatureString + (char)0x00B0 +
resources.getString(R.string.temperature_unit));
feelsLikeTextView.setText(feelsLikeString + (char)0x00B0 +
resources.getString(R.string.temperature_unit));
humidityTextView.setText(humidityString + (char)0x0025);
chanceOfPrecipitationTextView.setText(precipitationString +
(char)0x0025);
loadingTextView.setVisibility(View.GONE); //
//
forecastView.setVisibility(View.VISIBLE); //
} // onForecastLoaded
}; // weatherForecastListener

LocationLoadedListener
WeatherLocationLoadedListener ( 14.37) ReadLocationTask ( 14.5.6) String, locationTextView.
ReadForecastTask, .
14.37. LocationLoadedListener
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

//
private class WeatherLocationLoadedListener implements
LocationLoadedListener
{
private String zipcodeString; //
// WeatherLocationLoadedListener
public WeatherLocationLoadedListener(String zipcodeString)
{
this.zipcodeString = zipcodeString;
} // WeatherLocationLoadedListener
//
@Override
public void onLocationLoaded(String cityString, String stateString,
String countryString)
{
if (cityString == null) //
{
//
Toast errorToast = Toast.makeText(
context, context.getResources().getString(

14.5.
240
241
242
243
244
245
246
247
248
249
250
251
252
253

527

R.string.null_data_toast), Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0);
errorToast.show(); // Toast
return; //
} // if
// , TextView
locationTextView.setText(cityString + " " + stateString + ", " +
zipcodeString + " " + countryString);
//
new ReadForecastTask(zipcodeString, weatherForecastListener,
locationTextView.getContext()).execute();
} // onLocationLoaded
} // LocationLoadedListener
// SingleForecastFragment

14.5.6. ReadLocationTask
ReadLocationTask ,
. LocationLoadedListener
. , ,
, onLocationLoaded
.

package, import ReadLocationTask


14.38 ReadLocationTask,
,
- WeatherBug.
14.38. package, import
ReadLocationTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// ReadLocationTask.java
// .
package com.deitel.weatherviewer;
import
import
import
import
import

java.io.IOException;
java.io.InputStreamReader;
java.io.Reader;
java.net.MalformedURLException;
java.net.URL;

import
import
import
import
import
import
import

android.content.Context;
android.content.res.Resources;
android.os.AsyncTask;
android.util.JsonReader;
android.util.Log;
android.view.Gravity;
android.widget.Toast;


528

14

Weather Viewer

14.38 ()
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

//
class ReadLocationTask extends AsyncTask<Object, Object, String>
{
private static nal String TAG = "ReadLocatonTask.java";
private String zipcodeString; //
private Context context; // Context Activity
private Resources resources; // xml
//
private String cityString;
private String stateString;
private String countryString;
//
private LocationLoadedListener weatherLocationLoadedListener;

LocationLoadedListener
ReadLocationTask
LocationLoadedListener ( 14.39, 3741)
onLocationLoaded, ,
, ReadLocationTask - WeatherBug. ReadLocationTask ( 4451)
String, Context WeatherViewerActivity LocationLoadedListener.
Resources Context, XML- .
14.39. LocationLoadedListener
ReadLocationTask
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

//
public interface LocationLoadedListener
{
public void onLocationLoaded(String cityString, String stateString,
String countryString);
} // LocationLoadedListener
//
public ReadLocationTask(String zipCodeString, Context context,
LocationLoadedListener listener)
{
this.zipcodeString = zipCodeString;
this.context = context;
this.resources = context.getResources();
this.weatherLocationLoadedListener = listener;
} // ReadLocationTask

14.5.

529

doInBackground ReadLocationTask Method


doInBackground ( 14.40) InputStreamReader,
- WeatherBug , URL. JsonReader,
JSON, -. ( JSON
, weatherServiceURL
.) JSON ( JavaScript Object Notation,
JavaScript) JavaScript
( XML, ).
JSON , :
{ "1" : 1, "2'": 2 }

JSON
:
[ 1, 2, 3 ]

, , JSON- , true, false null. JSON


- .
14.40. doInBackground ReadLocationTask
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

//
@Override
protected String doInBackground(Object... params)
{
try
{
// URL- Weatherbug API
URL url = new URL(resources.getString(
R.string.location_url_pre_zipcode) + zipcodeString +
"&api_key=YOUR_API_KEY");
// InputStreamReader URL-
Reader forecastReader = new InputStreamReader(
url.openStream());
// JsonReader Reader
JsonReader forecastJsonReader = new JsonReader(forecastReader);
forecastJsonReader.beginObject(); //
//
String name = forecastJsonReader.nextName();
// ,
//
if (name.equals(resources.getString(R.string.location)))


530

14

Weather Viewer

14.40 ()
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

{
// JSON
forecastJsonReader.beginObject();
String nextNameString;
//
while (forecastJsonReader.hasNext())
{
nextNameString = forecastJsonReader.nextName();
// ,
// ,
if ((nextNameString).equals(
resources.getString(R.string.city)))
{
//
cityString = forecastJsonReader.nextString();
}
// if
else if ((nextNameString).equals(resources.
getString(R.string.state)))
{
stateString = forecastJsonReader.nextString();
} // else if
else if ((nextNameString).equals(resources.
getString(R.string.country)))
{
countryString = forecastJsonReader.nextString();
} // else if
else
{
forecastJsonReader.skipValue(); //
//
} // else
} // while
forecastJsonReader.close(); // JsonReader
} // if
} // try
catch (MalformedURLException e)
{
Log.v(TAG, e.toString()); // LogCat
} // atch
catch (IOException e)
{
Log.v(TAG, e.toString()); // LogCat
} // catch

return null; // , null


// doInBackground

14.5.

531

JsonReader beginObject beginArray, . 70 beginObject


JsonReader JSON. nextName
JsonReader ( 73),
,
. ,
( 80),
, ( 85110).
, ,
ReadLocationTask. JsonReader ,
boolean, double, int, long String.
String, getString JsonReader.
skipValue JsonReader.

, JSON, -
WeatherBug, JSON. WeatherBug JSON ,
.

onPostExecute ReadLocationTask
onPostExecute ( 14.41) GUI .
null ( - ),
, ,
onLocationLoaded LocationLoadedListener. Toast,
.
14.41. onPostExecute ReadLocationTask
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141

// UI
protected void onPostExecute(String nameString)
{
// ,
if (cityString != null)
{
// LocationLoadedListener
weatherLocationLoadedListener.onLocationLoaded(cityString,
stateString, countryString);
} // if
else
{
// Toast,
// ,
Toast errorToast = Toast.makeText(context, resources.getString(


532

14

Weather Viewer

14.41 ()
142
143
144
145
146
}
147 } //

R.string.invalid_zipcode_error), Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0); // Toast
errorToast.show(); // Toast
} // else
// onPostExecute
ReadLocationTask

14.5.7. ReadForecastTask
ReadForecastTask
.

package, import ReadForecastTask


14.42 ReadForecastTask. , .
Bitmap .
bitmapSampleSize ,
Bitmap.
ForecastListener ( 3741) ,
, Bitmap String , ,
.
14.42. package, import
ReadForecastTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// ReadForecastTask.java
// .
package com.deitel.weatherviewer;
import
import
import
import
import

java.io.IOException;
java.io.InputStreamReader;
java.io.Reader;
java.net.MalformedURLException;
java.net.URL;

import
import
import
import
import
import
import

android.content.Context;
android.content.res.Resources;
android.graphics.Bitmap;
android.graphics.BitmapFactory;
android.os.AsyncTask;
android.util.JsonReader;
android.util.Log;

class ReadForecastTask extends AsyncTask<Object, Object, String>


{
private String zipcodeString; //
//
private Resources resources;

14.5.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

533

//
private ForecastListener weatherForecastListener;
private static nal String TAG = "ReadForecastTask.java";
private
private
private
private
private

String
String
String
String
Bitmap

temperatureString; //
feelsLikeString;
//
humidityString;
//
chanceOfPrecipitationString; //
iconBitmap; //

private int bitmapSampleSize = -1;


//
public interface ForecastListener
{
public void onForecastLoaded(Bitmap image, String temperature,
String feelsLike, String humidity, String precipitation);
} // ForecastListener

ReadForecastTask setSampleSize
ReadForecastTask ( 14.43, 4450)
String, ForecastListener Context WeatherViewerActivity.
setSampleSize ( 5356)
, Bitmap. , Bitmap . WeatherProvider
- Bitmaps,
RemoteViews. , RemoteViews
.
14.43. ReadForecastTask setSampleSize
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

// ReadForecastTask
public ReadForecastTask(String zipcodeString,
ForecastListener listener, Context context)
{
this.zipcodeString = zipcodeString;
this.weatherForecastListener = listener;
this.resources = context.getResources();
} // ReadForecastTask
// Bitmap
public void setSampleSize(int sampleSize)
{
this.bitmapSampleSize = sampleSize;
} // setSampleSize

534

14

Weather Viewer

doInBackground onPostExecute ReadForecastTask


doInBackground method ( 14.44, 59101)
JSON - WeatherBug, ,
. URL-, -, JsonReader. beginObject nextName JsonReader
( 75 78),
String, R.string.hourly_forecast
String. JsonReader readForecast . onPostExecute ( 104110) Strings
onForecastLoaded ForecastLoadedListener .
14.44. doInBackground onPostExecute ReadForecastTask
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

//
protected String doInBackground(Object... args)
{
try
{
// url- JSON WeatherBug
URL webServiceURL = new URL(resources.getString(
R.string.pre_zipcode_url) + zipcodeString + "&ht=t&ht=i&"
+ "ht=cp&ht=&ht=h&api_key=YOUR_API_KEY");
// Reader url- WeatherBug
Reader forecastReader = new InputStreamReader(
webServiceURL.openStream());
// JsonReader Reader
JsonReader forecastJsonReader = new JsonReader(forecastReader);
forecastJsonReader.beginObject(); //
//
String name = forecastJsonReader.nextName();
// ,
//
if (name.equals(resources.getString(R.string.hourly_forecast)))
{
readForecast(forecastJsonReader); //
} // if
forecastJsonReader.close(); // JsonReader
} // try
catch (MalformedURLException e)
{
Log.v(TAG, e.toString());
} // catch
catch (IOException e)

14.5.
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

535

Log.v(TAG, e.toString());
} // catch
catch (IllegalStateException e)
{
Log.v(TAG, e.toString() + zipcodeString);
} // catch
return null;
// doInBackground

// UI
protected void onPostExecute(String forecastString)
{
// ForecastListener
weatherForecastListener.onForecastLoaded(iconBitmap,
temperatureString, feelsLikeString, humidityString,
chanceOfPrecipitationString);
} // onPostExecute

getIconBitmap ReadForecastTask
getIconBitmap ( 14.45) (String) Bitmap. JSON WeatherBug
, - WeatherBug.
URL-, . WeatherBug decodeStream
BitmapFactory.
14.45. getIconBitmap ReadForecastTask
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

// Bitmap ()
public static Bitmap getIconBitmap(String conditionString,
Resources resources, int bitmapSampleSize)
{
Bitmap iconBitmap = null; // Bitmap
try
{
// URL- ,
// WeatherBug
URL weatherURL = new URL(resources.getString(
R.string.pre_condition_url) + conditionString +
resources.getString(R.string.post_condition_url));
BitmapFactory.Options options = new BitmapFactory.Options();
if (bitmapSampleSize != -1)
{
options.inSampleSize = bitmapSampleSize;
} // if
// Bitmap
iconBitmap = BitmapFactory.decodeStream(weatherURL.

536

14

Weather Viewer

14.45 ()
132
133
134
135
136
137
138
139
140
141
142
143
144
145

openStream(), null, options);


} // try
catch (MalformedURLException e)
{
Log.e(TAG, e.toString());
} // catch
catch (IOException e)
{
Log.e(TAG, e.toString());
} // catch

return iconBitmap; //
// getIconBitmap

readForecast ReadForecastTask
readForecast ( 14.46) ,
JsonReader. beginArray beginObject JsonReader ( 151152)

JSON. . skipValue
JsonReader , .
14.46. readForecast ReadForecastTask
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

// JsonReader
private String readForecast(JsonReader reader)
{
try
{
reader.beginArray(); //
reader.beginObject(); //
//
while (reader.hasNext())
{
String name = reader.nextName(); //
// temperature
if (name.equals(resources.getString(R.string.temperature)))
{
// temperature ()
temperatureString = reader.nextString();
} // if
// "feels-like" temperature
// ( )
else if (name.equals(resources.getString(R.string.feels_like)))
{

14.5.
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

537

// "feels-like" temperature
// ( )
feelsLikeString = reader.nextString();
} // else if
// humidity ()
else if (name.equals(resources.getString(R.string.humidity)))
{
humidityString = reader.nextString(); // humidity
} // else if
// chance of precipitation
// ( )
else if (name.equals(resources.getString(
R.string.chance_of_precipitation)))
{
// chance of precipitation
chanceOfPrecipitationString = reader.nextString();
} // else if
// icon name ( )
else if (name.equals(resources.getString(R.string.icon)))
{
// icon name
iconBitmap = getIconBitmap(reader.nextString(), resources,
bitmapSampleSize);
} // else if
else //
{
reader.skipValue(); //
} // else
} // while
} // try
catch (IOException e)
{
Log.e(TAG, e.toString());
} // catch
return null;
} // readForecast
// ReadForecastTask

14.5.8. FiveDayForecastFragment
FiveDayForecastFragment .

package, import
FiveDayForecastFragment
14.47 FiveDayForecastFragment, , .

538

14

Weather Viewer

14.47. package, import


FiveDayForecastFragment
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

// FiveDayForecastFragment.java
// .
package com.deitel.weatherviewer;
import
import
import
import
import
import
import
import
import
import
import

android.content.Context;
android.content.res.Conguration;
android.os.Bundle;
android.view.Gravity;
android.view.LayoutInater;
android.view.View;
android.view.ViewGroup;
android.widget.ImageView;
android.widget.LinearLayout;
android.widget.TextView;
android.widget.Toast;

import com.deitel.weatherviewer.ReadFiveDayForecastTask.
FiveDayForecastLoadedListener;
import com.deitel.weatherviewer.ReadLocationTask.LocationLoadedListener;
public class FiveDayForecastFragment extends ForecastFragment
{
// Bundle
private static nal String ZIP_CODE_KEY = "id_key";
private static nal int NUMBER_DAILY_FORECASTS = 5;
private String zipcodeString; //
private View[] dailyForecastViews = new View[NUMBER_DAILY_FORECASTS];
private TextView locationTextView;

newInstance FiveDayForecastFragment
SingleForecastFragment,
newInstance ( 14.48) FiveDayForecastFragments.
( 3246) String. ( 4955)
Bundle, String, . 38 41
Bundle, String,
setArguments Fragment, onCreate ( 14.49).
14.48. newInstance FiveDayForecastFragment
31
32
33

// FiveDayForecastFragment
public static FiveDayForecastFragment newInstance(String zipcodeString)
{

14.5.
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

539

// ForecastFragment
FiveDayForecastFragment newFiveDayForecastFragment =
new FiveDayForecastFragment();
Bundle argumentsBundle = new Bundle(); //
// Bundle
// String Bundle
argumentsBundle.putString(ZIP_CODE_KEY, zipcodeString);

// Fragement
newFiveDayForecastFragment.setArguments(argumentsBundle);
return newFiveDayForecastFragment;// Fragment
// newInstance

// FiveDayForecastFragment Bundle
public static FiveDayForecastFragment newInstance(
Bundle argumentsBundle)
{
// Bundle
String zipcodeString = argumentsBundle.getString(ZIP_CODE_KEY);
return newInstance(zipcodeString); // Fragment
} // newInstance

onCreate getZipCode FiveDayForecastFragment


onCreate Fragment (. 14.49,
5865). getArguments Fragment Bundle,
getString Bundle String.
getZipcode ( 6871) WeatherViewerActivity FiveDayForecastFragment.
14.49. onCreate getZipCode FiveDayForecastFragment
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

// Fragment Bundle
@Override
public void onCreate(Bundle argumentsBundle)
{
super.onCreate(argumentsBundle);

// Bundle
this.zipcodeString = getArguments().getString(ZIP_CODE_KEY);
// onCreate

//
// Fragment
public String getZipcode()
{
return zipcodeString; // String
} // getZipcode

540

14

Weather Viewer

onCreateView FiveDayForecastFragment
Fragment onCreateView ( 14.50).
, five_day_forecast.xml LayoutInflator, null.
,
View, .
View LinearLayout. ReadLocationTask,

Fragment.
14.50. onCreateView FiveDayForecastFragment
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

// Fragement xml-
@Override
public View onCreateView(LayoutInater inater, ViewGroup container,
Bundle savedInstanceState)
{
//
View rootView = inater.inate(R.layout.ve_day_forecast_layout,
null);
// TextView,
locationTextView = (TextView) rootView.ndViewById(R.id.location);
// ViewGroup,
//
LinearLayout containerLinearLayout =
(LinearLayout) rootView.ndViewById(R.id.containerLinearLayout);
int id; // int
//
if (container.getContext().getResources().getConguration().
orientation == Conguration.ORIENTATION_LANDSCAPE)
{
id = R.layout.single_forecast_layout_landscape;
} // if
else //
{
id = R.layout.single_forecast_layout_portrait;
containerLinearLayout.setOrientation(LinearLayout.VERTICAL);
} // else
//
View forecastView;
for (int i = 0; i < NUMBER_DAILY_FORECASTS; i++)
{
forecastView = inater.inate(id, null); //
// View

14.5.
107
108
109
110
111
112
113
114
115
116
117
118
119
120

541

// View LinearLayout
containerLinearLayout.addView(forecastView);
dailyForecastViews[i] = forecastView;
// for

//
new ReadLocationTask(zipcodeString, rootView.getContext(),
new WeatherLocationLoadedListener(zipcodeString,
rootView.getContext())).execute();

return rootView;
// onCreateView

LocationLoadedListener
WeatherLocationLoadedListener FiveDayForecastFragment ( 14.51) LocationLoadedListener .
ReadLocationTask (String), , TextView.
14.51. LocationLoadedListener
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147

//
private class WeatherLocationLoadedListener implements
LocationLoadedListener
{
private String zipcodeString; //
private Context context;
// WeatherLocationLoadedListener
public WeatherLocationLoadedListener(String zipcodeString,
Context context)
{
this.zipcodeString = zipcodeString;
this.context = context;
} // WeatherLocationLoadedListener
// ,
@Override
public void onLocationLoaded(String cityString, String stateString,
String countryString)
{
if (cityString == null) //
{
//
Toast errorToast = Toast.makeText(context,
context.getResources().getString(R.string.null_data_toast),
Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0);

542

14

Weather Viewer

14.51 ()
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

errorToast.show(); // Toast
return; //
// if

// , TextView
locationTextView.setText(cityString + " " + stateString + ", " +
zipcodeString + " " + countryString);

//
new ReadFiveDayForecastTask(
weatherForecastListener,
locationTextView.getContext()).execute();
} // onLocationLoaded
// WeatherLocationLoadedListener

FiveDayForecastLoadedListener
FiveDayForecastLoadedListener ( 14.52) , DailyForecast, onForecastLoaded. DailyForecasts loadForecastIntoView ( 14.53).
14.52. FiveDayForecastLoadedListener
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

// AsyncTask
FiveDayForecastLoadedListener weatherForecastListener =
new FiveDayForecastLoadedListener()
{
//
//
@Override
public void onForecastLoaded(DailyForecast[] forecasts)
{
//
for (int i = 0; i < NUMBER_DAILY_FORECASTS; i++)
{
//
loadForecastIntoView(dailyForecastViews[i], forecasts[i]);
} // for
} // onForecastLoaded
}; // FiveDayForecastLoadedListener

loadForecastIntoView FiveDayForecastFragment
loadForecastIntoView ( 14.53) DailyForecast, View. Fragment
WeatherViewerActivity DailyForecast ,

14.5.

543

View ViewGroup. View


DailyForecast.
14.53. loadForecastIntoView FiveDayForecastFragment
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

// View
private void loadForecastIntoView(View view,
DailyForecast dailyForecast)
{
// Fragment
if (!FiveDayForecastFragment.this.isAdded())
{
return; //
} // if
//
else if (dailyForecast == null ||
dailyForecast.getIconBitmap() == null)
{
//
Toast errorToast = Toast.makeText(view.getContext(),
view.getContext().getResources().getString(
R.string.null_data_toast), Toast.LENGTH_LONG);
errorToast.setGravity(Gravity.CENTER, 0, 0);
errorToast.show(); // Toast
return; //
} // else if
// View
ImageView forecastImageView = (ImageView) view.ndViewById(
R.id.daily_forecast_bitmap);
TextView dayOfWeekTextView = (TextView) view.ndViewById(
R.id.day_of_week);
TextView descriptionTextView = (TextView) view.ndViewById(
R.id.daily_forecast_description);
TextView highTemperatureTextView = (TextView) view.ndViewById(
R.id.high_temperature);
TextView lowTemperatureTextView = (TextView) view.ndViewById(
R.id.low_temperature);
// View
forecastImageView.setImageBitmap(dailyForecast.getIconBitmap());
dayOfWeekTextView.setText(dailyForecast.getDay());
descriptionTextView.setText(dailyForecast.getDescription());
highTemperatureTextView.setText(dailyForecast.getHighTemperature());
lowTemperatureTextView.setText(dailyForecast.getLowTemperature());
} // loadForecastIntoView
} // FiveDayForecastFragment

544

14

Weather Viewer

14.5.9. ReadFiveDayForecastTask
ReadFiveDayForecastTask AsyncTask, JsonReader
- WeatherBug.

package, import ReadFiveDayForecastTask,


FiveDayForecastLoadedListener
14.54 ReadFiveDayForecastTask
, . FiveDayForecastLoadedListener ( 3033)
, DailyForecasts GUI .
14.54. ReadFiveDayForecast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

// ReadFiveDayForecastTask.java
// 5- .
package com.deitel.weatherviewer;
import
import
import
import
import

java.io.IOException;
java.io.InputStreamReader;
java.io.Reader;
java.net.MalformedURLException;
java.net.URL;

import
import
import
import
import
import
import

android.content.Context;
android.content.res.Resources;
android.content.res.Resources.NotFoundException;
android.graphics.Bitmap;
android.os.AsyncTask;
android.util.JsonReader;
android.util.Log;

class ReadFiveDayForecastTask extends AsyncTask<Object, Object, String>


{
private static nal String TAG = "ReadFiveDayForecastTask";
private
private
private
private
private

String zipcodeString;
FiveDayForecastLoadedListener weatherFiveDayForecastListener;
Resources resources;
DailyForecast[] forecasts;
static nal int NUMBER_OF_DAYS = 5;

//
public interface FiveDayForecastLoadedListener
{
public void onForecastLoaded(DailyForecast[] forecasts);
} // FiveDayForecastLoadedListener

14.5.

545

ReadFiveDayForecastTask
ReadFiveDayForecastTask ( 14.55) zipcodeString , FiveDayForecastLoadedListener Context WeatherViewerActivity.
, DailyForecasts.
14.55. ReadFiveDayForecast
35
36
37
38
39
40
41
42
43
44

// ReadForecastTask
public ReadFiveDayForecastTask(String zipcodeString,
FiveDayForecastLoadedListener listener, Context context)
{
this.zipcodeString = zipcodeString;
this.weatherFiveDayForecastListener = listener;
this.resources = context.getResources();
this.forecasts = new DailyForecast[NUMBER_OF_DAYS];
} // ReadFiveDayForecastTask

doInBackground ReadFiveDayForecastTask
doInBackground ( 14.56) - . InputStreamReader, - WeatherBug
, webServiceURL.
JSON ( 62) ,
. ( 70)
skipValue forecastJsonRead .
,
. readDailyForecast ,
- .
14.56. doInBackground ReadFiveDayForecastTask
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

@Override
protected String doInBackground(Object... params)
{
// url- JSON WeatherBug
try
{
URL webServiceURL = new URL("http://i.wxbug.net/REST/Direct/" +
"GetForecast.ashx?zip="+ zipcodeString + "&ht=t&ht=i&"
+ "nf=7&ht=cp&ht=&ht=h&api_key=YOUR_API_KEY");
// Reader url- WeatherBug
Reader forecastReader = new InputStreamReader(
webServiceURL.openStream());
// JsonReader Reader
JsonReader forecastJsonReader = new JsonReader(forecastReader);


546

14

Weather Viewer

14.56 ()
62
63
64
65
66
67
68
69
70

forecastJsonReader.beginObject(); // Object
//
String name = forecastJsonReader.nextName();
//
if (name.equals(resources.getString(R.string.forecast_list)))
{
forecastJsonReader.beginArray(); //
//
forecastJsonReader.skipValue(); //
//

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

//
for (int i = 0; i < NUMBER_OF_DAYS; i++)
{
//
forecastJsonReader.beginObject();

//
if (forecastJsonReader.hasNext())
{
//
forecasts[i] = readDailyForecast(forecastJsonReader);
} // if
} // for
// if

forecastJsonReader.close(); // JsonReader

} // try
catch (MalformedURLException e)
{
Log.v(TAG, e.toString());
} // catch
catch (NotFoundException e)
{
Log.v(TAG, e.toString());
} // catch
catch (IOException e)
{
Log.v(TAG, e.toString());
} // catch
return null;
// doInBackground

14.5.

547

readDailyForecast onPostExecute
ReadFiveDayForecastTask
readDailyForecast ( 14.57, 107161) JSON . ,
, String,
Bitmap. hasNext forecastReader
, .
, , . , nextString
JsonReader .
Bitmap - WeatherBug getIconBitmap . skipValue JsonReader .
DailyForecast .
onPostExecute ( 164167) GUI . onForecastLoaded FiveDayForecastListener
DailyForecasts FiveDayForecastFragment.
14.57. readDailyForecast onPostExecute

ReadFiveDayForecastTask
106

//

107
108
109
110
111
112
113
114
115
116
117
118

private DailyForecast readDailyForecast(JsonReader forecastJsonReader)


{
//
String[] dailyForecast = new String[4];
Bitmap iconBitmap = null; //

119
120
121
122
123
124
125
126
127
128
129
130
131

try
{
//
while (forecastJsonReader.hasNext())
{
String name = forecastJsonReader.nextName(); //
//
if (name.equals(resources.getString(R.string.day_of_week)))
{
dailyForecast[DailyForecast.DAY_INDEX] =
forecastJsonReader.nextString();
} // if
else if (name.equals(resources.getString(
R.string.day_prediction)))
{
dailyForecast[DailyForecast.PREDICTION_INDEX] =
forecastJsonReader.nextString();
} // end else if
else if (name.equals(resources.getString(R.string.high)))


548

14

Weather Viewer

14.57 ()
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168

{
dailyForecast[DailyForecast.HIGH_TEMP_INDEX] =
forecastJsonReader.nextString();
} // end else if
else if (name.equals(resources.getString(R.string.low)))
{
dailyForecast[DailyForecast.LOW_TEMP_INDEX] =
forecastJsonReader.nextString();
} // else if
//
else if (name.equals(resources.getString(R.string.day_icon)))
{
//
iconBitmap = ReadForecastTask.getIconBitmap(
forecastJsonReader.nextString(), resources, 0);
} // else if
else //
{
forecastJsonReader.skipValue(); //
//
} // else
} // while
forecastJsonReader.endObject()
} // try
catch (IOException e)
{
Log.e(TAG, e.toString());
} // catch

return new DailyForecast(dailyForecast, iconBitmap);


// readDailyForecast

// UI
protected void onPostExecute(String forecastString)
{
weatherFiveDayForecastListener.onForecastLoaded(forecasts);
} // onPostExecute
} // ReadFiveDayForecastTask

14.5.10. DailyForecast
DailyForecast ( 14.58) , . -, String,
. iconBitmap Bitmap , .
DailyForecast String, ,
, -

14.5.

549

. , DailyForecast,
accessor.
14.58. DailyForecast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// DailyForecast.java
// .
package com.deitel.weatherviewer;

16

nal private Bitmap iconBitmap;

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

import android.graphics.Bitmap;
public class DailyForecast
{
//
public static nal int DAY_INDEX = 0;
public static nal int PREDICTION_INDEX = 1;
public static nal int HIGH_TEMP_INDEX = 2;
public static nal int LOW_TEMP_INDEX = 3;
nal private String[] forecast;

//
//
//
//

// DailyForecast
public DailyForecast(String[] forecast, Bitmap iconBitmap)
{
this.forecast = forecast;
this.iconBitmap = iconBitmap;
} // DailyForecast
//
public Bitmap getIconBitmap()
{
return iconBitmap;
} // getIconBitmap
//
public String getDay()
{
return forecast[DAY_INDEX];
} // getDay
//
public String getDescription()
{
return forecast[PREDICTION_INDEX];
} // getDescription
//
public String getHighTemperature()

550

14

Weather Viewer

14.58 ()
45
46
47
48
49
50
51
52
53
54

{
return forecast[HIGH_TEMP_INDEX];
// getHighTemperature

//
public String getLowTemperature()
{
return forecast[LOW_TEMP_INDEX];
} // getLowTemperature
// DailyForecast

14.5.11. WeatherProvider
WeatherProvider AppWidgetProvider,
Weather Viewer. AppWidgetProviders
BroadcastReceivers, , .

package, import WeatherProvider


14.59 ReadFiveDayForecastTask, ,
. BITMAP_SAMPLE_SIZE Bitmap , RemoteViews View, . Android
, .
14.59. package, import WeatherProvider
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

// WeatherProvider.java
// Weather
package com.deitel.weatherviewer;
import
import
import
import
import
import
import
import
import
import
import
import

android.app.IntentService;
android.app.PendingIntent;
android.appwidget.AppWidgetManager;
android.appwidget.AppWidgetProvider;
android.content.ComponentName;
android.content.Context;
android.content.Intent;
android.content.SharedPreferences;
android.content.res.Resources;
android.graphics.Bitmap;
android.widget.RemoteViews;
android.widget.Toast;

import com.deitel.weatherviewer.ReadForecastTask.ForecastListener;
import com.deitel.weatherviewer.ReadLocationTask.LocationLoadedListener;
public class WeatherProvider extends AppWidgetProvider

14.5.
22
23
24
25

551

{
// Bitmap
private static nal int BITMAP_SAMPLE_SIZE = 4;

onUpdate, getZipcode onReceive WeatherProvider


onUpdate ( 14.60, 2732) ACTION_APPWIDGET_UPDATE
AppWidgetManager. startUpdateService (. 14.60) .
getZipcode ( 3548)
SharedPreferences .
onReceive ( 5161) , WeatherProvider
Intent.
Intent WeatherViewerActivity.WIDGET_UPDATE_BROADCAST.
Intent WeatherViewerActivity
.
. startUpdateService.
14.60. onUpdate, getZipcode onReceive WeatherProvider
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

// Weather App
@Override
public void onUpdate(Context context,
AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
startUpdateService(context); // WeatherService
} // onUpdate
//
private String getZipcode(Context context)
{
// SharedPreferences
SharedPreferences preferredCitySharedPreferences =
context.getSharedPreferences(
WeatherViewerActivity.SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);

//
// SharedPreferences
String zipcodeString = preferredCitySharedPreferences.getString(
WeatherViewerActivity.PREFERRED_CITY_ZIPCODE_KEY,
context.getResources().getString(R.string.default_zipcode));
return zipcodeString; // return the ZIP code string
// getZipcode

// , AppWidgetProvider
// Intent

552

14

Weather Viewer

14.60 ()
51
52
53
54
55
56
57
58
59
60
61
62

@Override
public void onReceive(Context context, Intent intent)
{
//
if (intent.getAction().equals(
WeatherViewerActivity.WIDGET_UPDATE_BROADCAST_ACTION))
{
startUpdateService(context); //
} // if
super.onReceive(context, intent);
} // onReceive

startUpdateService WeatherProvider
startUpdateService ( 14.61) IntentService,
WeatherService ( 14.62),
.
14.61. startUpdateService WeatherProvider
63
64
65
66
67
68
69
70
71
72
73
74
75

// WeatherService,
// ,
private void startUpdateService(Context context)
{
// Intent,
// WeatherService
Intent startServiceIntent;
startServiceIntent = new Intent(context, WeatherService.class);

// Intent
startServiceIntent.putExtra(context.getResources().getString(
R.string.zipcode_extra), getZipcode(context));
context.startService(startServiceIntent);
// startUpdateService

WeatherService WeatherProvider
IntentService WeatherService ( 14.62)
- WeatherBug View . IntentService ( 8083) String,
(Thread) . . onHandleIntent ( 89101)
WeatherService. Resources , Intent, .
ReadLocationTask,
.

14.5.

553

14.62. WeatherService WeatherProvider


76
77
78
79
80
81
82
83
84
85
86

// Weather Viewer
public static class WeatherService extends IntentService
implements ForecastListener
{
public WeatherService()
{
super(WeatherService.class.toString());
} // WeatherService
private Resources resources;
private String zipcodeString;

//
//
//
private String locationString; //
//

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

@Override
protected void onHandleIntent(Intent intent)
{
resources = getApplicationContext().getResources();
zipcodeString = intent.getStringExtra(resources.getString(
R.string.zipcode_extra));

//
new ReadLocationTask(zipcodeString, this,
new WeatherServiceLocationLoadedListener(
zipcodeString)).execute();
// onHandleIntent

onForecastLoaded WeatherService
onForecastLoaded ( 14.63) - WeatherBug, AsyncTask. , Bitmap null.
, ReadForecastTask ,
Toast. , PendingIntent ( 118120),
WeatherViewerActivity . PendingIntent Intent ,
Intent. PendingIntent
.
AppWidgetProvider View . AppWidgetProvider.

RemoteViews. RemoteViews
( 123124). PendingIntent setOnClickPendingIntent

554

14

Weather Viewer

remoteView ( 127128), PendingIntent


. Weather Viewer. ID
View View . TextView
ID TextView
setTextViewText RemoteView. ImageView
setImageViewBitmap RemoteView.
ComponentName ( 154155), WeatherProvider. AppWidgetManager getInstance ( 158). ,
RemoteViews, View ,
ComponentName RemoteViews updateAppWidget
AppWidgetManager ( 161).
14.63. onForecastLoaded WeatherService
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134

// ReadForecastTask
@Override
public void onForecastLoaded(Bitmap image, String temperature,
String feelsLike, String humidity, String precipitation)
{
Context context = getApplicationContext();
if (image == null) //
{
Toast.makeText(context, context.getResources().getString(
R.string.null_data_toast), Toast.LENGTH_LONG);
return; //
} // if
// PendingIntent,
// WeatherViewerActivity
Intent intent = new Intent(context, WeatherViewerActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
getBaseContext(), 0, intent, 0);
// RemoteViews
RemoteViews remoteView = new RemoteViews(getPackageName(),
R.layout.weather_app_widget_layout);
// PendingIntent
//
remoteView.setOnClickPendingIntent(R.id.containerLinearLayout,
pendingIntent);
//
remoteView.setTextViewText(R.id.location, locationString);
//
remoteView.setTextViewText(R.id.temperatureTextView,

14.5.
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

555

temperature + (char)0x00B0 + resources.getString(


R.string.temperature_unit));
// " "
remoteView.setTextViewText(R.id.feels_likeTextView, feelsLike +
(char)0x00B0 + resources.getString(R.string.temperature_unit));
//
remoteView.setTextViewText(R.id.humidityTextView, humidity +
(char)0x0025);
//
remoteView.setTextViewText(R.id.precipitationTextView,
precipitation + (char)0x0025);
//
remoteView.setImageViewBitmap(R.id.weatherImageView, image);
// Component Name
ComponentName widgetComponentName = new ComponentName(this,
WeatherProvider.class);
// AppWidgetManager
AppWidgetManager manager = AppWidgetManager.getInstance(this);

// AppWdiget Weather
manager.updateAppWidget(widgetComponentName, remoteView);
// onForecastLoaded

WeatherServiceLocationLoadedListener WeatherService
WeatherServiceLocationLoadedListener ( 14.64) - WeatherBug AsyncTask.
onLocationLoaded ( 177202) String,
, ReadForecastTask, . setSampleSize
ReadForecastTask ,
Bitmap. Bitmap,
RemoteView.
14.64. WeatherServiceLocationLoadedListener WeatherService
164
165
166
167
168
169

//
private class WeatherServiceLocationLoadedListener
implements LocationLoadedListener
{
private String zipcodeString; //


556

14

Weather Viewer

14.64 ()
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

// WeatherLocationLoadedListener
public WeatherServiceLocationLoadedListener(String zipcodeString)
{
this.zipcodeString = zipcodeString;
} // WeatherLocationLoadedListener
//
@Override
public void onLocationLoaded(String cityString,
String stateString, String countryString)
{
Context context = getApplicationContext();
if (cityString == null) //
{
Toast.makeText(context, context.getResources().getString(
R.string.null_data_toast), Toast.LENGTH_LONG);
return; //
} // if
// TextView
locationString = cityString + " " + stateString + ", " +
zipcodeString + " " + countryString;
// ReadForecastTask
ReadForecastTask readForecastTask = new ReadForecastTask(
zipcodeString, (ForecastListener) WeatherService.this,
WeatherService.this);

// Bitmap
readForecastTask.setSampleSize(BITMAP_SAMPLE_SIZE);
readForecastTask.execute();
} // onLocationLoaded
}// WeatherServiceLocationLoadedListener
} // WeatherService
// WeatherProvider

14.6.
Weather Viewer . ,
Android 3.x.

.
DialogFragment ListFragment Fragment
.
, Fragment
Activity. FragmentManager

Deitel & Associates, Inc

557

Fragments FragmentTransaction, , .
Android 3.x, ,

. ,
. JsonReader
JSON, , - WeatherBug.
(
AppWidgetProvider), , .
PendingIntent.
, Intent .

Deitel & Associates, Inc


, ,
. . , , deitel@deitel.com.
Resource Centers, Android, www.deitel.com/ResourceCenters.
html. (www.deitel.com/deitelfan) (@deitel).

. , . , . , .

Android :

.
.
.
.
.
.

, 198206, -, , 73, . 29.


005-93, 2; 95 3005 .
08.08.12. 70100/16. . . . 45,150. 2000.
.
180004, , . , 34.

?
!
?
?
? ,

?
!


!

www.piter.com/ePartners

www.piter.com,
,

( www.piter.com)
!
.
10% ,
, - c
. ,
, 5%
.
, , 500 ,
. Web.Money.
:
http://www.piter.com/book.phtml?978538800282
http://www.piter.com/book.phtml?978538800282&refer=0000
, 0000


WWW.PITER.COM

Оценить