目录
- 学习《Java和Android开发学习指南(第二版)》第39、40、41、42章——
- 教材学习中的问题和解决过程
- 代码调试中的问题和解决过程
- 上周错题总结
- [代码托管]
- statistics.sh脚本运行结果的截图
- 学习进度条
- 参考资料
学习《Java和Android开发学习指南(第二版)》第39、40、41、42章——
第39章偏好
Android带有一个SharedPreferences接口,它可以用来管理键/值对这样的应用程序设置。SharedPreferences还负责向一个文件写入数据。此外,Android还提供了Preference API,它带有连接到默认的SharedPreferences实例的用户接口类,以便可以很容易地创建一个UI来修改应用程序设置。
39.1SharedPreference
1.android.content.SharedPreferences接口提供了用于排序和读取应用程序设置的方法。
2.getXXX方法返回了和指定的键相关联的值(如果键值对存在的话)。
3.可以使用contains方法检查一个SharePreferences是否包含一个键值对。
4.可以使用getAll方法将所有的键值对获取为一个Map。
5.一个SharedPreferences中存储的值会自动持久化,并且将会在用户会话中存在。
39.2Preference API
1.要在一个SharedPreferences中存储一个键值对,通常使用Android Preference API来创建一个用户界面,使得用户能够编辑设置。
2.现在的Android版本中,通常使用PreferenceFragment来代替偏好xml文件。
39.3 使用Preference
1.代码清单39.1 AndroidManifest.xml文件
2.代码清单39.2 第一个活动的布局文件(activity_main.xml)
3.代码清单39.3 MainActivity类
package com.example.preferencedemo1;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onResume() {
super.onResume();
SharedPreferences sharedPref = PreferenceManager.
getDefaultSharedPreferences(this);
boolean allowMultipleUsers = sharedPref.getBoolean(
SettingsActivity.ALLOW_MULTIPLE_USERS, false);
String envId = sharedPref.getString(
SettingsActivity.ENVIRONMENT_ID, "");
String account = sharedPref.getString(
SettingsActivity.ACCOUNT, "");
TextView textView = (TextView) findViewById(R.id.info);
textView.setText("Allow multiple users: " +
allowMultipleUsers + "\nEnvironment Id: " + envId
+ "\nAccount: " + account);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
startActivity(new Intent(this,
SettingsActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
4.代码清单39.4 SettingsActivity类
package com.example.preferencedemo1;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
public class SettingsActivity extends Activity {
public static final String ALLOW_MULTIPLE_USERS =
"allowMultipleUsers";
public static final String ENVIRONMENT_ID = "envId";
public static final String ACCOUNT = "account";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setDisplayHomeAsUpEnabled(true);
getFragmentManager()
.beginTransaction()
.replace(android.R.id.content,
new SettingsFragment()).commit();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_settings, menu);
return true;
}
}
5.代码清单39.5 SettingsFragment类
package com.example.preferencedemo1;
import android.os.Bundle;
import android.preference.PreferenceFragment;
public class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
}
6.代码清单39.6 res/xml/preferences.xml文件
第40章操作空间
40.1 概览
Android设备提供了两种存储区域,内部的和外部的。
内部存储对于应用程序来说是私有的,用户和其他的应用程序不能访问它。
外部存储中所存储的文件将会和其他的应用程序分享,其他用户也能够访问外部存储。
40.1.1内部存储
1.Context类提供了各种方法,
通常使用这些方法来访问在内部存储中存储的文件,而且不应该将内部存储的位置直接编写到代码中。 |
40.1.2 外部存储
1.有两种类型的文件能够写入到外部存储中,私有文件和公有文件。私有文件是应用程序所私有的,当应用程序卸载的时候,将会删除这些文件。公有文件是要和其他的应用程序共享的,用户也可以访问它。
2.外部存储可以删除。试图读取或向其写入的时候,应该首先测试外部存储是否可用。
3.写入到外部存储需要用户的许可。
4.读取外部存储,应该在清单文件中声明users-permission元素。
40.2 创建一个Notes应用程序
1.代码清单40.1 AndroidManifest.xml文件
2.MainActivity类
package com.example.filedemo1;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private String selectedItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(
R.id.listView1);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setOnItemClickListener(
new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView,
View view, int position, long id) {
readNote(position);
}
});
}
@Override
public void onResume() {
super.onResume();
refreshList();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_add:
startActivity(new Intent(this,
AddNoteActivity.class));
return true;
case R.id.action_delete:
deleteNote();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void refreshList() {
ListView listView = (ListView) findViewById(
R.id.listView1);
String[] titles = fileList();
ArrayAdapter arrayAdapter =
new ArrayAdapter(
this,
android.R.layout.simple_list_item_activated_1,
titles);
listView.setAdapter(arrayAdapter);
}
private void readNote(int position) {
String[] titles = fileList();
if (titles.length > position) {
selectedItem = titles[position];
File dir = getFilesDir();
File file = new File(dir, selectedItem);
FileReader fileReader = null;
BufferedReader bufferedReader = null;
try {
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
StringBuilder sb = new StringBuilder();
String line = bufferedReader.readLine();
while (line != null) {
sb.append(line);
line = bufferedReader.readLine();
}
((TextView) findViewById(R.id.textView1)).
setText(sb.toString());
} catch (IOException e) {
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
}
}
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
}
}
}
}
}
private void deleteNote() {
if (selectedItem != null) {
deleteFile(selectedItem);
selectedItem = null;
((TextView) findViewById(R.id.textView1)).setText("");
refreshList();
}
}
}
3.代码清单40.3 AddNoteActivity类
package com.example.filedemo1;
import java.io.File;
import java.io.PrintWriter;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
public class AddNoteActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_note);
}
public void cancel(View view) {
finish();
}
public void addNote(View view) {
String fileName = ((EditText)
findViewById(R.id.noteTitle))
.getText().toString();
String body = ((EditText) findViewById(R.id.noteBody))
.getText().toString();
File parent = getFilesDir();
File file = new File(parent, fileName);
PrintWriter writer = null;
try {
writer = new PrintWriter(file);
writer.write(body);
finish();
} catch (Exception e) {
showAlertDialog("Error adding note", e.getMessage());
} finally {
if (writer != null) {
try {
writer.close();
} catch (Exception e) {
}
}
}
}
private void showAlertDialog(String title, String message) {
AlertDialog alertDialog = new
AlertDialog.Builder(this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.show();
}
}
40.3访问公共存储
1.代码清单40.4 FileDemo2的活动的布局文件
2.代码清单40.5 MainActivity类
package com.example.filedemo2;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
class KeyValue {
public String key;
public String value;
public KeyValue(String key, String value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return key;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final List keyValues = Arrays.asList(
new KeyValue("Alarms", Environment.DIRECTORY_ALARMS),
new KeyValue("DCIM", Environment.DIRECTORY_DCIM),
new KeyValue("Downloads",
Environment.DIRECTORY_DOWNLOADS),
new KeyValue("Movies", Environment.DIRECTORY_MOVIES),
new KeyValue("Music", Environment.DIRECTORY_MUSIC),
new KeyValue("Notifications",
Environment.DIRECTORY_NOTIFICATIONS),
new KeyValue("Pictures",
Environment.DIRECTORY_PICTURES),
new KeyValue("Podcasts",
Environment.DIRECTORY_PODCASTS),
new KeyValue("Ringtones",
Environment.DIRECTORY_RINGTONES)
);
ArrayAdapter arrayAdapter = new
ArrayAdapter(this,
android.R.layout.simple_list_item_activated_1,
keyValues);
ListView listView1 = (ListView)
findViewById(R.id.listView1);
listView1.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView1.setAdapter(arrayAdapter);
listView1.setOnItemClickListener(new
OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView,
View view, int position, long id) {
KeyValue keyValue = keyValues.get(position);
listDir(keyValue.value);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
private void listDir(String dir) {
File parent = Environment
.getExternalStoragePublicDirectory(dir);
String[] files = null;
if (parent == null || parent.list() == null) {
files = new String[0];
} else {
files = parent.list();
}
ArrayAdapter arrayAdapter = new
ArrayAdapter(this,
android.R.layout.simple_list_item_activated_1,
files);
ListView listView2 = (ListView)
findViewById(R.id.listView2);
listView2.setAdapter(arrayAdapter);
}
}
第41章操作数据库
Android拥有自己的技术来操作数据库,它和Java数据库连接(Java Database Connectivity,JDBC)无关,JDBC是Java开发者用来访问关系数据库中的数据的一种技术。Android带有SQLite,这是一个开源的数据库。
41.1 概览
1.Android带有自己的Database API。
2.SQLite中的一项功能是,当插入一行的时候,有一个整数的主键自动增加,不需要为该字段传入一个值。
41.2 Database API
41.2.1 SQLiteOpenHelper类
1.提供一个构造方法,它调用自己的超类,传入Context和数据库名称以及其他内容。
2.覆盖onCreate方法和onUpgrade方法。
3.SQLiteOpenHelper自动管理到底层数据库的连接。
41.2.2 SQLiteDatabase类
1.调用insert或execSQL方法来操作数据库中的数据。
2.要获取记录,使用query方法之一。
41.2.3 Cursor接口
1.在SQLiteDatabase上调用query方法将返回一个Cursor。
41.3 示例
1.代码清单41.1 AndroidManifest.xml文件
2.代码清单41.2 Contact类
package com.example.databasedemo1;
public class Contact {
private long id;
private String firstName;
private String lastName;
private String phone;
private String email;
public Contact() {
}
public Contact(String firstName, String lastName,
String phone, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.phone = phone;
this.email = email;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
3.代码清单41.3 DatabaseManager类
package com.example.databasedemo1;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DatabaseManager extends SQLiteOpenHelper {
public static final String TABLE_NAME = "contacts";
public static final String ID_FIELD = "_id";
public static final String FIRST_NAME_FIELD = "first_name";
public static final String LAST_NAME_FIELD = "last_name";
public static final String PHONE_FIELD = "phone";
public static final String EMAIL_FIELD = "email";
public DatabaseManager(Context context) {
super(context,
/*db name=*/ "contacts_db2",
/*cursorFactory=*/ null,
/*db version=*/1);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.d("db", "onCreate");
String sql = "CREATE TABLE " + TABLE_NAME
+ " (" + ID_FIELD + " INTEGER, "
+ FIRST_NAME_FIELD + " TEXT,"
+ LAST_NAME_FIELD + " TEXT,"
+ PHONE_FIELD + " TEXT,"
+ EMAIL_FIELD + " TEXT,"
+ " PRIMARY KEY (" + ID_FIELD + "));";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {
Log.d("db", "onUpdate");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
// re-create the table
onCreate(db);
}
public Contact addContact(Contact contact) {
Log.d("db", "addContact");
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(FIRST_NAME_FIELD, contact.getFirstName());
values.put(LAST_NAME_FIELD, contact.getLastName());
values.put(PHONE_FIELD, contact.getPhone());
values.put(EMAIL_FIELD, contact.getEmail());
long id = db.insert(TABLE_NAME, null, values);
contact.setId(id);
db.close();
return contact;
}
// Getting single contact
Contact getContact(long id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[] {
ID_FIELD, FIRST_NAME_FIELD, LAST_NAME_FIELD,
PHONE_FIELD, EMAIL_FIELD }, ID_FIELD + "=?",
new String[] { String.valueOf(id) }, null,
null, null, null);
if (cursor != null) {
cursor.moveToFirst();
Contact contact = new Contact(
cursor.getString(1),
cursor.getString(2),
cursor.getString(3),
cursor.getString(4));
contact.setId(cursor.getLong(0));
return contact;
}
return null;
}
// Getting All Contacts
public List getAllContacts() {
List contacts = new ArrayList();
String selectQuery = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
while (cursor.moveToNext()) {
Contact contact = new Contact();
contact.setId(Integer.parseInt(cursor.getString(0)));
contact.setFirstName(cursor.getString(1));
contact.setLastName(cursor.getString(2));
contact.setPhone(cursor.getString(3));
contact.setEmail(cursor.getString(4));
contacts.add(contact);
}
return contacts;
}
public Cursor getContactsCursor() {
String selectQuery = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getWritableDatabase();
return db.rawQuery(selectQuery, null);
}
public int updateContact(Contact contact) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(FIRST_NAME_FIELD, contact.getFirstName());
values.put(LAST_NAME_FIELD, contact.getLastName());
values.put(PHONE_FIELD, contact.getPhone());
values.put(EMAIL_FIELD, contact.getEmail());
return db.update(TABLE_NAME, values, ID_FIELD + " = ?",
new String[] { String.valueOf(contact.getId()) });
}
public void deleteContact(long id) {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, ID_FIELD + " = ?",
new String[] { String.valueOf(id) });
db.close();
}
}
4.代码清单41.4 MainActivity类
package com.example.databasedemo1;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.CursorAdapter;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
public class MainActivity extends Activity {
DatabaseManager dbMgr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(
R.id.listView);
dbMgr = new DatabaseManager(this);
Cursor cursor = dbMgr.getContactsCursor();
startManagingCursor(cursor);
ListAdapter adapter = new SimpleCursorAdapter(
this,
android.R.layout.two_line_list_item,
cursor,
new String[] {DatabaseManager.FIRST_NAME_FIELD,
DatabaseManager.LAST_NAME_FIELD},
new int[] {android.R.id.text1, android.R.id.text2},
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setOnItemClickListener(
new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView,
View view, int position, long id) {
Intent intent = new Intent(
getApplicationContext(),
ShowContactActivity.class);
intent.putExtra("id", id);
startActivity(intent);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add:
startActivity(new Intent(this,
AddContactActivity.class));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
5.代码清单41.5 AddContactActivity类
package com.example.databasedemo1;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
public class AddContactActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_contact);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.add_contact, menu);
return true;
}
public void cancel(View view) {
finish();
}
public void addContact(View view) {
DatabaseManager dbMgr = new DatabaseManager(this);
String firstName = ((TextView) findViewById(
R.id.firstName)).getText().toString();
String lastName = ((TextView) findViewById(
R.id.lastName)).getText().toString();
String phone = ((TextView) findViewById(
R.id.phone)).getText().toString();
String email = ((TextView) findViewById(
R.id.email)).getText().toString();
Contact contact = new Contact(firstName, lastName,
phone, email);
dbMgr.addContact(contact);
finish();
}
}
6.代码清单41.6 ShowContactActivity类
package com.example.databasedemo1;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class ShowContactActivity extends Activity {
long contactId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_show_contact);
getActionBar().setDisplayHomeAsUpEnabled(true);
Bundle extras = getIntent().getExtras();
if (extras != null) {
contactId = extras.getLong("id");
DatabaseManager dbMgr = new DatabaseManager(this);
Contact contact = dbMgr.getContact(contactId);
if (contact != null) {
((TextView) findViewById(R.id.firstName))
.setText(contact.getFirstName());
((TextView) findViewById(R.id.lastName))
.setText(contact.getLastName());
((TextView) findViewById(R.id.phone))
.setText(contact.getPhone());
((TextView) findViewById(R.id.email))
.setText(contact.getEmail());
} else {
Log.d("db", "contact null");
}
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.show_contact, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
deleteContact();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void deleteContact() {
new AlertDialog.Builder(this)
.setTitle("Please confirm")
.setMessage(
"Are you sure you want to delete " +
"this contact?")
.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int whichButton) {
DatabaseManager dbMgr =
new DatabaseManager(
getApplicationContext());
dbMgr.deleteContact(contactId);
dialog.dismiss();
finish();
}
})
.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int which) {
dialog.dismiss();
}
})
.create()
.show();
}
}
第42章获取图片
Android为获取静态图像的应用程序提供了两个选项,使用一个内建的意图来启动Camera或者使用Camera API。第一个选项很容易使用,但是缺乏Camera API所提供的功能。
42.1 概览
1.要告诉Camera把获取的图片存储在哪里,可以给Intent传入一个Uri实例。
2.系统通过传入3个参数来调用onActivityResult方法。第一个参数是requestCode,是调用startActivityForResult方法的时候传入的请求代码。第二个参数是结果代码。第三个参数包含了来自被调用的活动的数据。
42.2 使用相机
CameraDemo应用程序展示了如何使用内建的意图来激活Camera应用程序来使用它来拍照。
1.代码清单42.1 清单
2.代码清单42.2 菜单文件(menu_main.xml)
3.代码清单42.3 activity_main.xml文件
4.代码清单42.4 MainActivity类
package com.example.camerademo;
import java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
File pictureDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "CameraDemo");
private static final String FILE_NAME = "image01.jpg";
private Uri fileUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (!pictureDir.exists()) {
pictureDir.mkdirs();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_camera:
showCamera();
return true;
case R.id.action_email:
emailPicture();
return true;
default:
return super.onContextItemSelected(item);
}
}
private void showCamera() {
Intent intent = new Intent(
MediaStore.ACTION_IMAGE_CAPTURE);
File image = new File(pictureDir, FILE_NAME);
fileUri = Uri.fromFile(image);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
// check if the device has a camera:
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent,
CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {
if (requestCode ==
CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
ImageView imageView = (ImageView)
findViewById(R.id.imageView);
File image = new File(pictureDir, FILE_NAME);
fileUri = Uri.fromFile(image);
imageView.setImageURI(fileUri);
} else if (resultCode == RESULT_CANCELED) {
Toast.makeText(this, "Action cancelled",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, "Error",
Toast.LENGTH_LONG).show();
}
}
}
private void emailPicture() {
Intent emailIntent = new Intent(
android.content.Intent.ACTION_SEND);
emailIntent.setType("application/image");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[]{"[email protected]"});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"New photo");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
"From My App");
emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
startActivity(Intent.createChooser(emailIntent,
"Send mail..."));
}
}
42.3 Camera API
42.3.1 管理相机
使用takePicture方法可以确定从相机得到的最终的原始图像和JPEG图像做些什么。takePicture方法的四个参数介绍如下:
1.shutter。图像捕获瞬间的回调。
2.raw。解压缩图像数据的回调。
3.postview。预览图像数据的回调。
4.jpeg。JPEG图像数据的回调。
42.4 使用Camera API
1.代码清单42.5 布局文件(activity_main.xml)
2.代码清单42.6 MainActivity类
package com.example.cameraapidemo;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.media.AudioManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity
implements SurfaceHolder.Callback {
private Camera camera;
SoundPool soundPool;
int beepId;
File pictureDir = new File(Environment
.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES),
"CameraAPIDemo");
private static final String TAG = "camera";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pictureDir.mkdirs();
soundPool = new SoundPool(1,
AudioManager.STREAM_NOTIFICATION, 0);
Uri uri = Settings.System.DEFAULT_RINGTONE_URI;
beepId = soundPool.load(uri.getPath(), 1);
SurfaceView surfaceView = (SurfaceView)
findViewById(R.id.surfaceview);
surfaceView.getHolder().addCallback(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public void onResume() {
super.onResume();
try {
if (Build.VERSION.SDK_INT >=
Build.VERSION_CODES.GINGERBREAD) {
camera = Camera.open(0);
} else {
camera = Camera.open();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onPause() {
super.onPause();
if (camera != null) {
try {
camera.release();
camera = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void enableButton(boolean enabled) {
Button button = (Button) findViewById(R.id.button1);
button.setEnabled(enabled);
}
public void takePicture(View view) {
enableButton(false);
camera.takePicture(shutterCallback, null,
pictureCallback);
}
private ShutterCallback shutterCallback =
new ShutterCallback() {
@Override
public void onShutter() {
// play sound
soundPool.play(beepId, 1.0f, 1.0f, 0, 0, 1.0f);
}
};
private PictureCallback pictureCallback =
new PictureCallback() {
@Override
public void onPictureTaken(byte[] data,
final Camera camera) {
Toast.makeText(MainActivity.this, "Saving image",
Toast.LENGTH_LONG)
.show();
File pictureFile = new File(pictureDir,
System.currentTimeMillis() + ".jpg");
try {
FileOutputStream fos = new FileOutputStream(
pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d(TAG, e.getMessage());
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
try {
enableButton(true);
camera.startPreview();
} catch (Exception e) {
Log.d("camera",
"Error starting camera preview: "
+ e.getMessage());
}
}
}, 2000);
}
};
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e){
Log.d("camera", e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int w, int h3) {
if (holder.getSurface() == null){
Log.d(TAG, "surface does not exist, return");
return;
}
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
} catch (Exception e){
Log.d("camera", e.getMessage());
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed");
}
}
教材学习中的问题和解决过程
- 问题1:Cursor如何实现ListView的自动刷新?
- 问题解决方案1:将Cursor作为一个ListAdapter的数据源,而ListAdapter反过来可以用于填充一个ListView。针对ListView使用一个Cursor的优点在于,Cursor可以管理你的数据。换句话说,如果该数据更新了,Cursor可以自行刷新ListView。
- 问题2:uri和url的区别
- 问题解决方案2:
URL:(Uniform/Universal Resource Locator 的缩写,统一资源定位符)。
URI:(Uniform Resource Identifier 的缩写,统一资源标识符)(代表一种标准)。
关系:
URI 属于 URL 更高层次的抽象,一种字符串文本标准。
就是说,URI 属于父类,而 URL 属于 URI 的子类。URL 是 URI 的一个子集。
二者的区别在于,URI 表示请求服务器的路径,定义这么一个资源。而 URL 同时说明要如何访问这个资源(http://)。
代码调试中的问题和解决过程
- 问题1:在调试第42章的CameraDemo和CameraAPIDemo时,遇到Gradle里No cached version available for offline mode的问题。
- 问题解决方案2:在setting中gradle选项里把offline work前面的勾给去掉就好了。
- 问题2:Android游戏项目的设计运行无错后,在模拟器上运行总是闪退。
- 问题解决方案2:添加自己的Android设备继续调试。
上周错题总结
1.字符串“dxxxdxxd”,表达式 (d)(\w+)(d)对应的应该是dxxxdxxd,而不是xxxdxx。
错误原因:\w+将匹配第一个d和最后一个d之间的所有字符,(d)是两端的d,不能缺。
[代码托管]
https://gitee.com/EvelynYang/tenth_weeks
statistics.sh脚本运行结果的截图
在新建的AndroidProjects文件夹中运行脚本,第六周及之前都是在IdeaProjects文件夹里运行。
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 200/200 | 2/2 | 20/20 | |
第二周 | 300/500 | 1/3 | 18/38 | |
第三周 | 500/1000 | 1/4 | 38/76 | |
第四周 | 1000/2000 | 1/5 | 20/96 | |
第五周 | 1000/3000 | 1/6 | 25/121 | |
第六周 | 1000/4000 | 1/7 | 25/146 | |
第七周 | 1000/5000 | 1/8 | 25/171 | |
第八周 | 1000/6000 | 1/9 | 15/186 | |
第九周 | 1000/7000 | 1/10 | 20/206 | |
第十周 | 1000/8000 | 1/11 | 20/226 |
参考资料
- 《Java和Android开发学习指南(第二版)(Java for Android.2nd)》
- Android Studio中文社区