Android.
.
.
.
.
.
.
.
32.973.2-018.2
004.451
. , .
20 Android. . .: , 2014.
592.: . ( ).
ISBN 978-5-496-00502-9
, Android.
IT- Big Nerd Ranch, Android, API .
Android-
, Flickr, , , , .
,
Android .
ISBN 978-0321804334 .
A
uthorized translation from the English language edition,
entitled Android Programming: The Big Nerd Ranch Guide;
ISBN 0321804334; by Hardy, Brian; and by Phillips, Bill;
published by The Big Nerd Ranch Guide, Inc.
The Big Nerd Ranch, Inc., 2013
ISBN 978-5-496-00502-9
, 2014
,
, 2014
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or
mechanical, including photocopying, recording or by any information storage retrieval system, without permission from
Pearson Education, Inc.
The Big Nerd Ranch, Inc. .
.
, , , . , ,
, .
, 192102, -, . (. ), . 3, , . 7.
005-93, 2; 95 3005 .
30.10.13. 70100/16. . . . 47,730. 1700.
. 432049, , . , .27.
............................................................................................................. 16
Android......................................................................................................... 18
1. Android........................................................................ 24
.................................................................................................. 24
Android............................................................................................................ 25
Eclipse...................................................................................................................... 28
...................................................................... 29
.............................................................................................................. 33
...................................................................................................................... 34
android:layout_width android:layout_height.............................................................................. 34
android:orientation..................................................................................................................... 34
android:text............................................................................................................................... 35
...................................................................................................... 35
.............................................................................................. 36
XML View................................................................................................ 36
........................................................................................... 37
............................................................................................ 40
................................................................................................................... 40
..................................................................................................... 41
............................................................................................................... 41
..................................................................................................... 42
................................................................................................................................ 43
........................................................................................................................... 44
.............................................................................................................. 45
: Android................................................... 47
Android........................................................................................ 49
2. Android MVC................................................................................................ 51
............................................................................................................... 52
get- set-................................................................................................ 53
-- Android...................................................... 55
MVC...................................................................................................................... 56
.............................................................................................. 57
................................................................................................. 59
.................................................................................................................... 63
.......................................................................................................... 63
...................................................................................... 64
...................................................................................................................... 64
..................................................................................................... 65
XML.......................................................................................................... 66
................................................................................................................................. 67
. TextView.................................................................... 68
. ............................................................................... 68
. Button ImageButton....................................................................................... 68
3. Activity.............................................................................. 71
Activity........................................................................... 72
................................................................................................ 72
LogCat.................................................................................................................. 74
...................................................................................... 78
.................................................................... 78
............................................................................... 79
........................................................................................ 82
onSaveInstanceState(Bundle)............................................................................. 83
Activity................................................................................................. 84
: onSaveInstanceState(Bundle)................................................. 85
: ................................................................... 87
4. Android...................................................................... 89
DDMS....................................................................................................................... 90
.................................................................................................. 91
................................................................................................... 92
................................................................................................. 94
........................................................................................................ 95
.................................................................................................... 99
File Explorer.................................................................................................................................100
Android......................................................................................................101
Android Lint..............................................................................................................................101
R.............................................................................................................103
5. ...................................................................................... 104
.......................................................................................................105
..........................................................................................................106
.......................................................................................109
.....................................................................................110
Cheat QuizActivity.......................................................................................111
.......................................................................................................................113
.........................................................................................113
.......................................................................................................115
........................................................................................115
..............................................................................................................116
.......................................................................118
...............................................................................................................119
..............................................................................................................120
..............................................................................................................121
Android.....................................................................................123
................................................................................................................................125
8. ...................................................................................... 163
Crime.......................................................................................................................163
..................................................................................................................164
...............................................................................................................166
XML......................................................................................167
, ....................................................................................................167
, dp sp....................................................................................................168
Android.........................................................169
...................................................................................................................170
........................................................................................................................170
.................................................................................171
....................................................................................................173
....................................................................................174
............................................................................175
...............................................................................176
android:layout_weight..........................................................................................177
..............................................................................................178
.............................................................178
. ............................................................................................179
9. ListFragment................................................................... 180
CriminalIntent...................................................................................181
.........................................................................182
ListFragment.................................................................................................................184
........................................................................185
............................................................................185
Activity.......................................................................................................186
........................................................................................188
CrimeListActivity....................................................................................................189
ListFragment, ListView ArrayAdapter...........................................................................................191
ArrayAdapter<T>..........................................................................................................193
...................................................................................................195
.......................................................................................................196
...........................................................................................196
..................................................................................................198
10
CriminalIntentJSONSerializer............................................................................287
JSON Crime........................................................................288
Crime CrimeLab.......................................................................................289
onPause()...........................................................................290
....................................................................................291
. ......................................................................293
: Android - Java.........................293
...........................................................................................294
11
Crime ..........................................................341
.........................................................................................342
CrimeFragment..............................................................................342
ImageView............................................................................................................343
..........................................................................................................345
ImageView.......................................................345
............................................................................................................347
DialogFragment....................................................349
. Crime...........................................................................351
. ............................................................................................351
: Android...........................................................352
12
: include merge.........................................................................................404
. .............................................................................................405
13
............................................................................................................478
AlarmManager....................................................................................480
PendingIntent...........................................................................................................................482
PendingIntent...........................................................482
..................................................................................................................483
..................................................................................484
............................................................................................................................486
: ................................................................................488
( ) ...................................................................................488
.........................................................................................................488
.........................................................................................................488
.............................................................................................................489
.................................................................................................................489
................................................................................................490
..................................................................................................491
14
....................................................................................................................534
............................................................................................................................534
RunFragment..................................................................................................534
LocationManager............................................................................................535
....................................................537
......................................539
: .........................................................543
...............................................................544
. ,
,
.
. .
, .
, .
.
(Chris Stewart) (Owen Matthews)
.
-
(Christopher Moor)
,
.
(Bolot Kerimbaev) (Andrew
Lunsford)
.
(Frank Robles), (Jim Steele),
(Laura Cassell), (Mark Dalrymple)
(Magnus Dahl) .
(Aaron Hillegass).
, . . ( ,
.)
(Susan Loper)
,
. .
, .
NASA.
.
17
( Ellie Volckhausen), .
IntelligentEnglish.com , EPUB Kindle. DocBook
.
, Facebook
.
. , ,
,
. , . .
Android
Android .
Android : , .
, - ,
. , ,
.
Android . c
Java, Java . Android,
. , .
. , Big Nerd Ranch, ,
Android :
Android;
, .
. Android.
Android,
. - , - , ,
.
, ,
. , , Android.
19
, Java,
, , , , , , .
, 2. Java
. ;
.
- , Java , .
Java (, ).
Java ,
.
Big Nerd Ranch.
, .
, . , .
,
.
,
,
, , .
, . ,
.
:
.
,
.
forums.bignerdranch.com.
Android, .
Android.
, .
,
20
Android
. ,
.
GeoQuiz
Android, , .
CriminalIntent
.
, - ,
, , , .
HelloMoon .
,
, .
NerdLauncher .
RemoteControl
, ,
.
PhotoGallery Flickr Flickr. , , - ..
DragAndDraw .
RunTracker
. ,
SQLite, .
.
,
.
.
.
,
forums.bignerdranch.com.
?
. ,
. , , .
21
, Android.
. .
, . , .
, .
7 . .
Android- ,
. ,
. , ,
.
, Gingerbread Froyo. Android Ice Cream Sandwich Jelly Bean,
Key Lime Pie. ,
Froyo Gingerbread. (
Android 6.)
, , Froyo Gingerbread.
, ,
Android . ,
, Gingerbread
40% .
ADT Bundle
ADT (Android Developer Tools) Bundle.
:
Eclipse Android.
Eclipse Java, PC, Mac Linux. Eclipse
,
, .
Android Developer Tools Eclipse. ADT
21.1. ,
.
Android SDK Android SDK.
22
Android
Android SDK
.
Android
.
ADT Bundle Android zip-.
1. http://developer.android.com/sdk/index.html.
2. zip- , Eclipse
.
3. eclipse Eclipse.
Windows Eclipse , ,
Java Development Kit (JDK6),
www.oracle.com.
, http://developer.
android.com/sdk/index.html .
SDK
ADT Bundle SDK
. Android
.
Android SDK Manager.
Eclipse WindowAndroid SDK Manager.
, Android 2.2 (Froyo),
:
SDK Platform;
;
Google API.
, .
Android SDK Manager Android , .
23
.
Android . , .
, ,
comp@piter.com ( , ).
!
- http://www.piter.com
.
Android
, Android. ,
- .
.
, ,
GeoQuiz. ,
. , True False,
GeoQuiz .
. 1.1
False.
. 1.1. ,
GeoQuiz
(activity) (layout):
Activity Android SDK.
.
, ,
Android
25
Activity.
; .
GeoQuiz ,
Activity QuizActivity. QuizActivity , .1.1.
. ,
XML. ,
(, ).
GeoQuiz activity_quiz.xml.
XML ,
.1.1.
QuizActivity activity_quiz.xml .1.2.
, .
Android
Android. Android ,
. , Eclipse
FileNewAndroid Application Project,
.
GeoQuiz (.1.3).
. Package
Name com.bignerdranch.android.geoquiz.
:
DNS, .
Google Play.
Android. GeoQuiz ,
26
1. Android
.
Android 6.
. 1.3.
Android ,
.
; . (
, ,
. . forums.bignerdranch.com,
.)
Next.
Create custom launcher icon (.1.4). GeoQuiz .
, Create activity .
Android
27
. 1.4.
Next.
(. 1.5)
. BlankActivity ( ).
. 1.5.
28
1. Android
Next.
QuizActivity (.1.6). Activity .
, ,
.
. 1.6.
activity_quiz . , ;
,
.
, , .
Navigation Type None Finish.
Eclipse .
Eclipse
Eclipse (workbench window),
.1.7. ( Eclipse, Eclipse, .)
29
Package Explorer. ,
.
. , Eclipse activity_quiz.xml.
.
, X
(. 1.7). .
,
.
. 1.7.
,
Eclipse.
, ;
, .
30
1. Android
XML, activity_quiz.xml
.
activity_quiz.xml
. , XML
, 1.1.
1.1.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".QuizActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
, activity_quiz.xml
:
<?xml version="1.0" encoding="utf-8"?>
ADT 21 Android.
, .
(widgets): RelativeLayout
TextView.
,
. , . ,
, .
Android SDK , .
View (, TextView Button).
. 1.8 , RelativeLayout TextView,
1.1.
, . QuizActivity
:
LinearLayout;
TextView;
LinearLayout;
Button.
.1.9 , QuizActivity.
. 1.8.
()
()
. 1.9.
31
32
1. Android
activity_quiz.xml.
activity_quiz.xml , 1.2. XML, , ,
XML . .
, ;
, . : ,
.
,
android:text, . .
.
1.2. XML (activity_quiz.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".QuizActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/true_button" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
33
android:text="@string/false_button" />
</LinearLayout>
</LinearLayout>
View, .
. 1.10 XML 1.2.
. 1.10.
34
1. Android
.
, .
ViewGroup FrameLayout, TableLayout RelativeLayout.
ViewGroup, (child) ViewGroup.
LinearLayout : TextView
LinearLayout. LinearLayout Button.
, .
android:layout_width android:layout_height
android:layout_width android:layout_height,
, . ,
match_parent wrap_content:
match_parent .
wrap_content .
( fill_parent.
match_parent.)
LinearLayout match_parent.
LinearLayout , , Android
.
wrap_content.
.1.9 , .
TextView -
android:padding="24dp".
, . (, dp? ,
(density-independent pixels),
8.)
android:orientation
android:orientation LinearLayout ,
. LinearLayout ; LinearLayout .
.
LinearLayout , , . LinearLayout
35
. ( , ;
.)
android:text
TextView Button android:text.
,
: , .
, XML, .
(, android:text="True"), .
, . 15 ,
.
, activity_quiz.xml, .
.
strings.xml.
Package Explorer res/values,
strings.xml.
; strings.xml .
.
hello_world .
1.3. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GeoQuiz</string>
<string name="hello_world">Hello, world!</string>
<string name="question_text">Constantinople is the largest city in Turkey.</
string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="menu_settings">Settings</string>
</resources>
( menu_settings .
menu_settings , .)
@string/false_button XML GeoQuiz
"False" .
36
1. Android
strings.xml. activity_quiz.xml , , . (
, , - .)
strings.xml,
.
. res/values/,
resources string,
.
.
, . activity_quiz.xml Graphical Layout
.
. 1.11.
(activity_quiz.xml)
XML View
XML activity_quiz.xml View?
QuizActivity.
37
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_quiz, menu);
return true;
}
( import,
import, .)
Activity: onCreate(Bundle) onCreateOptionsMenu(Menu).
onCreateOptionsMenu(Menu).
16.
onCreate(Bundle) . , . ,
Activity:
public void setContentView(int layoutResID)
(inflates) .
, . , ,
.
. ,
, , XML ..
38
1. Android
DO NOT MODIFY.
package com.bignerdranch.android.geoquiz;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int ic_launcher=0x7f020000;
}
public static final class id {
public static final int menu_settings=0x7f070003;
}
public static final class layout {
public static final int activity_quiz=0x7f030000;
}
public static final class menu {
public static final int activity_quiz=0x7f060000;
}
public static final class string {
public static final int app_name=0x7f040000;
public static final int false_button=0x7f040003;
public static final int menu_settings=0x7f040006;
public static final int question_text=0x7f040001;
public static final int true_button=0x7f040002;
}
...
}
, R.layout.activity_quiz
activity_quiz layout R.
.
, :
setTitle(R.string.app_name);
Android ,
activity_quiz.xml.
39
.
, .
,
android:id. activity_quiz.xml android:id
.
1.6. (activity_quiz.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/true_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/true_button" />
<Button
android:id="@+id/false_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/false_button" />
</LinearLayout>
</LinearLayout>
: + android:id, android:text. , ,
.
activity_quiz.xml. R.java ,
R.id .
1.7. (R.java)
public final class R {
...
public static final class id {
public static final int false_button=0x7f070001;
public static final int menu_settings=0x7f070002;
public static final int true_button=0x7f070000;
}
...
40
1. Android
, , QuizActivity. .
QuizActivity.java. ( ;
.) .
1.8. (QuizActivity.java)
public class QuizActivity extends Activity {
private Button mTrueButton;
private Button mFalseButton;
...
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
}
, m
( ).
Android, .
. : Button (Button cannot be resolved
to a type).
, android.widget.Button
QuizActivity.java. :
import android.widget.Button;
Eclipse
, Java Android SDK.
,
.
, :
Command+Shift+O Mac;
Ctrl+Shift+O Windows Linux.
. ( , XML.)
-.
:
41
View;
, .
,
Activity:
public View findViewById(int id)
View.
QuizActivity.java . ,
View Button.
1.9. (QuizActivity.java)
public class QuizActivity extends Activity {
private Button mTrueButton;
private Button mFalseButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
...
}
mTrueButton = (Button)findViewById(R.id.true_button);
mFalseButton = (Button)findViewById(R.id.false_button);
Android (event-driven).
,
, . ( ,
, , .)
, ,
. , ,
(listener).
.
Android SDK , . ,
View.OnClickListener.
42
1. Android
True. QuizActivity.java
onCreate() .
1.10. True (QuizActivity.java)
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});
mFalseButton = (Button)findViewById(R.id.false_button);
. , ; : ,
, setOnClickListener(OnClickListener) .
,
.
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});
. ,
,
, .
OnClickListener, onClick(View).
43
onClick(View) , . ,
onClick(View) ,
, .
( ,
, Java ,
, .)
False.
1.11. False (QuizActivity.java)
...
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});
mFalseButton = (Button)findViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// , !
}
});
- . (toast) ,
- ,
, . ,
(.1.12).
strings.xml ,
.
1.12. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GeoQuiz</string>
<string name="question_text">Constantinople is the largest city
in Turkey.</string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="correct_toast">Correct!</string>
<string name="incorrect_toast">Incorrect!</string>
<string name="menu_settings">Settings</string>
</resources>
44
1. Android
. 1.12.
Toast:
public static Toast makeText(Context context, int resId, int duration)
, .
1.13.
Toast, Toast.
45
, Tab,
. ( , . Tab
.)
makeText(Context, int, int).
.
;
QuizActivity.this. Tab, , , 1.13.
1.13. (QuizActivity.java)
...
mTrueButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT).show();
}
});
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
}
});
Android . Android,
.
Android (AVD, Android Virtual Device),
WindowAndroid Virtual Device Manager.
AVD Manager, New... .
. Galaxy Nexus Google APIs
46
1. Android
Windows, , AVD
(RAM) 1024 512. OK.
Windows
512
1024
. 1.13. Android
,
GeoQuiz. Package Explorer
GeoQuiz. Run AsAndroid Application. Eclipse
, . Eclipse ,
LogCat .
, , GeoQuiz .
. ( ,
, , AVD
. AVD .)
: Android
47
GeoQuiz ,
LogCat. ;
. Text
, .
. 1.14. NullException 21
.
.
; , , .
Back (U- ),
Eclipse, .
, . 2 GeoQuiz ,
.
:
Android
, , Android. , Eclipse
, . Android , AndroidManifest.xml
( ) .apk.
, .
( .apk ,
.
Android http://http://developer.
android.com/tools/publishing/preparing.html.)
activity_quiz.xml View ? aapt (Android Asset Packaging Tool)
. .apk. , setContentView()
onCreate() QuizActivity, QuizActivity LayoutInflater
View, .
48
1. Android
(
XML.
, , SDK;
3.)
, XML,
, 8.
Java
-
Java (.class)
dalvik
-
dalvik (.dex)
apk
Android
(.apk)
. 1.15. GeoQuiz
Android
49
. 1.16. activity_quiz.xml
Android
, ,
Eclipse. ADT Android (, aapt),
Eclipse.
, - Eclipse. . maven
ant. Ant , .
:
, ant .
, tools/ platform-tools/ Android SDK
.
:
$ android update project -p .
50
1. Android
,
.
Android MVC
GeoQuiz
.
. 2.1. !
GeoQuiz TrueFalse.
/.
TrueFalse,
QuizActivity.
52
2. Android MVC
Package Explorer com.bignerdranch.android.geoquiz NewClass. TrueFalse,
java.lang.Object Finish.
. 2.2. TrueFalse
TrueFalse.java :
2.1. TrueFalse
public class TrueFalse {
private int mQuestion;
private boolean mTrueQuestion;
get- set-
53
get- set-
Eclipse m is get .
Eclipse ( Eclipse Mac, WindowsPreferences
Windows). Java Code Style.
Conventions for variable names: Fields (.2.3).
Edit m . s . ( GeoQuiz s ,
.)
, Use 'is' prefix for getters that return boolean . OK.
. 2.3. Java
54
2. Android MVC
? Eclipse
get- mQuestion, getQuestion()
getMQuestion() isTrueQuestion() isMTrueQuestion().
TrueFalse.java,
SourceGenerate Getters And Setters... Select
All, get- set- .
()
. 2.4. GeoQuiz
OK. Eclipse .
2.2. get- set-
public class TrueFalse {
private int mQuestion;
private boolean mTrueQuestion;
public TrueFalse(int question, boolean trueQuestion) {
mQuestion = question;
mTrueQuestion = trueQuestion;
}
public int getQuestion() {
return mQuestion;
}
public void setQuestion(int question) {
mQuestion = question;
}
-- Android
55
TrueFalse . QuizActivity
TrueFalse, , GeoQuiz
.
QuizActivity TrueFalse.
TextView Button
.
- Android
, , .2.4 : , . Android
, --,
MVC (Model-View-Controller). MVC, ,
.
-. ,
, , ,
/ ..
; .
Android
.
.
GeoQuiz TrueFalse.
, . :
- , .
Android .
. .
GeoQuiz , activity_quiz.xml.
; . ,
56
2. Android MVC
,
.
Android Activity, Fragment
Service. ( 7, 29.)
GeoQuiz
QuizActivity.
.2.5 , . :
;
-,
.
. 2.5. MVC
MVC
, .
; ,
.
, ;
, .
GeoQuiz ,
. GeoQuiz Next.
TrueFalse.
57
MVC .
, ,
.
, TrueFalse ,
/. TrueFalse
. , , ,
.
MVC
GeoQuiz Next.
Android
XML . GeoQuiz activity_quiz.xml.
, .2.6. (
, .)
. 2.6.
, :
android:text TextView.
.
TextView android:id.
, QuizActivity.
Button LinearLayout.
activity_quiz.xml .
2.3. TextView (activity_quiz.xml)
<LinearLayout
... >
<TextView
android:id="@+id/question_text_view"
58
2. Android MVC
2.3 ()
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/question_text"
/>
<LinearLayout
... >
...
</LinearLayout>
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_button" />
</LinearLayout>
activity_quiz.xml. ,
.
res/values/strings.xml.
.
2.4. (strings.xml)
...
<string name="app_name">GeoQuiz</string>
<string name="question_text">Constantinople is the largest city
in Turkey.</string>
<string name="true_button">True</string>
<string name="false_button">False</string>
<string name="next_button">Next</string>
<string name="correct_toast">Correct!</string>
...
strings.xml , ,
.
2.5. (strings.xml)
...
<string name="incorrect_toast">Incorrect!</string>
<string name="menu_settings">Settings</string>
<string name="question_oceans">The Pacific Ocean is larger than
the Atlantic Ocean.</string>
<string name="question_mideast">The Suez Canal connects the Red Sea
and the Indian Ocean.</string>
<string name="question_africa">The source of the Nile River is in Egypt.</string>
<string name="question_americas">The Amazon River is the longest river
in the Americas.</string>
<string name="question_asia">Lake Baikal is the world\'s oldest and deepest
freshwater lake.</string>
...
59
\' .
, \n
.
. activity_quiz.xml
.
, GeoQuiz.
QuizActivity.
GeoQuiz QuizActivity
. , activity_quiz.
xml, .
, , QuizActivity
GeoQuiz.
QuizActivity.java. TextView
Button . TrueFalse
.
2.6. TrueFalse (QuizActivity.java)
public class QuizActivity extends Activity {
private
private
private
private
Button mTrueButton;
Button mFalseButton;
Button mNextButton;
TextView mQuestionTextView;
private
new
new
new
new
new
};
TrueFalse TrueFalse.
( .
.
.)
mQuestionBank, mCurrentIndex
TrueFalse .
60
2. Android MVC
TextView .
2.7. TextView (QuizActivity.java)
public class QuizActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
mTrueButton = (Button)findViewById(R.id.true_button);
...
. GeoQuiz.
TextView.
Next. , View.OnClickListener.
TextView.
2.8. (QuizActivity.java)
public class QuizActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
...
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
}
});
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
});
61
mQuestionTextView .
, 2.9. mNextButton onCreate(Bundle)
.
2.9. updateQuestion() (QuizActivity.java)
public class QuizActivity extends Activity {
...
private void updateQuestion() {
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
updateQuestion();
}
});
updateQuestion();
GeoQuiz Next.
, . ,
.
, QuizActivity, :
private void checkAnswer(boolean userPressedTrue)
, ,
: True False.
TrueFalse. ,
.
62
2. Android MVC
QuizActivity.java checkAnswer(boolean),
2.10.
2.10. checkAnswer(boolean) (QuizActivity.java)
public class QuizActivity extends Activity {
...
private void updateQuestion() {
int question = mQuestionBank[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}
private void checkAnswer(boolean userPressedTrue) {
boolean answerIsTrue = mQuestionBank[mCurrentIndex].isTrueQuestion();
int messageResId = 0;
if (userPressedTrue == answerIsTrue) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
checkAnswer(boolean), 2.11.
2.11. checkAnswer(boolean) (QuizActivity.java)
public class QuizActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(new View.OnClickListener() {
});
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT).show();
checkAnswer(true);
}
63
mFalseButton = (Button)findViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(QuizActivity.this,
R.string.correct_toast,
Toast.LENGTH_SHORT).show();
checkAnswer(false);
}
});
mNextButton = (Button)findViewById(R.id.next_button);
...
GeoQuiz .
.
,
GeoQuiz .
. Mac,
. Windows
adb (Android Debug Bridge). Windows
adb, .
, ,
Devices. DDMS,
DDMS Eclipse. Devices
.
AVD, .
, Java
.
, adb. Devices
, , .
. Reset adb. , .
, Android. http://developer.android.com/tools/device.
html forums.bignerdranch.com
.
64
2. Android MVC
, Google Play.
Android 4.1 Settings
Applications. , Unknown sources
.
Android 4.2 SettingsSecurity
Unknown sources.
USB.
Android 4.0 SettingsApplications
Development USB debugging.
Android 4.0 4.1 SettingsDeveloper options.
Android 4.2 Developer options .
, SettingsAbout Tablet/Phone Build Number
7 . Settings, Developer options
USB debugging.
, .
,
http://developer.android.com/tools/device.html.
GeoQuiz , . Eclipse
,
. ; GeoQuiz
. ( , GeoQuiz
, , .)
GeoQuiz , , Next ,
.
(http://www.
bignerdranch.com/solutions/AndroidProgramming.zip).
Eclipse .
02_MVC/GeoQuiz/res.
drawable-hdpi, res/drawable-mdpi drawable-xhdpi.
.
mdpi
(~160 dpi)
hdpi
(~240 dpi)
xhdpi
(~320 dpi)
65
( ldpi,
.)
arrow_right.png arrow_left.png.
, .
, . ,
.
GeoQuiz.
Package Explorer res , .
Package Explorer.
66
2. Android MVC
. 2.8. ( )
. .png,
.jpg .gif, res/drawable, . (,
.)
, , gen/R.java
R.drawable. : R.drawable.arrow_left R.drawable.arrow_right.
,
; .
, .
Android 3 .
.
XML
. Next , .
XML?
, .
activity_quiz.xml Button.
2.12. Next (activity_quiz.xml)
<LinearLayout
... >
...
<LinearLayout
... >
...
</LinearLayout>
<Button
android:id="@+id/next_button"
67
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_question_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
/>
</LinearLayout>
XML .
@string/.
@drawable/.
res , 3.
GeoQuiz .
, , .
, GeoQuiz . GeoQuiz
Next, ,
. ( , Control+F12/
Ctrl+F12.)
. ?
3.
. ,
. ,
.
. ,
Android
Android.
- , ,
. ,
forums.bignerdranch.com. , ,
.
,
Eclipse .
Package Explorer,
Copy, .
68
2. Android MVC
,
Package Explorer .
. TextView
Next , ,
TextView.
. TextView View.OnClickListener,
Button, TextView View.
.
.
, .2.9.
. 2.9. !
. ,
.
. Button ImageButton
, ,
.
69
. 2.10.
ImageButton (
Button).
ImageButton ImageView
Button, TextView.
70
2. Android MVC
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_question_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
android:src="@drawable/arrow_right"
/>
, QuizActivity,
ImageButton.
ImageButton, Eclipse android:contentDescription.
. ,
, ( ).
, android:contentDescription ImageButton.
Activity
Activity . : , .
Activity ,
(
. .3.1
)
, .
Activity ,
.3.1,
( )
onCreate(Bundle).
, .
, onCreate()
(
setContentView(int));
( )
;
;
.
, onCre
ate() Activity
. , Android
. 3.1.
.
Activity
72
3. Activity
Activity
,
QuizActivity.
.
Android android.util.Log
. Log
. :
public static int d(String tag, String msg)
d debug () .
( Log .)
, .
TAG,
. .
QuizActivity.java TAG QuizActivity.
3.1. TAG (QuizActivity.java)
public class QuizActivity extends Activity {
private static final String TAG = "QuizActivity";
}
...
onCreate() Log.d() .
3.2. OnCreate() (QuizActivity.java)
public class QuizActivity extends Activity {
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);
...
Activity
73
3.3. (QuizActivity.java)
} // onCreate(Bundle)
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart() called");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause() called");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume() called");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop() called");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy() called");
}
}
. ,
- onCreate();
.
, @Override.
, .
, :
public class QuizActivity extends Activity {
@Override
public void onCreat(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
}
...
74
3. Activity
Activity onCreat(Bundle),
. ,
QuizActivity.onCreat(Bundle).
LogCat
,
LogCat , Android SDK.
LogCat, WindowShow ViewOther...
Android LogCat,
OK (. 3.2).
LogCat
,
. ,
LogCat
.
,
LogCat (. 3.3).
LogCat ,
.
. 3.2. LogCat
; LogCat
.
Eclipse ,
.3.4. LogCat,
(
Eclipse).
GeoQuiz; LogCat .
.
. TAG LogCat TAG,
QuizActivity.
( , , LogCat
. WindowShow ViewOther... Devices. , , LogCat.)
, TAG.
LogCat + ;
. QuizActivity
QuizActivity by Log Tag: (.3.5).
LogCat
75
. 3.3. LogCat
76
3. Activity
. 3.5. LogCat
OK; ,
QuizActivity (.3.6). GeoQuiz
QuizActivity.
. 3.6. GeoQuiz ,
( , QuizActivity
LogCat.)
. Back, LogCat. onPause(),
onStop() onDestroy().
. 3.7. Back
Back, Android: ,
. Android ,
.
LogCat
77
GeoQuiz. Home
LogCat. onPause() onStop(),
onDestroy().
. 3.8. Home
.
Recents Home (. 3.9).
Recents Home.
Home
Back
Recents
GeoQuiz LogCat.
, .
Home Android: ,
. Android ,
.
78
3. Activity
.
, .
, , .
.
, .
.
.
, 2. GeoQuiz,
Next ,
. ( , Control+F12/Ctrl+F12).
GeoQuiz . ,
, LogCat.
. 3.10. QuizActivity
, QuizActivity, ,
, . .
- . QuizActivity mCurrentIndex 0,
. , , .
. ,
. , ,
79
, , , ,
, .
,
. ,
.
; . ,
(, ) .
, , .
, , ,
Android .
LogCat. ( LogCat,
WindowShow View...)
Package Explorer res
. layout-land.
. 3.11.
80
3. Activity
. 3.12.
81
3.4. (layout-land/activity_quiz.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/question_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:padding="24dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:orientation="horizontal" >
...
</LinearLayout>
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="@string/next_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp"
/>
</LinearLayout>
</FrameLayout>
GeoQuiz. ,
. , ,
QuizActivity.
, .
QuizActivity.
Android ,
. QuizActivity , setContentView(R.layout.activity_quiz).
QuizActivity.onCreate().
82
3. Activity
Android QuizActivity
, .
. 3.13. QuizActivity
, Android
.
(,
), .
Android .
, ,
GeoQuiz.
, QuizActivity, ,
mCurrentIndex.
(, ). Activity
protected void onSaveInstanceState(Bundle outState)
onSaveInstanceState(Bundle)
83
onSaveInstanceState(Bundle)
onSaveInstanceState() , Bundle, onCreate().
mCurrentIndex .
QuizActivity.java , -.
3.5. (QuizActivity.java)
public class QuizActivity extends Activity {
private static final String TAG = "QuizActivity";
private static final String KEY_INDEX = "index";
Button mTrueButton;
...
onSaveInstanceState() mCurrentIndex
Bundle .
3.6. onSaveInstanceState() (QuizActivity.java)
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
}
updateQuestion();
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
Log.i(TAG, "onSaveInstanceState");
savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
}
, onCreate() , mCurrentIndex.
84
3. Activity
updateQuestion();
GeoQuiz Next.
, QuizActivity
.
, Bundle
, Serializable.
, onSaveInstanceState(),
Serializable.
onSaveInstanceState()
. ; .
,
Android .
Activity
onSaveInstanceState(Bundle) . ,
, Android .
Android .
,
. ,
, onSaveInstanceState().
onSaveInstanceState() Bundle. Bundle (activity record).
, ,
(.3.14).
, Activity ,
. .
,
onDestroy(). , onPause()
onSaveInstanceState() . onSaveInstanceState()
Bundle, onPause()
.
: onSaveInstanceState(Bundle)
85
( ;
)
Android
( )
(
)
. 3.14.
Android ,
.
, , .
.
? Back,
. .
;
, .
:
onSaveInstanceState(Bundle)
onSaveInstanceState(Bundle), ,
, .
.
86
3. Activity
.
Settings. , .
. 3.15. Settings
87
. 3.16.
android.util.Log , , , . Android
(.3.1). Log.
Log.
3.1.
ERROR
Log.e()
WARNING
Log.w()
INFO
Log.i()
DEBUG
Log.d()
( )
VERBOSE
Log.v()
88
3. Activity
:
, Throwable,
,
. 3.8
.
Java String.format, .
3.8. Android
// "debug"
Log.d(TAG, "Current question index: " + mCurrentIndex);
TrueFalse question;
try {
question = mAnswerKey[mCurrentIndex];
} catch (ArrayIndexOutOfBoundsException ex) {
// "error"
//
Log.e(TAG, "Index was out of bounds", ex);
}
Android
...
GeoQuiz , .
. 4.1 , .
Android , . , , ,
.
90
4. Android
. 4.1. GeoQuiz
DDMS
Eclipse WindowOpen PerspectiveDDMS.
. 4.2. DDMS
91
(perspective) Eclipse .
,
; Eclipse .
, .
, Eclipse . -
,
WindowReset Perspective...
, , Java. , ,
Eclipse.
.
.4.2 DDMS (Dalvik Debug Monitor Service). DDMS Android. DDMS LogCat Devices.
Devices , . ,
.
, ,
? Reset adb. adb
. , LogCat ?
, , LogCat
.
. , , LogCat. ,
. Android.
;
, , ;
, , .
,
. java.lang.NullPointerException.
. , ,
. ; Eclipse
.
mQuestionTextView updateQuestion(). NullPointerException : .
mQuestionTextView,
.
92
4. Android
. 4.3. LogCat
: ,
LogCat
. ,
.
, .
, . , ,
. , DDMS Eclipse Devices.
LogCat .
. , Next, .
.
93
...
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
//mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
...
GeoQuiz Next. .
. 4.4. Next
. , .
, :
, updateQuestion().
94
4. Android
, .
:
.
updateQuestion()
QuizActivity:
4.3. Exception
public class QuizActivity extends Activity {
...
public void updateQuestion() {
Log.d(TAG, "Updating question text for question #" + mCurrentIndex,
new Exception());
int question = mAnswerKey[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}
. 4.5.
95
,
Exception. , set- onTextChanged().
; ,
. ;
.
, , , ,
.
, LogCat .
, , ,
.
, , , .
http://stackoverflow.com forums.bignerdranch.com, .
LogCat, (
LogCat).
, TAG log
QuizActivity.java.
4.4. , (QuizActivity.java)
public class QuizActivity extends Activity {
...
public void updateQuestion() {
Log.d(TAG, "Updating question text for question #" + mCurrentIndex,
new Exception());
int question = mAnswerKey[mCurrentIndex].getQuestion();
mQuestionTextView.setText(question);
}
TAG
. , ,
.
Eclipse.
updateQuestion(), , .
,
, .
QuizActivity.java updateQuestion().
.
; .
96
4. Android
. 4.6.
,
( ).
GeoQuiz
Debug AsAndroid Application.
, ,
, .
, . GeoQuiz QuizActivity.
onCreate(Bundle), updateQuestion(),
.
,
Debug. Yes,
Debug.
. 4.7. Debug
Eclipse Debug.
. QuizActivity.java,
, .
Debug .
. , updateQuestion()
onCreate(Bundle). Next,
Resume, .
Next, , (
).
, ,
. Variables. .
97
- -
. 4.8. Debug
. 4.9.
:
(public) ;
(
);
(protected) ;
(private) .
98
4. Android
this .
, QuizActivity, ,
, Activity, Activity, ..
: mCurrentIndex.
mCurrentIndex. , 0.
. ,
. , Step Over
( , Step Return). (-
access$1(QuizActivity) Step Return
.)
OnClickListener
mNextButton, updateQuestion().
, .
, - , . :
, .
, DDMS Devices
-.
: Disconnect (. .4.8).
Java OnClickListener .
4.5. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
updateQuestion();
}
});
...
. Breakpoints
Variables. ( ,
WindowShow ViewBreakpoints.) X .
: .
? , ,
.
99
,
. ,
, , , .
.
( Debug AsAndroid Application),
, .
,
.
mQuestionTextView. RunAdd Java
Exception Breakpoint..., .
. 4.10.
,
, .
, .
Android ,
. ,
Suspend on caught exceptions.
100
4. Android
, . RuntimeException . RuntimeException
NullPointerException, ClassCastException
, ,
.
, ,
-. Debug Breakpoints.
RuntimeException.
Subclasses of this exception,
NullPointerException.
GeoQuiz. , , .
, ,
,
. , .
File Explorer
DDMS , .
File Explorer .
. 4.11.
, .
/data, , . Android
/data/data/[ ].
Android
101
. 4.12. GeoQuiz ( )
, 17 , .
Android
Android Java.
, Android
(, ), Java .
Android Lint
Android Lint Android.
, .
Android Lint Android
, . ,
Android Lint .
6 , Android Lint . , Android Line ,
XML. QuizActivity
.
4.6. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
mTrueButton = (Button)findViewById(R.id.true_button);
102
4. Android
4.6 ()
mTrueButton = (Button)findViewById(R.id.question_text_view);
}
...
-
TextView Button ,
. Java , Android
Lint .
Package Explorer GeoQuiz
Android ToolsRun Lint: Check for Common Errors;
Lint Warnings (.4.13).
. 4.13. Lint
Android Lint .
:
Button . onCreate(Bundle).
4.7. (QuizActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
mTrueButton = (Button)findViewById(R.id.question_text_view);
mTrueButton = (Button)findViewById(R.id.true_button);
}
...
Android Lint .
merge layout-land/activity_quiz.xml. , Android Lint
. FrameLayout merge, FrameLayout
.
Android
103
R
, -
( , ).
, Eclipse , .
.
, .
Android Lint
WindowRun Android Lint. Line .
ProjectClean. Eclipse , .
XML
R.java ,
.
XML. ,
.
, R.java .
gen
Eclipse R.java,
gen. Eclipse
gen R.
( ), .
.
Android Lint. .
, Eclipse,
http://stackoverflow.com
http://forums.bignerdranch.com.
GeoQuiz .
, ;
,
.
. 5.1. CheatActivity
, QuizActivity
, .
105
. 5.2. QuizActivity ,
Android?
:
;
( onCreate(Bundle));
() () .
.
CheatActivity, CheatActivity.
strings.xml , .
5.1. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<string name="question_asia">Lake Baikal is the world\'s oldest and deepest
freshwater lake.</string>
<string name="cheat_button">Cheat!</string>
<string name="warning_text">Are you sure you want to do this?</string>
<string name="show_answer_button">Show Answer</string>
<string name="judgment_toast">Cheating is wrong.</string>
</resources>
106
5.
.5.1 , CheatActivity.
.5.3.
. 5.3. CheatActivity
, res/layout
Package Explorer NewOther... Android
Android XML Layout File (.5.4). Next.
. 5.4.
107
. 5.5.
, XML. :
<?xml version="1.0" encoding="utf-8"?>
XML,
, ( ).
( , ,
. fragment_crime.xml res/layout res/layout, Eclipse .
Eclipse;
XML Java , .
, , Android).
LinearLayout . android:gravity .
108
5.
. 5.6. activity_cheat.xml
5.2. (activity_cheat.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="@string/warning_text_view" />
<TextView
android:id="@+id/answerTextView"
109
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp" />
<Button
android:id="@+id/showAnswerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show_answer_button" />
</LinearLayout>
, ,
.
activity_cheat.xml
, ,
.
,
. , .
,
.
Package Explorer com.bignerdranch.
android.geoquiz NewClass.
CheatActivity. Superclass: android.app.Activity (.5.7).
Finish; Eclipse CheatActivity.java .
onCreate(), ,
activity_cheat.xml, setContentView().
5.3. onCreate() (CheatActivity.java)
public class CheatActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
}
}
110
5.
. 5.7. CheatActivity
(manifest) XML ,
Android. AndroidManifest.xml .
Package Explorer AndroidManifest.xml . ,
AndroidManifest.xml .
,
.
QuizActivity, . CheatActivity
.
AndroidManifest.xml CheatActivity (5.4).
5.4. CheatActivity (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.geoquiz"
Cheat QuizActivity
111
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.bignerdranch.android.geoquiz.QuizActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CheatActivity"
android:label="@string/app_name" />
</application>
</manifest>
android:name .
, ,
package manifest .
,
CheatActivity. .
Cheat QuizActivity
, QuizActivity,
CheatActivity. ,
layout/activity_quiz.xml layout-land/activity_quiz.xml.
LinearLayout.
Next.
5.5. Cheat! (layout/activity_quiz.xml)
...
</LinearLayout>
<Button
android:id="@+id/cheat_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
112
5.
5.5 ()
android:text="@string/cheat_button" />
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next_button" />
</LinearLayout>
FrameLayout.
5.6. Cheat! (layout-land/activity_quiz.xml)
...
</LinearLayout>
<Button
android:id="@+id/cheat_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:text="@string/cheat_button" />
<Button
android:id="@+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="@string/next_button"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="4dp" />
</FrameLayout>
});
}
113
// CheatActivity
updateQuestion();
...
CheatActivity.
,
Activity:
public void startActivity(Intent intent)
, startActivity() ,
Activity. ,
. startActivity(), .
, , ActivityManager.
ActivityManager Activity onCreate().
Android
. 5.8.
ActivityManager , ? Intent.
(intent) , . ,
(services), (broadcast
receivers) (content providers).
114
5.
,
Intent ,
.
ActivityManager, , :
public Intent(Context packageContext, Class<?> cls)
Class , ActivityManager.
Context ActivityManager, Class.
Android
. 5.9. : ActivityManager ,
});
}
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
startActivity(i);
}
updateQuestion();
, ActivityManager , Class. , .
, ActivityNotFoundException .
.
115
Intent Class Context (explicit)
. .
, ActivityManager, .
.
, (implicit) .
21.
GeoQuiz. Cheat!;
.
Back. CheatActivity , QuizActivity.
, QuizActivity CheatActivity,
. . 5.10,
.
True?
QuizActivity CheatActivity
CheatActivity.
116
5.
CheatActivity ,
mAnswerKey[mCurrentIndex].isTrueQuestion();
. 5.11. :
- , mCurrentIndex QuizActivity.
onSaveInstanceState(Bundle).
Intent.putExtra()
,
public Intent putExtra(String name, boolean value)
Intent.putExtra() ,
. String,
.
CheatActivity.java .
5.9. (CheatActivity.java)
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE =
"com.bignerdranch.android.geoquiz.answer_is_true";
...
117
, , .
5.9,
.
QuizActivity .
5.10. (QuizActivity.java)
...
mCheatButton.setOnClickListener(new View.OnClickListener() {
});
}
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(i);
}
updateQuestion();
,
Intent .
public boolean getBooleanExtra(String name, boolean defaultValue)
getBooleanExtra() ,
, .
CheatActivity .
5.11. (CheatActivity.java)
public class CheatActivity extends Activity {
...
private boolean mAnswerIsTrue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
: Activity.getIntent() Intent,
startActivity(Intent).
, CheatActivity , TextView Show Answer.
118
5.
5.12. (CheatActivity.java)
public class CheatActivity extends Activity {
...
private boolean mAnswerIsTrue;
private TextView mAnswerTextView;
private Button mShowAnswer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cheat);
mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
mAnswerTextView = (TextView)findViewById(R.id.answerTextView);
mShowAnswer = (Button)findViewById(R.id.showAnswerButton);
mShowAnswer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnswerIsTrue) {
mAnswerTextView.setText(R.string.true_button);
} else {
mAnswerTextView.setText(R.string.false_button);
}
}
});
: TextView TextView.
setText(int). TextView.setText() ;
, .
GeoQuiz. Cheat!, CheatActivity. Show Answer, .
.
; CheatActivity
QuizActivity, .
,
Activity:
public void startActivityForResult(Intent intent, int requestCode)
, . ,
, .
,
, .
119
QuizActivity mCheatButton
startActivityForResult(Intent, int).
5.13. startActivityForResult() (QuizActivity.java)
...
mCheatButton.setOnClickListener(new View.OnClickListener() {
});
}
@Override
public void onClick(View v) {
Intent i = new Intent(QuizActivity.this, CheatActivity.class);
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();
i.putExtra(CheatActivity.EXTRA_ANSWER_IS_TRUE, answerIsTrue);
startActivity(i);
startActivityForResult(i, 0);
}
updateQuestion();
QuizActivity .
,
0.
,
:
public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent data)
120
5.
QuizActivity
. , Intent, , Activity.setResult(int, Intent)
QuizActivity.
,
CheatActivity CheatActivity. , CheatActivity QuizActivity. ,
CheatActivity.
CheatActivity ,
, CheatActivity.
CheatActivity ,
, .
Show Answer.
5.14. (CheatActivity.java)
public class CheatActivity extends Activity {
public static final String EXTRA_ANSWER_IS_TRUE =
"com.bignerdranch.android.geoquiz.answer_is_true";
public static final String EXTRA_ANSWER_SHOWN =
"com.bignerdranch.android.geoquiz.answer_shown";
...
private void setAnswerShownResult(boolean isAnswerShown) {
Intent data = new Intent();
data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
setResult(RESULT_OK, data);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// ,
//
setAnswerShownResult(false);
...
mShowAnswer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnswerIsTrue) {
mAnswerTextView.setText(R.string.true_button);
} else {
mAnswerTextView.setText(R.string.false_button);
}
setAnswerShownResult(true);
}
});
121
QuizActivity,
, setResult().
.5.12.
Android
Cheat!,
onClick(View)
Show Answer,
setResult()
Back
. 5.12. GeoQuiz
QuizActivity.java , CheatActivity. onActivityResult()
.
122
5.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data == null) {
return;
}
mIsCheater = data.getBooleanExtra(CheatActivity.EXTRA_ANSWER_SHOWN, false);
}
...
onActivityResult() QuizActivity ,
.
, .
, checkAnswer(boolean) QuizActivity. , , .
5.16. mIsCheater (QuizActivity.java)
private void checkAnswer(boolean userPressedTrue) {
boolean answerIsTrue = mAnswerKey[mCurrentIndex].isTrueQuestion();
int messageResId = 0;
if (mIsCheater) {
messageResId = R.string.judgment_toast;
} else {
if (userPressedTrue == answerIsTrue) {
messageResId = R.string.correct_toast;
} else {
messageResId = R.string.incorrect_toast;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mAnswerKey.length;
Android
});
...
123
mIsCheater = false;
updateQuestion();
GeoQuiz. ,
.
Android
,
. , GeoQuiz ,
, . ,
. GeoQuiz
QuizActivity.
GeoQuiz, QuizActivity
. intent-filter QuizActivity (5.17).
5.17. QuizActivity (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...
<application
... >
<activity
android:name="com.bignerdranch.android.geoquiz.QuizActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".CheatActivity"
android:label="@string/app_name" />
</application>
</manifest>
QuizActivity ,
Cheat!. CheatActivity QuizActivity.
(.5.13).
Back CheatActivity ,
QuizActivity .
124
5.
Cheat!
Back
. 5.13. GeoQuiz
GeoQuiz
Eclipse
Back
GeoQuiz
125
Back
. 5.15. GeoQuiz
Back ,
.
, ActivityManager (back stack)
.
; ,
ActivityManager
, .
, .
, ,
. , ,
.
GeoQuiz ,
. .
,
:
, CheatActivity, .
QuizActivity, mIsCheater.
Next , ,
, .
!
Android SDK
, GeoQuiz, Android.
,
.
Android SDK
6.1 SDK, And
roid, , , 2013 .
6.1. API Android,
API
17
Jelly Bean
4.2
1,6
4.1
14,9
16
15
4.0.3, 4.0.4
28,6
13
Honeycomb ( )
3.2
0,9
3.1.x
0,3
2.3.32.3.7
43,9
0,2
12
10
Gingerbread
9
8
Froyo
2.2.x
7,5
Eclair
2.1.x
1,9
Android
127
Android
- Android.
, Android , Android: Froyo,
Gingerbread, Honeycomb, Ice Cream Sandwich Jelly Bean,
-.
, . , Android
. ,
( 22).
, Google TV ( Android)
, .
128
6. Android SDK
.
,
.
Honeycomb
Android Honeycomb. Honeycomb
Android, . Honeycomb
Google TV, ,
Ice
Cream Sandwich. , , .
,
Gingerbread ,
, .
, , .
, Android Gingerbread (API 10)
Honeycomb (API 11). Android
; , .
Android.
, . ,
, (
) .
( , ).
Android,
Gingerbread . ,
.
GeoQuiz
SDK. (, Android SDK API .)
, , .
SDK (Minimum Required SDK)
SDK (Target SDK) . AndroidManifest.xml
Package Explorer. uses-sdk android:minSdkVersion
android:targetSdkVersion.
6.1. minSdkVersion (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.geoquiz"
Android
129
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
...
</manifest>
. 6.1. ?
130
6. Android SDK
API
SDK SDK , . ,
GeoQuiz , SDK
Froyo (API 8)?
Froyo, .
Android
131
. 6.2.
. Android Lint ,
, .
, SDK, Android Lint
.
GeoQuiz API 8 .
API 11 , .
QuizActivity.java. onCreate(Bundle)
, .
6.2. (QuizActivity.java)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);
132
6. Android SDK
6.2 ()
ActionBar actionBar = getActionBar();
actionBar.setSubtitle("Bodies of Water");
mIsCheater = false;
. (
), .
, ActionBar.
API 11,
. ActionBar
16, , Froyo.
GeoQuiz Package Explorer
Android ToolsRun Lint: Check for Common Errors. API 17, .
, Android Lint SDK .
: Class requires API level11
(current min is8). Android Lint ,
.
? SDK
11. , .
Gingerbread , .
, ActionBar , Android .
6.3. Android
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);
Build.VERSION.SDK_INT Android .
, Honeycomb. (
http://developer.android.com/reference/android/os/Build.VERSION_CODES.
html.)
ActionBar ,
Honeycomb .
Android
133
Lint
, Android Lint , , .
onCreate(Bundle).
6.4. Android Lint
@TargetApi(11)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate() called");
setContentView(R.layout.activity_quiz);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
actionBar.setSubtitle("TFFTT");
}
}
. 6.3.
, if ,
Android .
. Android Lint . ,
, .
134
6. Android SDK
6.4
. if : getActionBar(),
if, ,
. @TargetApi(11) (Android
Lint), : .
, , .
, @TargetApi, ,
SDK_INT. ,
.
GeoQuiz Honeycomb
.
GeoQuiz Froyo Gingerbread ( ). ,
, .
Android
Android Lint , API .
, API
, Android.
. Android
SDK , , , ,
.
Android . http://developer.android.com/.
: (Design), (Develop)
(Distribute).
.
.
, Google
Play . ,
.
.
Android Training
API Guides
Reference
, , , .. SDK,
Tools
Android
135
.
, SDK;
docs .
, API getActionBar(),
.
API .
.
, Reference .
;
Activity. .
Methods, Activity.
. 6.4. Activity
, getActionBar() ,
. , getActionBar() API 11.
, Activity , , API 8,
API. ,
136
6. Android SDK
, API level:17.
8. , Android
API 8, .
, .
,
,
, .. Android
, - .
.
GeoQuiz TextView API ,
.
TextView , . TextView
TextView Android. ,
( CharSequence).
137
. 6.6.
XML, TextView.
UI-
FragmentManager
CriminalIntent.
: , , .
. 7.1. CriminalIntent
139
CriminalIntent , .
, Twitter,
Facebook . , .
CriminalIntent ,
13. /: .
.
, /
:
. .
Back
, .
, ,
?
, CriminalIntent .
.
. 7.2. /
. ,
,
. .
:
, .
140
7. UI- FragmentManager
.
. , setContentView(), .
( )
.
Android.
Android , ,
(fragments).
,
.
.
, , UI. UI- , .
, .
,
, .
, ,
.
, Android .
, /.
.
.
. ; (. 7.3).
.
UI-
, /.
, .
, :
, , .
11 22,
.
CriminalIntent
141
. 7.3.
CriminalIntent
CriminalIntent. .7.4
, CriminalIntent .
. 7.4. CriminalIntent
. ,
.
, .7.4, UI- CrimeFragment. (host) CrimeFragment
CrimeActivity.
, , (.7.5).
142
7. UI- FragmentManager
.
.
CriminalIntent ;
. .7.6 CriminalIntent.
, ,
, .
, CrimeFragment , GeoQuiz
: ,
.
()
. 7.6. CriminalIntent ( )
143
Activity.
Crime .
. (,
- !),
Crime.
Crime.
CrimeFragment (mCrime) .
CrimeActivity FrameLayout,
, CrimeFragment.
CrimeFragment LinearLayout EditText.
CrimeFragment EditText (mTitleField)
, .
; . Android (NewAndroid Application Project). CriminalIntent com.bignerdranch.android.criminalintent,
. 7.7. API ,
Froyo.
. 7.7. CriminalIntent
144
7. UI- FragmentManager
,
, Next.
Next.
, CrimeActivity Finish
(.7.8).
. 7.8. CrimeActivity
API 11 Android
. CriminalIntent SDK 8,
Android.
, :
Android.
(support library) .
libs/android-support-v4.jar, . Fragment (android.support.v4.app.Fragment),
Android API 4 .
; .
FragmentActivity (android.
support.v4.app.FragmentActivity). , . Honeycomb
145
Android Activity .
Activity
Activity. API FragmentActivity Activity,
Activity Android.
Android 4.2
Android 2.3
Android 4.2
Android 2.3
. 7.9.
. 7.9 .
( android.support.v4.app.Fragment)
, ,
.
Package Explorer CrimeActivity.java. CrimeActivity FragmentActivity.
onCreateOptionsMenu(Menu). ( CriminalIntent 16.)
7.1. (CrimeActivity.java)
public class CrimeActivity extends Activity FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
146
7. UI- FragmentManager
7.1 ()
getMenuInflater().inflate(R.menu.activity_crime, menu);
return true;
Crime
Package Explorer com.bignerdranch.android.criminalintent NewClass.
Crime, java.lang.Object Finish.
Crime.java .
7.2. Crime (Crime.java)
public class Crime {
private UUID mId;
private String mTitle;
public Crime() {
//
mId = UUID.randomUUID();
}
mId, ,
get-, mTitle get- set-.
SourceGenerate Getters and Setters. get- mId,
, ,
getId(), .7.10.
7.3. get- set- (Crime.java)
public class Crime {
private UUID mId;
private String mTitle;
public Crime() {
mId = UUID.randomUUID();
}
public UUID getId() {
return mId;
}
public String getTitle() {
UI-
147
return mTitle;
, Crime CriminalIntent
.
, , Froyo Gingerbread.
, .
UI-
UI-, :
;
.
148
7. UI- FragmentManager
. 7.11 . : ,
, ,
,
.
. 7.11.
.
, . ,
.
, -, . , ;
.
UI-
149
,
CriminalIntent.
UI- :
;
.
, .
,
.
, , 13.
,
. , . ,
, .
, .
, CrimeActivity CrimeFragment. ,
CrimeActivity.
UI- -,
.
CrimeActivity FrameLayout, .7.12.
. 7.12. CrimeActivity
FrameLayout CrimeFragment.
: ;
CrimeFragment. ( )
.
150
7. UI- FragmentManager
activity_crime.xml , ;
, .
CriminalIntent, .
FrameLayout, CrimeActivity
.
. 7.13. FrameLayout
,
FrameLayout. .
UI-
151
UI-
UI- :
;
, , ;
, .
CrimeFragment
CrimeFragment , Crime. Crime CrimeFragment
,
.
. 7.14 CrimeFragment. LinearLayout, EditText
.
. 7.14. CrimeFragment
, res/layout
Package Explorer NewAndroid XML File. ,
Layout,
fragment_crime.xml. LinearLayout
Finish.
, XML.
LinearLayout . .7.14,
fragment_crime.xml. 7.5.
152
7. UI- FragmentManager
7.5. (fragment_crime.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<EditText android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/crime_title_hint"
/>
</LinearLayout>
res/values/strings.xml, crime_title_hint
hello_world menu_settings, .
7.6. (res/values/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CriminalIntent</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_crime">CrimeActivity</string>
<string name="crime_title_hint">Enter a title for the crime.</string>
</resources>
. menu_settings
. , Package Explorer
res/menu/activity_crime.xml. ,
, menu_settings.
CriminalIntent,
Package Explorer.
Eclipse .
. , fragment_crime.xml.
CrimeFragment
com.bignerdranch.android.criminalintent
NewClass. CrimeFragment
Browse, .
Fragment. . android.
support.v4.app.Fragment Fragment .
OK.
( android.support.v4.app.Fragment, ,
CriminalIntent CriminalIntent/libs/.)
UI-
153
. 7.15. Fragment
CrimeFragment , -
.
.
GeoQuiz . CriminalIntent
.
Activity,
onCreate(Bundle).
CrimeFragment.java Crime
Fragment.onCreate(Bundle).
7.7. Fragment.onCreate(Bundle) (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}
154
7. UI- FragmentManager
. -, Fragment.onCreate(Bundle) , Activity.
onCreate(Bundle) . Fragment.onCreate()
Fragment , , .
-, , Bundle
. Fragment.on
SaveI nstanceState(Bundle) , Activity.onSave
InstanceState(Bundle).
, Fragment.onCreate(): . Fragment.
onCreate(),
:
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState)
,
View . LayoutInflater ViewGroup
. Bundle , .
CrimeFragment.java onCreateView(),
fragment_crime.xml.
7.8. onCreateView() (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCrime = new Crime();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
return v;
}
}
onCreateView() ,
LayoutInflater.inflate() . ,
. ,
. false,
.
UI-
155
onCreateView() EditText
. ,
EditText .
7.9. EditText (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(
CharSequence c, int start, int before, int count) {
mCrime.setTitle(c.toString());
}
public void beforeTextChanged(
CharSequence c, int start, int count, int after) {
//
}
});
return v;
Fragment.onCreateView() ,
Activity.onCreate(). , View.findViewById(int). Activity.
findViewById(int), ,
, View.findViewById(int) .
Fragment ,
.
, . 7.9 ,
TextWatcher. , :
onTextChanged().
onTextChanged() toString() CharSequence,
. ,
Crime.
156
7. UI- FragmentManager
CrimeFragment . ,
CriminalIntent . ,
.
, CrimeFragment
CrimeActivity.
UI- FragmentManager
Honeycomb Fragment, Activity
: , FragmentManager.
.
FragmentManager :
( ).
. 7.16. FragmentManager
CriminalIntent FragmentManager.
, FragmentManager .
FragmentManager. CrimeActivity.java
onCreate().
7.10. FragmentManager (CrimeActivity.java)
public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
FragmentManager fm = getSupportFragmentManager();
UI-
157
getSupportFragmentManager(),
FragmentActivity. , Honeycomb,
Activity getFragmentManager().
FragmentManager ,
. (
, .)
7.11. CrimeFragment (CrimeActivity.java)
public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
, 7.11, .
add() .
.
7.12. (CrimeActivity.java)
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
, , ,
.
. FragmentManager ,
.
FragmentManager.beginTransaction() FragmentTransaction . FragmentTransaction , FragmentTransaction ,
158
7. UI- FragmentManager
FragmentTransaction void, .
, 7.12 : , add, .
add() .
:
CrimeFragment.
: FrameLayout,
activity_crime.xml.
:
FragmentManager, .
FragmentManager.
CrimeFragment FragmentManager, .
7.13.
(CrimeActivity.java)
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
, FragmentManager CrimeFragment
FrameLayout. UI-
FragmentManager.
, 7.11,
.
FragmentManager R.id.fragmentContainer.
, FragmentManager .
? CrimeActivity.onCreate() CrimeActivity
- .
FragmentManager . FragmentManager , , .
, , fragment null.
UI-
159
CrimeFragment ,
.
CrimeActivity CrimeFragment. ,
CriminalIntent. ,
fragment_crime.xml (. 7.17).
,
, . ,
CriminalIntent .
FragmentManager
FragmentManager
.
FragmentManager
. onAttach(Activity), onCreate(Bundle) onCreateView() FragmentManager.
onActivityCreated() onCreate()
-. CrimeFragment CrimeActivity.onCreate(),
.
160
7. UI- FragmentManager
. 7.18. ()
, ,
, ? FragmentManager ,
. ,
, ,
onAttach(Activity), onCreate(Bundle), onCreateView(), onActivityCreated(Bundle),
onStart() onResume().
,
FragmentManager -
.
,
.
: Activity.onCreate(),
onActivityCreated() Activity.onCreate().
161
Activity.onStart(). ? SDK
Honeycomb onActivityCreated()
FragmentActivity,
. ; onStart()
Activity.onCreate().
. :
, ,
.
; , .
, ,
.
- , ,
, ( ).
YAGNI.
YAGNI You Arent Gonna Need It ( );
, , , . ? YAGNI. YAGNI
.
, . UI-
, .
, ,
, .
, ,
.
, , , : AUF,
Always Use Fragments ( ). , ,
. AUF!
:
Honeycomb,ICS, Jelly Bean ..
,
SDK API 11.
SDK,
162
7. UI- FragmentManager
.
.
,
:
SDK API
11 .
Activity (android.app.Activity) FragmentActivity. API 11
.
android.app.Fragment android.support.v4.app.Fragment.
FragmentManager, getFragmentManager() getSupportFragmentManager().
,
CriminalIntent.
Crime
Crime.java . Date
, mSolved , .
8.1. Crime (Crime.java)
public class Crime {
private UUID mId;
private String mTitle;
private Date mDate;
private boolean mSolved;
public Crime() {
mId = UUID.randomUUID();
mDate = new Date();
}
...
Date Date
mDate .
get- set- (SourceGenerate
Getters and Setters...).
8.2. get- set- (Crime.java)
public class Crime {
...
public void setTitle(String title) {
mTitle = title;
}
164
8.
8.2 ()
fragment_crime.xml CrimeFragment.java.
CrimeFragment .
. 8.1. CriminalIntent, 2
Crime
165
8.3. (fragment_crime.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_title_label"
style="?android:listSeparatorTextViewStyle"
/>
<EditText android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:hint="@string/crime_title_hint"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"
style="?android:listSeparatorTextViewStyle"
/>
<Button android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
/>
<CheckBox android:id="@+id/crime_solved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="@string/crime_solved_label"
/>
</LinearLayout>
: Button android:text.
, Crime,
.
Button? .
.
12 ,
DatePicker, .
, ,
style margin. CriminalIntent
.
res/values/strings.xml .
166
8.
8.4. (strings.xml)
<resources>
<string name="app_name">CriminalIntent</string>
<string name="title_activity_crime">CrimeActivity</string>
<string name="crime_title_hint">Enter a title for this crime.</string>
<string name="crime_title_label">Title</string>
<string name="crime_details_label">Details</string>
<string name="crime_solved_label">Solved?</string>
</resources>
CheckBox , . CheckBox mSolved Crime.
Button mDate.
CrimeFragment.java .
8.5. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private Crime mCrime;
private EditText mTitleField;
private Button mDateButton;
private CheckBox mSolvedCheckBox;
@Override
public void onCreate(Bundle savedInstanceState) {
...
, DatePicker CheckBox.
onCreateView() ,
.
8.6. Button (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mTitleField.addTextChangedListener(new TextWatcher() {
...
});
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);
}
return v;
167
, .
, . 12
.
CheckBox: ,
mSolved Crime.
8.7. CheckBox (CrimeFragment.java)
...
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
});
}
//
mCrime.setSolved(isChecked);
return v;
XML
, fragment_crime.xml,
.
,
(style) XML, ,
. ,
, .
168
8.
<style name="BigTextStyle">
<item name="android:textSize">20sp</item>
<item name="android:layout_margin">3dp</item>
</style>
( 24). res/values/,
: @style/my_own_style.
TextView fragment_crime.xml;
style, , Android. TextView ,
. (theme) .
,
.
Android ,
. CriminalIntent
Holo Light with Dark Action Bar, .
(theme attribute reference). fragment_crime.xml, ?android:listSeparatorTextViewStyle.
Android:
listSeparatorTextViewStyle.
. .
Android listSeparatorTextViewStyle,
.
, TextView .
, , 24.
, dp sp
fragment_crime.xml margin dp.
; , .
(
, , ).
, . .
,
.
Android
, drawable-ldpi, drawable-mdpi drawable-hdpi.
, , ?
?
169
Android ,
; ,
. Android , :
dp ( dip) density-independent pixel (, ); .
, ,
. dp . dp
1/160 .
.
sp scale-independent pixel (, ). , ,
. sp
.
pt, mm, in ( dp),
(1/72 ), .
:
.
dp sp. Android .
Android
8.3 16dp .
Android,
170
8.
48dp. http://developer.
android.com/design/index.html.
Android , .
Android SDK,
.
,
, ActionBarSherlock, 16.
, ,
layout_ (android:layout_marginLeft),
(android:text).
, layout_,
. .
layout_,
. , ,
,
.
(, LinearLayout) ,
, , .
LinearLayout fragment_crime.xml,
android:layout_width android:layout_height.
LinearLayout .
LinearLayout FrameLayout
CrimeActivity.
fragment_crime.xml margin padding. ,
. , , ,
, . margin
; .
, .
, . android:padding ,
. ,
. Button ,
.
171
8.8.
<Button android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:padding="80dp"
/>
. 8.3.
, .
, XML.
CrimeFragment.
, LinearLayout .
.
CriminalIntent ,
CrimeFragment .
172
8.
. 8.4. CrimeFragment
; ,
.
res/layout-land ( res/
Package Explorer NewFolder), fragment_crime.xml res/layout-land/.
, res/
layout/fragment_crime.xml, . res/layout-land/
fragment_crime.xml Graphical Layout.
. .
, .
. 8.5.
173
.
.
.
, .
? .8.6.
. 8.6. CrimeFragment
:
LinearLayout .
LinearLayout.
Button CheckBox LinearLayout.
Button CheckBox.
,
. Layouts . LinearLayout
(Horizontal) .
LinearLayout LinearLayout, LinearLayout.
. Layout
, ,
.
174
8.
LinearLayout ,
. Layout Parameters, Margins.
, LinearLayout .
Left
16dp. (Right)
.
(
.
, ,
. , , XML
margin EditText
LinearLayout).
. 8.8.
XML fragment_crime.
xml
. LinearLayout .
175
Button CheckBox
LinearLayout. , Button LinearLayout.
,
Button LinearLayout .
CheckBox.
,
.
, :
.
CheckBox Button. LinearLayout (match_par. 8.9. Button
ent) (Button)
CheckBox
, Check LinearLayout
Box (. 8.10).
176
8.
LinearLayout,
.
. Width wrap_content.
16dp . ,
LinearLayout,
.
Weight Layout Parameters
1. android:layout_weight .8.6.
CheckBox
: Width wrap_content, Weight 1,
.
, .
XML, . 8.11.
. 8.9
wrap_content
XML.
8.9.
XML ,
(layout-land/fragment_crime.xml)
...
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"
style="?android:listSeparatorTextViewStyle"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" >
<Button
android:id="@+id/crime_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
<CheckBox
android:id="@+id/crime_solved"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/crime_solved_label" />
</LinearLayout>
</LinearLayout>
177
android:layout_weight
android:layout_weight LinearLayout,
. , ,
. LinearLayout
layout_width layout_weight.
LinearLayout .
LinearLayout layout_width ( layout_height ). layout_width Button, CheckBox
wrap_content, ,
(. 8.12).
( , , .
, LinearLayout,
.)
. 8.12. 1: layout_width
LinearLayout
layout_weight (. 8.13).
. 8.13. 2:
layout_weight
. 8.14.
layout_weight 2:1
. .
178
8.
fragment_crime.xml . -
, 1.0 100;
0.66 66 .
, LinearLayout 50% ? ,
layout_width 0dp wrap_content.
LinearLayout layout_weight
(. 8.15).
, Android
ADT. XML.
XML (
).
. , ,
.8.6. ,
XML, .
, CriminalIntent, , .
,
.
,
null:
, , , android:id ,
.
179
.
Date (timestamp),
. toString() Date , . ,
,
(, Oct 12, 2012).
android.text.format.DateFormat. Android.
DateFormat
.
, (Tuesday, Oct 12, 2012).
ListFragment
. 9.1.
CriminalIntent
181
. 9.2 CriminalIntent .
. 9.2. CriminalIntent
CrimeLab,
Crime.
CriminalIntent
: CrimeListActivity CrimeListFragment.
CrimeListFragment ListFragment Fragment,
. CrimeLab .
( CrimeActivity CrimeFragment . 9.2?
, . 10 CriminalIntent.)
. 9.2 , CrimeListActivity
CrimeListFragment. FrameLayout,
. ListView. ListFragment ListView
.
CriminalIntent
CriminalIntent
Crime Crime.
ArrayList<E> Java,
. , .
182
9. ListFragment
-
(singleton) . .
, , ,
, .
,
get(), . ,
get() . , get() .
com.bignerdranch.android.criminalintent NewClass. CrimeLab Finish.
CrimeLab.java CrimeLab get(Context).
9.1. (CrimeLab.java)
public class CrimeLab {
private static CrimeLab sCrimeLab;
private Context mAppContext;
private CrimeLab(Context appContext) {
mAppContext = appContext;
}
183
Context . .
, .
CrimeLab Crime . CrimeLab ArrayList Crime.
: getCrimes() , getCrime(UUID)
Crime ( 9.2).
9.2. ArrayList Crime (CrimeLab.java)
public class CrimeLab {
private ArrayList<Crime> mCrimes;
private static CrimeLab sCrimeLab;
private Context mAppContext;
private CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
}
public static CrimeLab get(Context c) {
...
}
public ArrayList<Crime> getCrimes() {
return mCrimes;
}
ArrayList Crime, , .
100 Crime ( 9.3).
9.3. (CrimeLab.java)
private CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
for (int i = 0; i < 100; i++) {
Crime c = new Crime();
c.setTitle("Crime #" + i);
c.setSolved(i % 2 == 0); //
mCrimes.add(c);
}
}
184
9. ListFragment
100
.
ListFragment
CrimeListFragment. Browse,
. ListFragment android.support.v4.app,
Finish, CrimeListFragment.
ListFragment Honeycomb, . , ,
android.support.v4.app.ListFragment.
CrimeListFragment.java onCreate(Bundle),
, .
9.4.
onCreate(Bundle)
(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
}
}
getActivity(). Fragment
-
. Activity.setTitle(int), (
) .
onCreateView() CrimeListFragment. ListFragment , ListView; .
CrimeListFragment.onCreateView()
.
strings.xml .
9.5. (strings.xml)
...
<string name="crime_solved_label">Solved?</string>
<string name="crimes_title">Crimes</string>
</resources
CrimeListFragment ,
CrimeLab. CrimeListFragment.onCreate()
CrimeLab .
185
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivity().setTitle(R.string.crimes_title);
mCrimes = CrimeLab.get(getActivity()).getCrimes();
}
CrimeListActivity,
CrimeListFragment.
CrimeListActivity.
CrimeListActivity ,
activity_crime.xml (9.7). FrameLayout
,
.
9.7. activity_crime.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
activity_crime.xml , ,
. activity_fragment.xml, .
activity_crime.xml ( ). Package
Explorer res/layout/activity_crime.xml. (
activity_crime.xml, fragment_crime.xml.)
RefactorRename... activity_fragment.xml. .
ADT
. Eclipse CrimeActivity.java,
CrimeActivity, 9.8.
9.8. CrimeActivity (CrimeActivity.java)
public class CrimeActivity extends FragmentActivity {
/** . */
186
9. ListFragment
9.8 ()
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crime);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
Activity
CrimeListActivity CrimeActivity. , CrimeActivity (9.8):
. , -
: CrimeFragment FragmentManager.
9.9. CrimeActivity (CrimeActivity.java)
public class CrimeActivity extends FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
, , . ,
.
SingleFragmentActivity CriminalIntent.
FragmentActivity abstract,
SingleFragmentActivity (. 9.3).
187
. 9.3. SingleFragmentActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
188
9. ListFragment
activity_fragment.xml.
FragmentManager ,
, .
9.10 CrimeActivity createFragment(), .
SingleFragmentActivity ,
, .
CrimeListActivity.
CrimeListActivity. SingleFragmentActivity .
Browse, SingleFragmentActivity, Eclipse . Finish.
. 9.4. SingleFragmentActivity
Eclipse CrimeListActivity.java,
createFragment(). CrimeListFragment.
9.11. CrimeListActivity (CrimeListActivity.java)
public class CrimeListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
}
189
if (fragment == null) {
fragment = new CrimeFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
@Override
protected Fragment createFragment() {
return new CrimeFragment();
}
SingleFragmentActivity
. ,
.
CrimeListActivity
, CrimeListActivity , .
, ,
CriminalIntent; ,
CrimeListActivity .
CrimeListActivity CrimeActivity CrimeListActivity,
9.13.
9.13. CrimeListActivity (AndroidManifest.xml)
...
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name=".CrimeListActivity">
190
9. ListFragment
9.13 ()
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".CrimeActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
. 9.5. CrimeListActivity
ListView , ListFragment
. CrimeListFragment Crime, , ListView.
.
191
TextView
192
9. ListFragment
, . ListView ,
.
ListView ? (adapter) , ListView , ListView.
:
;
;
ListView.
, Adapter.
ArrayAdapter<T> ,
( ArrayList) T.
.9.7 ArrayAdapter<T>.
.
. 9.7. ArrayAdapter<T>
ListView ,
. . 9.8
ListView .
ListView ,
getCount() ( ,
).
ArrayAdapter<T>
193
. 9.8. ListView-Adapter
ArrayAdapter<T>
ArrayAdapter<T> CrimeListFragment :
public ArrayAdapter(Context context, int textViewResourceId, T[] objects)
Context,
.
, ArrayAdapter .
.
CrimeListFragment.java ArrayAdapter<T>
ListView CrimeListFragment (9.14).
194
9. ListFragment
ArrayAdapter<Crime> adapter =
new ArrayAdapter<Crime>(getActivity(),
android.R.layout.simple_list_item_1,
mCrimes);
setListAdapter(adapter);
setListAdapter(ListAdapter) ListFragment,
ListView,
CrimeListFragment.
, (android.R.layout.simple_list_item_1),
,
Android SDK. TextView.
9.15. android.R.layout.simple_list_item_1
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/listItemFirstLineStyle"
android:paddingTop="2dip"
android:paddingBottom="3dip"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
,
TextView.
, ListFragment, . ListView ,
.
CriminalIntent. . , , ,
.
ArrayAdapter<T>.getView() toString().
, Crime, toString()
, TextView.
Crime toString() ,
java.lang.Object,
.
Crime,
Crime.java toString() ,
.
ArrayAdapter<T>
195
CriminalIntent. .
. 9.9.
. 9.10.
ListView getView()
, .
,
ListFragment:
public void onListItemClick(ListView l, View v, int position, long id)
196
9. ListFragment
: ,
, onListItemClick().
CrimeListFragment.java onListItemClick().
Crime , ,
Crime.
9.17.
onListItemClick()
Crime (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = (Crime)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
}
getListAdapter() ListFragment, ,
ListFragment. getItem(int) position
onListItemClick() Crime.
CriminalIntent. ,
Crime.
Crime Crime.toString().
, , .
:
XML , ;
ArrayAdapter<T>, , , .
CriminalIntent
, (. 9.11).
TextView CheckBox.
, . Package Explorer res/
layout NewOther...Android XML File.
Layout, list_item_crime.xml,
RelativeLayout Finish.
RelativeLayout -
197
. , CheckBox
RelativeLayout. TextView
CheckBox.
. 9.11. .9.12
. CheckBox ,
. , TextView CheckBox
.
. 9.12. (list_item_crime.xml)
198
9. ListFragment
TextView
TextView .
, .
:
+. android:id.
android:text. . , ,
.
, .
,
.
, Crime.
Crime, , Crime.
CrimeListFragment.java ArrayAdapter
CrimeListFragment.
9.18.
(CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
Crime c = (Crime)(getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
}
private class CrimeAdapter extends ArrayAdapter<Crime> {
Crime.
, 0.
Array
Adapter<T>:
public View getView(int position, View convertView, ViewGroup parent)
convertView ,
.
199
,
. , ListView, ,
, .
CrimeAdapter getView().
,
Crime ( 9.19).
9.19. getView() (CrimeListFragment.java)
private class CrimeAdapter extends ArrayAdapter<Crime> {
public CrimeAdapter(ArrayList<Crime> crimes) {
super(getActivity(), 0, crimes);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// ,
if (convertView == null) {
convertView = getActivity().getLayoutInflater()
.inflate(R.layout.list_item_crime, null);
}
// Crime
Crime c = getItem(position);
TextView titleTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_titleTextView);
titleTextView.setText(c.getTitle());
TextView dateTextView =
(TextView)convertView.findViewById(R.id.crime_list_item_dateTextView);
dateTextView.setText(c.getDate().toString());
CheckBox solvedCheckBox =
(CheckBox)convertView.findViewById(R.id.crime_list_item_solvedCheckBox);
solvedCheckBox.setChecked(c.isSolved());
return convertView;
getView() , , .
.
, getItem(int)
Adapter Crime .
Crime
Crime. ListView.
CrimeListFragment.
CrimeListFragment.java onCreate() onListItemClick()
CrimeAdapter, 9.20.
200
9. ListFragment
CrimeAdapter
. CrimeAdapter Crime, Crime .
. - ,
CheckBox, .
CheckBox .
CheckBox
onListItemClick().
. 9.13.
201
ListView , , (, CheckBox
Button), . ,
, .
CheckBox
, :
list_item_crime.xml CheckBox.
9.21. CheckBox (list_item_crime.xml)
...
<CheckBox android:id="@+id/crime_list_item_solvedCheckBox"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_alignParentRight="true"
android:enabled="false"
android:focusable="false"
android:padding="4dp" />
...
.
, CrimeAdapter .
, ,
list_item_crime.xml .
10
CriminalIntent. ,
CrimeActivity,
CrimeFragment
Crime.
, . Fragment.startActivity(Intent),
Activity .
onListItemClick() CrimeListFragment
, CrimeActivity. (
Crime;
.)
203
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
startActivity(i);
. 10.2. CrimeFragment
CrimeFragment Crime,
, Crime .
CrimeFragment, Crime ,
mCrimeId (extra) Intent. onListItemClick()
204
10.
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
putExtra(),
(mCrimeId).
putExtra(String, Serializable), UUID Serializable.
mCrimeId , CrimeActivity, CrimeFragment.
, : ,
. ,
.
CrimeFragment getActivity() CrimeActivity. CrimeFragment
, onCreate(Bundle)
CrimeActivity Crime.
10.3. Crime (CrimeFragment.java)
public class CrimeFragment extends Fragment {
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";
private Crime mCrime;
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID crimeId = (UUID)getActivity().getIntent()
.getSerializableExtra(EXTRA_CRIME_ID);
}
mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
205
getActivity(), 10.3
. getIntent() Intent , CrimeActivity .
getSerializableExtra(String) Intent, UUID .
Crime
CrimeLab. CrimeLab.get() Context,
CrimeFragment CrimeActivity.
CrimeFragment Crime
, CrimeFragment Crime,
Crime. onCreateView(), (
).
. 10.3. ,
10.4. (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mTitleField = (EditText)v.findViewById(R.id.crime_title);
206
10.
10.4 ()
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
...
});
...
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
...
});
...
}
return v;
CriminalIntent. Crime #4 ,
CrimeFragment .
, -,
. , . CrimeFragment
, , ,
Intent, EXTRA_CRIME_ID.
, CrimeFragment , ,
CrimeFragment .
, mCrimeId , CrimeFragment ( CrimeActivity). CrimeFragment ,
. ,
, (arguments bundle).
Bundle.
-, ,
Activity. (argument).
, Bundle,
put- Bundle (
Intent) .
Bundle args = new Bundle();
args.putSerializable(EXTRA_MY_OBJECT, myObject);
args.putInt(EXTRA_MY_INT, myInt);
args.putCharSequence(EXTRA_MY_STRING, myString);
207
, Fragment.
setArguments(Bundle).
, .
Android Fragment
newInstance(). , .
- ,
newInstance().
newInstance() ,
.
CrimeFragment newInstance(UUID), UUID,
, ,
.
10.5. newInstance(UUID) (CrimeFragment.java)
public static CrimeFragment newInstance(UUID crimeId) {
Bundle args = new Bundle();
args.putSerializable(EXTRA_CRIME_ID, crimeId);
CrimeFragment fragment = new CrimeFragment();
fragment.setArguments(args);
return fragment;
}
CrimeActivity CrimeFragment.newInstance(UUID)
, CrimeFragment.
UUID, . CrimeActivity,
createFragment() CrimeActivity CrimeFragment.newInstance(UUID).
10.6. newInstance(UUID) (CrimeActivity.java)
@Override
protected Fragment createFragment() {
return new CrimeFragment();
UUID crimeId = (UUID)getIntent()
.getSerializableExtra(CrimeFragment.EXTRA_CRIME_ID);
}
return CrimeFragment.newInstance(crimeId);
, . CrimeActivity CrimeFragment , ,
newInstance(UUID). ; -
, ,
(
).
208
10.
,
getArguments() Fragment, get- Bundle .
CrimeFragment.onCreate()
UUID .
10.7. (CrimeFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
UUID crimeId = (UUID)getActivity().getIntent()
.getSerializableExtra(EXTRA_CRIME_ID);
UUID crimeId = (UUID)getArguments().getSerializable(EXTRA_CRIME_ID);
}
mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
CriminalIntent. ,
CrimeFragment .
, ,
CriminalIntent.
, .
CriminalIntent,
. ,
.
, ( ),
. ActivityManager,
.
CrimeListFragment CrimeActivity,
. CrimeActivity,
, .
Back ,
CrimeActivity . CrimeListActivity .
CrimeListActivity ,
onResume() . CrimeListActivity
FragmentManager onResume() ,
209
. CrimeListFragment.
Back
. 10.4. CriminalIntent
CrimeListFragment onResume() .
10.8. onResume() (CrimeListFragment.java)
@Override
public void onResume() {
super.onResume();
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}
onResume(), onStart()? , .
, .
, onStart(),
. , ,
onResume().
CriminalIntent.
. ,
.
CriminalIntent .
.
210
10.
. 10.5. CriminalIntent
, . ? ,
GeoQuiz. startActivityForResult() Activity
Fragment.startActivityForResult(). Activity.onActivityResult() Fragment.onActivityResult().
public class CrimeListFragment extends ListFragment {
private static final int REQUEST_CRIME = 1;
...
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
Log.d(TAG, c.getTitle() + " was clicked");
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
startActivityForResult(i, REQUEST_CRIME);
}
211
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CRIME) {
//
}
}
Fragment.startActivityForResult(Intent,int)
Activity.
-.
.
,
. , Fragment
startActivityForResult() onActivityResult(), setResult().
- ;
:
public class CrimeFragment extends Fragment {
...
CriminalIntent 20.
11
ViewPager
, CrimeFragment. ViewPager.
ViewPager
, .
. 11.1.
CrimePagerActivity
213
. 11.2. CrimePagerActivity
:
CrimePagerActivity;
, ViewPager;
ViewPager CrimePagerActivity;
CrimeListFragment.onListItemClick() ,
CrimePagerActivity CrimeActivity.
CrimePagerActivity
CrimePagerActivity FragmentActivity. ViewPager.
CrimePagerActivity.
FragmentActivity. onCreate(Bundle)
ViewPager .
11.1. ViewPager (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity {
private ViewPager mViewPager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
214
11. ViewPager
XML-
. , Android ,
.
.
XML . , .
;
. , XML .
;
ViewPager . FragmentManager , ,
, .
ViewPager ,
.
:
ViewPager;
ViewPager mViewPager;
, ;
ViewPager .
: XML res/values.
Android XML res/values/ids.xml
viewPager.
11.2. (res/values/ids.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<item type="id" name="viewPager" />
</resources>
, ViewPager. ViewPager
.
11.3. (CrimePagerActivity.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
}
215
ViewPager . Fragment
ViewPager ; SDK ViewPager.
ViewPager PagerAdapter
ViewPager - AdapterView ( ListView).
AdapterView , Adapter.
ViewPager PagerAdapter.
ViewPager PagerAdapter AdapterView Adapter. ,
FragmentStatePagerAdapter PagerAdapter,
.
FragmentStatePagerAdapter : getCount() getItem(int). getItem(int)
CrimeFragment,
.
CrimePagerActivity PagerAdapter
ViewPager getCount() getItem(int).
11.4. PagerAdapter (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity {
private ViewPager mViewPager;
private ArrayList<Crime> mCrimes;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
mCrimes = CrimeLab.get(this).getCrimes();
FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
@Override
public int getCount() {
return mCrimes.size();
}
});
@Override
public Fragment getItem(int pos) {
Crime crime = mCrimes.get(pos);
return CrimeFragment.newInstance(crime.getId());
}
216
11. ViewPager
CrimePagerActivity
CrimeActivity
CrimePagerActivity.
CrimeListFragment
CrimePagerActivity CrimeActivity.
CrimeListFragment.java onListItemClick() ,
CrimePagerActivity.
11.5. (CrimeListFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimeActivity
Intent i = new Intent(getActivity(), CrimeActivity.class);
// CrimePagerActivity rime
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
}
CrimePagerActivity , . ,
CrimeActivity.
CrimePagerActivity
217
, , CrimeActivity.java Package
Explorer.
CriminalIntent. Crime #0, . ,
. :
. ViewPager ,
, , .
setOffscreenPageLimit(int).
ViewPager .
Back . , .
ViewPager PagerAdapter
. , , ViewPager .
CrimePagerActivity.onCreate() ; .
Crime, mId crimeId
, Crime.
11.7. (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
...
FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
...
});
218
11. ViewPager
11.7 ()
CriminalIntent.
Crime.
:
, ( ), Crime.
ViewPager.OnPageChangeListener.
11.8. OnPageListener (CrimePagerActivity.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = new ViewPager(this);
mViewPager.setId(R.id.viewPager);
setContentView(mViewPager);
mCrimes = CrimeLab.get(this).getCrimes();
FragmentManager fm = getSupportFragmentManager();
mViewPager.setAdapter(new FragmentStatePagerAdapter(fm) {
...
});
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
public void onPageScrollStateChanged(int state) { }
public void onPageScrolled(int pos, float posOffset, int posOffsetPixels) {
});
...
onPageChangeListener ,
ViewPager.
CrimePagerActivity Crime.
, ,
onPageSelected(). onPageScrolled() ,
CrimePagerActivity
219
, onPageScrollStateChanged() ,
.
CriminalIntent ,
mTitle
Crime. ! ViewPager .
FragmentStatePagerAdapter FragmentPagerAdapter
PagerAdapter, ; FragmentPagerAdapter.
FragmentPagerAdapter , FragmentStatePagerAdapter,
.
Fragment
Item1
Fragment
Item2
Fragment
Item3
Fragment
Item1
Fragment
Item2
Fragment
Item3
. 11.3. FragmentStatePagerAdapter
FragmentStatePagerAdapter
. FragmentManager . FragmentStatePagerAdapter ,
Bundle onSaveInstanceState(Bundle).
,
.
, FragmentPagerAdapter .
, FragmentPagerAdapter
detach(Fragment) remove(Fragment).
220
11. ViewPager
, FragmentManager. , , FragmentPagerAdapter,
.
Fragment
Item1
Fragment
Item2
Fragment
Item3
Fragment
Item1
Fragment
Item2
Fragment
Item3
. 11.4. FragmentPagerAdapter
. , FragmentStatePagerAdapter . CriminalIntent
, ,
. , FragmentStatePagerAdapter.
,
, FragmentPagerAdapter .
. ,
. ViewPager
.
, ,
.
: ViewPager
ViewPager PagerAdapter
. ,
.
: ViewPager
221
, : -,
ViewPager ,
. -, .
PagerAdapter ,
, ViewPager-PagerAdapter
AdapterView-Adapter.
ViewPager, AdapterView? AdapterView Gallery . ?
AdapterView ,
Fragment.
Adapter , View .
, FragmentManager, .
, Gallery Adapter ,
.
ViewPager. Adapter PagerAdapter. Adapter,
.
.
getView(), , PagerAdapter
:
public Object instantiateItem(ViewGroup container, int position)
public void destroyItem(ViewGroup container, int position, Object object)
public abstract boolean isViewFromObject(View view, Object object)
pagerAdapter.instantiateItem(ViewGroup, int)
ViewGroup; destroyItem(ViewGroup, int, Object)
. : instantiateItem(ViewGroup,
int) . PagerAdapter
.
, ViewPager - . , , ViewPager isViewFromObject(View, Object). Object ,
instantiateItem(ViewGroup, int). ,
ViewPager instantiateItem(ViewGroup, 5) A,
isViewFromObject(View, A) true, View
5, false .
ViewPager, PagerAdapter, ,
, . PagerAdapter
instantiateItem(ViewGroup, int)
Object. isViewFromObject(View, Object) :
222
11. ViewPager
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
PagerAdapter , ViewPager, . ,
FragmentPagerAdapter FragmentStatePagerAdapter.
12
. .
,
.
CrimeFragment , . 12.1.
. 12.1.
224
12.
. 12.2. CrimePagerActivity
DialogFragment
225
:
DatePickerFragment;
AlertDialog;
FragmentManager.
DatePicker
CrimeFragment DatePickerFragment.
, (12.1).
12.1. (values/strings.xml)
<resources>
...
<string name="crime_solved_label">Solved?</string>
<string name="crimes_title">Crimes</string>
<string name="date_picker_title">Date of crime:</string>
</resources>
DialogFragment
DatePickerFragment
DialogFragment : android.support.v4.app.DialogFragment.
DialogFragment :
public Dialog onCreateDialog(Bundle savedInstanceState)
FragmentManager - DialogFragment .
DatePickerFragment.java onCreateDialog(),
AlertDialog OK. ( DatePicker
.)
12.2. DialogFragment (DatePickerFragment.java)
public class DatePickerFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok, null)
.create();
}
}
AlertDialog.Builder,
AlertDialog.
Context AlertDialog.Builder,
AlertDialog.Builder.
226
12.
AlertDialog.Builder :
public AlertDialog.Builder setTitle(int titleId)
public AlertDialog.Builder setPositiveButton(int textId,
DialogInterface.OnClickListener listener)
setPositiveButton() ,
DialogInterface.OnClickListener. 12.2 Android
OK null .
.
(Positive)
. AlertDialog
: (Negative) (Neutral).
( ). Froyo Gingerbread .
, ).
AlertDialog.Builder.create(),
AlertDialog.
AlertDialog AlertDialog.Builder ; .
.
DialogFragment
, DialogFragment
FragmentManager -.
DialogFragment FragmentManager
:
public void show(FragmentManager manager, String tag)
public void show(FragmentTransaction transaction, String tag)
DialogFragment
227
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
mDateButton.setEnabled(false);
mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
dialog.show(fm, DIALOG_DATE);
}
});
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
...
}
}
return v;
...
CriminalIntent ,
. OK (.12.3).
. 12.3. AlertDialog
228
12.
AlertDialog DatePicker AlertDialog.Builder:
public AlertDialog.Builder setView(View view)
View
(-).
Package Explorer dialog_date.xml
DatePicker. View
(DatePicker), setView().
DatePicker , .12.4.
DatePickerFragment.onCreateDialog() .
12.4. DatePicker AlertDialog (DatePickerFragment.java)
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View v = getActivity().getLayoutInflater()
.inflate(R.layout.dialog_date, null);
CriminalIntent. ,
DatePicker.
, DatePicker
, ?
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
DatePicker dp = new DatePicker(getActivity());
DialogFragment
229
.
, , DatePicker TimePicker.
, .
, .
Crime , .
;
. , CrimeFragment
DatePickerFragment (. . 5.10).
DatePickerFragment ,
newInstance(Date) Date .
CrimeFragment
, Intent
Intent CrimeFragment.onActivityResult().
Fragment.onActivityResult() ,
- Activity.onActivityResult()
. , onActivityResult()
, .
230
12.
. 12.7.
CrimeFragment DatePickerFragment
DatePickerFragment
DatePickerFragment, DatePickerFragment, DatePickerFragment .
newInstance(), .
DatePickerFragment.java newInstance(Date).
12.5. newInstance(Date) (DatePickerFragment.java)
public class DatePickerFragment extends DialogFragment {
public static final String EXTRA_DATE =
"com.bignerdranch.android.criminalintent.date";
private Date mDate;
public static DatePickerFragment newInstance(Date date) {
Bundle args = new Bundle();
args.putSerializable(EXTRA_DATE, date);
DialogFragment
231
return fragment;
...
CrimeFragment DatePickerFragment
DatePickerFragment.newInstance(Date).
12.6. newInstance() (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup parent, Bundle savedInstanceState) {
...
mDateButton = (Button)v.findViewById(R.id.crime_date);
updateDate();
mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = new DatePickerFragment();
DatePickerFragment dialog = DatePickerFragment
.newInstance(mCrime.getDate());
dialog.show(fm, DIALOG_DATE);
}
});
}
return v;
232
12.
12.7 ()
calendar.setTime(mDate);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
View v = getActivity().getLayoutInflater()
.inflate(R.layout.dialog_date, null);
DatePicker datePicker = (DatePicker)v.findViewById(R.id.dialog_date_datePicker);
datePicker.init(year, month, day, new OnDateChangedListener() {
public void onDateChanged(DatePicker view, int year, int month, int day) {
// , Date
mDate = new GregorianCalendar(year, month, day).getTime();
});
...
//
//
getArguments().putSerializable(EXTRA_DATE, mDate);
CrimeFragment
CrimeFragment DatePickerFragment, - .
DialogFragment
233
startActivityForResult(), ActivityManager .
, ActivityManager ,
.
CrimeFragment (target fragment) DatePickerFragment.
Fragment:
public void setTargetFragment(Fragment fragment, int requestCode)
, , ,
startActivityForResult().
, .
FragmentManager . ,
getTargetFragment() getTargetRequestCode() , .
CrimeFragment.java ,
CrimeFragment DatePickerFragment.
12.8. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";
private static final String DIALOG_DATE = "date";
private static final int REQUEST_DATE = 0;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mDateButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FragmentManager fm = getActivity()
.getSupportFragmentManager();
DatePickerFragment dialog = DatePickerFragment
.newInstance(mCrime.getDate());
dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
dialog.show(fm, DIALOG_DATE);
}
});
}
}
...
return v;
234
12.
, CrimeFragment DatePickerFragment ,
CrimeFragment. Intent .
?
, DatePickerFragment CrimeFragment.
onActivityResult(int, int, Intent).
Activity.onActivityResult() ActivityManager
.
Activity.onActivityResult() ; ActivityManager. , FragmentManager Fragment.onActivityResult() .
, Fragment.onActivityResult()
. :
, , setTargetFragment(),
, ;
;
Intent, .
DatePickerFragment , ,
, CrimeFragment.onActivityResult(). onCreateDialog() null setPositiveButton() DialogInterface.OnClickListener,
.
12.9. (DatePickerFragment.java)
private void sendResult(int resultCode) {
if (getTargetFragment() == null)
return;
Intent i = new Intent();
i.putExtra(EXTRA_DATE, mDate);
getTargetFragment()
.onActivityResult(getTargetRequestCode(), resultCode, i);
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
...
return new AlertDialog.Builder(getActivity())
.setView(v)
.setTitle(R.string.date_picker_title)
.setPositiveButton(android.R.string.ok, null)
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
DialogFragment
235
sendResult(Activity.RESULT_OK);
})
.create();
, , onCreateView().
, updateDate(), onCreateView() onActivityResult().
12.11. updateDate() (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
public void updateDate() {
mDateButton.setText(mCrime.getDate().toString());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mDateButton = (Button)v.findViewById(R.id.crime_date);
mDateButton.setText(mCrime.getDate().toString());
updateDate();
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mDateButton.setText(mCrime.getDate().toString());
updateDate();
}
}
236
12.
.
CriminalIntent ,
. Crime; CrimeFragment. Crime
, .
DialogFragment
onActivityResult()
,
.
.
, , , .
startActivityForResult()
.
onActivityResult(), , .
. 12.8.
DialogFragment
237
, ,
DialogFragment . show() .
onActivityResult().
B (
)
B (
)
. 12.9.
onActivityResult() ,
, . ,
.
, ,
onCreateDialog() DialogFragment.onCreateView().
238
12.
.
TimePickerFragment . TimePicker , CrimeFragment
TimePickerFragment.
,
. , , .
.
13
MediaPlayer
CriminalIntent
. MediaPlayer.
. 13.1. , !
240
13. MediaPlayer
MediaPlayer Android - .
(,
, ) (WAV, MP3, Ogg Vorbis,
MPEG-4, 3GPP ..).
HelloMoon.
Holo Dark.
Next. ,
. HelloMoonActivity.
HelloMoon ( ) (http://www.bignerdranch.com/solutions/AndroidProgramming.zip).
:
13_Audio/HelloMoon/res/drawable-mdpi/armstrong_on_moon.jpg
13_Audio/HelloMoon/res/raw/one_small_step.wav
241
armstrong_on_moon.jpg
(~160 dpi), Android
. armstrong_on_moon.jpg drawable-mdpi.
res/raw. raw
,
Android.
res/raw ,
. ( res
NewFolder.) one_small_step.wav .
( res/raw/ 13_Audio/HelloMoon/res/raw/apollo_17_stroll.
mpg.
.)
res/values/strings.xml ,
HelloMoon:
13.1. (strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string
<string
<string
<string
<string
<string
name="app_name">HelloMoon</string>
name="hello_world">Hello world!</string>
name="menu_settings">Settings</string>
name="hellomoon_play">Play</string>
name="hellomoon_stop">Stop</string>
name="hellomoon_description">Neil Armstrong stepping
onto the moon</string>
</resources>
( HelloMoon
Play Stop? 15 ,
.)
, ;
HelloMoon.
HelloMoon HelloMoonActivity, HelloMoonFragment.
AudioPlayer , MediaPlayer.
, MediaPlayer ; HelloMoonFragment
MediaPlayer .
.
AudioPlayer,
.
:
;
;
.
242
13. MediaPlayer
. 13.3. HelloMoon
HelloMoonFragment
XML- Android fragment_hello_moon.xml. TableLayout.
fragment_hello_moon.xml .13.4.
. 13.4. HelloMoon
HelloMoonFragment
243
.
ImageView TableRow? TableRow
. , ImageView .
TableRow, TableLayout
.
TableLayout, , , Button
, .
, TableRow .
, TableLayout.
, LinearLayout
.
. ?
HelloMoon Holo Dark.
. , . ( ,
, , .)
application :
...
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
</application>
</manifest>
android:theme ; , .
, @style/
AppTheme. Package Explorer res/values/styles.xml.
style AppBaseTheme parent android:Theme.
13.2. (res/values/styles.xml)
<style name="AppBaseTheme" parent="android:Theme.Light">
<style name="AppBaseTheme" parent="android:Theme">
res values ;
styles.xml. API.
res/values-11/styles.xml API 1113,
res/values-14/styles.xml API 14 .
244
13. MediaPlayer
HelloMoonFragment
HelloMoonFragment
android.support.v4.app.Fragment.
HelloMoonFragment.onCreateView(),
.
13.3. HelloMoonFragment (HelloMoonFragment.java)
public class HelloMoonFragment extends Fragment {
private Button mPlayButton;
private Button mStopButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);
mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);
return v;
CriminalIntent
. HelloMoon
,
fragment.
activity_hello_moon.xml fragment, 13.4.
13.4. (activity_hello_moon.xml)
<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/helloMoonFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.bignerdranch.android.hellomoon.HelloMoonFragment">
</fragment>
HelloMoonFragment
245
,
HelloMoonActivity. HelloMoonActivity
FragmentActivity:
13.5. HelloMoonActivity FragmentActivity (HelloMoonActivity.java)
public class HelloMoonActivity extends Activity FragmentActivity {
/** . */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello_moon);
}
}
HelloMoon. HelloMoonActivity
HelloMoonFragment.
, .
, .
: .setContentView()
activity_hello_moon.xml, fragment.
FragmentManager HelloMoonFragment .
HelloMoonFragment onCreateView() ,
, , fragment.
(/
(/
)
(
)
( setContentView()
)
. 13.5.
?
, FragmentManager:
246
13. MediaPlayer
, , , .
, , . ,
.
.
FragmentManager. .
.
, HelloMoonFragment, .
com.bignerdranch.android.hellomoon
AudioPlayer. java.lang.Object.
AudioPlayer.java MediaPlayer
.
13.6.
MediaPlayer (AudioPlayer.java)
public class AudioPlayer {
private MediaPlayer mPlayer;
public void stop() {
if (mPlayer != null) {
mPlayer.release();
mPlayer = null;
}
}
247
,
. MediaPlayer
release() . . MediaPlayer stop()
MediaPlayer ,
.
release(), .
: MediaPlayer
, - .
play(Context).
stop() stop()
.
13.7. (AudioPlayer.java)
...
public void play(Context c) {
stop();
mPlayer = MediaPlayer.create(c, R.raw.one_small_step);
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
stop();
}
});
mPlayer.start();
@Override
public void onDestroy() {
super.onDestroy();
mPlayer.stop();
}
248
13. MediaPlayer
HelloMoonFragment.java. :
AudioPlayer .
13.9. Play (HelloMoonFragment.java)
public class HelloMoonFragment extends Fragment {
private AudioPlayer mPlayer = new AudioPlayer();
private Button mPlayButton;
private Button mStopButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_hello_moon, parent, false);
mPlayButton = (Button)v.findViewById(R.id.hellomoon_playButton);
mPlayButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.play(getActivity());
}
});
mStopButton = (Button)v.findViewById(R.id.hellomoon_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mPlayer.stop();
}
});
return v;
HelloMoon, Play
.
MediaPlayer.
MediaPlayer Android MediaPlayer
Developer Guide http://developer.android.com/guide/topics/media/mediaplayer.html.
.
.
MediaPlayer.
. HelloMoon
249
:
, , . MediaPlayer, .
.
( ) Android
SurfaceView. , Surface, SurfaceView. Surface,
SurfaceHolder SurfaceView. 19.
, SurfaceHolder MediaPlayer MediaPlayer.setDisplay(SurfaceHolder).
VideoView .
VideoView MediaPlayer, SurfaceView.
MediaController,
.
VideoView ,
Uri. Uri, Android,
:
Uri resourceUri = Uri.parse("android.resource://" +
"com.bignerdranch.android.hellomoon/raw/apollo_17_stroll");
. HelloMoon
HelloMoon ,
. apollo_17_stroll.mpg ,
res/raw.
.
14
HelloMoon .
, . .
, HelloMoonActivity . , FragmentManager HelloMoonFragment. FragmentManager
: onPause(), onStop()
onDestroy(). HelloMoonFragment.onDestroy() MediaPlayer, .
3 GeoQuiz Activity.onSaveInstanceState(Bundle) . , . Fragment
onSaveInstanceState(Bundle), .
MediaPlayer
.
, , MediaPlayer .
HelloMoonFragment.onCreate() .
251
retainInstance false. ,
,
-. setRetainInstance(true) ,
,
.
, (
mPlayButton, mPlayer mStopButton) . , .
HelloMoon. ,
, .
, .
,
.
FragmentManager .
,
:
. ,
, .
FragmentManager retainInstance .
false ( ), FragmentManager
. FragmentManager .
252
14.
. 14.1. UI-
, retainInstance true, , .
FragmentManager
.
. 14.2. UI-
, (detached) . ,
-.
: ?
253
. 14.3.
:
setRetainInstance(true);
- ( ).
,
.
:
?
: , ? ! .
, . ,
.
254
14.
? , Android
. , ,
Android ,
.
, .
- , ,
.
onSaveInstanceState(Bundle)
onSaveInstanceState(Bundle) ,
. , , onSaveInstanceState() .
CriminalIntent. CrimeFragment
,
, View
. onSaveInstanceState()
.
Fragment.onSaveInstanceState() .
,
.
; ,
Serializable .
, .
, , .
, GeoQuiz.
, .
,
. ,
, .
GeoQuiz , GeoQuiz QuizFragment,
QuizActivity. Fragment.onSaveInstanceState()
QuizFragment ?
.14.4 ,
: ( ),
.
onSaveInstanceState(Bundle)
255
( )
. 14.4.
;
. .
QuizFragment,
. GeoQuiz ,
QuizFragment . , setRetainInstance(true) QuizFragment.
onCreate().
14.2. QuizFragment
public class QuizFragment extends Fragment {
...
private int mCurrentIndex = 0;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}
...
256
14.
.
.14.4, QuizFragment , ,
,
.
, . GeoQuiz 100 ? ()
. . onSaveInstanceState(). ,
,
.
, ,
,
, onSaveInstanceState(Bundle)
.
Honeycomb
Ice Cream Sandwich. ,
.
,
, .
? onRetainNonConfigurationInstance().
onRetainNonConfigurationInstance(),
. , getLa
stNonConfigurationInstance().
? , .
. ,
.
(, MediaPlayer),
? , onRetainNonConfigurationInstance() ,
onDestroy().
onRetainNonConfigurationInstance() ,
.
, , .
15
(localization) . HelloMoon ,
. , Android
.
258
15.
. Android ,
:
.
Android .
ISO 639-1.
-es. HelloMoon res: res/raw-es/ res/values-es/.
(http://www.
bignerdranch.com/solutions/AndroidProgramming.zip). :
15_Localization/HelloMoon/res/raw-es/one_small_step.wav
15_Localization/HelloMoon/res/values-es/strings.xml
.
, ,
. ,
( Settings ;
Android Language and input, Language and Keyboard
- .
, (Espaol).
(Espaa Estados Unidos) ,
-es, .
( +
. , -es-rES, r , ES
ISO 3166-1-alpha-2. :
,
Android: ,
r ( ).
HelloMoon, Tocar , .
-en. , raw values raw-en/ values-en/.
. .
.
: Android , , .
259
. drawable
-mdpi, -hdpi -xhdpi.
drawable Android
.
, Android
.
http://developer.android.com/guide/practices/screens_support.
html, ,
res/drawable/ .
: (, values-es/), (, layout-land/), (, drawable-mdpi/) API
(, values-v11/).
.15.1 , Android
.
15.1.
(MCC) (MNC)
260
15.
15.1 ()
(dpi)
API
http://developer.android.com/guide/topics/resources/
providing-resources.html#AlternativeResources.
,
. , .15.1.
, , HelloMoon
app_name
. app_name
.
, .
values-land .
,
, app_name. app_name
, 15.1.
15.1.
(values-land/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hello, Moon! How are you? Is it cold up there?</string>
<string name="app_name">HelloMoon</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="hellomoon_play">Play</string>
<string name="hellomoon_stop">Stop</string>
<string name="hellomoon_image_description">Neil Armstrong stepping
261
( values)
,
.
.
app_name: values/
strings.xml, values-es/strings.xml
values-land/strings.xml.
, HelloMoon
. , values-es/strings.xml.
. 15.2. Android
, , ,
( ).
, Ajustes Configuracin
idioma ().
. ,
HelloMoon
, values-es-land.
,
. , values-es-land ,
values-land-es .
(, - , -es-rES
, . .)
262
15.
values-land/strings.xml values-es-land/ ,
15.2 (, , res/values-esland/strings.xml ).
15.2.
(values-es-land/strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hello, Moon! How are you? Is it cold up there?</string>
<string name="app_name">Hola, Luna! Como ests? Hace fro ah arriba?</string>
</resources>
, HelloMoon ,
.
. 15.3.
, Android , app_name
. Android
app_name:
values-es/strings.xml
values-land/strings.xml
values-es-land/strings.xml
.
.
Android , .
263
.
, ,
values-land/ values-es-land/
.
. , API .
-v11 API 11 .
API. , API 17;
-ldltr ( ) -ldrtl ( ).
API,
-ldltr -ldltr-v17. ( .)
, . Android
, ;
( ) ,
.
Android
(. .15.1),
MCC. MCC
, MCC, .
, Android
, .
MCC ,
, Android .
(values-es/ values-es-land/) .
(values-land/) , .
Android .
, Android
. values-es/ , values-es-land/.
, Android values-es-land/.
, Android,
, .
264
15.
: one_small_step.wav, app_name, armstrong_on_moon.jpg.
XML . ,
@drawable/armstrong_on_moon,
R.drawable.armstrong_on_moon. , , , .
res/.
res/ , .
res Android
. drawable/, layout/, menu/, raw/ values/.
res ( ) http://developer.android.com/guide/topics/resources/providing-resources.
html#ResourceTypes.
, res/, .
res/my_stuff , Android
.
, res/ .
.
;
. . ,
, ,
.
Android. ,
(, activity_, dialog_ list_item_). , res/layout/
CriminalIntent activity_crime_pager, activity_fragment,
dialog_date, fragment_crime list_item_crime. , .
. ,
, ..
, , .
.
, , API, ..
265
, fragment_hello_moon.xml . ,
.
API
. 15.4.
, ,
, . , .
, LogCat Resource not
found... .
,
. Ajustes Configuracin idioma ().
16
Up
. 16.1.
267
(options menu).
.
. ,
,
, .
18.
.
18.
strings.xml ( 16.1).
, . , , .
16.1. (res/values/strings.xml)
...
<string name="crimes_title">Crimes</string>
<string name="crime_date_label">Date:</string>
<string name="date_picker_title">Date of crime:</string>
<string name="new_crime">New Crime</string>
<string name="show_subtitle">Show Subtitle</string>
<string name="hide_subtitle">Hide Subtitle</string>
<string name="subtitle">If you see something, say something.</string>
<string name="delete_crime">Delete</string>
</resources>
. 16.2. Honeycomb
268
16.
, Android.
, .
,
Android. , .
XML
, .
XML res/menu . Android ,
.
Package Explorer res/ menu.
NewAndroid XML File.
, Menu,
fragment_crime_list.xml.
. 16.3.
269
showAsAction ,
(overflow menu).
ifRoom withText, .
, . , ,
.
. ,
.
,
(. 16.4).
. 16.4.
270
16.
. , , ,
.
, .
. , , .
Androids Icon Design Guidelines http://developer.android.com/guide/practices/
ui_guidelines/icon_design.html.
, ,
. .
, Android SDK
--android-SDK/platforms/-API/data/res.
, Android 4.2 Mac /Developer/androidsdk-mac_86/platforms/android-17/data/res.
SDK
ic_menu_add. drawable . icon android:icon="@
drawable/ic_menu_add", , .
Activity. , Android Activity onCreateOptionsMenu(Menu).
271
,
, . Fragment
, CrimeListFragment.
:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
public boolean onOptionsItemSelected(MenuItem item)
MenuInflater.inflate(int, Menu)
. Menu , .
onCreateOptionsMenu() .
, ,
, . , Fragment .
FragmentManager Fragment.onCreateOptionsMenu(Menu, MenuInflater) onCreateOptionsMenu()
. FragmentManager,
onCreateOptionsMenu(). :
public void setHasOptionsMenu(boolean hasMenu)
CrimeListFragment.onCreate() FragmentManager,
CrimeListFragment .
16.4. hasOptionsMenu (CrimeListFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
getActivity().setTitle(R.string.crimes_title);
...
272
16.
CriminalIntent (. 16.5).
?
.
.
. 16.5.
. 16.6.
,
(. 16.7).
. 16.7.
273
CriminalIntent Honeycomb, .
. .16.8
Gingerbread.
. 16.8. Gingerbread
, ;
. SDK.
Honeycomb onCreateOptionsMenu()
.
,
. onCreateOptionsMenu()
.
(, .
ActionBarSherlock
API.
ActionBarSherlock 18.)
New Crime,
Crime . CrimeLab.
java .
274
16.
, , 100 . CrimeLab.java
, .
16.6. ! (CrimeLab.java)
public CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
for (int i = 0; i < 100; i++) {
Crime c = new Crime();
c.setTitle("Crime #" + i);
c.setDate(new Date());
c.setSolved(i % 2 == 0); //
mCrimes.add(c);
}
}
,
onOptionsItemSelected(MenuItem).
MenuItem, .
, .
, ,
.
, .
CrimeListFragment.java onOptionsItemSelected(MenuItem),
. Crime,
CrimeLab CrimePagerActivity Crime.
16.7. (CrimeListFragment.java)
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
275
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivityForResult(i, 0);
return true;
default:
return super.onOptionsItemSelected(item);
. , true; ,
. default ,
.
CriminalIntent . .
( .
.)
CriminalIntent Back
. Back
. ,
.
Android
.
( Home
). Android
,
. Up).
Up
CrimePagerActivity. , .
, Up, ,
, .16.9.
, ,
:
public abstract void setDisplayHomeAsUpEnabled(boolean showHomeAsUp)
276
16.
. 16.9. Up
CrimeFragment.onCreateView() setDisplayHomeAsUpEnabled(true).
16.8. Up (CrimeFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
...
:
Up.
. . , API 1113, ,
setDisplayHomeAsUpEnabled(true).
277
Up
,
onOptionsItemSelected(MenuIt
em). , FragmentManager, CrimeFragment .
CrimeFragment.onCreate() setHasOptionsMenu(true),
CrimeListFragment.
16.9. (CrimeFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
...
setHasOptionsMenu(true);
}
XML. : android.R.id.home.
CrimeFragment.java onOptionsItemSelected(MenuItem),
.
16.10.
(Home)
(CrimeFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
//
return true;
default:
return super.onOptionsItemSelected(item);
}
}
. ,
CrimePagerActivity:
Intent intent = new Intent(getActivity(), CrimeListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
FLAG_ACTIVITY_CLEAR_TOP Android ,
, .
278
16.
CriminalListActivity
. 16.10. FLAG_ACTIVITY_CLEAR_TOP
,
, NavUtils
.
. AndroidManifest.xml.
CrimePagerActivity , CrimeListActivity
.
16.11. (AndroidManifest.xml)
<activity android:name=".CrimePagerActivity"
android:label="@string/app_name">
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value=".CrimeListActivity"/>
</activity>
...
, .
PackageManager,
, .
- .
NavUtils,
.
NavUtils:
public static void navigateUpFromSameTask(Activity sourceActivity)
CrimeFragment.onOptionsItemSelected() ,
, , NavUtils.getParentActivityName(Activity). , navigat
eUpFromSameTask(Activity) .
16.12. NavUtils (CrimeFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if (NavUtils.getParentActivityName(getActivity()) != null) {
279
NavUtils.navigateUpFromSameTask(getActivity());
}
return true;
default:
return super.onOptionsItemSelected(item);
,
. onCreateView()
setDisplayHomeAsUpEnabled(true).
16.13. (CrimeFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (NavUtils.getParentActivityName(getActivity()) != null) {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
}
...
, , , , CrimeListActivity.
280
16.
, , . ,
. res menu-v11.
fragment_crime_list.xml .
res/menu-v11/fragment_crime_list.xml Show Subtitle,
.
16.14. Show Subtitle (res/menu-v11/fragment_crime_list.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_new_crime"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_crime"
android:showAsAction="ifRoom|withText"/>
<item android:id="@+id/menu_item_show_subtitle"
android:title="@string/show_subtitle"
android:showAsAction="ifRoom"/>
</menu>
onOptionsItemSelected()
.
16.15. Show Subtitle (CrimeListFragment.java)
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
getActivity().getActionBar().setSubtitle(R.string.subtitle);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
: Android Lint, .
, R.id.
menu_item_show_subtitle .
CriminalIntent . Froyo Gingerbread (
). , Show Subtitle
. ,
, .
281
, :
Show Subtitle. ,
.
onOptionsItemSelected()
.
16.16. (CrimeListFragment.java)
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
item.setTitle(R.string.hide_subtitle);
} else {
getActivity().getActionBar().setSubtitle(null);
item.setTitle(R.string.show_subtitle);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
, Hide Subtitle. ,
, Show Subtitle.
CriminalIntent ,
.
,
Android
. , .
Android : ,
. ,
, . ,
CrimeListFragment,
.
CrimeListFragment.java , onCreate() CrimeListFragment .
282
16.
16.17.
CrimeListFragment
(CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
mCrimes;
private boolean mSubtitleVisible;
private final String TAG = "CrimeListFragment";
private ArrayList<Crime>
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
setRetainInstance(true);
mSubtitleVisible = false;
}
onOptionsItemSelected()
.
16.18.
subtitleVisible
(CrimeListFragment.java)
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
...
return true;
case R.id.menu_item_show_subtitle:
if (getActivity().getActionBar().getSubtitle() == null) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
mSubtitleVisible = true;
item.setTitle(R.string.hide_subtitle);
}
else {
getActivity().getActionBar().setSubtitle(null);
mSubtitleVisible = false;
item.setTitle(R.string.show_subtitle);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
, . CrimeListFragment.java onCreateView() ,
mSubtitleVisible true.
16.19.
, mSubtitleVisible
(CrimeListFragment.java)
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
283
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}
}
}
return v;
onCreateOptionsMenu()
, .
16.20.
mSubtitleVisible
(CrimeListFragment.java)
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem showSubtitle = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible && showSubtitle != null) {
showSubtitle.setTitle(R.string.hide_subtitle);
}
}
CriminalIntent. ,
. ,
.
.
CriminalIntent
. - .
ListView AdapterView, View,
. , ListView
, .
AdapterView:
public void setEmptyView(View emptyView)
XML , ListView,
. @android:id/
list @android:id/empty , .
284
16.
CrimeListFragment onCreateView(),
.
CrimeListFragment XML- , FrameLayout
, ListView View,
.
(, ).
,
,
.
17
.
CriminalIntent
JSON, .
Android (sandbox).
(
, ).
/data/data,
. CriminalIntent
/data/data/com.bignerdranch.android.criminalintent.
, ; ,
API.
, .
, SD-, ( )
. SD- ,
. ,
, .
() , API
. (,
.)
CriminalIntent
:
.
286
17.
. , .
: ,
, .
CriminalIntent JSON,
- Android Context.
.17.1
CriminalIntent.
Crime
CrimeLab
JSON
Crime
Crime JSON
( -)
(
-)
crimes.json
( )
. 17.1. CriminalIntent
CriminalIntent
287
).
Java , java.io.File java.io.FileInputStream.
JSON
CriminalIntent CrimeLab
,
JSON CriminalIntentJSONSerializer
Crime.
CriminalIntentJSONSerializer
ArrayList Crime
JSON CriminalIntentJSONSerializer.
com.bignerdranch.android.criminalintent.
java.lang.Object.
, 17.1.
import , , Eclipse Organize Imports.
17.1. CriminalIntentJSONSerializer
public class CriminalIntentJSONSerializer {
private Context mContext;
private String mFilename;
public CriminalIntentJSONSerializer(Context c, String f) {
mContext = c;
mFilename = f;
}
public void saveCrimes(ArrayList<Crime> crimes)
throws JSONException, IOException {
// JSON
JSONArray array = new JSONArray();
for (Crime c : crimes)
array.put(c.toJSON());
//
Writer writer = null;
try {
OutputStream out = mContext
.openFileOutput(mFilename, Context.MODE_PRIVATE);
writer = new OutputStreamWriter(out);
writer.write(array.toString());
} finally {
if (writer != null)
writer.close();
}
CrimeLab, JSON
288
17.
. (unit-testing) ,
.
, ,
Context . ,
, Context.
saveCrimes(ArrayList<Crime>) JSONArray,
toJSON() JSONArray. ( toJSON() ,
Crime. .)
, Context.openFileOutput(). .
,
.
, Context.getFilesDir(), openFileOutput()
.
,
Java. Writer, OutputStream OutputStreamWriter
java.io. OutputStream, openFileOutput(), OutputStreamWriter,
Java String
, OutputStream .
JSON Crime
mCrimes JSON,
Crime JSON. Crime.java
toJSON(), Crime
JSON JSONObject JSONArray.
17.2. JSON (Crime.java)
public class Crime {
private
private
private
private
static
static
static
static
final
final
final
final
String
String
String
String
JSON_ID = "id";
JSON_TITLE = "title";
JSON_SOLVED = "solved";
JSON_DATE = "date";
private
private
private
private
UUID mId;
String mTitle;
boolean mSolved;
Date mDate = new Date();
public Crime() {
mId = UUID.randomUUID();
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put(JSON_ID, mId.toString());
Crime CrimeLab
289
json.put(JSON_TITLE, mTitle);
json.put(JSON_SOLVED, mSolved);
json.put(JSON_DATE, mDate.getTime());
return json;
@Override
public String toString() {
return mTitle;
}
Crime CrimeLab
CriminalIntentJSONSerializer Crime
JSON
.
? : ,
.
CrimeLab, .
.
, - , . CriminalIntent ,
.
, SQLite.
SQLite Android 34.
CrimeLab.java CriminalIntentJSONSerializer onCreate(). saveCrimes(),
. ,
.
17.3. CrimeLab (CrimeLab.java)
public class CrimeLab {
private static final String TAG = "CrimeLab";
private static final String FILENAME = "crimes.json";
private ArrayList<Crime> mCrimes;
private CriminalIntentJSONSerializer mSerializer;
private static CrimeLab sCrimeLab;
private Context mAppContext;
private CrimeLab(Context appContext) {
mAppContext = appContext;
mCrimes = new ArrayList<Crime>();
}
...
290
17.
17.3 ()
public void addCrime(Crime c) {
mCrimes.add(c);
}
.
, Toast .
onPause()
saveCrimes()? onPause(). onStop() onDestroy(),
. ,
, onStop()
onDestroy() .
17.4.
onPause()
(CrimeFragment.java)
...
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(getActivity());
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onPause() {
super.onPause();
CrimeLab.get(getActivity()).saveCrimes();
}
CriminalIntent.
Home ; ,
. LogCat.
Crime CrimeLab
291
CriminalIntent
.
Crime.java , JSONObject.
17.5. Crime(JSONObject) (Crime.java)
public class Crime {
...
public Crime() {
mId = UUID.randomUUID();
}
public Crime(JSONObject json) throws JSONException {
mId = UUID.fromString(json.getString(JSON_ID));
mTitle = json.getString(JSON_TITLE);
mSolved = json.getBoolean(JSON_SOLVED);
mDate = new Date(json.getLong(JSON_DATE));
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put(JSON_ID, mId.toString());
json.put(JSON_TITLE, mTitle);
json.put(JSON_SOLVED, mSolved);
json.put(JSON_DATE, mDate.getTime());
return json;
}
CriminalIntentJSONSerializer.java CriminalIntentJSONSerializer .
17.6. loadCrimes() (CriminalIntentJSONSerializer.java)
public CriminalIntentJSONSerializer(Context c, String f) {
mContext = c;
mFilename = f;
}
public ArrayList<Crime> loadCrimes() throws IOException, JSONException {
ArrayList<Crime> crimes = new ArrayList<Crime>();
BufferedReader reader = null;
try {
// StringBuilder
InputStream in = mContext.openFileInput(mFilename);
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder jsonString = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
// Line breaks are omitted and irrelevant
jsonString.append(line);
}
// JSON JSONTokener
JSONArray array = (JSONArray) new JSONTokener(jsonString.toString())
.nextValue();
292
17.
17.6 ()
// Crime JSONObject
for (int i = 0; i < array.length(); i++) {
crimes.add(new Crime(array.getJSONObject(i)));
}
} catch (FileNotFoundException e) {
// " ";
} finally {
if (reader != null)
reader.close();
}
return crimes;
try {
mCrimes = mSerializer.loadCrimes();
} catch (Exception e) {
mCrimes = new ArrayList<Crime>();
Log.e(TAG, "Error loading crimes: ", e);
}
; , .
: Android - Java
293
CriminalIntent .
.
, . (, ); , CriminalIntent.
, .
Eclipse.
,
. ,
.
,
. ,
.
, (, ,
) .
, ,
(, ).
.
, . android.os.Environment
, . ( Context,
). ,
CriminalIntentJSONSerializer.
:
Android - Java
Android Linux.
Android Linux Unix.
(/),
, .
,
Linux.
Android
Java.
APK, /data/app com.bignerdranch.
android.criminalintent-1.apk.
294
17.
,
.
, , Context
: Application, Activity Service, .
, .17.1.
17.1. Context
File getFilesDir()
FileInputStream
(
)
openFileInput(String name)
FileOutputStream
openFileOutput(String name,
,
( )
int mode)
File getDir(String name,
int mode)
( , )
String[] fileList()
(, openFileInput(String))
File getCacheDir()
,
-. ,
Java
, java.io.File java.io.FileInputStream.
API, , ,
Java. Android java.nio.*.
18
. .
:
( ), .
. 18.1.
296
18.
Honeycomb .
.
.
API : .
, .
. - ,
.
API 11
Froyo Gingerbread.
(,
. ,
ActionBarSherlock
API. ActionBarSherlock .)
res/menu/ XML crime_list_item_context.xml,
menu. item, 18.1.
18.1. (crime_list_item_context.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_delete_crime"
android:icon="@android:drawable/ic_menu_delete"
android:title="@string/delete_crime" />
</menu>
. Fragment
, 16.
:
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo)
Fragment:
public boolean onContextItemSelected(MenuItem item)
297
CrimeListFragment.java onCreateContextMenu() .
18.2. (CrimeListFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context, menu);
}
.
Fragment,
:
public void registerForContextMenu(View view)
. , . ListView,
.
CrimeListFragment.onCreateView() ListView .
18.3. ListView (CrimeListFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
298
18.
18.3 ()
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}
}
ListView listView = (ListView)v.findViewById(android.R.id.list);
registerForContextMenu(listView);
}
return v;
. 18.2.
299
, . CrimeLab.java deleteCrime(Crime).
18.4. (CrimeLab.java)
public void addCrime(Crime c) {
mCrimes.add(c);
}
public void deleteCrime(Crime c) {
mCrimes.remove(c);
}
onContextItemSelec
ted(MenuItem). MenuItem ,
. , ,
. , .
getMenuInfo() MenuItem.
, ContextMenu.ContextMenuInfo.
CrimeListFragment onContextItemSelected(MenuItem),
,
Crime . Crime
.
18.5. (CrimeListFragment.java)
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
getActivity().getMenuInflater().inflate(R.menu.crime_list_item_context, menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo)item.getMenuInfo();
int position = info.position;
CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
Crime crime = adapter.getItem(position);
switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeLab.get(getActivity()).deleteCrime(crime);
adapter.notifyDataSetChanged();
return true;
}
return super.onContextItemSelected(item);
300
18.
getMenuInfo()
, .
Crime.
CriminalIntent,
. ( ,
.)
, ,
Android. , .18.2
Jelly Bean.
,
.
, , , , .
, .
. 18.3.
, , . ,
301
, Froyo Gingerbread,
, .
,
. ,
, .
CrimeListFragment.onCreateView()
CHOICE_MODE_MULTIPLE_MODAL. , ListView, , .
18.6.
@TargetApi(11)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);
...
ListView listView = (ListView)v.findViewById(android.R.id.list);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
// Froyo Gingerbread
registerForContextMenu(listView);
} else {
// Honeycomb
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
}
}
return v;
ListView ,
AbsListView.MultiChoiceModeListener. ,
.
public abstract void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked)
MultiChoiceModeListener ActionMode.
Callback. , ActionMode, ActionMode.Callback
ActionMode. ActionMode.Callback
:
public abstract boolean onCreateActionMode(ActionMode mode, Menu menu)
302
18.
ActionMode. , .
public abstract boolean onPrepareActionMode(ActionMode mode, Menu menu)
onCreateActionMode() ,
.
public abstract boolean onActionItemClicked(ActionMode mode, MenuItem item)
, . , .
public abstract void onDestroyActionMode(ActionMode mode)
ActionMode - ,
. (-).
,
.
CrimeListFragment.onCreateView() ,
MultiChoiceModeListener . -
onCreateActionMode() onActionItemClicked(ActionMode,
MenuItem).
18.7. MultiChoiceModeListener (CrimeListFragment.java)
...
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
// ,
//
}
// ActionMode.Callback
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.crime_list_item_context, menu);
return true;
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
// ,
//
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_delete_crime:
CrimeAdapter adapter = (CrimeAdapter)getListAdapter();
CrimeLab crimeLab = CrimeLab.get(getActivity());
for (int i = adapter.getCount() - 1; i >= 0; i--) {
303
if (getListView().isItemChecked(i)) {
crimeLab.deleteCrime(adapter.getItem(i));
}
}
mode.finish();
adapter.notifyDataSetChanged();
return true;
default:
return false;
}
}
public void onDestroyActionMode(ActionMode mode) {
// ,
//
}
});
return v;
Eclipse , : , onCreateActionMode(),
false. true; false .
onCreateActionMode() ,
MenuInflater ActionMode, .
.
, ActionMode.setTitle()
. MenuInflater
.
. 18.4.
304
18.
onActionItemClicked() Crime
CrimeLab, . ActionMode.finish() .
CriminalIntent. , .
() .
. .
.
. ,
- .
, ,
.
.
.
, .
.
XML. ( ) ,
. ( , ,
StateListDrawable.)
,
, ,
drawable . res/drawable XML
res/drawable/background_activated.xml. selector
, 18.8.
18.8.
(res/drawable/background_activated.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:state_activated="true"
android:drawable="@android:color/darker_gray"
/>
</selector>
: ,
, , android:drawable.
. android:state_activated
305
false, ,
.
res/layout/list_item_crime.xml
.
18.9. (res/layout/list_item_crime.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/background_activated">
...
</RelativeLayout>
CriminalIntent.
.
. 18.5. ,
25.
, , ListView. GridView
306
18.
AdapterView, 26. , -
,
ListView, GridView?
, View.OnLongClickListener.
ActionMode Activity.
startActionMode(). ( MultiChoiceModeListener
ActionMode .)
startActionMode() ,
ActionMode.Callback. ActionMode.Callback,
ActionMode, :
public
public
public
public
abstract
abstract
abstract
abstract
: ?
,
(gracious fallback):
.
SDK .
. ,
. (duplication).
:
.
, .
,
.
. android.support.v4.app.Fragment ,
android.app.Fragment .
,
,
.
Gingerbread, ,
. ActionBarSherlock (Jake Wharton) . http://www.actionbarsherlock.com.
ActionBarSherlock Android
: ActionBarSherlock
307
Android.
ActionBarSherlock .
( ,
. .)
? .
,
Android.
, Android
. ,
. , . ,
ICS Jelly Bean
, .
, . -,
,
. , . -,
.
, ,
.
,
, .
. CrimeFragment
, .
,
. CrimeFragment.
: ActionBarSherlock
Android ,
. , ,
16.
ActionBarSherlock ( ABS, )
.
, ,
308
18.
.
http://www.actionbarsherlock.com. Android, .
, ABS
, .
, ABS .
, jar- ABS
Android. Android,
,
. Android Android, . ,
Android,
, jar-.
ABS , ABS
:
;
Eclipse ActionBarSherlock;
ActionBarSherlock CriminalIntent;
CriminalIntent ActionBarSherlock.
( ActionBarSherlock, CriminalIntent. CriminalIntent ABS ).
. 18.6. Android
: ActionBarSherlock
309
ABS, http://www.actionbarsherlock.com/download.
html, zip tgz ( )
.
, Eclipse,
Package Explorer NewProject....
Android ,
; Android Project From Existing Code (. 18.6).
. Browse ,
ABS.
: library, samples website. library,
Open, Finish.
. 18.7. ABS
310
18.
Android.
, . Add
. 18.9. ActionBarSherlock
, ActionBarSherlock,
.
. ActionBarSherlock
, ActionBarSherlock,
CriminalIntent. ABS ,
Android, Activity, Fragment ActionBar. (
) ABS Sherlock,
.
, .
Sherlock-, .
ABS CriminalIntent
ABS :
SingleFragmentActivity CrimePagerActivity , SherlockFragmentActivity (
FragmentActivity).
,
SherlockFragment, SherlockDialogFragment SherlockListFragment
( ).
Menu, MenuItem MenuInflater com.actionbarsherlock.view.
.
, CrimeFragment CrimeListFragment
311
. , ,
.
CrimeFragment : import ,
Command+Shift+O/Ctrl+Shift+O com.actionbarsherlock.view.
CrimeListFragment, . , CrimeListFragment
MultiChoiceModeListener,
Android.
?
MenuItem, Menu MenuInflater onCreateOptionsMenu()
onOptionsItemSelected(). :
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
...
}
:
@Override
public void onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu,
com.actionbarsherlock.view.MenuInflater inflater) {
...
}
, , ABS
. . getSherlockActivity().
getSupportActionBar() getActivity().getActionBar().
SherlockActivity ,
. res/menu-v11/
fragment_crime_list.xml res/menu,
.
?
CriminalIntent MultiChoiceModeListener . ,
MultiChoiceModeListener .
?
ListView: ListView.CHOICE_MODE_MULTIPLE.
ListView.CHOICE_MODE_MULTIPLE_MODAL,
312
18.
Android. ListView.CHOICE_MODE_MULTIPLE , :
Android. ListView.CHOICE_MODE_MULTIPLE
ListView .
, ListView.CHOICE_MODE_NONE.
, CHOICE_MODE_MULTIPLE_MODAL. ,
, getSherlockActivity().startActionMode(). , com.actionbarsherlock.view.ActionMode.Callback, Android.
, . OnItemLongClickListener ListView.
setOnItemLongClickListener().
19
I: Viewfinder
.
API
.
API . ,
, . ,
- : .
?
. . Android ,
, MediaStore.ACTION_IMAGE_CAPTURE.
21.
, , -
. ,
. CriminalIntent , API .
,
SurfaceView
.
314
19. I: Viewfinder
. 19.1.
. 19.2 , .
. 19.2. CriminalIntent
Camera . ;
.
315
SurfaceView . SurfaceView
.
CrimeCameraFragment,
CrimeCameraFragment CrimeCameraActivity.
CrimeCameraFragment. , CrimeFragment
CrimeCameraActivity.
Android XML Layout fragment_crime_camera.xml
FrameLayout. , .19.3.
316
19. I: Viewfinder
. 19.4.
strings.xml .
19.1. (strings.xml)
...
<string name="show_subtitle">Show Subtitle</string>
<string name="subtitle">Sometimes tolerance is not a virtue.</string>
<string name="take">Take!</string>
</resources>
CrimeCameraFragment
CrimeCameraFragment
android.support.v4.app.Fragment. CrimeCameraFragment.java
, 19.2. onCreateView() ,
.
, -;
.
19.2. (CrimeCameraFragment.java)
public class CrimeCameraFragment extends Fragment {
private static final String TAG = "CrimeCameraFragment";
private Camera mCamera;
private SurfaceView mSurfaceView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime_camera, parent, false);
Button takePictureButton = (Button)v
.findViewById(R.id.crime_camera_takePictureButton);
317
takePictureButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getActivity().finish();
}
});
mSurfaceView = (SurfaceView)v.findViewById(R.id.crime_camera_surfaceView);
return v;
CrimeCameraActivity
SingleFragmentActivity CrimeCameraActivity.
createFragment(),
CrimeCameraFragment.
19.3. (CrimeCameraActivity.java)
public class CrimeCameraActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeCameraFragment();
}
}
CrimeCameraActivity .
, ,
uses-permission.
AndroidManifest.xml , 19.4.
19.4. (AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.criminalintent"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<application
...
318
19. I: Viewfinder
19.4 ()
<activity android:name=".CrimeCameraActivity"
android:screenOrientation="landscape"
android:label="@string/app_name">
</activity>
</application>
</manifest>
uses-feature ,
. android.hardware.camera ,
Google Play, ,
.
,
android:screenOrientation.
,
.
screenOrientation . , , ,
.
<activity>.
API
.
,
.
. CrimeCameraFragment
Camera. ,
, ,
.
.
Camera:
public static Camera open(int cameraId)
public static Camera open()
public final void release()
API
319
,
. ( : onResume()
, .)
CrimeCameraFragment.onResume()
Camera.open(int) 0, ,
. ,
(, , Nexus 7), .
API 8 Camera.open() .
Froyo, Camera.open() API
8.
19.5. onResume() (CrimeCameraFragment.java)
@TargetApi(9)
@Override
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(0);
} else {
mCamera = Camera.open();
}
}
(Android Lint
. ,
, 26.)
, ,
. onPause() .
19.6. (CrimeCameraFragment.java)
public void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
mCamera = Camera.open(0);
} else {
mCamera = Camera.open();
}
}
@Override
public void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
release(). .
320
19. I: Viewfinder
, ,
,
. - , null .
return v;
setType() SURFACE_TYPE_PUSH_BUFFERS ,
. Eclipse
.
? setType() SURFACE_TYPE_PUSH_BUFFERS
Honeycomb.
19.7 @SuppressWarnings.
, Android.
Android 20.
SurfaceHolder Surface.
Surface .
Surface :
SurfaceView , SurfaceView . , , Surface ,
.
View, SurfaceView,
. (client) Surface ,
API
321
- . CrimeCameraFragment
Camera.
. 19.5.SurfaceView, SurfaceHolder Surface
: , Surface , . (.19.6),
Camera SurfaceHolder Surface
Surface.
. 19.6.
322
19. I: Viewfinder
SurfaceHolder , SurfaceHolder.Callback.
Surface Surface .
SurfaceHolder.Callback :
public abstract void surfaceCreated(SurfaceHolder holder)
, SurfaceView. Surface .
public abstract void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
, surfaceChanged().
, .
Surface .
public abstract void surfaceDestroyed(SurfaceHolder holder)
SurfaceView Surface .
Surface Surface.
Camera,
Surface:
public final void setPreviewDisplay(SurfaceHolder holder)
Surface. surfaceCreated().
public final void startPreview()
Surface . surfaceChanged().
public final void stopPreview()
Surface. surfaceDestroyed().
CrimeCameraFragment.java SurfaceHolder.Callback
Surface .
19.8. SurfaceHolder.Callback (CrimeCameraFragment.java)
...
SurfaceHolder holder = mSurfaceView.getHolder();
// setType() SURFACE_TYPE_PUSH_BUFFERS
// , ,
//
// Camera 3.0.
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
//
//
try {
API
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e(TAG, "Error setting up preview display", exception);
}
public
//
//
if
}
});
}
323
// ;
//
Camera.Parameters parameters = mCamera.getParameters();
Size s = null; //
parameters.setPreviewSize(s.width, s.height);
mCamera.setParameters(parameters);
try {
mCamera.startPreview();
} catch (Exception e) {
Log.e(TAG, "Could not start preview", e);
mCamera.release();
mCamera = null;
}
return v;
, .
.
surfaceChanged()
null. ,
.
, .
Camera.Parameters,
:
public List<Camera.Size> getSupportedPreviewSizes()
324
19. I: Viewfinder
android.hardware.Camera.Size,
.
Surface,
surfaceChanged(), ,
Surface.
CrimeCameraFragment , .
, .
19.9. (CrimeCameraFragment.java)
/** .
* CameraPreview.java
* - ApiDemos Android. */
private Size getBestSupportedSize(List<Size> sizes, int width, int height) {
Size bestSize = sizes.get(0);
int largestArea = bestSize.width * bestSize.height;
for (Size s : sizes) {
int area = s.width * s.height;
if (area > largestArea) {
bestSize = s;
largestArea = area;
}
}
return bestSize;
}
surfaceChanged().
19.10. getBestSupportedSize() (CrimeCameraFragment.java)
...
holder.addCallback(new SurfaceHolder.Callback() {
...
});
API
325
CrimeCameraActivity
CrimeFragment
, CrimeFragment . CrimeFragment CrimeCameraActivity.
CrimeFragment, . 19.7.
, .19.7,
LinearLayout ImageButton.
.19.8 , CrimeFragment .
. 19.7. CrimeFragment
. 19.8.
(layout/fragment_crime.xml)
326
19. I: Viewfinder
. 19.9.
(layout-land/fragment_crime.xml)
CrimeFragment ImageButton,
OnClickListener, CrimeCameraActivity.
19.11. CrimeCameraActivity (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private ImageButton mPhotoButton;
...
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mPhotoButton = (ImageButton)v.findViewById(R.id.crime_imageButton);
mPhotoButton.setOnClickListener(new View.OnClickListener() {
API
});
}
327
@Override
public void onClick(View v) {
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);
}
return v;
, mPhotoButton .
PackageManager.
onCreateView(), ImageButton
.
19.12. (CrimeFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mPhotoButton = (ImageButton)v.findViewById(R.id.crime_imageButton);
mPhotoButton.setOnClickListener(new View.OnClickListener() {
...
});
// ,
//
PackageManager pm = getActivity().getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) &&
!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)) {
mPhotoButton.setEnabled(false);
}
}
...
PackageManager hasSystemFeature(String),
. :
FEATURE_CAMERA , FEATURE_CAMERA_FRONT . ,
ImageButton .
CriminalIntent.
. .
Take! CrimeFragment.
328
19. I: Viewfinder
. 19.10.
. ,
.
. 19.10
.
,
. .
, CrimeCameraFragment
; CrimeCameraActivity. CrimeCameraActivity.java onCreate(Bundle).
19.13. (CrimeCameraActivity.java)
public class CrimeCameraActivity extends SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
// .
requestWindowFeature(Window.FEATURE_NO_TITLE);
//
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
}
@Override
protected Fragment createFragment() {
return new CrimeCameraFragment();
}
? requestWindowFeature()
addFlags()
329
. 19.11.
API ,
CrimeFragment.
:
,
CrimeFragment CrimeCameraActivity. ,
.
. . ,
, .
,
.
: adb.
.
CrimeCameraActivity AndroidManifest.xml:
330
19. I: Viewfinder
<activity android:name=".CrimeCameraActivity"
android:exported="true"
android:screenOrientation="landscape"
android:label="@string/app_name">
</activity>
.
exported true, Android,
. (
exported true.)
adb platform-tools Android SDK.
platform-tools tools
.
adb (Android Debug Bridge). adb ,
Eclipse. LogCat,
, , , ,
.
adb , .
CriminalIntent
, . CrimeCameraActivity :
$ adb shell am start -n com.bignerdranch.android.criminalintent/.
CrimeCameraActivity
Starting: Intent { cmp=com.bignerdranch.android.criminalintent/.CrimeCameraActivity
}
, CrimeCameraActivity
.
. :
$ adb shell
shell@android:/ $ am start -n com.bignerdranch.android.criminalintent/.
CrimeCameraAct\
ivity
am ,
(Activity Manager). Android, .
am adb shell am.
20
II:
JPEG
, JPEG Crime CrimeFragment.
DialogFragment.
. 20.1.
332
20. II:
CrimeCameraFragment . , ,
.
layout/fragment_crime_camera.xml FrameLayout ProgressBar,
.20.2.
@android:style/Widget.ProgressBar.Large .
. 20.3.
333
FrameLayout ( ProgressBar ) .
, Take!
.
: FrameLayout match_
parent. FrameLayout
. , FrameLayout,
ProgressBar, LinearLayout.
FrameLayout , - LinearLayout. ProgressBar
. FrameLayout
match_parent android:clickable="true" ,
FrameLayout (
).
LinearLayout, Take!.
CrimeCameraFragment.java. FrameLayout,
.
20.1. FrameLayout (CrimeCameraFragment.java)
public class CrimeCameraFragment extends Fragment {
...
private View mProgressContainer;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime_camera, parent, false);
mProgressContainer = v.findViewById(R.id.crime_camera_progressContainer);
mProgressContainer.setVisibility(View.INVISIBLE);
...
...
}
return v;
, ;
JPEG.
Camera:
public final void takePicture(Camera.ShutterCallback shutter,
Camera.PictureCallback raw,
Camera.PictureCallback jpeg)
ShutterCallback , ,
.
334
20. II:
PictureCallback
.
PictureCallback JPEG-
..
takePicture().
- , null takePicture().
CriminalIntent ShutterCallback
JPEG.
.20.4 .
. 20.4. CrimeCameraFragment
, .
public static interface Camera.ShutterCallback {
public abstract void onShutter();
}
public static interface Camera.PictureCallback {
public abstract void onPictureTaken (byte[] data, Camera camera);
}
CrimeCameraFragment.java Camera.ShutterCallback,
, Camera.PictureCallback,
JPEG.
20.2. takePicture() (CrimeCameraFragment.java)
...
private View mProgressContainer;
private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
public void onShutter() {
};
335
//
mProgressContainer.setVisibility(View.VISIBLE);
};
...
if (success) {
Log.i(TAG, "JPEG saved at " + filename);
}
getActivity().finish();
onPictureTaken() ,
. - Java
JPEG, Camera.
, .
: mProgressContainer .
, PictureCallback , .
Take!,
takePicture(). null
, .
20.3. takePicture() (CrimeCameraFragment.java)
@Override
@SuppressWarnings("deprecation")
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
336
20. II:
20.3 ()
takePictureButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getActivity().finish();
if (mCamera != null) {
mCamera.takePicture(mShutterCallback, null, mJpegCallback);
}
}
});
...
}
return v;
. , .
Camera.Parameters:
public List<Camera.Size> getSupportedPictureSizes()
surfaceChanged() getBestSupportedSize()
,
Surface. .
20.4.
getBestSupportedSize()
(CrimeCameraFragment.java)
...
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mCamera == null) return;
// ;
//
Camera.Parameters parameters = mCamera.getParameters();
Size s = getBestSupportedSize(parameters.getSupportedPreviewSizes(), w, h);
parameters.setPreviewSize(s.width, s.height);
s = getBestSupportedSize(parameters.getSupportedPictureSizes(), w, h);
parameters.setPictureSize(s.width, s.height);
mCamera.setParameters(parameters);
});
...
CriminalIntent Take!. ,
, LogCat CrimeCameraFragment.
CrimeCameraFragment , . API . CrimeFragment
.
CrimeFragment
337
CrimeFragment
CrimeFragment , CrimeCameraFragment
. .20.5
CrimeFragment CrimeCameraFragment.
Android
. 20.5. CrimeCameraActivity
CrimeFragment CrimeCameraActivity .
CrimeCameraFragment , , setResult(). ActivityManager
CrimePagerActivity onActivityResult(). FragmentManager, CrimePagerActivity, CrimeFragment
CrimeFragment.onActivityResult().
CrimeCameraActivity
CrimeFragment CrimeCameraActivity.
CrimeFragment.java ,
, CrimeCameraActivity .
20.5. CrimeCameraActivity (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private static final int REQUEST_DATE = 0;
private static final int REQUEST_PHOTO = 1;
...
338
20. II:
20.5 ()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mPhotoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);
startActivityForResult(i, REQUEST_PHOTO);
}
});
...
CrimeCameraFragment
CrimeCameraFragment
CrimeCameraActivity.setResult(int, Intent). CrimeCameraFragment.
java ,
RESULT_OK, , RESULT_CANCELED, - .
20.6.
(CrimeCameraFragment.java)
public class CrimeCameraFragment extends Fragment {
private static final String TAG = "CrimeCameraFragment";
public static final String EXTRA_PHOTO_FILENAME =
"com.bignerdranch.android.criminalintent.photo_filename";
...
private Camera.PictureCallback mJpegCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
...
try {
...
} catch (Exception e) {
...
} finally {
...
}
Log.i(TAG, "JPEG saved at " + filename);
//
if (success) {
Intent i = new Intent();
i.putExtra(EXTRA_PHOTO_FILENAME, filename);
getActivity().setResult(Activity.RESULT_OK, i);
} else {
CrimeFragment
};
339
getActivity().setResult(Activity.RESULT_CANCELED);
}
getActivity().finish();
CrimeFragment
CrimeFragment
CriminalIntent.
CrimeFragment.java onActivityResult(),
, .
TAG CrimeFragment, .
20.7. (CrimeFragment.java)
public class CrimeFragment extends Fragment {
private static final String TAG = "CrimeFragment"
public static final String EXTRA_CRIME_ID =
"com.bignerdranch.android.criminalintent.crime_id";
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Log.i(TAG, "filename: " + filename);
}
}
CriminalIntent CrimeCameraActivity.
LogCat , CrimeFragment .
, CrimeFragment ,
:
: Photo,
. Crime mPhoto
Photo. CrimeFragment Photo mPhoto Crime.
340
20. II:
CrimeFragment: CrimeFragment
ImageView Crime.
DialogFragment ImageFragment
.
. 20.6 CrimeFragment, Crime Photo.
. 20.6. CrimeFragment
Photo
com.bignerdranch.android.criminalintent. Photo java.lang.Object.
Photo.java , 20.8.
20.8. Photo (Photo.java)
...
public class Photo {
private static final String JSON_FILENAME = "filename";
private String mFilename;
/** Photo, */
public Photo(String filename) {
mFilename = filename;
}
341
: Photo .
Photo , JSON, Crime
Photo.
Crime
Crime Photo,
JSON ( 20.9).
20.9. Photo Crime (Crime.java)
public class Crime {
...
private static final String JSON_DATE = "date";
private static final String JSON_PHOTO = "photo";
...
private Date mDate = new Date();
private Photo mPhoto;
...
public Crime(JSONObject json) throws JSONException {
...
mDate = new Date(json.getLong(JSON_DATE));
if (json.has(JSON_PHOTO))
mPhoto = new Photo(json.getJSONObject(JSON_PHOTO));
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
...
json.put(JSON_DATE, mDate.getTime());
if (mPhoto != null)
json.put(JSON_PHOTO, mPhoto.toJSON());
return json;
}
342
20. II:
20.9 ()
...
public Photo getPhoto() {
return mPhoto;
}
CrimeFragment.java onActivityResult(),
Photo Crime.
20.10. Photo
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data
.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Log.i(TAG, "filename: " + filename);
CrimeFragment
.
CrimeFragment ImageView.
343
ImageView
layout/fragment_crime.xml ImageView,
. 20.8.
344
20. II:
ImageView (. 20.9).
CrimeFragment.java ImageView
onCreateView().
20.11. ImageView (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private ImageButton mPhotoButton;
private ImageView mPhotoView;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mPhotoButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Launch the camera activity
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivityForResult(i, REQUEST_PHOTO);
}
});
mPhotoView = (ImageView)v.findViewById(R.id.crime_imageView);
...
, ImageView ,
CriminalIntent.
345
ImageView , , ,
. - . , .
Android
8- .
, ,
,
.
ImageView
com.bignerdranch.android.criminalintent
PictureUtils. PictureUtils.java ,
.
20.12. PictureUtils (PictureUtils.java)
public class PictureUtils {
/**
* BitmapDrawable ,
* .
*/
@SuppressWarnings("deprecation")
public static BitmapDrawable getScaledDrawable(Activity a, String path) {
Display display = a.getWindowManager().getDefaultDisplay();
float destWidth = display.getWidth();
float destHeight = display.getHeight();
//
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
float srcWidth = options.outWidth;
float srcHeight = options.outHeight;
int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth) {
if (srcWidth > srcHeight) {
inSampleSize = Math.round(srcHeight / destHeight);
} else {
inSampleSize = Math.round(srcWidth / destWidth);
}
}
options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;
346
20. II:
20.12 ()
347
mPhotoButton.setImageDrawable(b);
@Override
public void onStart() {
super.onStart();
showPhoto();
}
CrimeFragment.onActivityResult() showPhoto(),
CrimeCameraActivity.
20.15. showPhoto() onActivityResult() (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Photo p = new Photo(filename);
mCrime.setPhoto(p);
showPhoto();
Log.i(TAG, "Crime: " + mCrime.getTitle() + "has a photo");
}
}
}
PictureUtils BitmapDrawable,
ImageView ( ).
20.16. (PictureUtils.java)
public class PictureUtils {
/**
* ...
*/
@SuppressWarnings("deprecation")
public static BitmapDrawable getScaledDrawable(Activity a, String path) {
...
}
public static void cleanImageView(ImageView imageView) {
if (!(imageView.getDrawable() instanceof BitmapDrawable))
return;
//
BitmapDrawable b = (BitmapDrawable)imageView.getDrawable();
b.getBitmap().recycle();
imageView.setImageDrawable(null);
348
20. II:
Bitmap.recycle() . ,
, . Bitmap.recycle()
(native) , .
. ( Android.
Honeycomb Java Bitmap.)
recycle(),
. -
(finalizer),
.
.
,
. ,
( ) recycle()
.
CrimeFragment onStop() cleanImageView().
20.17. (CrimeFragment.java)
@Override
public void onStart() {
super.onStart();
showPhoto();
}
@Override
public void onStop() {
super.onStop();
PictureUtils.cleanImageView(mPhotoView);
}
onStart() onStop() .
, .
onResume() onPause(),
.
,
, , .
onResume() onPause(), . ,
, ,
.
CriminalIntent. ,
ImageView. CriminalIntent .
,
, .
CrimeCameraActivity ,
. , .
.
DialogFragment
349
DialogFragment
Crime.
. 20.10. DialogFragment
350
20. II:
20.18 ()
args.putSerializable(EXTRA_IMAGE_PATH, imagePath);
ImageFragment fragment = new ImageFragment();
fragment.setArguments(args);
fragment.setStyle(DialogFragment.STYLE_NO_TITLE, 0);
return fragment;
DialogFragment.STYLE_NO_TITLE,
, .20.10.
ImageFragment , AlertDialog.
, , onCreateView()
View ( onCreateDialog() Dialog).
ImageFragment.java onCreateView(), ImageView .
ImageView.
onDestroyView(), ,
.
20.19. ImageFragment (ImageFragment.java)
return mImageView;
@Override
public void onDestroyView() {
super.onDestroyView();
PictureUtils.cleanImageView(mImageView);
}
351
, CrimeFragment.
CrimeFragment.java mPhotoView.
ImageFragment FragmentManager
CrimePagerActivity show() ImageFragment.
ImageFragment FragmentManager.
20.20. ImageFragment (CrimeFragment.java)
public class CrimeFragment extends Fragment {
...
private static final String DIALOG_IMAGE = "image";
...
@Override
@TargetApi(11)
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
...
mPhotoView = (ImageView)v.findViewById(R.id.crime_imageView);
mPhotoView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Photo p = mCrime.getPhoto();
if (p == null)
return;
});
...
FragmentManager fm = getActivity()
.getSupportFragmentManager();
String path = getActivity()
.getFileStreamPath(p.getFilename()).getAbsolutePath();
ImageFragment.newInstance(path)
.show(fm, DIALOG_IMAGE);
CriminalIntent. ,
.
. Crime
. API , . Photo
CrimeFragment ImageFragment.
.
Crime, . onActivityResult(int,
352
20. II:
.
. CrimeFragment
/ , . Delete Photo ,
ImageView.
:
Android
19,
. .
: ?
, . API
, , .
- ,
SurfaceHolder.setType(int) SurfaceHolder.
SURFACE_TYPE_PUSH_BUFFERS, 19.
Android SurfaceHolder
. , setType() .
, ,
- . , BitmapDrawable
BitmapDrawable(Bitmap),
. ,
, , , View.setBackgroundDrawable(Drawable).
Display.getWidth() Display.getHeight(), . getSize(Point),
, getWidth() getHeight().
- ,
. , ,
, Microsoft Apple.
Microsoft API , .
, Microsoft . Microsoft
API, .
, . - Microsoft .
, Apple API ,
. Apple ,
: Android
353
. , API
. Apple ,
.
Apple , :
float destWidth;
float destHeight;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB_MR2) {
Point size = display.getSize();
destWidth = size.x;
destHeight = size.y;
} else {
destWidth = display.getWidth();
destHeight = display.getHeight();
}
21
Android
(implicit intent).
, .
, , .
CriminalIntent
. ,
, .
. 21.1.
355
, .
,
, .
, CriminalIntent
:
CrimeFragment
;
Crime mSuspect, ;
.
CrimeFragment .
, .
21.1. (strings.xml)
<string name="take">Take!</string>
<string name="crime_suspect_text">Choose Suspect</string>
<string name="crime_report_text">Send Crime Report</string>
</resources>
layout/fragment_crime.xml Button,
.21.2. : LinearLayout,
.
. 21.2.
(layout/fragment_crime.xml)
LinearLayout,
.
356
21.
. 21.3.
.21.3, .
, ScrollView.
. 21.4.
ScrollView,
ScrollView.
. 21.4.
(layout_land/fragment_crime.xml)
357
CriminalIntent,
.
Crime.java, JSON
. JSON / JSON .
21.2. (Crime.java)
public class Crime {
...
private static final String JSON_PHOTO = "photo";
private static final String JSON_SUSPECT = "suspect";
...
private Photo mPhoto;
private String mSuspect;
public Crime(JSONObject json) throws JSONException {
mId = UUID.fromString(json.getString(JSON_ID));
...
if (json.has(JSON_PHOTO))
mPhoto = new Photo(json.getJSONObject(JSON_PHOTO));
if (json.has(JSON_SUSPECT))
mSuspect = json.getString(JSON_SUSPECT);
}
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
...
if (mPhoto != null)
json.put(JSON_PHOTO, mPhoto.toJSON());
json.put(JSON_SUSPECT, mSuspect);
return json;
}
public void setPhoto(Photo p) {
mPhoto = p;
}
, .
358
21.
, ,
. :
<string name="crime_report">%1$s! The crime was discovered on %2$s. %3$s, and %4$s
CrimeFragment.java , ,
.
21.4. getCrimeReport() (CrimeFragment.java)
private String getCrimeReport() {
String solvedString = null;
if (mCrime.isSolved()) {
solvedString = getString(R.string.crime_report_solved);
} else {
solvedString = getString(R.string.crime_report_unsolved);
}
String dateFormat = "EEE, MMM dd";
String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();
,
.
359
Intent , .
, ,
, .
Intent i = new Intent(getActivity(), CrimeCameraActivity.class);
startActivity(i);
, , ,
. , .
, .
(action)
Intent. , URL- Intent.ACTION_VIEW,
Intent.ACTION_SEND.
,
(, URL -), URI URI
, ContentProvider.
, , MIME (, text/html
audio/mpeg3). ,
.
, , ,
. Android android.intent.category.
LAUNCHER ,
. , android.intent.category.INFO ,
, .
- Intent.ACTION_VIEW Uri URL- .
. ( ,
.)
ACTION_VIEW
. , -,
, ACTION_VIEW.
<activity
android:name=".BrowserActivity"
android:label="@string/app_name" >
360
21.
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" android:host="www.bignerdranch.com" />
</intent-filter>
</activity>
DEFAULT . action
, ,
DEFAULT . DEFAULT
. ( LAUNCHER,
23.)
, , .
.
, . ,
.
, ,
CriminalIntent. ,
, ;
. ,
ACTION_SEND. , text/plain.
CrimeFragment.onCreateView() Send Crime
Report .
startActivity(Intent).
21.5. (CrimeFragment.java)
...
Button reportButton = (Button)v.findViewById(R.id.crime_reportButton);
reportButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.crime_report_subject));
startActivity(i);
}
});
}
return v;
361
Intent, , .
,
.
Intent. , , ,
.
.
, Intent.
, ,
, .
CriminalIntent Send Crime Report.
, ,
:
, . , - . 21.5. ,
.
.
, : ,
,
.
,
. , CriminalIntent :
,
.
, .
, , Intent
:
public static Intent createChooser(Intent target, String title)
, createChooser(), startActivity().
CrimeFragment.java ,
.
362
21.
21.6. (CrimeFragment.java)
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
i.putExtra(Intent.EXTRA_SUBJECT,
getString(R.string.crime_report_subject));
i = Intent.createChooser(i, getString(R.string.send_report));
startActivity(i);
}
. 21.6.
Android
, . .
Intent.ACTION_PICK, ContactsContract.
363
Contacts.CONTENT_URI. , Android
.
,
startActivityForResult() . CrimeFragment.java .
21.7. (CrimeFragment.java)
...
private static final int REQUEST_PHOTO = 1;
private static final int REQUEST_CONTACT = 2;
...
private ImageButton mPhotoButton;
private Button mSuspectButton;
...
onCreateView() .
startActivityForResult(). (
Crime).
21.8. (CrimeFragment.java)
...
mSuspectButton = (Button)v.findViewById(R.id.crime_suspectButton);
mSuspectButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(i, REQUEST_CONTACT);
}
});
if (mCrime.getSuspect() != null) {
mSuspectButton.setText(mCrime.getSuspect());
}
}
return v;
364
21.
. 21.7.
.
, Android
API
ContentProvider. . ContentProvider ContentResolver.
ACTION_PICK, onActivityResult().
URI , .
CrimeFragment.java onActivityResult() CrimeFragment.
21.9. (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
...
} else if (requestCode == REQUEST_PHOTO) {
365
...
} else if (requestCode == REQUEST_CONTACT) {
Uri contactUri = data.getData();
// ,
// .
String[] queryFields = new String[] {
ContactsContract.Contacts.DISPLAY_NAME
};
// - contactUri
// "where"
Cursor c = getActivity().getContentResolver()
.query(contactUri, queryFields, null, null, null);
//
if (c.getCount() == 0) {
c.close();
return;
}
// - .
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mSuspectButton.setText(suspect);
c.close();
21.9 ,
. Cursor . ,
,
. ,
Crime Choose Suspect.
( .
. , Contacts Provider API: http://developer.android.com/guide/topics/providers/
contacts-provider.html.)
? . .
URI , Intent.
FLAG_GRANT_READ_URI_PERMISSION. Android,
CriminalIntent
. ,
, .
366
21.
, , - .
Android
.
, ?
, .
,
PackageManager. :
PackageManager pm = getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(yourIntent, 0);
boolean isIntentSafe = activities.size() > 0;
queryIntentActivities() PackageManager.
, , . ,
,
.
onCreateView() ,
.
.
, . .
,
URI :
Uri number = Uri.parse("tel:5551234");
Intent.ACTION_DIAL Intent.ACTION_
CALL. ACTION_CALL
, ; ACTION_DIAL
, .
ACTION_DIAL. ACTION_CALL , . ,
.
22
CriminalIntent ,
. .22.1
-.
. 22.1.
368
22.
AVD. AVD,
WindowAndroid Virtual Device Manager. New
(Device) AVD , .22.2.
AVD API 17.
. 22.2. AVD
CrimeListActivity
, . ,
.
CrimeListActivity CrimeListFragment, CrimeFragment, . 22.3.
. 22.3.
SingleFragmentActivity
369
:
SingleFragmentActivity,
;
, ;
CrimeListActivity,
, .
SingleFragmentActivity
CrimeListActivity SingleFragmentActivity.
SingleFragmentActivity ,
activity_fragment.xml.
SingleFragmentActivity , ,
.
SingleFragmentActivity.java ,
, .
22.1. SingleFragmentActivity (SingleFragmentActivity.java)
public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();
protected int getLayoutResId() {
return R.layout.activity_fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
setContentView(getLayoutResId());
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
SingleFragmentActivity ,
, getLayoutResId()
, activity_fragment.xml.
370
22.
. 22.4. (layout/activity_twopane.xml)
@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
}
CriminalIntent ,
(.22.5).
371
, .
.
. 22.5.
CrimeListActivity .
-.
-
- (alias resource) , . - res/values/
refs.xml.
-,
activity_fragment.xml, activity_twopane.xml.
Package Explorer res/layout/
Android XML. ,
Values, refs.xml,
resources Finish. , 22.3.
22.3. - (res/values/refs.xml)
<resources>
<item name="activity_masterdetail" type="layout">@layout/activity_fragment</item>
</resources>
.
: R.layout.activity_masterdetail. :
372
22.
type .
res/values/, R.layout.
R.layout.activi
ty_fragment. CrimeListActivity.
22.4. (CrimeListActivity.java)
@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
return R.layout.activity_masterdetail;
}
CriminalIntent ,
. CrimeListActivity
.
res/values/, . , CrimeListActivity .
, activity_masterdetail
activity_twopane.xml.
Package Explorer res/ values-sw600dp. res/values/refs.xml res/
values-sw600dp/ , .
22.5.
(res/values-sw600dp/refs.xml)
<resources>
<item name="activity_masterdetail" type="layout">@layout/activity_fragment</item>
<item name="activity_masterdetail" type="layout">@layout/activity_twopane</item>
</resources>
-sw600dp ? sw
Smallest Width ( ),
, , .
-sw600dp, : , 600dp .
.
: sw Android 3.2. ,
Android 3.0 3.1 .
, -xlarge.
res/
values-xlarge. res/values-sw600dp/refs.xml res/values-xlarge/.
373
, ,
22.6.
22.6.
3.2
(res/values-xlarge/refs.xml)
<resources>
<item name="activity_masterdetail" type="layout">@layout/activity_twopane</item>
</resources>
:
, , ,
CrimeFragment CrimeListActivity.
,
CrimeListFragment.onListItemClick() . CrimePagerActivity onListItemClick()
FragmentManager, CrimeListActivity,
, CrimeFragment .
:
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime crime = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimeFragment
Fragment fragment = CrimeFragment.newInstance(crime.getId());
FragmentManager fm = getActivity().getSupportFragmentManager();
fm.beginTransaction()
.add(R.id.detailFragmentContainer, fragment)
.commit();
}
, Android. ,
.
FragmentManager , , ,
-, .
, CrimeListFragment CrimeFragment
CrimeListActivity , CrimeListActivity detailFragmentContainer.
- CrimeListFragment, CrimeListFragment.
374
22.
-, .
- - .
-, Callbacks.
, -. , ,
.
, .
CrimeListFragment.Callbacks
Callbacks,
, Callbacks. - Callbacks, .
Fragment:
public void onAttach(Activity activity)
(
, ).
null :
public void onDetach()
null, , .
CrimeListFragment.java CrimeListFragment
Callb acks . mCallbacks
onAttach(Activity) onDetach(), .
22.7. (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
private ArrayList<Crime> mCrimes;
private boolean mSubtitleVisible;
private Callbacks mCallbacks;
/**
* -.
*/
public interface Callbacks {
void onCrimeSelected(Crime crime);
}
@Override
375
CrimeListFragment -. , CrimeListFragment.
Callbacks, CrimeListFragment .
, CrimeListFragment CrimeListFragment.Callbacks. ,
- CrimeListFragment.Callbacks. , .
CrimeListActivity CrimeListFragment.Callbacks.
onCrimeSelected(Crime) .
22.8. (CrimeListActivity.java)
public class CrimeListActivity extends SingleFragmentActivity
implements CrimeListFragment.Callbacks {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
@Override
protected int getLayoutResId() {
return R.layout.activity_twopane;
}
CrimeListFragment onListItemClick(), ,
.
, CrimeListActivity.
onCrimeSelected(Crime).
onCrimeSelected(Crime) CrimeListActivity
:
CrimePagerActivity;
CrimeFragment
detailFragmentContainer.
376
22.
, , . , detailFragmentContainer. .
, ,
; ,
detailFragmentContainer CrimeFragment.
detailFragmentContainer, , CrimeFragment detail
FragmentContainer ( ) CrimeFragment,
.
CrimeListActivity.java onCrimeSelected(Crime),
.
22.9. CrimeFragment (CrimeListActivity.java)
public void onCrimeSelected(Crime crime) {
if (findViewById(R.id.detailFragmentContainer) == null) {
// CrimePagerActivity
Intent i = new Intent(this, CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
} else {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
Fragment oldDetail = fm.findFragmentById(R.id.detailFragmentContainer);
Fragment newDetail = CrimeFragment.newInstance(crime.getId());
if (oldDetail != null) {
ft.remove(oldDetail);
}
ft.add(R.id.detailFragmentContainer, newDetail);
ft.commit();
}
}
, CrimeListFragment onCrimeSelected(Crime)
, CrimePagerActivity.
CrimeListFragment.java onListItemClick() onOptions
ItemSelected(MenuItem) , Callbacks.
onCrimeSelected(Crime).
22.10. (CrimeListFragment.java)
public void onListItemClick(ListView l, View v, int position, long id) {
// Crime
Crime c = ((CrimeAdapter)getListAdapter()).getItem(position);
// CrimePagerActivity
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, c.getId());
startActivity(i);
mCallbacks.onCrimeSelected(c);
}
377
...
@TargetApi(11)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
i.putExtra(CrimeFragment.EXTRA_CRIME_ID, crime.getId());
startActivity(i);
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
mCallbacks.onCrimeSelected(crime);
return true;
...
}
}
onOptionsItemSelected() . ,
( ).
CriminalIntent . ;
CrimeFragment detailFragmentContainer.
- , CrimeFragment .
. 22.6.
.
CrimeListFragment.onResume().
378
22.
.
CrimeFragment.
CrimeFragment.Callbacks
CrimeFragment :
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}
CrimeFragment onCrimeUpdated(Crime) -
Crime. onCrimeUpdated(Crime)
CrimeListActivity CrimeListFragment.
CrimeFragment, CrimeListFragment
, CrimeListFragment.
22.11. updateUI() (CrimeListFragment.java)
public class CrimeListFragment extends ListFragment {
...
public void updateUI() {
((CrimeAdapter)getListAdapter()).notifyDataSetChanged();
}
CrimeFragment.java ,
mCallbacks onAttach() onDetach().
22.12. CrimeFragment (CrimeFragment.java)
...
private ImageView mPhotoView;
private Button mSuspectButton;
private Callbacks mCallbacks;
/**
* -
*/
public interface Callbacks {
void onCrimeUpdated(Crime crime);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (Callbacks)activity;
}
@Override
public void onDetach() {
super.onDetach();
379
mCallbacks = null;
CrimeFragment.Callbacks CrimeListActivity,
onCrimeUpdated(Crime).
22.13. (CrimeListActivity.java)
public void onCrimeUpdated(Crime crime) {
FragmentManager fm = getSupportFragmentManager();
CrimeListFragment listFragment = (CrimeListFragment)
fm.findFragmentById(R.id.fragmentContainer);
listFragment.updateUI();
}
CrimeFragment.java onCrimeUpdated(Crime)
Crime.
22.14. onCrimeUpdated(Crime) (CrimeFragment.java)
@Override
@TargetApi(11)
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_crime, parent, false);
...
mTitleField = (EditText)v.findViewById(R.id.crime_title);
mTitleField.setText(mCrime.getTitle());
mTitleField.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence c, int start, int before, int count)
});
mSolvedCheckBox = (CheckBox)v.findViewById(R.id.crime_solved);
mSolvedCheckBox.setChecked(mCrime.isSolved());
mSolvedCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
});
...
}
}
...
mCrime.setTitle(c.toString());
mCallbacks.onCrimeUpdated(mCrime);
getActivity().setTitle(mCrime.getTitle());
//
mCrime.setSolved(isChecked);
mCallbacks.onCrimeUpdated(mCrime);
return v;
380
22.
onCrimeUpdated(Crime) onActivityResult(),
, .
, CrimeFragment
.
22.15. onCrimeUpdated(Crime) (2) (CrimeFragment.java)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != Activity.RESULT_OK) return;
if (requestCode == REQUEST_DATE) {
Date date = (Date)data.getSerializableExtra(DatePickerFragment.EXTRA_DATE);
mCrime.setDate(date);
mCallbacks.onCrimeUpdated(mCrime);
updateDate();
} else if (requestCode == REQUEST_PHOTO) {
// Photo Crime
String filename = data
.getStringExtra(CrimeCameraFragment.EXTRA_PHOTO_FILENAME);
if (filename != null) {
Photo p = new Photo(filename);
mCrime.setPhoto(p);
mCallbacks.onCrimeUpdated(mCrime);
showPhoto();
}
} else if (requestCode == REQUEST_CONTACT) {
...
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setSuspect(suspect);
mCallbacks.onCrimeUpdated(mCrime);
mSuspectButton.setText(suspect);
c.close();
CrimeListActivity CrimeFragment.Callbacks.
CriminalIntent . :
, CrimeFragment, CrimeFragment.
Callbacks. CrimeFragment.Callbacks CrimePagerActivity.
CrimePagerActivity , onCrime
Updated(Crime) . CrimePagerActivity
CrimeFragment, onResume().
22.16. CrimeFragment.Callbacks (CrimePagerActivity.java)
public class CrimePagerActivity extends FragmentActivity
implements CrimeFragment.Callbacks {
...
381
. 22.7. , ,
CriminalIntent . 13
, ,
, . ?
, CriminalIntent.
:
Android 3.2
.
small, normal, large xlarge.
.22.1 .
22.1.
small
320 x 426dp
normal
320 x 470dp
large
480 x 640dp
xlarge
720 x 960dp
382
22.
Android3.2.
, .
.22.2 .
22.2.
wXXXdp
: XXX dp
hXXXdp
: XXX dp
swXXXdp
: ( )
XXX dp
, ,
, 300dp.
res/layout-w300dp (w width,
). h
(Height, ).
,
.
sw (Smallest Width, ).
,
, . 1024 800, sw
800. 800 1024, sw 800.
23
-,
Android. , ,
Android.
NerdLauncher
(NewAndroid Application Project) ,
CriminalIntent (. 23.1).
NerdLauncher com.bignerdranch.android.nerdlauncher.
, . . NerdLauncherActivity
Finish.
NerdLauncherActivity SingleFragmentActivity, . Package Explorer
SingleFragmentActivity.java CriminalIntent. com.
bignerdranch.android.nerdlauncher. Eclipse .
activity_fragment.xml. res/layout/activity_fragment.xml res/layout NerdLauncher.
NerdLauncher .
, .
, .
384
23.
. 23.1. NerdLauncher
NerdLauncherFragment ListFragment ,
ListView,
ListFragment.
NerdLauncherFragment
android.support.v4.app.ListFragment. .
NerdLauncherActivity.java NerdLauncherActivity
SingleFragmentActivity. createFragment() , NerdLauncherFragment.
23.1. SingleFragmentActivity (NerdLauncherActivity.java)
public class NerdLauncherActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nerd_launcher);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_nerd_launcher, menu);
return true;
}
@Override
public Fragment createFragment() {
return new NerdLauncherFragment();
}
385
NerdLauncher . NerdLauncher ,
. MAIN
LAUNCHER. :
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
NerdLauncherFragment.java onCreate(Bundle)
. PackageManager , .
, PackageManager.
23.2. PackageManager (NerdLauncherFragment.java)
public class NerdLauncherFragment extends ListFragment {
private static final String TAG = "NerdLauncherFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent startupIntent = new Intent(Intent.ACTION_MAIN);
startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = getActivity().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(startupIntent, 0);
? ,
MAIN/LAUNCHER
MAIN/LAUNCHER, startActivity().
, startActivity(Intent) ,
.
.
386
23.
ArrayAdapter, , ListView.
23.4. (NerdLauncherFragment.java)
...
Collections.sort(activities, new Comparator<ResolveInfo></ResolveInfo>() {
...
}
});
387
NerdLauncher; ListView,
.
. 23.2.
. .
.
388
23.
ResolveInfo , . ResolveInfo ActivityInfo. ( ,
ResolveInfo, .)
NerdLauncherFragment.java onListItemClick() ActivityInfo .
, .
23.5. onListItemClick() (NerdLauncherFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
ResolveInfo resolveInfo = (ResolveInfo)l.getAdapter().getItem(position);
ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo == null) return;
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClassName(activityInfo.applicationInfo.packageName, activityInfo.name);
}
startActivity(i);
: . ,
, .
, .
, .
23.5
Intent:
public Intent setClassName(String packageName, String className)
,
. Intent,
Context Class:
public Intent(Context packageContext, Class<?> cls)
ComponentName
, . Activity Class
Intent, Activity.
ComponentName
Intent :
public Intent setComponent(ComponentName component)
setClassName(), , .
NerdLauncher , .
389
Android
. (task) , .
, . Back .
Back , .
. , , .
.
.
,
.
Back
( )
. 23.3.
390
23.
.
CriminalIntent .
, CriminalIntent (, ).
, , .
, NerdLauncher,
NerdLaucher. , CriminalIntent NerdLauncher . ( Recents,
; Home.)
CriminalIntent. CrimeListActivity NerdLauncher. NerdLauncher
CriminalIntent,
.
. 23.4. CriminalIntent
, NerdLauncher .
, .
, .
NerdLauncher CriminalIntent.
, CriminalIntent .
NerdLauncher
391
23.6. (NerdLauncherFragment.java)
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClassName(activityInfo.applicationInfo.packageName, activityInfo.name);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
. 23.5. CriminalIntent
CriminalIntent NerdLauncher
CriminalIntent. FLAG_ACTIVITY_NEW_TASK
. CrimeListActivity , Android
.
NerdLauncher
, ?
NerdLauncher
. AndroidManifest.xml NerdLauncher
.
23.7. NerdLauncher (AndroidManifest.xml)
<intent-filter>
<action android:name="android.intent.action.MAIN" />
392
23.
. ,
ResolveInfo.loadLabel()
. ResolveInfo
loadIcon() , .
: NerdLauncher .
, NerdLauncher
.
ActivityManager, , , . PackageManager
Activity getActivityManager()
.
ActivityManager Activity.
getSystemService() Activity.ACTIVITY_SERVICE.
getRunningTasks() , ( ).
, moveTaskToFront().
Android .
:
.
, ,
.
, , , ..
( , ) (thread). Android
Dalvik.
393
, Android ( ).
,
.
,
. , -
,
(multi-threading),
Android , .
. , .
. ,
.
, ,
. ,
CriminalIntent NerdLauncher CriminalIntent ,
CrimeListActivity .
CriminalIntent.
, , ,
.
CriminalIntent, CriminalIntent
.
CriminalIntent
CriminalIntent)
. 23.6.
, Back , .
. Android? ,
Android . Home ,
. , , . ,
Google Play ,
.
24
,
, . ,
.
,
. , .
, , .
,
. ,
;
.
, .24.1.
. 24.1.
RemoteControl
RemoteControl
, .24.1.
.
, .
Delete . Enter
.
RemoteControl
395
RemoteControl
Android Application ,
.24.2.
. 24.2. RemoteControl
RemoteControlActivity.
RemoteControlActivity
RemoteControlActivity SingleFragmentActivity,
SingleFragmentActivity.java CriminalIntent com.
bignerdranch.android.remotecontrol. activity_fragment.xml
res/layout RemoteControl.
RemoteControlActivity.java. RemoteControlActivity
SingleFragmentActivity; RemoteControlFragment. ( ). ,
RemoteControlActivity.onCreate(),
.
24.1. RemoteControlActivity (RemoteControlActivity.java)
public class RemoteControlActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
396
24.
24.1 ()
}
setContentView(R.layout.activity_remote_control);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_remote_control, menu);
return true;
}
@Override
protected Fragment createFragment() {
return new RemoteControlFragment();
}
@Override
public void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
}
AndroidManifest.xml .
24.2. (AndroidManifest.xml)
<activity
android:name="com.bignerdranch.android.remotecontrol.RemoteControlActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
RemoteControlFragment
Package Explorer activity_remote_control.xml fragment_remote_control.xml. , .
fragment_remote_control.xml XML 24.3.
24.3. (layout/fragment_remote_control.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RemoteControlActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
RemoteControl
397
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_remote_control_tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="*" >
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="center"
android:text="0"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="15dp"
android:background="#555555"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textSize="20dp" />
<TableRow android:layout_weight="1" >
<Button
android:id="@+id/fragment_remote_control_zeroButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="1" />
<Button
android:id="@+id/fragment_remote_control_enterButton"
android:layout_width="0dp"
android:layout_height="match_parent"
android:text="Enter" />
</TableRow>
</TableLayout>
android:stretchColumns="*" ,
. ,
dp sp. , .
, RemoteControlFragment.
android.support.v4.app.Fragment. RemoteControlFragment.
java onCreateView() .
398
24.
return v;
, .
, .
, ,
399
, 0.
Enter
.
RemoteControl. . , ?
. 24.3.
, , .
. ,
, . ,
?
, Android ,
.
CSS. XML -. : ,
, .
, , <resources> XML
res/values. , , ,
styles.xml.
400
24.
Android styles.xml. ( :
RemoteControl
Android; ,
.)
, ,
RemoteButton. , .
24.5. RemoteControl (values/styles.xml)
<resources>
<style name="AppTheme" parent="android:Theme.Light" />
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
</style>
</resources>
<style> <item>. ,
XML, , .
RemoteButton,
. ,
.
, style .
fragment_remote_control.xml:
.
24.6. (layout/activity_remote.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
>
...
<TableRow android:layout_weight="1" >
<Button
android:id="@+id/fragment_remote_control_zeroButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="1" />
<Button
401
android:id="@+id/fragment_remote_control_enterButton"
android:layout_width="0dp"
android:layout_height="match_parent"
style="@style/RemoteButton"
android:text="Enter" />
</TableRow>
</TableLayout>
RemoteControl. , ,
.
.
12 ,
.
34 .
. res/layout/
button_row.xml .
24.7. (layout/button_row.xml)
<?xml version="1.0" encoding="utf-8"?>
<TableRow xmlns:android="http://schemas.android.com/apk/res/android" >
<Button style="@style/RemoteButton" />
<Button style="@style/RemoteButton" />
<Button style="@style/RemoteButton" />
</TableRow>
. ?
include.
24.8. (layout/fragment_remote_control.xml)
<TableRow android:layout_weight="1" >
<Button
android:id="@+id/fragment_remote_control_zeroButton"
style="@style/RemoteButton"
android:text="0" />
<Button
android:id="@+id/fragment_remote_control_oneButton"
style="@style/RemoteButton"
android:text="1" />
<Button
android:id="@+id/fragment_remote_control_enterButton"
style="@style/RemoteButton"
android:text="Enter" />
</TableRow>
<include
android:layout_weight="1"
layout="@layout/button_row" />
402
24.
24.8 ()
<include
android:layout_weight="1"
layout="@layout/button_row" />
<include
android:layout_weight="1"
layout="@layout/button_row" />
<include
android:layout_weight="1"
layout="@layout/button_row" />
, , , layout/button_row.xml,
. ,
, .
,
. .
24.9. (RemoteControlFragment.java)
View.OnClickListener numberButtonListener = new View.OnClickListener() {
...
};
Button zeroButton = (Button)v.findViewById(R.id.fragment_remote_control_zeroButton);
zeroButton.setOnClickListener(numberButtonListener);
Button oneButton = (Button)v.findViewById(R.id.fragment_remote_control_oneButton);
oneButton.setOnClickListener(numberButtonListener);
TableLayout tableLayout = (TableLayout)v
.findViewById(R.id.fragment_remote_control_tableLayout);
int number = 1;
for (int i = 2; i < tableLayout.getChildCount() - 1; i++) {
TableRow row = (TableRow)tableLayout.getChildAt(i);
for (int j = 0; j < row.getChildCount(); j++) {
Button button = (Button)row.getChildAt(j);
button.setText("" + number);
button.setOnClickListener(numberButtonListener);
number++;
}
}
for 2, ,
.
numberButtonListener, , .
. :
Delete Enter.
24.10. (RemoteControlFragment.java)
for (int i = 2; i < tableLayout.getChildCount() - 1; i++) {
...
}
TableRow bottomRow = (TableRow)tableLayout
.getChildAt(tableLayout.getChildCount() - 1);
403
.
, .
.
. 24.4. ,
, ,
. RemoteButton.
404
24.
24.11. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
</style>
. 24.5. !
: include merge
include RemoteButton ( ). :
<include layout="@layout/some_partial_layout"/>
@layout/some_partial_layout.
include
, ,
.
405
, , . ,
.
include , . -,
;
, . -,
android:id android:layout_*
, include.
,
.
merge include.
.
merge, merge
include,
merge .
, .
, , merge .
: XML ,
.
.
Delete Enter , , .
, .
, RemoteButton, .
.
, , .
. parent ,
. ,
(, ParentStyleName.
MyStyleName).
25
, .
, , Android .
, .
. 25.1. RemoteControl
(drawables). Android ,
XML
407
, , , Drawable,
. : BitmapDrawable,
.
:
, , 9- .
XML,
XML.
XML
XML, : android:background .
25.1. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">#ccd7ee</item>
</style>
. 25.2. ?
408
25.
. ,
.
- ?
Button , View .
, Drawable.
.
,
.
XML.
XML , drawable ( ).
Package Explorer res/drawable.
XML button_shape_normal.xml.
shape. ( normal, ?
, .)
25.2.
(drawable/button_shape_normal.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="3dp" />
<gradient
android:angle="90"
android:endColor="#cccccc"
android:startColor="#acacac" />
</shape>
. corners
, gradient /
.
shape : ,
, .., .
http://developer.android.com/guide/topics/resources/
drawable-resource.html.
styles.xml Button, Drawable .
25.3. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
409
<item name="android:background">#ccd7ee</item>
<item name="android:background">@drawable/button_shape_normal</item>
</style>
RemoteControl , .
. 25.3.
, .
Button
(state list).
,
View. (
18.) ,
, .
.
, , .
Package Explorer button_shape_normal.xml
button_shape_pressed.xml. button_shape_pressed.xml
180 , .
410
25.
25.4. (drawable/button_shape_pressed.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="3dp" />
<gradient
android:angle="90"
android:angle="270"
android:endColor="#cccccc"
android:startColor="#acacac" />
</shape>
.
selector
item, .
res/drawable/, XML button_shape.xml selector.
25.5. (drawable/button_shape.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/button_shape_normal"
android:state_pressed="false"/>
<item android:drawable="@drawable/button_shape_pressed"
android:state_pressed="true"/>
</selector>
,
. , ,
,
. ,
, .
res/drawable/ button_text_color.xml.
25.6.
(drawable/button_text_color.xml)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:color="#ffffff"/>
<item android:state_pressed="true" android:color="#556699"/>
</selector>
styles.xml Button, .
411
25.7. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textColor">#556699</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">@drawable/button_shape_normal</item>
<item name="android:background">@drawable/button_shape</item>
<item name="android:textColor">@drawable/button_text_color</item>
</style>
RemoteControl. , .
. 25.4.
Android, ,
. , ,
XML: .
412
25.
, .
layer-list,
inset, .
XML res/drawable/. button_shape_
shadowed.xml layer-list.
25.8. (drawable/button_shape_shadowed.xml)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<corners android:radius="5dp" />
<gradient
android:angle="90"
android:centerColor="#303339"
android:centerY="0.05"
android:endColor="#000000"
android:startColor="#00000000" />
</shape>
</item>
<item>
<inset
android:drawable="@drawable/button_shape"
android:insetBottom="5dp" />
</item>
</layer-list>
,
( ).
inset,
5dp, .
,
.
, ,
.
, , .
, drawable/folder . ,
.
styles.xml
.
25.9. (values/styles.xml)
<style name="RemoteButton">
<item name="android:layout_width">0dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:textSize">20dp</item>
<item name="android:layout_margin">3dp</item>
<item name="android:background">@drawable/button_shape_normal</item>
9-
413
<item name="android:background">@drawable/button_shape_shadowed</item>
<item name="android:textColor">@drawable/button_text_color</item>
</style>
:
, .
remote_background.xml shape.
(: ,
).
25.10.
(drawable/remote_background.xml)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:centerY="0.05"
android:endColor="#dbdbdb"
android:gradientRadius="500"
android:startColor="#f4f4e9"
android:type="radial" />
</shape>
fragment_remote_control.xml.
25.11. (layout/fragment_remote_control.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_remote_control_tableLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/remote_background"
android:stretchColumns="*" >
.
( , ,
TableLayout.) , ,
. , .
9-
,
XML.
. (stretchable)
, Android
9- (9-patch).
TextView
. ,
414
25.
. TextView,
. window.png
TextView , .
. 25.5.
bar.png .
; .
. 25.6.
9-
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:gravity="center"
android:text="0"
android:textColor="#ffffff"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="15dp"
android:layout_weight="1"
android:background="#555555"
android:background="@drawable/bar"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textStyle="italic"
android:textSize="20dp" />
...
</TableLayout>
, .
. 25.7.
415
416
25.
. ,
. , TextView
.
9- .
9- , Android
, .
, , .
9-?
33 9 , (patches).
, , .
. 25.8. 9-
9- png, : .9.png,
. . ,
.
9- ,
draw9patch, Android SDK.
tools SDK. , File.
, ,
. window.
png, . 25.9.
.
?
, (
). , , . ,
.
9-
417
. 25.9. 9-
, window_
patch.9.png. , 9-
;
window.9.png window.png, .
9- bar.png. ,
4 .
. 25.10. 9-
bar_patch.9.png.
418
25.
. 25.11. RemoteControl
25.13. 9- (layout/fragment_remote_control.xml)
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
... >
<TextView
android:id="@+id/fragment_remote_control_selectedTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:background="@drawable/window"
android:background="@drawable/window_patch"
android:gravity="center"
android:text="0"
android:textSize="50dp" />
<TextView
android:id="@+id/fragment_remote_control_workingTextView"
android:layout_width="match_parent"
android:layout_height="0dp"
9-
419
android:layout_margin="15dp"
android:layout_weight="1"
android:background="@drawable/bar"
android:background="@drawable/bar_patch"
android:gravity="center"
android:text="0"
android:textColor="#cccccc"
android:textSize="20dp" />
...
</TableLayout>
. ! . , ?
, . ,
.
,
.
26
HTTP
. ,
?
,
.
Android PhotoGallery. Flickr,
, Flickr.
26.1 .
. 26.1. PhotoGallery
PhotoGallery
421
( .26.1
Flickr. Flickr , , .
, Flickr, http://pressroom.yahoo.net/pr/ycorp/
permissions.aspx.)
PhotoGallery .
XML, . : , ,
, -.
HTTP
Android. -
HTTP.
Flickr, (
27).
. 26.2. PhotoGallery
PhotoGallery
Android. , .26.3.
422
26. HTTP
. 26.3. PhotoGallery
PhotoGalleryActivity.
PhotoGallery ,
. PhotoGalleryActivity
SingleFragmentActivity, ,
activity_fragment.xml. ,
PhotoGalleryFragment, .
SingleFragmentActivity.java activity_fragment.xml
.
PhotoGalleryActivity.java PhotoGalleryActivity SingleFragmentActivity ; , ,
createFragment(). createFragment()
PhotoGalleryFragment.
( , .
, PhotoGalleryFragment.)
26.1. (PhotoGalleryActivity.java)
public class PhotoGalleryActivity extends Activity {
public class PhotoGalleryActivity extends SingleFragmentActivity {
/* , */
@Override
public Fragment createFragment() {
return new PhotoGalleryFragment();
}
PhotoGallery
423
PhotoGallery GridView,
PhotoGalleryFragment.
GridView AdapterView, , ListView. ListView, GridView
GridFragment, . , PhotoGalleryFragment.
PhotoGalleryFragment ,
GridView .
, layout/activity_photo_gallery.xml
layout/fragment_photo_gallery.xml.
GridView, . 26.4.
120dp GridView , .
120dp, stretchMode GridView
.
, PhotoGalleryFragment. ,
GridView ( 26.2).
26.2. (PhotoGalleryFragment.java)
package com.bignerdranch.android.photogallery;
...
public class PhotoGalleryFragment extends Fragment {
GridView mGridView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
setRetainInstance(true);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
424
26. HTTP
26.2 ()
false);
return v;
, PhotoGallery , (
).
PhotoGallery . Java.
Flickr, FlickrFetchr.
F l i c k r F e t c h r :
getUrlBytes(String) getUrl(String) . getUrlBytes(String)
URL .
getUrl(String) getUrlBytes(String) String.
FlickrFetchr.java getUrlBytes(String) getUrl(String)
( 26.3).
26.3. (FlickrFetchr.java)
package com.bignerdranch.android.photogallery;
...
public class FlickrFetchr {
byte[] getUrlBytes(String urlSpec) throws IOException {
URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return null;
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
425
URL , http://www.google.com.
openConnection()
URL-. URL.openConnection() URLConnection,
HTTP,
HttpURLConnection. HTTP- , , ..
HttpURLConnection ,
getInputStream() ( getOutputStream()
POST-).
.
URL
read(), . InputStream
. , ByteArrayOutputStream.
getUrlBytes(String),
getUrl(String). ,
getUrlBytes(String), String.
?
, .
: . ,
.
,
AndroidManifest.xml.
26.4. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
426
26. HTTP
AsyncTask
. FlickrFetchr.getURL(String)
PhotoGalleryFragment. .
AsyncTask. AsyncTask
, doInBackground().
PhotoGalleryFragment.java PhotoGalleryFragment FetchItemsTask. AsyncTask.doInBackground() .
PhotoGalleryFragment.onCreate().
26.5. AsyncTask (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";
GridView mGridView;
...
PhotoGalleryFragment.onCreate() execute()
FetchItemsTask.
26.6. AsyncTask (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";
GridView mGridView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
...
427
new FetchItemsTask().execute();
execute() AsyncTask,
doInBackground(). , LogCat
HTML Google Javascriptlicious , . 26.5.
, ,
Android.
. - - ,
. - Android , Honeycomb
Android. , Android
NetworkOnMainThreadException. ? ,
, (thread),
.
(thread) . Android
. . , ,
.
, ,
: 1. ,
1
, . . .
428
26. HTTP
, ,
. , ,
, .
( Android
)
( )
( )
. 26.6.
,
- . , ? -
, .
, .
. , . , ,
.. (
,
, UI-.)
. , ,
. ,
( , AsyncTask) .
:
. ,
ANR (Application Not Responding). ,
Android ,
, Back. .
XML Flickr
429
. 26.7.
Android, Honeycomb, .
,
. Android
.
? AsyncTask.
, AsyncTask.
, -
.
XML Flickr
Flickr XML API. www.flickr.com/services/api/.
Request Formats.
REST, API http://api.flickr.com/
services/rest/. , Flickr
.
API API Methods. photos flickr.photos.getRecent.
; ,
430
26. HTTP
, flickr. ,
PhotoGallery.
getRecent API.
API, http://www.flickr.com/services/
api/ API keys.
Yahoo. API;
. API
: 4f721bgafa75bf6d2cb9af54f937bb70.
- Flickr.
GET- http://api.flickr.com/services/rest/?method=flickr.
photos.getRecent&api_key=xxx.
.
FlickrFetchr.
26.7. (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";
private
private
private
private
static
static
static
static
final
final
final
final
String
String
String
String
ENDPOINT = "http://api.flickr.com/services/rest/";
API_KEY = "yourApiKeyHere";
METHOD_GET_RECENT = "flickr.photos.getRecent";
PARAM_EXTRAS = "extras";
XML Flickr
431
Uri.Builder URL-
API- Flickr. Uri.Builder URL- . Uri.
Builder.appendQueryParameter(String,String)
.
, AsyncTask PhotoGalleryFragment
fetchItems().
26.9. fetchItems() (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask<Void,Void,Void> {
@Override
protected Void doInBackground(Void... params) {
try {
String result = new FlickrFetchr().getUrl("http://www.google.com");
Log.i(TAG, "Fetched contents of URL: " + result);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch URL: ", ioe);
}
new FlickrFetchr().fetchItems();
return null;
}
}
432
26. HTTP
, XML Flickr ; ? , .
, PhotoGallery, GalleryItem. .26.9
PhotoGallery.
: .26.9 -,
.
GalleryItem .
26.10. (GalleryItem.java)
package com.bignerdranch.android.photogallery;
public class GalleryItem {
private String mCaption;
private String mId;
private String mUrl;
XML Flickr
433
,
XML, Flickr. XML
XmlPullParser.
XmlPullParser
XmlPullParser , XML. XmlPullParser
Android .
GalleryItem.
FlickrFetchr , XML, . , XmlPullParser
XML. GalleryItem
ArrayList.
26.11. Flickr (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";
private static final String ENDPOINT = "http://api.flickr.com/services/rest/";
private static final String API_KEY = "your API key";
private static final String METHOD_GET_RECENT = "flickr.photos.getRecent";
private static final String XML_PHOTO = "photo";
...
public void fetchItems() {
...
}
void parseItems(ArrayList<GalleryItem> items, XmlPullParser parser)
throws XmlPullParserException, IOException {
int eventType = parser.next();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG &&
XML_PHOTO.equals(parser.getName())) {
String id = parser.getAttributeValue(null, "id");
String caption = parser.getAttributeValue(null, "title");
String smallUrl = parser.getAttributeValue(null, EXTRA_SMALL_URL);
eventType = parser.next();
434
26. HTTP
, XmlPullParser XML,
, START_TAG, END_TAG END_DOCUMENT.
, getText(), getName() getAttributeValue(), ,
XmlPullParser. XmlPullParser
XML, next(). ,
, .
. 26.10. XmlPullParser
AsyncTask
435
AsyncTask
GridView PhotoGalleryFragment.
GridView, ListView, AdapterView,
, .
PhotoGalleryFragment.java ArrayList GalleryItems, ArrayAdapter , Android.
26.13. setupAdapter() (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";
GridView mGridView;
ArrayList<GalleryItem> mItems;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
mGridView = (GridView)v.findViewById(R.id.gridView);
setupAdapter();
}
return v;
void setupAdapter() {
if (getActivity() == null || mGridView == null) return;
if (mItems != null) {
mGridView.setAdapter(new ArrayAdapter<GalleryItem>(getActivity(),
android.R.layout.simple_gallery_item, mItems));
} else {
mGridView.setAdapter(null);
}
436
26. HTTP
GridView GridFragment,
. ,
setupAdapter().
GridView. onCreateView(),
GridView .
.
android.R.layout.simple_gallery_item TextView. , GalleryItem toString()
mCaption . ,
GridView GalleryItem .
: getActivity()
null. , , - .
,
. , . .
, AsyncTask, , , .
, , ; .
Flickr setupAdapter(). ,
, setupAdapter() doInBackground()
FetchItemsTask. . ,
,
Flickr. , , ,
? ,
.
.
, .
? AsyncTask onPostExecute(), . onPostExecute() doInBackground(). , onPostExecute() ,
, .
FetchItemsTask, mItems setupAdapter() .
26.14. (PhotoGalleryFragment.java)
private class
private class
@Override
protected
protected
AsyncTask
437
new FlickrFetchr().fetchItems();
return new FlickrFetchr().fetchItems();
return null;
@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
mItems = items;
setupAdapter();
}
. -,
FetchItemsTask. , AsyncTask; , doInBackground(),
onPostExecute(). -, doInBackground() , GalleryItem.
.
,
onPostExecute(). -, onPostExecute().
, doInBackground(), mItems
GridView.
. ,
, GalleryItem.
. 26.11. Flickr
438
26. HTTP
: AsyncTask
- AsyncTask,
. ?
- .
:
AsyncTask<String,Void,Void> task = new AsyncTask<String,Void,Void>() {
public Void doInBackground(String... params) {
for (String parameter : params) {
Log.i(TAG, "Received parameter: " + parameter);
}
};
return null;
execute(), . doInBackground().
- . :
final ProgressBar progressBar = /* */;
progressBar.setMax(100);
AsyncTask<Integer,Integer,Void> task = new AsyncTask<Integer,Integer,Void>() {
public Void doInBackground(Integer... params) {
for (Integer progress : params) {
publishProgress(progress);
Thread.sleep(1000);
}
}
public void onProgressUpdate(Integer... params) {
int progress = params[0];
progressBar.setProgress(progress);
}
};
task.execute(25, 50, 75, 100);
.
, , AsyncTask
publishProgress() onProgressUpdate().
: doInBackground()
publishProgress(). onProgressUpdate() . , onProgressUpdate(), publishProgress() doInBackground().
439
AsyncTask
AsyncTask , .
AsyncTask
.
AsyncTask , AsyncTask.cancel(boolean)
AsyncTask.
AsyncTask.cancel(boolean)
. cancel(false), true isCancelled(). AsyncTask isCancelled()
doInBackground() .
cancel(true)
, doInBackground(). AsyncTask.cancel(true)
AsyncTask.
.
.
getRecent 100.
page ,
.
,
.
,
.
27
Looper, Handler
HandlerThread
XML Flickr
.
Looper, Handler HandlerThread
PhotoGallery.
GridView
PhotoGalleryFragment TextView
GridView. TextView
GalleryItem.
, ,
ImageView. ImageView , mUrl GalleryItem.
gallery_item.xml. ImageView (. 27.1).
. 27.1. (res/layout/gallery_item.xml)
GridView
441
ImageView GridView, ,
.
.
ImageView, scaleType centerCrop. ,
, .
ImageView,
.
brian_up_close.jpg res/drawable-hdpi.
PhotoGalleryFragment ArrayAdapter , getView()
ImageView .
27.1. GalleryItemAdapter (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
...
void setupAdapter() {
if (getActivity() == null || mGridView == null) return;
if (mItems != null) {
mGridView.setAdapter(new ArrayAdapter<GalleryItem>(getActivity(),
android.R.layout.simple_gallery_item, mItems));
mGridView.setAdapter(new GalleryItemAdapter(mItems));
} else {
mGridView.setAdapter(null);
}
return convertView;
442
. 27.2. AdapterView-ArrayAdapter
PhotoGallery, .
. 27.3.
443
PhotoGallery : PhotoGalleryFragment AsyncTask, XML Flickr
, XML GalleryItem.
GalleryItem URL,
.
. , doInBackground()
FetchItemsTask. GalleryItem 100 URL-
. ,
100. onPostExecute() GridView.
. -,
, .
.
-, . . , 1000? ,
? .
, .
GridView . getView() .
AsyncTask ,
. ( , ,
.)
AsyncTask . .
, GridView ,
?
-.
, .
, .
. ,
.
444
. , , .
, , -
.
. , , , .
.
Android , ,
(message queue). , , (message loop);
, (. 27.4).
. 27.4.
Looper, .
, ,
.
, .
HandlerThread,
Looper.
ThumbnailDownloader, HandlerThread.
ThumbnailDownloader
445
; Token,
ThumbnailDownloader<Token> .
queueThumbnail()
(27.2).
27.2. (ThumbnailDownloader.java)
public class ThumbnailDownloader<Token> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
public ThumbnailDownloader() {
super(TAG);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
}
446
27.3 ()
@Override
public void onDestroy() {
super.onDestroy();
mThumbnailThread.quit();
Log.i(TAG, "Background thread destroyed");
}
...
: -, , getLooper()
start() ThumbnailDownloader. ,
(
Looper ). -, quit()
onDestroy(). . HandlerThread,
, ).
, GalleryItemAdapter.getView()
GalleryItem position, queueThumbnail()
ImageView URL- .
27.4. ThumbnailDownloader (PhotoGalleryFragment.java)
private class GalleryItemAdapter extends ArrayAdapter<GalleryItem> {
...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
ImageView imageView = (ImageView)convertView
.findViewById(R.id.gallery_item_imageView);
imageView.setImageResource(R.drawable.brian_up_close);
GalleryItem item = getItem(position);
mThumbnailThread.queueThumbnail(imageView, item.getUrl());
return convertView;
PhotoGallery LogCat.
GridView LogCat , , ThumbnailDownloader
.
, HandlerThread , , queueThumbnail(),
ThumbnailDownloader.
, , (message
handler).
447
. , (
),
, ,
.
Message .
:
what int, ;
obj , ;
target , Handler, .
Message Handler.
, Handler. , Handler ,
.
, Handler. Handler ;
.
Message Looper,
Looper Message.
Handler Looper.
448
Looper Handler.
, Message Handler Handler.
Handler . Handler.obtainMessage().
, .
Handler.obtainMessage() ,
Message, ,
.
Message , sendToTarget(),
.
Looper.
queueThumbnail(). what , MESSAGE_DOWNLOAD. obj Token
ImageView, queueThumbnail().
Looper ,
. , Handler.handleMessage() .
handleMessage() FlickrFetchr
URL- .
, 27.5.
449
Handler
Handler
Handler
. 27.7.
27.5. , (ThumbnailDownloader.java)
public class ThumbnailDownloader<Token> extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;
Handler mHandler;
Map<Token, String> requestMap =
Collections.synchronizedMap(new HashMap<Token, String>());
public ThumbnailDownloader() {
super(TAG);
}
@SuppressLint("HandlerLeak")
@Override
protected void onLooperPrepared() {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_DOWNLOAD) {
@SuppressWarnings("unchecked")
Token token = (Token)msg.obj;
Log.i(TAG, "Got a request for url: " + requestMap.get(token));
handleRequest(token);
}
}
};
}
public void queueThumbnail(Token token, String url) {
Log.i(TAG, "Got a URL: " + url");
requestMap.put(token, url);
mHandler
.obtainMessage(MESSAGE_DOWNLOAD, token)
.sendToTarget();
450
27.5 ()
private void handleRequest(final Token token) {
try {
final String url = requestMap.get(token);
if (url == null)
return;
byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
final Bitmap bitmap = BitmapFactory
.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap created");
onLooperPrepared()
@SuppressLint("HandlerLeak"). Android Lint Handler. Handler Looper. , Handler
, .
HandlerThread, .
@SuppressWarnings("unchecked") . , Token , Message.
obj Object. -
. ,
(type erasure) Android.
requestMap HashMap.
, Token , URL-, Token.
queueThumbnail() - Token-URL.
, Token obj
.
Handler.handleMessage() Handler onLooperPrepared(). HandlerThread.onLooperPrepared() ,
Looper ,
Handler.
Handler.handleMessage() , Token
handleRequest().
handleRequest(). URL-,
FlickrFetchr. FlickrFetchr.getUrlBytes(),
.
451
, BitmapFactory , getUrlBytes().
PhotoGallery LogCat .
, ImageView, GalleryItemAdapter.
, .
.
, ThumbnailDownloader Handler
.
Handler
, HandlerThread , Handler HandlerThread.
Looper.
Handler
Looper . Handler
. Handler Looper -.
, Handler ,
. , Handler
ThumbnailDownloader.
. 27.8. ThumbnailDownloader
Handler, .
452
!
ImageView.
. 27.9. ThumbnailDownloader
ThumbnailDownloader.java mRes
ponseHandler Handler, .
, Handler , .
27.6. (ThumbnailDownloader.java)
public class ThumbnailDownloader extends HandlerThread {
private static final String TAG = "ThumbnailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;
Handler mHandler;
Map<Token,String> requestMap =
Collections.synchronizedMap(new HashMap<Token,String>());
Handler mResponseHandler;
Listener<Token> mListener;
public interface Listener<Token> {
void onThumbnailDownloaded(Token token, Bitmap thumbnail);
}
public void setListener(Listener<Token> listener) {
mListener = listener;
}
public ThumbnailDownloader() {
super(TAG);
public ThumbnailDownloader(Handler responseHandler) {
super(TAG);
mResponseHandler = responseHandler;
}
PhotoGalleryFragment , Handler
ThumbnailDownloader, Listener
453
454
27.8. (ThumbnailDownloader.java)
...
private void handleRequest(final Token token) {
try {
final String url = requestMap.get(token);
if (url == null)
return;
byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
final Bitmap bitmap = BitmapFactory
.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap created");
mResponseHandler.post(new Runnable() {
public void run() {
if (requestMap.get(token) != url)
return;
requestMap.remove(token);
mListener.onThumbnailDownloaded(token, bitmap);
}
});
} catch (IOException ioe) {
Log.e(TAG, "Error downloading image", ioe);
}
mResponseHandler Looper , .
? requestMap. ,
GridView . ,
ThumbnailDownloader Bitmap, , GridView ImageView URL. , Token
, .
, Token requestMap Token.
,
, .
, ThumbnailDownloader
ImageView. .
.
27.9. (ThumbnailDownloader.java)
public void clearQueue() {
mHandler.removeMessages(MESSAGE_DOWNLOAD);
requestMap.clear();
}
PhotoGalleryFragment .
455
27.10. (PhotoGalleryFragment.java)
@Override
public void onDestroyView() {
super.onDestroyView();
mThumbnailThread.clearQueue();
}
. PhotoGallery. ,
.
PhotoGallery Flickr. : Flickr
-.
: AsyncTask
, Handler Looper, AsyncTask
.
, . AsyncTask
HandlerThread?
. ,
AsyncTask .
, . AsyncTask
, . AsyncTask , ,
.
, : Android3.2
AsyncTask . Android 3.2, AsyncTask
AsyncTask. Executor
AsyncTask . , AsyncTask ,
AsyncTask AsyncTask
.
AsyncTask
, . ,
, Handler ,
.
, (
). , .
456
:
;
.
Bitmap, .
,
.
LRU (Least Recently Used): , .
Android LruCache,
LRU. LruCache ThumbnailDownloader. , URL-
Bitmap, . , , ,
.
, , , .
Bitmap .
,
. , GalleryItem
10 10 GalleryItem.
28
PhotoGallery Flickr. ,
Android.
, , .
Android , ( ) .
, API. , ,
Jelly Bean.
Flickr
, Flickr. Flickr
flickr.photos.search. flickr.
photos.search red:
http://api.flickr.com/services/rest/?method=flickr.photos.search
&api_key=XXX&extras=url_s&text=red
, , . , XML,
GalleryItem, .
, 28.1, FlickrFetchr
. search getRecent GalleryItem , fetchItems()
, downloadGalleryItems(String).
fetchItems() fetchItems(),
.
458
28.
static
static
static
static
static
static
final
final
final
final
final
final
String
String
String
String
String
String
ENDPOINT = "http://api.flickr.com/services/rest/";
API_KEY = "4f721bbafa75bf6d2cb5af54f937bb70";
METHOD_GET_RECENT = "flickr.photos.getRecent";
METHOD_SEARCH = "flickr.photos.search";
PARAM_EXTRAS = "extras";
PARAM_TEXT = "text";
parseItems(items, parser);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
} catch (XmlPullParserException xppe) {
Log.e(TAG, "Failed to parse items", xppe);
}
return items;
459
}
}
if (query != null) {
return new FlickrFetchr().search(query);
} else {
return new FlickrFetchr().fetchItems();
}
@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
...
}
...
PhotoGallery Android.
.
Honeycomb Android . ,
.
Android ,
3.0.
Activity.onSearchRequested(). ,
.
460
28.
; strings.xml. ( , .)
28.4. (res/values/strings.xml)
<resources>
...
<string
<string
<string
<string
name="title_activity_photo_gallery">PhotoGalleryActivity</string>
name="search_hint">Search Flickr</string>
name="search">Search</string>
name="clear_search">Clear Search</string>
</resources>
. ,
onSearchRequested().
.
28.5. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
...
...
@Override
public void onDestroyView() {
...
}
461
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_photo_gallery, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_search:
getActivity().onSearchRequested();
return true;
case R.id.menu_item_clear:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
, .
. 28.1.
, .
onSearchRequested() , PhotoGalleryActivity (searchable activity).
462
28.
. XML.
searchable, , . res/xml,
XML searchable.xml. searchable.
28.6. (res/xml/searchable.xml)
<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:hint="@string/search_hint"
/>
463
.
. startActivity() , action.
intent.action.SEARCH. .
, ,
, action.intent.action.SEARCH.
. 16, . android:value
android:resource .
. ,
:
<meta-data android:name="metadata.value"
android:value="@string/app_name" />
<meta-data android:name="metadata.resource"
android:resource="@string/app_name" />
. 28.2.
464
28.
, onSearchRequested(),
.
,
3.0 ;
, .28.3.
. 28.3.
465
Android
. :
.
. AndroidManifest.xml , .
,
. , .
Android
,
ACTION_SEARCH
. 28.4.
, . . ?
android:launchMode="singleTop" ( 28.7),
.
(launch mode)? ,
,
.
466
28.
28.1.
standard
singleTop
singleTask
. , , ,
,
singleInstance
.
,
. ,
, , . :
,
.
PhotoGalleryActivity ( , SearchView Honeycomb).
singleTop.
,
PhotoGalleryActivity
.
onNewIntent(Intent)
Activity.
PhotoGalleryFragment.
PhotoGalleryFragment updateItems(),
FetchItemsTask .
28.8. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
new FetchItemsTask().execute();
updateItems();
467
PhotoGalleryActivity onNewIntent(Intent),
PhotoGalleryFragment:
28.9. onNewIntent() (PhotoGalleryActivity.java)
public class PhotoGalleryActivity extends SingleFragmentActivity {
private static final String TAG = "PhotoGalleryActivity";
@Override
public Fragment createFragment() {
return new PhotoGalleryFragment();
}
@Override
public void onNewIntent(Intent intent) {
PhotoGalleryFragment fragment = (PhotoGalleryFragment)
getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.i(TAG, "Received a new search query: " + query);
}
fragment.updateItems();
, 17.
.
468
28.
SharedPreferences
Context.getSharedPreferences(String,int).
,
. PreferenceManager.ge
tDefaultSharedPreferences(Context),
(private) .
PhotoGalleryActivity SharedPreferences
.
28.11. (PhotoGalleryActivity.java)
@Override
public void onNewIntent(Intent intent) {
PhotoGalleryFragment fragment = (PhotoGalleryFragment)
getSupportFragmentManager()
.findFragmentById(R.id.fragmentContainer);
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
Log.i(TAG, "Received a new search query: " + query);
PreferenceManager.getDefaultSharedPreferences(this)
.edit()
.putString(FlickrFetchr.PREF_SEARCH_QUERY, query)
.commit();
}
fragment.updateItems();
SharedPreferences.edit()
SharedPreferences.Editor.
469
SharedPreferences. , , FragmentTransaction:
.
, commit() Editor
SharedPreferences.
SharedPreferences.getString(), getInt() , . PhotoGalleryFragment
SharedPreferences .
28.12. (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask<Void,Void,ArrayList<GalleryItem>> {
@Override
protected ArrayList<GalleryItem> doInBackground(Void... params) {
String query = "android"; // just for testing
Activity activity = getActivity();
if (activity == null)
return new ArrayList<GalleryItem>();
@Override
protected void onPostExecute(ArrayList<GalleryItem> items) {
...
}
,
JSON, ?
. PhotoGallery,
- , .
,
updateItems().
28.13. (PhotoGalleryFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_item_clear:
PreferenceManager.getDefaultSharedPreferences(getActivity())
.edit()
.putString(FlickrFetchr.PREF_SEARCH_QUERY, null)
.commit();
470
28.
updateItems();
return true;
default:
return super.onOptionsItemSelected(item);
SearchView Android
3.0
.
Honeycomb.
Honeycomb SearchView. SearchView
(action view) ,
. SearchView
( ,
).
, , .
android:actionViewClass .
28.14. (res/menu/fragment_photo_
gallery.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_search"
android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"
/>
<item android:id="@+id/menu_item_clear"
...
/>
</menu>
, : Android,
. .
, SearchView onOptionsItemSelected().
,
, .
( , , SearchViewCompat . , SearchViewCompat SearchView,
. ,
SearchView , .
.)
471
, PhotoGallery ,
SearchView. , . SearchView
, .
onCreateOptionsMenu() , SearchView.
SearchManager , . SearchManager getSearchableInfo(ComponentName) ,
, SearchableInfo.
SearchableInfo SearchView. ,
28.15.
28.15. SearchView (PhotoGalleryFragment.java)
@Override
@TargetApi(11)
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_photo_gallery, menu);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// SearchView
MenuItem searchItem = menu.findItem(R.id.menu_item_search);
SearchView searchView = (SearchView)searchItem.getActionView();
// searchable.xml
// SearchableInfo
SearchManager searchManager = (SearchManager)getActivity()
.getSystemService(Context.SEARCH_SERVICE);
ComponentName name = getActivity().getComponentName();
SearchableInfo searchInfo = searchManager.getSearchableInfo(name);
searchView.setSearchableInfo(searchInfo);
SearchView. MenuItem ,
getActionView().
SearchManager . SearchManager , , .
SearchManager ,
. ,
, , searchable.xml,
SearchableInfo, getSearchabl
eInfo(ComponentName).
SearchableInfo SearchView
setSearchableInfo(SearchableInfo). SearchView
. 3.0
, .
472
28.
. 28.5.
SearchView , .
: , , .
, SearchView .
.
.
, singleTop. ,
,
. , - ,
.
.
Activity.startSearch().
onSearchRequested() Activity.startSearch() .
473
startSearch() ,
EditText, Bundle , -, ,
- ( , ).
Activity.startSearch()
.
,
Toast.
XML, Flickr.
.
29
, , ;
, ,
.
? ,
, ,
RSS?
(service).
PhotoGallery
. ,
, .
IntentService
. IntentService. , , , . IntentService PollService.
.
onHandleIntent(Intent) PollService
. onHandleIntent(Intent) ,
.
29.1. PollService (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
public PollService() {
super(TAG);
}
IntentService
475
@Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "Received an intent: " + intent);
}
2. 2.
3. 3.
4. 1.
5. 2 .
6. 3 .
.
. 29.1. IntentService
IntentService , .
IntentService , -
.
IntentService ,
onHandleIntent(Intent) .
476
29.
.
, .
IntentService. .
, IntentService, ,
. ! ,
, ,
AndroidManifest.xml. PollService.
29.2. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...
<application
... >
<activity
android:name=".PhotoGalleryActivity"
... >
...
</activity>
<service android:name=".PollService" />
</application>
</manifest>
PhotoGalleryFragment.
29.3. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
updateItems();
Intent i = new Intent(getActivity(), PollService.class);
getActivity().startService(i);
, (.29.2).
. 29.2.
477
, : LogCat . -
! ? ?
, , -.
: , , , .
.
.
Android. ,
.
Android. ,
. ,
, .
, . ,
? , ,
.
Flickr .
, .
Android
. , , .
,
ConnectivityManager , .
API , , .
ConnectivityManager.getBackgroundDataSetting(),
ConnectivityManager.getActiveNetworkInfo() null.
29.4 ,
.
29.4. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
ConnectivityManager cm = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
@SuppressWarnings("deprecation")
boolean isNetworkAvailable = cm.getBackgroundDataSetting() &&
cm.getActiveNetworkInfo() != null;
if (!isNetworkAvailable) return;
}
478
29.
? Android
getBackgroundDataSetting() , false.
,
. :
, .
Android 4.0, Ice Cream Sandwich, :
. ,
getActiveNetworkInfo() null. ,
.
, .
getActiveNetworkInfo(),
ACCESS_NETWORK_STATE.
29.5. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
</application>
</manifest>
Flickr ,
. SharedPreferences. FlickrFetchr
.
29.6. (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "PhotoFetcher";
public static final String PREF_SEARCH_QUERY = "searchQuery";
public static final String PREF_LAST_RESULT_ID = "lastResultId";
479
. :
1. SharedPreferences.
2. FlickrFetchr.
3. , .
4. ,
.
5. SharedPreferences.
PollService.java . 29.7 , ,
.
29.7. (PollService.java)
@Override
protected void onHandleIntent(Intent intent) {
...
if (!isNetworkAvailable) return;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String query = prefs.getString(FlickrFetchr.PREF_SEARCH_QUERY, null);
String lastResultId = prefs.getString(FlickrFetchr.PREF_LAST_RESULT_ID, null);
ArrayList<GalleryItem> items;
if (query != null) {
items = new FlickrFetchr().search(query);
} else {
items = new FlickrFetchr().fetchItems();
}
if (items.size() == 0)
return;
String resultId = items.get(0).getId();
if (!resultId.equals(lastResultId)) {
Log.i(TAG, "Got a new result: " + resultId);
} else {
Log.i(TAG, "Got an old result: " + resultId);
}
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
480
29.
? .
PhotoGallery , .
, , .
AlarmManager
, -
, , , - .
Handler, Handler.sendMessageDelayed() Handler.postDelayed(). ,
, .
, Handler.
Handler AlarmManager
, .
AlarmManager, ?
PendingIntent. , PendingIntent :
PollService.
, AlarmManager.
PollService setServiceAlarm(Context,boole
an), . ; ,
PollService, , .
.
29.8. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
private static final int POLL_INTERVAL = 1000 * 15; // 15
public PollService() {
super(TAG);
}
@Override
public void onHandleIntent(Intent intent) {
...
}
public static void setServiceAlarm(Context context, boolean isOn) {
Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
context, 0, i, 0);
AlarmManager alarmManager = (AlarmManager)
AlarmManager
481
context.getSystemService(Context.ALARM_SERVICE);
if (isOn) {
alarmManager.setRepeating(AlarmManager.RTC,
System.currentTimeMillis(), POLL_INTERVAL, pi);
} else {
alarmManager.cancel(pi);
pi.cancel();
}
PendingIntent, PollService.
PendingIntent.getService(), Context.startService(Intent). :
Context ; , PendingIntent
; Intent; , , PendingIntent ( ).
, .
, AlarmManager.setRepeating().
: (
), , , ,
PendingIntent, .
AlarmManager.cancel(PendingIntent).
PendingIntent. ,
PendingIntent .
PhotoGalleryFragment.
29.9. (PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
updateItems();
Intent i = new Intent(getActivity(), PollService.class);
getActivity().startService(i);
PollService.setServiceAlarm(getActivity(), true);
PhotoGallery. Back
.
- LogCat? PollService , 15 . AlarmManager.
482
29.
, AlarmManager ,
PollService.
(, . ,
, .)
PendingIntent
PendingIntent. PendingIntent
-. PendingIntent.getService(), : , ,
startService(Intent). send()
PendingIntent, ,
.
, PendingIntent
, . PendingIntent ,
. , ( ) - PendingIntent , send()
.
PendingIntent ,
PendingIntent. ,
PendingIntent PendingIntent.
PendingIntent
PendingIntent .
setServiceAlarm(boolean) isOn:
AlarmManager.cancel(PendingIntent) ,
PendingIntent, PendingIntent.
PendingIntent , ,
PendingIntent, , .
PendingIntent.FLAG_NO_CREATE PendingIntent.getService(). , PendingIntent ,
null.
isServiceAlarmOn(Context), PendingIntent.FLAG_NO_CREATE .
29.10. isServiceAlarmOn() (PollService.java)
public static void setServiceAlarm(Context context, boolean isOn) {
...
}
public static boolean isServiceAlarmOn(Context context) {
Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
483
context, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null;
PendingIntent ,
null PendingIntent , .
, ( ,
), .
menu/fragment_photo_gallery.xml .
29.11. (menu/fragment_photo_gallery.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_item_search"
android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:actionViewClass="android.widget.SearchView"
/>
<item android:id="@+id/menu_item_clear"
android:title="@string/clear_search"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction="ifRoom"
/>
<item android:id="@+id/menu_item_toggle_polling"
android:title="@string/start_polling"
android:showAsAction="ifRoom"
/>
</menu>
,
. (
; .)
29.12. (res/values/strings.xml)
<resources>
...
<string
<string
<string
<string
<string
<string
string>
name="search">Search</string>
name="clear_search">Clear Search</string>
name="start_polling">Poll for new pictures</string>
name="stop_polling">Stop polling</string>
name="new_pictures_title">New PhotoGallery Pictures</string>
name="new_pictures_text">You have new pictures in PhotoGallery.</
</resources>
484
29.
29.13.
(PhotoGalleryFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
updateItems();
PollService.setServiceAlarm(getActivity(), true);
...
@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_search:
...
case R.id.menu_item_clear:
...
updateItems();
return true;
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
return true;
default:
return super.onOptionsItemSelected(item);
.
?
.
.
, .
, onPrepareOptionsMenu(Menu).
,
.
485
onPrepareOptionsMenu(Menu), ,
, menu_item_toggle_polling, .
29.14. onPrepareOptionsMenu(Menu) (PhotoGalleryFragment.java)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
...
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
3.0
. ,
. , PhotoGallery
3.0.
3.0 . .
onPrepareOptionsMenu(Menu) Activity.invalidateOptionsMenu().
onOptionsItemSelected(MenuItem),
3.0 .
29.15. (PhotoGalleryFragment.java)
@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
...
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
getActivity().invalidateOptionsMenu();
return true;
default:
return super.onOptionsItemSelected(item);
486
29.
4.2.
- .
.
, .
- ,
(notifications) , , ,
.
, Notification.
Notification - , AlertDialog 12. Notification :
, ;
, ;
,
;
PendingIntent, .
Notification ,
notify(int, Notification) NotificationManager.
PollService ,
29.16. Notification
NotificationManager.notify(int, Notification).
29.16. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
...
String resultId = items.get(0).getId();
if (!resultId.equals(lastResultId)) {
Log.i(TAG, "Got a new result: " + resultId);
Resources r = getResources();
PendingIntent pi = PendingIntent
.getActivity(this, 0, new Intent(this, PhotoGalleryActivity.class), 0);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(r.getString(R.string.new_pictures_title))
.setSmallIcon(android.R.drawable.ic_menu_report_image)
.setContentTitle(r.getString(R.string.new_pictures_title))
487
.setContentText(r.getString(R.string.new_pictures_text))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
.
setTicker(CharSequence) setSmallIcon(int).
.
,
, .
setSmallIcon(int).
setContentTitle(CharSequence) setContentText(CharSequence) .
, .
AlarmManager, PendingIntent. PendingIntent, setContentIntent(PendingIntent),
.
setAutoCancel(true) :
.
NotificationManager.notify(). ,
. ,
, .
.
, . ! , ,
.
29.17. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
public static final int POLL_INTERVAL = 1000 * 15; // 15
public static final int POLL_INTERVAL = 1000 * 60 * 5; // 5
public PollService() {
super(TAG);
}
488
29.
:
IntentService .
IntentService ,
.
, .
( )
,
( ). - ,
.
.
, IntentService.
, IntentService , .
,
.
, startService(Intent), .
.
onCreate() .
onStartCommand(Intent,int,int) ,
startService(Intent).
. ,
,
.
onStartCommand(Intent,int,int),
.
onDestroy() , . .
: ? . , onStartCommand(); Service.
START_NOT_STICKY, START_REDELIVER_INTENT START_STICKY.
IntentService (non-sticky) ,
. ,
489
. ,
START_NOT_STICKY START_REDELIVER_INTENT.
Android , stopSelf()
stopSelf(int). , stopSelf(), .
, onStartCommand().
IntentService stopSelf(int). , onStartCommand().
,
( IntentService).
START_NOT_STICKY START_REDELIVER_INTENT ? , .
START_NOT_STICKY .
START_REDELIVER_INTENT, , ,
.
START_NOT_STICKY START_REDELIVER_INTENT
. ,
START_NOT_STICKY. PhotoGallery .
, START_NOT_STICKY.
IntentService.
START_REDELIVER_INTENT, IntentService.setIntentRedelivery(true).
(sticky) , -
, Context.stopService(Intent).
, START_STICKY.
,
Context.stopService(Intent). - , onStartCommand()
null-.
(,
), ,
. ,
.
, ,
.
(binding) bi
ndService(Intent,ServiceConnection,int). .
490
29.
bindService(Intent,ServiceConnection,int). ServiceConnection ,
.
:
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
//
MyBinder binder = (MyBinder)service;
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = new Intent(c, MyService.class);
c.bindService(i, mServiceConnection, 0);
}
@Override
public void onDestroy() {
super.onDestroy();
getActivity().getApplicationContext().unbindService(mServiceConnection);
}
:
onBind(Intent) , .
IBinder, ServiceConnection.onSer
viceConnected(ComponentName,IBinder).
onUnbind(Intent) .
MyBinder? ,
Java, . (handle), :
private class MyBinder extends IBinder {
public MyService getService() {
return MyService.this;
}
}
@Override
public void onBind(Intent intent) {
return new MyBinder();
}
491
Android,
Android .
. ,
.
,
.
,
. AIDL
Messenger.
30
Android - . WiFi
, ,
.
, Android
(broadcast intent).
, , ,
. (broadcast receivers).
( )
( )
Android
(
)
(
)
(
)
(
)
(
)
(
)
. 30.1.
,
,
493
. , ,
.
PhotoGallery , . , .
, ,
. , BOOT_COMPLETED.
.
Java StartupReceiver, android.content.
BroadcastReceiver. Eclipse
onReceive(Context,Intent). .
30.1. (StartupReceiver.java)
package com.bignerdranch.android.photogallery;
...
public class StartupReceiver extends BroadcastReceiver {
private static final String TAG = "StartupReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received broadcast intent: " + intent.getAction());
}
, ,
. ,
. ,
(, ).
,
: receiver . StartupReceiver BOOT_COMPLETED.
,
uses-permission.
AndroidManifest.xml StartupReceiver.
494
30.
30.2. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
... >
<activity
... >
...
</activity>
<service android:name=".PollService" />
<receiver android:name=".StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
,
.
,
.
.
Android
. 30.2. BOOT_COMPLETED
495
,
.
onReceive(Context,Intent) , .
PhotoGallery,
DDMS. LogCat
. Devices, , ,
PhotoGallery. ,
,
.
, , . ,
API , onReceive(Context,Intent).
, onReceive(Context,Intent) ,
.
, , .
.
; ,
.
, . PollService ,
.
30.3. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes
public static final String PREF_IS_ALARM_ON = "isAlarmOn";
public PollService() {
super(TAG);
}
...
public static void setServiceAlarm(Context context, boolean isOn) {
Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(
context, 0, i, 0);
AlarmManager alarmManager = (AlarmManager)
496
30.
30.3 ()
context.getSystemService(Context.ALARM_SERVICE);
if (isOn) {
alarmManager.setRepeating(AlarmManager.RTC,
System.currentTimeMillis(), POLL_INTERVAL, pi);
} else {
alarmManager.cancel(pi);
pi.cancel();
}
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(PollService.PREF_IS_ALARM_ON, isOn)
.commit();
StartupReceiver .
30.4. (StartupReceiver.java)
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "Received broadcast intent: " + intent.getAction());
PhotoGallery. , .
, PhotoGallery. , ,
.
, .
:
. ,
sendBroadcast(Intent) .
,
.
PollService.
497
30.5. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes
public static final String PREF_IS_ALARM_ON = "isAlarmOn";
public static final String ACTION_SHOW_NOTIFICATION =
"com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";
public PollService() {
super(TAG);
}
@Override
public void onHandleIntent(Intent intent) {
...
if (!resultId.equals(lastResultId)) {
...
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
.
, StartupReceiver, .
, PhotoGalleryFragment ,
. , , . -
, PhotoGalleryFragment .
.
, .
registerReceiver(BroadcastReceiver, IntentFilter), unregisterReceiver(BroadcastReceiver). ,
. registerReceiver() unregisterReceiver()
, .
498
30.
VisibleFragment, Fragment. ,
( 31).
30.6. VisibleFragment (VisibleFragment.java)
package com.bignerdranch.android.photogallery;
...
public abstract class VisibleFragment extends Fragment {
public static final String TAG = "VisibleFragment";
private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getActivity(),
"Got a broadcast:" + intent.getAction(),
Toast.LENGTH_LONG)
.show();
}
};
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new
IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification, filter);
}
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mOnShowNotification);
}
: IntentFilter,
. IntentFilter ,
XML:
<intent-filter>
<action android:name="com.bignerdranch.android.photogallery.SHOW_NOTIFICATION" />
</intent-filter>
IntentFilter, XML,
. addCategory(String),
addAction(String), addDataPath(String) .
. ,
, , Context.unregisterReceiver(Broadcast
Receiver). onResume()
499
PhotoGallery . , .
. 30.3.
500
30.
,
. .
. , receiver
android:exported="false".
. , AndroidManifest.xml permission.
XML AndroidManifest.xml,
(private) .
30.8. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<permission android:name="com.bignerdranch.android.photogallery.PRIVATE"
android:protectionLevel="signature" />
<uses-permission
<uses-permission
<uses-permission
<uses-permission
android:name="android.permission.INTERNET" />
android:name="android.permission.ACCESS_NETWORK_STATE" />
android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
android:name="com.bignerdranch.android.photogallery.PRIVATE" />
<application
... >
...
</application>
</manifest>
signature ( ).
, , .
, .
.
, .
, . , .
,
sendBroadcast().
501
30.9. (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
private static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 minutes
public static final String PREF_IS_ALARM_ON = "isAlarmOn";
public static final String ACTION_SHOW_NOTIFICATION =
"com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";
public static final String PERM_PRIVATE =
"com.bignerdranch.android.photogallery.PRIVATE";
public PollService() {
super(TAG);
}
@Override
public void onHandleIntent(Intent intent) {
...
if (!resultId.equals(lastResultId)) {
...
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
, sendBroadcast().
, , .
?
, . registerReceiver().
30.10. (VisibleFragment.java)
@Override
public void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_
NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification, filter);
getActivity().registerReceiver(mOnShowNotification, filter,
PollService.PERM_PRIVATE, null);
}
502
30.
android:protectionLevel. Android, . signature.
, ,
,
. , . ,
, .
, , , .
30.1. protectionLevel
normal
, ,
.
, . android.permission.RECEIVE_BOOT_COMPLETED
, . , , ,
,
dangerous
, normal
,
, ,
, ,
. , ,
. Android
signature
, , ,
, .
, . ,
, , ,
.
, ,
signatureOrSystem
signature,
Android. , ,
, ,
503
,
.
. 30.4.
,
. onReceive() , .
, - ,
, .
.
. 30.5.
.
. ,
,
, (result receiver).
504
30.
, .
: ,
. ;
.
setResultCode(int) Activity.
RESULT_CANCELED.
VisibleFragment,
SHOW_NOTIFICATION.
30.11. (VisibleFragment.java)
private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getActivity(),
"Got a broadcast:" + intent.getAction(),
Toast.LENGTH_LONG)
.show();
Log.i(TAG, "canceling notification");
setResultCode(Activity.RESULT_CANCELED);
}
};
/,
. ,
setResultData(String) setResultExtras(Bundle).
, setResult(int,String,Bundle).
,
.
- ,
. PollService. Notification .
Notification ( ,
).
30.12. (PollService.java)
void showBackgroundNotification(int requestCode, Notification notification) {
Intent i = new Intent(ACTION_SHOW_NOTIFICATION);
i.putExtra("REQUEST_CODE", requestCode);
i.putExtra("NOTIFICATION", notification);
Context.sendOrderedBroadcast(Intent,String,BroadcastReceiver,Handle
r,int,String,Bundle) sendBroadcast(Intent,String). , : ,
Handler ,
505
,
.
,
.
.
. PollService.
, .
, .
BroadcastReceiver NotificationReceiver. .
30.13. (NotificationReceiver.java)
public class NotificationReceiver extends BroadcastReceiver {
private static final String TAG = "NotificationReceiver";
@Override
public void onReceive(Context c, Intent i) {
Log.i(TAG, "received result: " + getResultCode());
if (getResultCode() != Activity.RESULT_OK)
//
//
return;
int requestCode = i.getIntExtra("REQUEST_CODE", 0);
Notification notification = (Notification)
i.getParcelableExtra("NOTIFICATION");
, . ,
,
. , .
, 999 ( 1000 ).
,
. android:exported="false",
.
30.14. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
... >
...
<application
506
30.
30.14 ()
/>
... >
...
<receiver android:name=".StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".NotificationReceiver"
android:exported="false">
<intent-filter
android:priority="-999">
<action
android:name="com.bignerdranch.android.photogallery.SHOW_NOTIFICATION"
</intent-filter>
</receiver>
</application>
</manifest>
NotificationManager.
30.15. (PollService.java)
@Override
public void onHandleIntent(Intent intent) {
...
if (!resultId.equals(lastResultId)) {
...
Notification notification = new NotificationCompat.Builder(this)
...
.build();
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION), PERM_PRIVATE);
}
showBackgroundNotification(0, notification);
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
PhotoGallery
. , .
, ,
507
PollService.POLL_INTERVAL 5 ,
, ,
, ?
. . .
, .
, .
BroadcastReceiver.goAsync().
BroadcastReceiver.PendingResult,
. ,
PendingResult AsyncTask
, ,
PendingResult.
. -, . -, :
, .
, goAsync() : .
, . , .
31
-
WebView
, Flickr, .
, PhotoGallery . -
. , ,
WebView -.
Flickr
URL- Flickr.
XML,
, , .
<photo id="8232706407" owner="70490293@N03" secret="9662732625"
server="8343" farm="9" title="111_8Q1B2033" ispublic="1"
isfriend="0" isfamily="0"
url_s="http://farm9.staticflickr.com/8343/8232706407_9662732625_m.jpg"
height_s="240" width_s="163" />
, XML? , .
Flickr http://www.flickr.com/services/api/misc.urls.
html, , URL- :
http://www.flickr.com/photos/-/-
id XML.
mId GalleryItem.
? , ,
owner XML . ,
owner, URL- XML :
http://www.flickr.com/photos/owner/id
, GalleryItem.
Flickr
509
eventType = parser.next();
URL- .
510
31. - WebView
:
URL- . URL-
.
GridView. ,
9 - GridFragment.
onListItemClick() , setOnItemClickListener()
GridView.
. PhotoGalleryFragment.
31.3.
-
(PhotoGalleryFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
mGridView = (GridView)v.findViewById(R.id.gridView);
setupAdapter();
mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> gridView, View view, int pos,
long id) {
GalleryItem item = mItems.get(pos);
Uri photoPageUri = Uri.parse(item.getPhotoPageUrl());
Intent i = new Intent(Intent.ACTION_VIEW, photoPageUri);
});
}
startActivity(i);
return v;
PhotoGallery . , .
: WebView
- . ,
HTML .
-,
511
.
,
- .
- WebView. ,
(
).
WebView. , , .
. 31.1. (res/layout/fragment_photo_page.xml)
: RelativeLayout
.
.
PhotoPageFragment VisibleFragment,
. , WebView
URL- .
31.4. (PhotoPageFragment.java)
package com.bignerdranch.android.photogallery;
...
public class PhotoPageFragment extends VisibleFragment {
private String mUrl;
private WebView mWebView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mUrl = getActivity().getIntent().getData().toString();
}
512
31. - WebView
31.4 ()
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, parent, false);
mWebView = (WebView)v.findViewById(R.id.webView);
return v;
.
- PhotoPageActivity SingleFragmentActivity.
31.5. - (PhotoPageActivity.java)
package com.bignerdranch.android.photogallery;
...
public class PhotoPageActivity extends SingleFragmentActivity {
@Override
public Fragment createFragment() {
return new PhotoPageFragment();
}
}
PhotoGalleryFragment,
.
31.6. (PhotoGalleryFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> gridView, View view, int pos,
long id) {
GalleryItem item = mItems.get(pos);
Uri photoPageUri = Uri.parse(item.getPhotoPageUrl());
Intent i = new Intent(Intent.ACTION_VIEW, photoPageUri);
Intent i = new Intent(getActivity(), PhotoPageActivity.class);
i.setData(photoPageUri);
});
}
startActivity(i);
return v;
, .
513
31.7. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
...
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".PhotoGalleryActivity"
android:launchMode="singleTop"
android:label="@string/title_activity_photo_gallery" >
...
</activity>
<activity
android:name=".PhotoPageActivity" />
<service android:name=".PollService" />
<receiver android:name=".StartupReceiver">
...
</receiver>
</application>
</manifest>
PhotoGallery . .
- .
WebView Flickr,
. ,
URL- .
JavaScript.
. , Flickr
. Android Lint (- ), Lint .
, WebViewClient
shouldOverrideUrlLoading(WebView,String) false.
, .
31.8. (PhotoPageFragment.java)
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_photo_page, parent, false);
mWebView = (WebView)v.findViewById(R.id.webView);
514
31. - WebView
31.8 ()
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
mWebView.loadUrl(mUrl);
}
return v;
WebChromeClient
WebView,
, .
fragment_photo_page.xml .
515
. 31.2. (fragment_photo_page.xml)
516
31. - WebView
});
mWebView.loadUrl(mUrl);
}
return v;
, onProgressChanged(WebView,int) onReceivedTitle(WebView,
String). , onProgressChanged(WebView,int),
0 100. 100, , , ProgressBar,
View.INVISIBLE.
PhotoGallery .
WebView
. , , WebView -. , WebView
, onSaveInstanceState(),
: JavaScript
517
,
.
( VideoView) Android
. ,
. WebView
.
( , ?
. , .)
PhotoPageActivity , AndroidManifest.xml.
31.10. (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
...
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
...
<activity
android:name=".PhotoPageActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
...
</application>
</manifest>
, - , (
Android 3.2) .
. ;
.
:
JavaScript
, WebViewClient WebChromeClient
, WebView. JavaScript ,
518
31. - WebView
WebView . http://
developer.android.com/reference/android/webkit/WebView.html
addJavascriptInterface(Object,String).
.
mWebView.addJavascriptInterface(new Object() {
public void send(String message) {
Log.i(TAG, "Received message: " + message);
}
}, "androidObject");
:
<input type="button" value="In WebView!"
onClick="sendToAndroid('In Android land')" />
<script type="text/javascript">
function sendToAndroid(message) {
androidObject.send(message);
}
</script>
- . , HTML
.
32
.
View BoxDrawingView.
, .
. 32.1.
520
32.
DragAndDraw
BoxDrawingView DragAndDraw.
NewAndroid Application Project. ,
.32.2, DragAndDrawActivity.
. 32.2. DragAndDraw
DragAndDrawActivity
DragAndDrawActivity SingleFragmentActivity, . Package
Explorer SingleFragmentActivity.java com.bignerdranch.android.
draganddraw. activity_fragment.xml res/layout
DragAndDraw.
DragAndDrawActivity.java DragAndDrawActivity SingleFragmentActivity. DragAndDrawFragment
(, ).
32.1. (DragAndDrawActivity.java)
public class DragAndDrawActivity extends Activity SingleFragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_drag_and_draw);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_drag_and_draw, menu);
return true;
}
DragAndDraw
521
@Override
public Fragment createFragment() {
return new DragAndDrawFragment();
}
DragAndDrawFragment
DragAndDrawFragment, activity_drag_
and_draw.xml fragment_drag_and_draw.xml.
DragAndDrawFragment BoxDrawingView
, . BoxDrawingView.
DragAndDrawFragment
android.support.v4.app.ListFragment. onCreateView(),
fragment_drag_and_draw.xml.
32.2. (DragAndDrawFragment.java)
public class DragAndDrawFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_drag_and_draw, parent, false);
return v;
}
}
DragAndDraw , .
. 32.3. DragAndDraw
522
32.
Android ,
, .
:
; , .
;
. ,
. .
:
. View ,
.
.
,
.
.
BoxDrawingView
BoxDrawingView
View.
BoxDrawingView View .
BoxDrawingView.java .
32.3. BoxDrawingView (BoxDrawingView.java)
public class BoxDrawingView extends View {
//
public BoxDrawingView(Context context) {
this(context, null);
}
// XML
public BoxDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
}
,
, . , ,
523
. 32.4. BoxDrawingView
524
32.
BoxDrawingView,
. ,
View. ,
android.view android.widget.
, , .
, android.view android.widget,
.
DragAndDraw ,
. , , .
BoxDrawingView
.
View:
public void setOnTouchListener(View.OnTouchListener l)
, setOnClickListener(View.OnClickListener).
View.OnTouchListener,
, .
View, View:
public boolean onTouchEvent(MotionEvent event)
MotionEvent ,
, . .
ACTION_DOWN
ACTION_MOVE
ACTION_UP
ACTION_CANCEL
onTouchEvent() MotionEvent:
public final int getAction()
BoxDrawingView.java onTouch(), .
525
return true;
: X Y PointF. . PointF
Android -, .
DragAndDraw LogCat.
. X Y
, BoxDrawingView.
BoxDrawingView , . .
:
( );
( ).
,
MotionEvent. Box.
Box , .
526
32.
BoxDrawingView, Box
(. 32.5).
. 32.5. DragAndDraw
BoxDrawingView , Box
.
32.7.
(BoxDrawingView.java)
public class BoxDrawingView extends View {
public static final String TAG = "BoxDrawingView";
private Box mCurrentBox;
private ArrayList<Box> mBoxes = new ArrayList<Box>();
...
public boolean onTouchEvent(MotionEvent event) {
PointF curr = new PointF(event.getX(), event.getY());
527
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, " ACTION_DOWN");
// Reset drawing state
mCurrentBox = new Box(curr);
mBoxes.add(mCurrentBox);
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, " ACTION_MOVE");
if (mCurrentBox != null) {
mCurrentBox.setCurrent(curr);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, " ACTION_UP");
mCurrentBox = null;
break;
case MotionEvent.ACTION_CANCEL:
Log.i(TAG, " ACTION_CANCEL");
mCurrentBox = null;
break;
return true;
ACTION_DOWN mCurrentBox
Box , . Box
( ,
, BoxDrawingView Box ).
mCurrentBox.
mCurrent. , , mCurrentBox . Box ;
.
invalidate() ACTION_MOVE.
BoxDrawingView ,
. : .
onDraw()
(invalid).
, . Android
draw() View .
, . .
528
32.
, View
.
,
View:
protected void onDraw(Canvas canvas)
...
// XML
public BoxDrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
// (ARGB)
mBoxPaint = new Paint();
mBoxPaint.setColor(0x22ff0000);
// -
mBackgroundPaint = new Paint();
mBackgroundPaint.setColor(0xfff8efe0);
Paint
.
529
: - ,
.
left, right,
top bottom . left top , bottom
right .
Canvas.drawRect()
.
DragAndDraw .
. 32.6.
530
32.
.
View ? ,
View:
protected Parcelable onSaveInstanceState()
protected void onRestoreInstanceState(Parcelable state)
, onSaveInstanceState(Bundle)
Activity Fragment. Bundle , Parcelable. Bundle
Parcelable , Parcelable . ( Parcelable .
, .)
, : .
MotionEvent.
:
,
;
.
, .
MotionEvent:
public
public
public
public
public
final
final
final
final
final
int getActionMasked()
int getActionIndex()
int getPointerId(int pointerIndex)
float getX(int pointerIndex)
float getY(int pointerIndex)
ACTION_POINTER_UP ACTION_
POINTER_DOWN.
33
RunTracker,
GPS :
, . RunTracker
.
RunTracker GPS .
RunTracker , .
RunTracker
Android ,
. 33.1.
. -, SDK API 9. -,
Google API, Android. Google API
.
Google APIs , Android SDK
Manager. WindowAndroid SDK Manager Google
APIs, . 33.2. Install 1 package.
532
33.
. 33.1. RunTracker
SDK
Google APIs.
, .
RunActivity.
RunFragment
533
RunActivity
RunActivity ( RunTracker) SingleFragmentActivity. SingleFragmentActivity.java com.bignerdranch.android.runtracker, activity_fragment.xml res/layout/.
RunFragment
RunFragment. RunFragment (. 33.3)
.
.
. 33.3. RunTracker
534
33.
, . 33.3.
res/values/strings.xml , ,
.
33.2. RunTracker (strings.xml)
<resources>
<string name="app_name">RunTracker</string>
<string name="started">Started:</string>
<string name="latitude">Latitude:</string>
<string name="longitude">Longitude:</string>
<string name="altitude">Altitude:</string>
<string name="elapsed_time">Elapsed Time:</string>
<string name="start">Start</string>
<string name="stop">Stop</string>
<string name="gps_enabled">GPS Enabled</string>
<string name="gps_disabled">GPS Disabled</string>
<string name="cell_text">Run at %1$s</string>
</resources>
TableLayout,
. TableLayout TableRow
LinearLayout. TableRow TextView:
, . LinearLayout Button.
, . , (http://www.
bignerdranch.com/solutions/AndroidProgramming.zip). 33_Location/RunTracker/res/layout/fragment_run.xml res/layout .
RunFragment
RunFragment.
.
33.3. RunFragment (RunFragment.java)
public class RunFragment extends Fragment {
private Button mStartButton, mStopButton;
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
LocationManager
535
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);
mStartedTextView = (TextView)view.findViewById(R.id.run_startedTextView);
mLatitudeTextView = (TextView)view.findViewById(R.id.run_latitudeTextView);
mLongitudeTextView =
(TextView)view.findViewById(R.id.run_longitudeTextView);
mAltitudeTextView = (TextView)view.findViewById(R.id.run_altitudeTextView);
mDurationTextView = (TextView)view.findViewById(R.id.run_durationTextView);
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStopButton = (Button)view.findViewById(R.id.run_stopButton);
return view;
, ,
.33.3.
LocationManager
, .
Android LocationManager.
, .
.
( , )
LocationListener. ( onLocationChanged(Location)),
,
.
LocationListener ,
. ,
RunFragment, LocationListener
requestLocationUpdates() requestSingleUpdate()
LocationManager .
. RunTracker ( )
.
, ,
. PendingIntent
API, Android 2.3 (Gingerbread).
536
33.
PendingIntent,
LocationManager
Intent . , ( )
, LocationManager ,
, , . , ,
.
LocationManager,
(. ), RunManager,
33.4.
33.4. RunManager (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";
public static final String ACTION_LOCATION =
"com.bignerdranch.android.runtracker.ACTION_LOCATION";
private static RunManager sRunManager;
private Context mAppContext;
private LocationManager mLocationManager;
//
// RunManager.get(Context)
private RunManager(Context appContext) {
mAppContext = appContext;
mLocationManager = (LocationManager)mAppContext
.getSystemService(Context.LOCATION_SERVICE);
}
public static RunManager get(Context c) {
if (sRunManager == null) {
//
//
sRunManager = new RunManager(c.getApplicationContext());
}
return sRunManager;
}
private PendingIntent getLocationPendingIntent(boolean shouldCreate) {
Intent broadcast = new Intent(ACTION_LOCATION);
int flags = shouldCreate ? 0 : PendingIntent.FLAG_NO_CREATE;
return PendingIntent.getBroadcast(mAppContext, 0, broadcast, flags);
}
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
// LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
537
: RunManager ,
API. , , ( ,
LocationManager).
startLocationUpdates() LocationManager GPS .
requestLocationUpdates(String, long, float, PendingIntent)
( ) ( ) .
, . RunTracker
, ,
.
GPS .
, , ,
.
getLocationPendingIntent(boolean) Intent,
.
, shouldCreate PendingIntent.
getBroadcast() ( ),
PendingIntent .
, isTrackingRun() getLocationPen
dingIntent(false) null ,
PendingIntent .
, . .
RunTracker
538
33.
, BroadcastReceiver, .
, LocationReceiver
.
33.5. LocationReceiver (LocationReceiver.java)
public class LocationReceiver extends BroadcastReceiver {
private static final String TAG = "LocationReceiver";
@Override
public void onReceive(Context context, Intent intent) {
// Location,
Location loc = (Location)intent
.getParcelableExtra(LocationManager.KEY_LOCATION_CHANGED);
if (loc != null) {
onLocationReceived(context, loc);
return;
}
// , -
if (intent.hasExtra(LocationManager.KEY_PROVIDER_ENABLED)) {
boolean enabled = intent
.getBooleanExtra(LocationManager.KEY_PROVIDER_ENABLED, false);
onProviderEnabledChanged(enabled);
}
}
protected void onLocationReceived(Context context, Location loc) {
Log.d(TAG, this + " Got location from " + loc.getProvider() + ": "
+ loc.getLatitude() + ", " + loc.getLongitude());
}
protected void onProviderEnabledChanged(boolean enabled) {
Log.d(TAG, "Provider " + (enabled ? "enabled" : "disabled"));
}
}
539
33.6.
(AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.runtracker"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:required="true"
android:name="android.hardware.location.gps"/>
<application android:label="@string/app_name"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:theme="@style/AppTheme">
<activity android:name=".RunActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".LocationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.bignerdranch.android.runtracker.ACTION_
LOCATION"/>
</intent-filter>
</receiver>
</application>
</manifest>
, .
, .
, , Start Stop
, RunManager.
updateUI().
33.7. (RunFragment.java)
public class RunFragment extends Fragment {
private RunManager mRunManager;
private Button mStartButton, mStopButton;
540
33.
33.7 ()
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
updateUI();
}
});
mStopButton = (Button)view.findViewById(R.id.run_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.stopLocationUpdates();
updateUI();
}
});
updateUI();
}
return view;
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
RunTracker , , LogCat
.
Emulator Control DDMS
GPS. , .
,
541
.
LogCat . , RunFragment
LocationReceiver, Location
. , Run,
. Run, ,
.
33.8. Run (Run.java)
public class Run {
private Date mStartDate;
public Run() {
mStartDate = new Date();
}
public Date getStartDate() {
return mStartDate;
}
public void setStartDate(Date startDate) {
mStartDate = startDate;
}
public int getDurationSeconds(long endMillis) {
return (int)((endMillis - mStartDate.getTime()) / 1000);
}
public static String formatDuration(int durationSeconds) {
int seconds = durationSeconds % 60;
int minutes = ((durationSeconds - seconds) / 60) % 60;
int hours = (durationSeconds - (minutes * 60) - seconds) / 3600;
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
}
Run RunFragment.
33.9. (RunFragment.java)
public class RunFragment extends Fragment {
private BroadcastReceiver mLocationReceiver = new LocationReceiver() {
@Override
protected void onLocationReceived(Context context, Location loc) {
mLastLocation = loc;
if (isVisible())
updateUI();
}
542
33.
33.9 ()
@Override
protected void onProviderEnabledChanged(boolean enabled) {
int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
}
};
private RunManager mRunManager;
private Run mRun;
private Location mLastLocation;
private Button mStartButton, mStopButton;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
mRun = new Run();
updateUI();
}
});
}
...
@Override
public void onStart() {
super.onStart();
getActivity().registerReceiver(mLocationReceiver,
new IntentFilter(RunManager.ACTION_LOCATION));
}
@Override
public void onStop() {
getActivity().unregisterReceiver(mLocationReceiver);
super.onStop();
}
private void updateUI() {
boolean started = mRunManager.isTrackingRun();
if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());
int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
543
mLatitudeTextView.setText(Double.toString(mLastLocation.getLatitude()));
mLongitudeTextView.setText(Double.toString(mLastLocation.getLongitude()));
mAltitudeTextView.setText(Double.toString(mLastLocation.getAltitude()));
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
Run Location.
, updateUI(). Run
.
LocationReceiver mLocationReceiver, . ,
GPS .
, onStart() onStop()
, . onCreate(Bundle) onDestroy(), mLastLocation
, .
RunTracker.
.
, ,
, . , ,
LocationManager .
GPS, . -
,
Intent LocationManager.
33.10. (RunManager.java)
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
//
// ( ).
544
33.
33.10 ()
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
//
lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}
// LocationManager
PendingIntent pi = getLocationPendingIntent(true);
mLocationManager.requestLocationUpdates(provider, 0, 0, pi);
,
GPS. , ,
, ; .
LocationManager .
getAllProviders(). ,
,
.
.
, RunTracker,
. ,
, , . .
LocationManager , .
Emulator Control DDMS. ,
( ), GPX
KML , .
, .
.
1. ACCESS_MOCK_LOCATION.
545
2. LocationManager.addTestProvider().
3. setTestProviderEnabled().
4. setTestProviderStatus().
5. setTestProviderLocation().
6. removeTestProvider().
, . Big Nerd Ranch
TestProvider, ,
.
Android Course Resources Github
https://github.com/bignerdranch/AndroidCourseResources
TestProvider Eclipse.
RunTracker GPS
, ,
.
33.11. (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";
public static final String ACTION_LOCATION =
"com.bignerdranch.android.runtracker.ACTION_LOCATION";
private static final String TEST_PROVIDER = "TEST_PROVIDER";
private static RunManager sRunManager;
private Context mAppContext;
private LocationManager mLocationManager;
...
public void startLocationUpdates() {
String provider = LocationManager.GPS_PROVIDER;
// ,
// .
if (mLocationManager.getProvider(TEST_PROVIDER) != null &&
mLocationManager.isProviderEnabled(TEST_PROVIDER)) {
provider = TEST_PROVIDER;
}
Log.d(TAG, "Using provider " + provider);
//
// , .
Location lastKnown = mLocationManager.getLastKnownLocation(provider);
if (lastKnown != null) {
//
lastKnown.setTime(System.currentTimeMillis());
broadcastLocation(lastKnown);
}
546
33.
, TestProvider ,
Allow mock locations Developer options Settings (. 33.4).
TestProvider ,
.
RunTracker .
, ,
.
. 33.4.
34
SQLite
JSON. , RunTracker
, .
Android SQLite.
SQLite , ,
API
.
Android Java- SQLite SQLiteDatabase, Cursor.
RunTracker ,
. .
- , . Android,
. SQLiteOpenHelper ,
.
RunTracker SQLiteOpenHelper RunDatabaseHelper. RunManager RunDatabaseHelper
API , . RunDatabaseHelper ,
RunManager API.
API SQLiteOpenHelper ,
548
34. SQLite
.
SQKite . , RunTracker, SQLiteOpenHelper,
.
-
. RunTracker
: Run Location.
: run ( )
location ( ). Run
Location, location
run_id, _id run.
.34.1.
. 34.1. RunTracker
RunDatabaseHelper , 34.1.
34.1. RunDatabaseHelper (RunDatabaseHelper.java)
public class RunDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "runs.sqlite";
private static final int VERSION = 1;
private static final String TABLE_RUN = "run";
private static final String COLUMN_RUN_START_DATE = "start_date";
public RunDatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// "run"
db.execSQL("create table run (" +
549
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//
//
}
SQLiteOpenHelper :
onCreate(SQLiteDatabase) onUpgrade(SQLiteDatabase, int, int). onCreate() , onUpgrade()
.
,
, . , null
CursorFactory .
RunTracker , SQLiteOpenHelper
. , , 1.
onUpgrade()
, .
onCreate() SQL CREATE TABLE. insertRun(Run),
run .
;
long ContentValues,
.
SQLiteOpenHelper , SQLiteDatabase: getWritableDatabase() getReadableDatabase().
, getWritableDatabase(),
getReadableDatabase().
SQLiteDatabase
SQLiteOpenHelper, (, ) , , ,
.
550
34. SQLite
, Run . , 34.2.
34.2. Run
public class Run {
private long mId;
private Date mStartDate;
public Run() {
mId = -1;
mStartDate = new Date();
}
public long getId() {
return mId;
}
public void setId(long id) {
mId = id;
}
public Date getStartDate() {
return mStartDate;
}
RunManager,
. API, .
, Run.
34.3. (RunManager.java)
public class RunManager {
private static final String TAG = "RunManager";
private static final String PREFS_FILE = "runs";
private static final String PREF_CURRENT_RUN_ID = "RunManager.currentRunId";
public static final String ACTION_LOCATION =
"com.bignerdranch.android.runtracker.ACTION_LOCATION";
private static final String TEST_PROVIDER = "TEST_PROVIDER";
private
private
private
private
private
private
551
...
private void broadcastLocation(Location location) {
Intent broadcast = new Intent(ACTION_LOCATION);
broadcast.putExtra(LocationManager.KEY_LOCATION_CHANGED, location);
mAppContext.sendBroadcast(broadcast);
}
public Run startNewRun() {
// Run
Run run = insertRun();
//
startTrackingRun(run);
return run;
}
, RunManager. startNewRun()
insertRun() Run,
startTrackingRun(Run) , ,
.
RunFragment Start .
RunFragment startTrackingRun(Run) .
Run .
;
RunManager.
, stopRun()
. RunFragment Stop.
RunFragment,
RunManager. , 34.4.
552
34. SQLite
34.4. (RunFragment.java)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);
...
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.startLocationUpdates();
mRun = new Run();
mRun = mRunManager.startNewRun();
updateUI();
}
});
mStopButton = (Button)view.findViewById(R.id.run_stopButton);
mStopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRunManager.stopLocationUpdates();
mRunManager.stopRun();
updateUI();
}
});
updateUI();
}
return view;
private
private
private
private
...
static
static
static
static
final
final
final
final
String
String
String
String
553
COLUMN_LOCATION_ALTITUDE = "altitude";
COLUMN_LOCATION_TIMESTAMP = "timestamp";
COLUMN_LOCATION_PROVIDER = "provider";
COLUMN_LOCATION_RUN_ID = "run_id";
RunManager .
34.6. (RunManager.java)
private Run insertRun() {
Run run = new Run();
run.setId(mHelper.insertRun(run));
return run;
}
,
insertLocation(Location).
BroadcastReceiver , RunTracker.
LocationReceiver TrackingLocationReceiver
.
TrackingLocationReceiver .
34.7.
TrackingLocationReceiver: ,
(TrackingLocationReceiver.java)
public class TrackingLocationReceiver extends LocationReceiver {
@Override
protected void onLocationReceived(Context c, Location loc) {
RunManager.get(c).insertLocation(loc);
}
554
34. SQLite
, ACTION_LOCATION.
34.8. TrackingLocationReceiver (AndroidManifest.xml)
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
...
<receiver android:name=".LocationReceiver"
<receiver android:name=".TrackingLocationReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.bignerdranch.android.runtracker.ACTION_LOCATION"/>
</intent-filter>
</receiver>
</application>
,
. RunTracker
, ,
. , , ,
onLocationReceived()
TrackingLocationReceiver. ,
, LogCat.
RunTracker
, RunFragment
Start.
.
. ,
CriminalIntent , ,
, , RunTracker
.
SQLiteDatabase Cursor, .
API , .
,
String.
, Java-
, Run Location.
, , ,
Cursor .
555
/**
* Run, ,
* null, .
*/
public Run getRun() {
if (isBeforeFirst() || isAfterLast())
return null;
Run run = new Run();
long runId = getLong(getColumnIndex(COLUMN_RUN_ID));
run.setId(runId);
long startDate = getLong(getColumnIndex(COLUMN_RUN_START_DATE));
run.setStartDate(new Date(startDate));
return run;
}
RunCursor : getRun().
getRun() , ,
556
34. SQLite
CursorAdapter
, RunListActivity
. RunListFragment .
34.11. RunListActivity (RunListActivity.java)
public class RunListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new RunListFragment();
}
}
CursorAdapter
557
RunCursor
ListView, RunListFragment. Android API (
) CursorAdapter, , .
.
CursorAdapter
, .
558
34. SQLite
RunListFragment RunCursorAdapter.
34.14. RunCursorAdapter (RunListFragment.java)
public class RunListFragment extends ListFragment {
private RunCursor mCursor;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//
mCursor = RunManager.get(getActivity()).queryRuns();
// ,
RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
setListAdapter(adapter);
}
@Override
public void onDestroy() {
mCursor.close();
super.onDestroy();
}
private static class RunCursorAdapter extends CursorAdapter {
private RunCursor mRunCursor;
public RunCursorAdapter(Context context, RunCursor cursor) {
super(context, cursor, 0);
mRunCursor = cursor;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//
//
LayoutInflater inflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflater
.inflate(android.R.layout.simple_list_item_1, parent, false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
//
Run run = mRunCursor.getRun();
//
TextView startDateTextView = (TextView)view;
String cellText =
context.getString(R.string.cell_text, run.getStartDate());
startDateTextView.setText(cellText);
559
, CriminalIntent.
.
34.15. (run_list_options.xml)
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/menu_item_new_run"
android:showAsAction="always"
android:icon="@android:drawable/ic_menu_add"
android:title="@string/new_run"/>
</menu>
; strings.xml.
34.16. New Run (strings.xml)
<string name="stop">Stop</string>
<string name="gps_enabled">GPS Enabled</string>
<string name="gps_disabled">GPS Disabled</string>
<string name="cell_text">Run at %1$s</string>
<string name="new_run">New Run</string>
</resources>
560
34. SQLite
RunListFragment
, .
34.17. (RunListFragment.java)
public class RunListFragment extends ListFragment {
private static final int REQUEST_NEW_RUN = 0;
private RunCursor mCursor;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
//
mCursor = RunManager.get(getActivity()).queryRuns();
// ,
RunCursorAdapter adapter = new RunCursorAdapter(getActivity(), mCursor);
setListAdapter(adapter);
}
...
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.run_list_options, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_run:
Intent i = new Intent(getActivity(), RunActivity.class);
startActivityForResult(i, REQUEST_NEW_RUN);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_NEW_RUN == requestCode) {
mCursor.requery();
((RunCursorAdapter)getListAdapter()).notifyDataSetChanged();
}
}
private static class RunCursorAdapter extends CursorAdapter {
private RunCursor mRunCursor;
onActivityResult() ,
.
561
, ( ), . Loader.
. , RunFragment
. RunFragement
RunActivity, .
RunFragment newInstance(long), .
34.18. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";
...
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;
public static RunFragment newInstance(long runId) {
Bundle args = new Bundle();
args.putLong(ARG_RUN_ID, runId);
RunFragment rf = new RunFragment();
rf.setArguments(args);
return rf;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RunActivity.
RUN_ID, RunFragment
newInstance(long) .
, .
34.19. (RunActivity.java)
public class RunActivity extends SingleFragmentActivity {
/** long */
public static final String EXTRA_RUN_ID =
"com.bignerdranch.android.runtracker.run_id";
@Override
protected Fragment createFragment() {
return new RunFragment();
long runId = getIntent().getLongExtra(EXTRA_RUN_ID, -1);
if (runId != -1) {
562
34. SQLite
34.19 ()
return RunFragment.newInstance(runId);
} else {
return new RunFragment();
}
, ,
RunListFragment RunActivity .
34.20.
onListItemClick()
(RunListFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// id ;
// CursorAdapter .
Intent i = new Intent(getActivity(), RunActivity.class);
i.putExtra(RunActivity.EXTRA_RUN_ID, id);
startActivity(i);
}
private static class RunCursorAdapter extends CursorAdapter {
563
query() .
TABLE_RUN ,
WHERE
. ,
RunCursor .
RunManager getRun(long),
queryRun(long) Run
( ).
34.22. getRun(long) (RunManager.java)
public Run getRun(long id) {
Run run = null;
RunCursor cursor = mHelper.queryRun(id);
cursor.moveToFirst();
// ,
if (!cursor.isAfterLast())
run = cursor.getRun();
cursor.close();
return run;
}
Run RunCursor,
queryRun(long). RunCursor
. , isAfterLast()
false, Run .
RunCursor ,
close() ,
.
, RunFragment
.
, 34.23.
34.23. (RunFragment.java)
public class RunFragment extends Fragment {
private static final String TAG = "RunFragment";
private static final String ARG_RUN_ID = "RUN_ID";
private BroadcastReceiver mLocationReceiver = new LocationReceiver() {
@Override
protected void onLocationReceived(Context context, Location loc) {
if (!mRunManager.isTrackingRun(mRun))
return;
mLastLocation = loc;
if (isVisible())
updateUI();
}
@Override
564
34. SQLite
34.23 ()
protected void onProviderEnabledChanged(boolean enabled) {
int toastText = enabled ? R.string.gps_enabled : R.string.gps_disabled;
Toast.makeText(getActivity(), toastText, Toast.LENGTH_LONG).show();
}
};
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());<
// Run
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
mRun = mRunManager.getRun(runId);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_run, container, false);
...
mStartButton = (Button)view.findViewById(R.id.run_startButton);
mStartButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRun = mRunManager.startNewRun();
if (mRun == null) {
mRun = mRunManager.startNewRun();
} else {
mRunManager.startTrackingRun(mRun);
}
updateUI();
}
});
...
return view;
...
private void updateUI() {
boolean started = mRunManager.isTrackingRun();
boolean trackingThisRun = mRunManager.isTrackingRun(mRun);
565
if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());
int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
mLatitudeTextView.setText(Double.toString(mLastLocation.
getLatitude()));
mLongitudeTextView.setText(Double.toString(mLastLocation.
getLongitude()))
mAltitudeTextView.setText(Double.toString(mLastLocation.
getAltitude()));
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started);
mStopButton.setEnabled(started && trackingThisRun);
: RunFragment
. Run,
LocationCursor Location.
LocationCursor RunDatabaseHelper.
34.24. (RunDatabaseHelper.java)
public LocationCursor queryLastLocationForRun(long runId) {
Cursor wrapped = getReadableDatabase().query(TABLE_LOCATION,
null, //
COLUMN_LOCATION_RUN_ID + " = ?", //
new String[]{ String.valueOf(runId) },
null, // group by
null, // having
COLUMN_LOCATION_TIMESTAMP + " desc", //
"1"); // limit 1
return new LocationCursor(wrapped);
}
// ... RunCursor ...
public static class LocationCursor extends CursorWrapper {
public LocationCursor(Cursor c) {
super(c);
}
public Location getLocation() {
if (isBeforeFirst() || isAfterLast())
return null;
//
566
34. SQLite
34.24 ()
RunFragment
.
34.26.
(RunFragment.java)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
// Run
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
567
mRun = mRunManager.getRun(runId);
mLastLocation = mRunManager.getLastLocationForRun(runId);
RunTracker
, ( ) .
, .
!
.
:
.
,
.
: , (, ).
,
,
RunActivity.
35
34 SQLite Cursor .
;
.
Loader
. Loader Android 3.0 (Honeycomb); ,
.
Loader LoaderManager
(loader) () . , , ContentProvider,
.
, .
: Loader, AsyncTaskLoader CursorLoader (.35.1). Loader , .
API, LoaderManager
.
AsyncTaskLoader Loader, AsyncTask . ,
, AsyncTaskLoader.
, CursorLoader AsyncTaskLoader Cursor ContentProvider ContentResolver. , RunTracker
Loader LoaderManager
569
CursorLoader , SQLiteDatabase.
LoaderManager. ,
, .
Fragment Activity
LoaderManager getLoaderManager().
initLoader(int, Bundle, LoaderCallbacks<D>)
Loader.
, Bundle (
null),
LoaderCallbacks<D>.
,
LoaderCallbacks,
. 35.1.
Fragment.
Loader
restartLoader(int, Bundle, LoaderC all
backs<D>) .
( )
.
LoaderCallbacks<D> : onCreateLoader() ,
onLoadFinished() onLoaderReset().
RunTracker.
, ,
AsyncTask? , LoaderManager
(, ).
AsyncTask ,
, . setRetainInstance(true) Fragment,
,
.
( !) .
,
, ,
. ,
(retained) ; ,
,
.
570
35.
RunTracker
RunTracker : (RunCursor), (Run) (Location).
SQLite,
Loader
.
AsyncTaskLoader.
, SQLiteCursorLoader,
CursorLoader, Cursor,
. , DataLoader<D>, ;
AsyncTaskLoader .
RunListFragment RunManager
RunCursor, , onCreate(Bundle).
,
. RunListFragment LoaderManager ( )
LoaderCallbacks
.
RunListFragment ( , ),
AsyncTaskLoader SQLiteCursorLoader (35.1). CursorLoader,
ContentProvider.
35.1. SQLite (SQLiteCursorLoader.java)
public abstract class SQLiteCursorLoader extends AsyncTaskLoader<Cursor> {
private Cursor mCursor;
public SQLiteCursorLoader(Context context) {
super(context);
}
protected abstract Cursor loadCursor();
@Override
public Cursor loadInBackground() {
Cursor cursor = loadCursor();
if (cursor != null) {
// ,
cursor.getCount();
}
return cursor;
}
@Override
public void deliverResult(Cursor data) {
571
@Override
protected void onStartLoading() {
if (mCursor != null) {
deliverResult(mCursor);
}
if (takeContentChanged() || mCursor == null) {
forceLoad();
}
}
@Override
protected void onStopLoading() {
// , .
cancelLoad();
}
@Override
public void onCanceled(Cursor cursor) {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
@Override
protected void onReset() {
super.onReset();
// ,
onStopLoading();
.
deliverResult(Cursor) . ( , ), deliverResult()
572
35.
. , . , ,
.
RunTracker,
API AsyncTaskLoader.
RunListCursorLoader RunListFragment .
35.2. RunListCursorLoader (RunListFragment.java)
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// id ;
// CursorAdapter .
Intent i = new Intent(getActivity(), RunActivity.class);
i.putExtra(RunActivity.EXTRA_RUN_ID, id);
startActivity(i);
}
private static class RunListCursorLoader extends SQLiteCursorLoader {
public RunListCursorLoader(Context context) {
super(context);
}
@Override
protected Cursor loadCursor() {
//
return RunManager.get(getContext()).queryRuns();
}
}
private static class RunCursorAdapter extends CursorAdapter {
573
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// ( )
setListAdapter(null);
}
574
35.
35.4 ()
}
super.onDestroy();
...
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_NEW_RUN == requestCode) {
mCursor.requery();
((RunCursorAdapter)getListAdapter()).notifyDataSetChanged();
//
getLoaderManager().restartLoader(0, null, this);
}
}
, , .
,
. ListFragment
, null.
SQLiteCursorLoader ,
(, ), RunFragment , RunManager.
.
DataLoader,
AsyncTaskLoader. DataLoader ,
AsyncTaskLoader, loadInBackground().
DataLoader 35.5.
35.5. (DataLoader.java)
public abstract class DataLoader<D> extends AsyncTaskLoader<D> {
private D mData;
public DataLoader(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
if (mData != null) {
deliverResult(mData);
} else {
forceLoad();
}
}
575
@Override
public void deliverResult(D data) {
mData = data;
if (isStarted())
super.deliverResult(data);
}
DataLoader D . onStartLoading() ,
.
forceLoad() .
deliverResult(D) ,
.
, DataLoader RunLoader RunFragment.
35.6. (RunLoader.java)
public class RunLoader extends DataLoader<Run> {
private long mRunId;
public RunLoader(Context context, long runId) {
super(context);
mRunId = runId;
}
@Override
public Run loadInBackground() {
return RunManager.get(getContext()).getRun(mRunId);
}
576
35.
@Override
public void onLoaderReset(Loader<Run> loader) {
//
}
extends Fragment {
String TAG = "RunFragment";
String ARG_RUN_ID = "RUN_ID";
int LOAD_RUN = 0;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
577
if (runId != -1) {
mRun = mRunManager.getRun(runId);
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_RUN, args, new RunLoaderCallbacks());
}
RunTracker , ,
, .
( ), , , ,
.
.
,
, .
LastLocationLoader, .
35.9. LastLocationLoader (LastLocationLoader.java)
public class LastLocationLoader extends DataLoader<Location> {
private long mRunId;
RunLoader, ,
getLastLocationForRun(long) RunManager .
LocationLoaderCallbacks RunFragment.
35.10. LocationLoaderCallbacks (RunFragment.java)
private class LocationLoaderCallbacks implements LoaderCallbacks<Location> {
@Override
public Loader<Location> onCreateLoader(int id, Bundle args) {
return new LastLocationLoader(getActivity(), args.getLong(ARG_RUN_ID));
}
@Override
public void onLoadFinished(Loader<Location> loader, Location location) {
578
35.
35.10 ()
mLastLocation = location;
updateUI();
@Override
public void onLoaderReset(Loader<Location> loader) {
//
}
RunLoaderCallbacks, , mLastLocation .
RunManager
onCreate(Bundle), ID LOAD_LOCATION.
35.11. (RunFragment.java)
public class RunFragment
private static final
private static final
private static final
private static final
...
extends Fragment {
String TAG = "RunFragment";
String ARG_RUN_ID = "RUN_ID";
int LOAD_RUN = 0;
int LOAD_LOCATION = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mRunManager = RunManager.get(getActivity());
//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_RUN, args, new RunLoaderCallbacks());
mLastLocation = mRunManager.getLastLocationForRun(runId);
lm.initLoader(LOAD_LOCATION, args, new LocationLoaderCallbacks());
}
}
RunTracker . ,
, .
36
RunTracker
. Google Maps API ( 2) .
RunMapFragment,
,
.
, ,
Maps API.
Google Play services SDK ( , Maps API)
Android 2.2
Google Play. .
580
36.
RunTracker
Google Play services Maps API
(
API). XML
RunTracker. , MAPS_RECEIVE RunTracker.
RunMapActivity.
36.1. Maps API (AndroidManifest.xml)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.bignerdranch.android.runtracker"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="15" />
<permission
android:name="com.bignerdranch.android.runtracker.permission.MAPS_RECEIVE"
android:protectionLevel="signature"/>
<uses-permission
android:name="com.bignerdranch.android.runtracker.permission.MAPS_RECEIVE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission
android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
581
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:required="true"
android:name="android.hardware.location.gps" />
<uses-feature
android:required="true"
android:glEsVersion="0x00020000"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".RunListActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RunActivity"
android:label="@string/app_name" />
<activity android:name=".RunMapActivity"
android:label="@string/app_name" />
<receiver android:name=".TrackingLocationReceiver"
android:exported="false">
<intent-filter>
<action
android:name="com.bignerdranch.android.runtracker.ACTION_LOCATION"/>
</intent-filter>
</receiver>
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="your-maps-API-key-here"/>
</application>
</manifest>
. Maps API MapFragment SupportMapFragment,
MapView
GoogleMap.
SupportMapFragment RunMapFragment ,
36.2.
36.2. RunMapFragment (RunMapFragment.java)
public class RunMapFragment extends SupportMapFragment {
private static final String ARG_RUN_ID = "RUN_ID";
private GoogleMap mGoogleMap;
582
36.
36.2 ()
public static RunMapFragment newInstance(long runId) {
Bundle args = new Bundle();
args.putLong(ARG_RUN_ID, runId);
RunMapFragment rf = new RunMapFragment();
rf.setArguments(args);
return rf;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v = super.onCreateView(inflater, parent, savedInstanceState);
// GoogleMap
mGoogleMap = getMap();
//
mGoogleMap.setMyLocationEnabled(true);
}
return v;
newInstance(long) RunMapFragment
,
RunFragment. .
onCreateView()
, ,
GoogleMap . GoogleMap
, MapView,
.
setMyLocationEnabled(boolean),
.
RunMapFragment RunMapActivity
. ,
.
36.3. - (RunMapActivity.java)
public class RunMapActivity extends SingleFragmentActivity {
/** long */
public static final String EXTRA_RUN_ID =
"com.bignerdranch.android.runtracker.run_id";
@Override
protected Fragment createFragment() {
long runId = getIntent().getLongExtra(EXTRA_RUN_ID, -1);
if (runId != -1) {
return RunMapFragment.newInstance(runId);
} else {
return new RunMapFragment();
}
}
583
RunMapActivity RunFragment . ,
. , , , ,
.
36.4. (res/values/strings.xml)
<string name="new_run">New Run</string>
<string name="map">Map</string>
<string name="run_start">Run Start</string>
<string name="run_started_at_format">Run started at %s</string>
<string name="run_finish">Run Finish</string>
<string name="run_finished_at_format">Run finished at %s</string>
</resources>
RunFragment Map.
36.5. Map (fragment_run.xml)
<Button android:id="@+id/run_stopButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/stop"
/>
<Button android:id="@+id/run_mapButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/map"
/>
</LinearLayout>
</TableLayout>
RunFragment .
36.6. Map (RunFragment.java)
public class RunFragment extends Fragment {
...
private RunManager mRunManager;
private Run mRun;
private Location mLastLocation;
private Button mStartButton, mStopButton, mMapButton;
private TextView mStartedTextView, mLatitudeTextView,
mLongitudeTextView, mAltitudeTextView, mDurationTextView;
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
584
36.
36.6 ()
...
mMapButton = (Button)view.findViewById(R.id.run_mapButton);
mMapButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getActivity(), RunMapActivity.class);
i.putExtra(RunMapActivity.EXTRA_RUN_ID, mRun.getId());
startActivity(i);
}
});
updateUI();
}
return view;
...
private void updateUI() {
boolean started = mRunManager.isTrackingRun();
boolean trackingThisRun = mRunManager.isTrackingRun(mRun);
if (mRun != null)
mStartedTextView.setText(mRun.getStartDate().toString());
int durationSeconds = 0;
if (mRun != null && mLastLocation != null) {
durationSeconds = mRun.getDurationSeconds(mLastLocation.getTime());
mLatitudeTextView.setText(Double.toString
(mLastLocation.getLatitude()));
mLongitudeTextView.setText(Double.toString
(mLastLocation.getLongitude()));
mAltitudeTextView.setText(Double.toString
(mLastLocation.getAltitude()));
mMapButton.setEnabled(true);
} else {
mMapButton.setEnabled(false);
}
mDurationTextView.setText(Run.formatDuration(durationSeconds));
mStartButton.setEnabled(!started);
mStopButton.setEnabled(started && trackingThisRun);
RunTracker . , Map.
.36.1, ,
- Big Nerd Ranch.
585
. 36.1. RunTracker
, . Maps
API , . RunDatabaseHelper RunManager , LocationCursor .
36.7. (RunDatabaseHelper.java)
public LocationCursor queryLocationsForRun(long runId) {
Cursor wrapped = getReadableDatabase().query(TABLE_LOCATION,
null,
COLUMN_LOCATION_RUN_ID + " = ?", //
new String[]{ String.valueOf(runId) },
null, // group by
null, // having
COLUMN_LOCATION_TIMESTAMP + " asc"); //
return new LocationCursor(wrapped);
//
}
queryLocationsForRun(long) queryLastLocation
ForRun(long) , SQLite,
, .
586
36.
RunManager, (faade)
RunMapFragment.
36.8. , II (RunManager.java)
public LocationCursor queryLocationsForRun(long runId) {
return mHelper.queryLocationsForRun(runId);
}
RunMapFragment . -
,
Loader. LocationListCursorLoader .
36.9. (LocationListCursorLoader.java)
public class LocationListCursorLoader extends SQLiteCursorLoader {
private long mRunId;
public LocationListCursorLoader(Context c, long runId) {
super(c);
mRunId = runId;
}
@Override
protected Cursor loadCursor() {
return RunManager.get(getContext()).queryLocationsForRun(mRunId);
}
RunMapFragment .
36.10. RunMapFragment (RunMapFragment.java)
public class RunMapFragment extends SupportMapFragment
implements LoaderCallbacks<Cursor> {
private static final String ARG_RUN_ID = "RUN_ID";
private static final int LOAD_LOCATIONS = 0;
private GoogleMap mGoogleMap;
private LocationCursor mLocationCursor;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//
Bundle args = getArguments();
if (args != null) {
long runId = args.getLong(ARG_RUN_ID, -1);
if (runId != -1) {
LoaderManager lm = getLoaderManager();
lm.initLoader(LOAD_LOCATIONS, args, this);
}
}
587
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
long runId = args.getLong(ARG_RUN_ID, -1);
return new LocationListCursorLoader(getActivity(), runId);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mLocationCursor = (LocationCursor)cursor;
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
//
mLocationCursor.close();
mLocationCursor = null;
}
RunMapFragment LocationCursor .
.
onLoaderReset(Loader<Cursor>) ,
.
LoaderManager , .
, , .
:
GoogleMap. updateUI() ,
onLoadFinished().
36.11. (RunMapFragment.java)
private void updateUI() {
if (mGoogleMap == null || mLocationCursor == null)
return;
// .
// .
PolylineOptions line = new PolylineOptions();
// LatLngBounds .
LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
//
mLocationCursor.moveToFirst();
while (!mLocationCursor.isAfterLast()) {
Location loc = mLocationCursor.getLocation();
LatLng latLng = new LatLng(loc.getLatitude(), loc.getLongitude());
line.add(latLng);
latLngBuilder.include(latLng);
mLocationCursor.moveToNext();
}
//
mGoogleMap.addPolyline(line);
//
588
36.
36.11 ()
//
// .
Display display = getActivity().getWindowManager().getDefaultDisplay();
// .
LatLngBounds latLngBounds = latLngBuilder.build();
CameraUpdate movement = CameraUpdateFactory.newLatLngBounds(latLngBounds,
display.getWidth(), display.getHeight(), 15);
mGoogleMap.moveCamera(movement);
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
long runId = args.getLong(ARG_RUN_ID, -1);
return new LocationListCursorLoader(getActivity(), runId);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mLocationCursor = (LocationCursor)cursor;
updateUI();
}
589
, , ,
, .
, .
updateUI() , .
36.12. (RunMapFragment.java)
private void updateUI() {
if (mGoogleMap == null || mLocationCursor == null)
return;
// .
// .
PolylineOptions line = new PolylineOptions();
// LatLngBounds .
LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder();
//
mLocationCursor.moveToFirst();
while (!mLocationCursor.isAfterLast()) {
Location loc = mLocationCursor.getLocation();
LatLng latLng = new LatLng(loc.getLatitude(), loc.getLongitude());
Resources r = getResources();
// ,
if (mLocationCursor.isFirst()) {
String startDate = new Date(loc.getTime()).toString();
MarkerOptions startMarkerOptions = new MarkerOptions()
.position(latLng)
.title(r.getString(R.string.run_start))
.snippet(r.getString(R.string.run_started_at_format, startDate));
mGoogleMap.addMarker(startMarkerOptions);
} else if (mLocationCursor.isLast()) {
// ,
// ,
String endDate = new Date(loc.getTime()).toString();
MarkerOptions finishMarkerOptions = new MarkerOptions()
.position(latLng)
.title(r.getString(R.string.run_finish))
.snippet(r.getString(R.string.run_finished_at_format, endDate));
mGoogleMap.addMarker(finishMarkerOptions);
}
line.add(latLng);
latLngBuilder.include(latLng);
mLocationCursor.moveToNext();
}
//
mGoogleMap.addPolyline(line);
MarkerOptions , .
590
36.
, .
,
( )
icon(BitmapDescriptor) BitmapDescriptorFactory.
, .
RunTracker
. !
.
RunMapFragment
, .
. LocationReceiver RunMapFragment,
.
(overlay) , .
37
! .
, ; , .
; Android.
, : Android. -,
.
, ? .
. . ,
, . . ,
: .
. . -
?
.
, .
. Android #android-dev irc.freenode.net. Android
Developer Office Hours (https://plus.google.com/+AndroidDevelopers/posts)
Android
.
.
.
Android http://www.github.com. , , .
, .
592
37.
, Big Nerd Ranch
http://www.bignerdranch.com/books. ,
. ,
, .
http://www.bignerdranch.com.
, , .
, .