在第5章,你已经创建了一个地震监视器,它能基于Internet种子显示出最近的地震列表。
在接下来的例子里,你将为这个地震Viewer创建一个Preference页,来允许用户配置应用程序设定来获得更加个性的体验。你将为程序提供以下功能:自动更新、更新频率和最小地震级别的过滤。
在本章的后面,你将进一步扩展这个例子,通过创建一个Content Provider来保存地震数据和与其它应用程序共享数据。
1. 打开你在第5章创建的Earthquake工程。
添加“Preference”屏幕中显示标签的字符串资源。另外,为新的菜单项添加一个字符串,来允许用户访问Preference屏幕。
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string name=”app_name”>Earthquake</string>
<string name=”quake_feed”>
http://earthquake.usgs.gov/eqcenter/catalogs/1day-M2.5.xml
</string>
<string name=”menu_update”>Refresh Earthquakes</string>
<string name=”auto_update_prompt”>Auto Update?</string>
<string name=”update_freq_prompt”>Update Frequency</string>
<string name=”min_quake_mag_prompt”>Minimum Quake Magnitude</string>
<string name=”menu_preferences”>Preferences</string>
</resources>
2. 创建一个新的preferences.xml layout资源,来未Preferences Activity布局UI。包括一个Checkbox来显示“自动更新”的切换,Spinner来选择更新的频率和级别过滤。
<LinearLayout
xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/auto_update_prompt”
/>
<CheckBox android:id=”@+id/checkbox_auto_update”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
/>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/update_freq_prompt”
/>
<Spinner
android:id=”@+id/spinner_update_freq”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:drawSelectorOnTop=”true”
/>
<TextView
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/min_quake_mag_prompt”
/>
<Spinner
android:id=”@+id/spinner_quake_mag”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:drawSelectorOnTop=”true”
/>
<LinearLayout
android:orientation=”horizontal”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”>
<Button
android:id=”@+id/okButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@android:string/ok”
/>
<Button
android:id=”@+id/cancelButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”@android:string/cancel”
/>
</LinearLayout>
</LinearLayout>
3. 在/res/values/arrays.xml文件中创建四个数组资源。它们负责提供更新频率和最小级别的值。
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string-array name=”update_freq_options”>
<item>Every Minute</item>
<item>5 minutes</item>
<item>10 minutes</item>
<item>15 minutes</item>
<item>Every Hour</item>
</string-array>
<array name=”magnitude”>
<item>3</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</array>
<string-array name=”magnitude_options”>
<item>3</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</string-array>
<array name=”update_freq_values”>
<item>1</item>
<item>5</item>
<item>10</item>
<item>15</item>
<item>60</item>
</array>
</resources>
4. 创建Preferences Activity。它将用于显示应用程序的Preference。重写onCreate方法来显示第2步中创建的layout,并且获得Checkbox和两个Spinner控件的引用。然后调用一下populateSpinners方法。
package com.paad.earthquake;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Spinner;
public class Preferences extends Activity {
CheckBox autoUpdate;
Spinner updateFreqSpinner;
Spinner magnitudeSpinner;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.preferences);
updateFreqSpinner = (Spinner)findViewById(R.id.spinner_update_freq);
magnitudeSpinner = (Spinner)findViewById(R.id.spinner_quake_mag);
autoUpdate = (CheckBox)findViewById(R.id.checkbox_auto_update);
populateSpinners();
}
private void populateSpinners() {
}
}
5. 填充populateSpinners方法,使用ArrayAdapter绑定每个Spinner到相应的数组上。
private void populateSpinners()
{
// Populate the update frequency spinner
ArrayAdapter<CharSequence> fAdapter;
fAdapter = ArrayAdapter.createFromResource(this, R.array.update_freq_options,
android.R.layout.simple_spinner_item);
fAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
updateFreqSpinner.setAdapter(fAdapter);
// Populate the minimum magnitude spinner
ArrayAdapter<CharSequence> mAdapter;
mAdapter = ArrayAdapter.createFromResource(this, R.array.magnitude_options,
android.R.layout.simple_spinner_item);
mAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
magnitudeSpinner.setAdapter(mAdapter);
}
6. 为Shared Preference的标识添加公共的静态字符串值,以及为每个Preference值添加键值。更新onCreate方法,来获取保存的Shared Preference,并调用updateUIFromPreferences方法。在updateUIFromPreferences方法,通过get<type>方法获得在Shared Preference中保存的值并更新到当前UI上。
public static final String USER_PREFERENCE = “USER_PREFERENCES”;
public static final String PREF_AUTO_UPDATE = “PREF_AUTO_UPDATE”;
public static final String PREF_MIN_MAG = “PREF_MIN_MAG”;
public static final String PREF_UPDATE_FREQ = “PREF_UPDATE_FREQ”;
SharedPreferences prefs;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.preferences);
updateFreqSpinner = (Spinner)findViewById(R.id.spinner_update_freq);
magnitudeSpinner = (Spinner)findViewById(R.id.spinner_quake_mag);
autoUpdate = (CheckBox)findViewById(R.id.checkbox_auto_update);
populateSpinners();
prefs = getSharedPreferences(USER_PREFERENCE, Activity.MODE_PRIVATE);
updateUIFromPreferences();
}
private void updateUIFromPreferences() {
boolean autoUpChecked = prefs.getBoolean(PREF_AUTO_UPDATE, false);
int updateFreqIndex = prefs.getInt(PREF_UPDATE_FREQ, 2);
int minMagIndex = prefs.getInt(PREF_MIN_MAG, 0);
updateFreqSpinner.setSelection(updateFreqIndex);
magnitudeSpinner.setSelection(minMagIndex);
autoUpdate.setChecked(autoUpChecked);
}
7. 在onCreate方法中,为OK和Cancel按钮添加事件处理函数。Cancel关闭Activity,而OK应该先调用savePreferences方法。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.preferences);
updateFreqSpinner = (Spinner)findViewById(R.id.spinner_update_freq);
magnitudeSpinner = (Spinner)findViewById(R.id.spinner_quake_mag);
autoUpdate = (CheckBox)findViewById(R.id.checkbox_auto_update);
populateSpinners();
prefs = getSharedPreferences(USER_PREFERENCE, Activity.MODE_PRIVATE);
updateUIFromPreferences();
Button okButton = (Button) findViewById(R.id.okButton);
okButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view)
{
savePreferences();
Preferences.this.setResult(RESULT_OK);
finish();
}
});
Button cancelButton = (Button) findViewById(R.id.cancelButton);
cancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view)
{
Preferences.this.setResult(RESULT_CANCELED);
finish();
}
});
}
private void savePreferences() {
}
8. 填充savePreference方法来记录当前的设置项,把UI的选择结果存到Shared Preference对象中。
private void savePreferences() {
int updateIndex = updateFreqSpinner.getSelectedItemPosition();
int minMagIndex = magnitudeSpinner.getSelectedItemPosition();
boolean autoUpdateChecked = autoUpdate.isChecked();
Editor editor = prefs.edit();
editor.putBoolean(PREF_AUTO_UPDATE, autoUpdateChecked);
editor.putInt(PREF_UPDATE_FREQ, updateIndex);
editor.putInt(PREF_MIN_MAG, minMagIndex);
editor.commit();
}
9. 以上完成了Preferences Activity。为了能在应用程序中能访问它,应将其添加到应用程序的manifest中。
<activity android:name=”.Preferences”
android:label=”Earthquake Preferences”>
</activity>
10. 现在,回到Earthquake Activity中,为显示Preferences Activity添加菜单项。扩展onCreateOptionsMenu方法,在其中添加一个新的项目来打开Preferences Activity。
static final private int MENU_PREFERENCES = Menu.FIRST+1;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_UPDATE, Menu.NONE, R.string.menu_update);
menu.add(0, MENU_PREFERENCES, Menu.NONE, R.string.menu_preferences);
return true;
}
11. 修改onOptionsItemSelected方法,当选择新的菜单项时显示Preferences Activity。创建一个显式的Intent,并将其传入startActivityForResult中。它将启动Preferences画面,并在其保存后通过onActivityResult处理函数通知Earthquake类。
private static final int SHOW_PREFERENCES = 1;
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId())
{
case (MENU_UPDATE):
{
refreshEarthquakes();
return true;
}
case (MENU_PREFERENCES):
{
Intent i = new Intent(this, Preferences.class);
startActivityForResult(i, SHOW_PREFERENCES);
return true;
}
}
return false;
}
12. 启动应用程序,从Activity 菜单中选择Preferences。Preferences Activity应该显示如图6-1.
图6-1
13. 所有剩下来的就是应用设置到Earthquake上。自动更新的实现将留到第8章,在那里,你将学习如何使用Service和后台线程。现在,你可以在适当的位置添加框架代码,并应用地震级别过滤。
14. 创建updateFromPreferences方法并读取Shared Preference中的值,将这些值存到实例的变量中。
int minimumMagnitude = 0;
boolean autoUpdate = false;
int updateFreq = 0;
private void updateFromPreferences()
{
SharedPreferences prefs = getSharedPreferences(Preferences.USER_PREFERENCE, Activity.MODE_PRIVATE);
int minMagIndex = prefs.getInt(Preferences.PREF_MIN_MAG, 0);
if (minMagIndex < 0)
minMagIndex = 0;
int freqIndex = prefs.getInt(Preferences.PREF_UPDATE_FREQ, 0);
if (freqIndex < 0)
freqIndex = 0;
autoUpdate = prefs.getBoolean(Preferences.PREF_AUTO_UPDATE, false);
Resources r = getResources();
// Get the option values from the arrays.
int[] minMagValues = r.getIntArray(R.array.magnitude);
int[] freqValues = r.getIntArray(R.array.update_freq_values);
// Convert the values to ints.
minimumMagnitude = minMagValues[minMagIndex];
updateFreq = freqValues[freqIndex];
}
15. 通过更新addNewQuake方法来应用级别过滤。在将Quake对象添加到list之前检查一下Quake的级别。
private void addNewQuake(Quake _quake) {
if (_quake.getMagnitude() > minimumMagnitude)
{
// Add the new quake to our list of earthquakes.
earthquakes.add(_quake);
// Notify the array adapter of a change.
aa.notifyDataSetChanged();
}
}
16. 重写onActivityResult方法,当保存了Preferences Activity的变更后调用updateFromPreferences方法和refresh方法。
@Override
public void onActivityResult(int requestCode, int resultCode,Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SHOW_PREFERENCES)
if (resultCode == Activity.RESULT_OK) {
updateFromPreferences();
refreshEarthquakes();
}
}
17. 最后,在onCreate方法中调用updateFromPreferences(在调用refreshEarthquake方法之前),确保设置项在Activity第一次启动时应用了。
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
earthquakeListView = (ListView)this.findViewById(R.id.earthquakeListView);
earthquakeListView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView _av, View _v, int _index, long arg3)
{
selectedQuake = earthquakes.get(_index);
showDialog(QUAKE_DIALOG);
}
});
int layoutID = android.R.layout.simple_list_item_1;
aa = new ArrayAdapter<Quake>(this, layoutID, earthquakes);
earthquakeListView.setAdapter(aa);
updateFromPreferences();
refreshEarthquakes();
}