public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mRegisteredSensor = false;
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);//取得SensorManager实例
}
protected void onResume(){
super.onResume();
List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ORIENTATION);//接受SensorManager的一个列表(Listener),这里我们指定类型为TYPE_ORIENTATION(方向感应器)
if (sensors.size() > 0){
Sensor sensor = sensors.get(0);
mRegisteredSensor = mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);//注册SensorManager,this->接收sensor的实例,第二个参数为接收传感器类型的列表,第三个参数为接受的频率
}
}
protected void onPause(){
if (mRegisteredSensor){
mSensorManager.unregisterListener(this);//通过unregisterListener来卸载\取消注册
mRegisteredSensor = false;
}
super.onPause();
}
public void onAccuracyChanged(Sensor sensor, int accuracy){//当进准度发生改变时调用此方法,sensor->传感器,accuracy->精准度
}
public void onSensorChanged(SensorEvent event){// 此方法在传感器在被改变时触发
if (event.sensor.getType() == Sensor.TYPE_ORIENTATION){// 接受方向感应器的类型
//这里我们可以得到数据,然后根据需要来处理,由于模拟器上面无法测试效果,因此我们暂时不处理数据
float x = event.values[SensorManager.DATA_X];
float y = event.values[SensorManager.DATA_Y];
float z = event.values[SensorManager.DATA_Z];
}
}
}
第二部分 语音识别
Android中通过RecognizerIntent来实现语音识别。
RecognizerIntent包含以下常量:
ACTION_RECOGNIZE_SPEECH 开启语音活动
ACTION_WEB_SEARCH 开启网络语音模式,结果将以网页搜索显示
EXTRA_LANGUAGE 设置一个语言库
EXTRA_LANGUAGE_MODEL 语音识别模式
EXTRA_MAX_RESULTS 返回最大的结果
EXTRA_PROMPT 提示用户可以开始语音了
EXTRA_RESULTS 将字符串返回到一个ArrayList中
LANGUAGE_MODEL_FREE_FORM 在一种语言模式上自由语音
LANGUAGE_MODEL_WEB_SEARCH 使用语音模式在Web上搜索
RESULT_AUDIO_ERROR 返回结果时,音频遇到错误
RESULT_CLIENT_ERROR 返回结果时,客户端遇到错误
RESULT_NETWORK_ERROR 返回结果时,网络遇到错误
RESULT_NO_MATCH 没有检测到语音的错误
RESULT_SERVER_ERROR 返回结果时,服务器遇到错误
在使用语音识别时需要通过Intent来传递一个动作以及一些属性,然后通过startActivityForResult来开始语音 代码如下:
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RcognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"开始语音");
startActivityForResult(intent ,VOICE_RECOGNITION_REQUEST_CODE);//通过startActivityForResult来开始语音
另外还需要实现onActivityResult方法,当语音接收时,会触发来获得语音的字符序列。
下例中我们实现了 点击按钮时开始语音,并在onActivityResult方法中取得结果并显示出来
main.xml
<?xml version="1.0" encoding="utf-8"?>
<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/hello"
/>
<Button
android:id="@+id/Button01"
android:text="开始使用语音识别"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<ListView
android:id="@+id/ListView01"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
public class Activity01 extends Activity{
private static final int VOICE_RECOGNITION_REQUEST_CODE = 4321;
private ListView mList;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mList = (ListView) findViewById(R.id.ListView01);
Button button = (Button) findViewById(R.id.Button01);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){
try{
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);//通过Intent传递语音识别的模式,开启语音
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);//语言模式和自由形式的语音识别
intent.putExtra(RecognizerIntent.EXTRA_PROMPT,"开始语音");//提示语音开始
startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);//开始执行我们的Intent、语音识别
}catch (ActivityNotFoundException e){//如果找不到设置,就会抛出ActivityNotFoundException
Toast.makeText(Activity01.this,"ActivityNotFoundException", Toast.LENGTH_LONG).show(); //找不到语音设备装置
}
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data){//当语音结束时的回调函数onActivityResult
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK){// 判断是否是我们执行的语音识别
ArrayList<String> results = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);// 取得语音的字符
//设置视图更新
//mList.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,results));
String resultsString = "";
for (int i = 0; i < results.size(); i++){
resultsString += results.get(i);
}
Toast.makeText(this, resultsString, Toast.LENGTH_LONG).show();
super.onActivityResult(requestCode, resultCode, data);
}
}
}
第三部分 账户管理
Android中通过android.accounts包中所包含的集中式的账户管理API,用于安全地存储和访问认证的令牌和密码。
android.accounts包中的功能:
AccountManagerCallback 账户管理回调接口
AccountManagerFuture 代表一个AccountMananger的异步调用结果
OnAccountUpdateListener 账户监听回调接口
AbstractAccountAuthenticator 创建AccountAuthenticators(账户验证)的一个基类
Account AccountManager中的一个账户信息及类型
AccountAuthenticatorActivity 用于实现一个AbstractAccountAuthenticator的活动
AccountAuthenticatorResponse 账户验证响应
AccountManager 账户管理器
AuthenticatorDescription 一个Parcelable类型的值包括了账户验证的信息
mAccountManager = AccountManager.get(this);//通过AccountManager类的get方法获得AccountManager对象
AccountManager中的常用方法
addAccount 添加一个账户
addOnAccountUpdatedListenenr 添加一个账户更新的监听器
removeOnAccountsUpdatedListener 取消一个账户更新的监听器
clearPassword 清除指定账户的密码数据
getAccounts 得到所有的账户信息
getAccountsByType 得到指定类型的账户信息
getPassword 得到指定账户的密码
setUserData 设置指定账户的信息
setPassword 设置指定账户的密码
removeAccount 删除一个账户
public class SleepyAccountAuthenticatorActivity extends AccountAuthenticatorActivity{//此类供用户输入信息
protected void onCreate(Bundle icicle){
super.onCreate(icicle);
setContentView(R.layout.new_account);
final Button done = (Button) findViewById(R.id.new_account_done);
final EditText server = (EditText) findViewById(R.id.new_account_server);
final EditText username = (EditText) findViewById(R.id.new_account_username);
final EditText password = (EditText) findViewById(R.id.new_account_password);
final Activity self = this;
done.setOnClickListener(new OnClickListener() {
public void onClick(View v){
Account account = new Account(username.getText().toString(), getString(R.string.ACCOUNT_TYPE));//通过指定账户名和账户类型构建一个Account对象
Bundle userdata = new Bundle(); //将服务器数据通过Bundle方式加入进来
userdata.putString("SERVER", server.getText().toString());
AccountManager am = AccountManager.get(self);//取得AccountManager
if (am.addAccountExplicitly(account, password.getText().toString(), userdata)){//通添addAccountExplicitly向账户管理器中添加一个账户信息
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, username.getText().toString());
result.putString(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.ACCOUNT_TYPE));
setAccountAuthenticatorResult(result);
}
finish();
}
});
}
}
public class SleepyAccountAuthenticator extends AbstractAccountAuthenticator{//在添加、操作账户信息时会通过AbstractAccountAuthenticator实现异步调用
private String _tag = "SleepyAccountAuthenticator";
private Context _context;
public SleepyAccountAuthenticator(Context context){
super(context);
_context = context;
}
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
throws NetworkErrorException{//添加账户addAccount方法
Log.d(_tag, accountType + " - " + authTokenType);
Bundle ret = new Bundle();
Intent intent = new Intent(_context, SleepyAccountAuthenticatorActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
ret.putParcelable(AccountManager.KEY_INTENT, intent);
return ret;
}
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options){
Log.d(_tag, ".confirmCredentials");
return null;
}
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType){
Log.d(_tag, ".editProperties");
return null;
}
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle loginOptions) throws NetworkErrorException{
Log.d(_tag, ".getAuthToken");
return null;
}
public String getAuthTokenLabel(String authTokenType){
Log.d(_tag, ".getAuthTokenLabel");
return null;
}
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException{
Log.d(_tag, ".hasFeatures");
return null;
}
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle loginOptions){
Log.d(_tag, ".updateCredentials");
return null;
}
}
public class SleepyAccountsService extends Service{//账户服务类
private SleepyAccountAuthenticator _saa;
public IBinder onBind(Intent intent){
IBinder ret = null;
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)){
ret = getSleepyAuthenticator().getIBinder();//通过getIBinder方法返回一个IBinder
return ret;
}
}
private SleepyAccountAuthenticator getSleepyAuthenticator(){
if (_saa == null)
_saa = new SleepyAccountAuthenticator(this);
return _saa;
}
}
public class Activity01 extends Activity{
private String _tag = "Activity01";
private TextView _accountList;
private AccountManager _am;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.manage_accounts);
_accountList = (TextView) findViewById(R.id.manage_accounts_accountlist);
_am = AccountManager.get(this);//取得AccountManager对象
Button newacc = (Button) findViewById(R.id.manage_accounts_newaccount);
final Activity self = this;
newacc.setOnClickListener(new OnClickListener() {
public void onClick(View v){
_am.addAccount(getString(R.string.ACCOUNT_TYPE), null, null, null, self, new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> amfuture){
try{
Log.d(_tag, amfuture.getResult().toString());
}catch (Exception e){
Log.e(_tag, e.getMessage(), e);
}
listAccounts();
}
}, null);
}
});
listAccounts();
}
private void listAccounts(){//显示出所有账户
Account[] accounts = _am.getAccountsByType(getString(R.string.ACCOUNT_TYPE));//得到指定类型的账户
_accountList.setText("账户列表:");
for (Account account : accounts){
_accountList.setText(_accountList.getText().toString() + '\n' + account.name + " - " + account.type);
}
}
}
res.xml.authenticator.xml文件
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.yarin.AccountType"
android:icon="@drawable/icon"
android:smallIcon="@drawable/icon"
android:label="@string/ACCOUNT_LABEL"
android:accountPreferences="@xml/account_preferences"
/>
res.xml.account_preferences.xml文件
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
</PreferenceScreen>
res.layout.new_account.xml文件
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_height="wrap_content"
android:text="用户名"
></TextView>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/new_account_username"
android:text="请输入用户名"
></EditText>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
></TextView>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/new_account_password"
android:text="请输入密码"
></EditText>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务器"
></TextView>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/new_account_server"
android:text="请输入账户服务器"
></EditText>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/new_account_done"
android:text="完成"
></Button>
</LinearLayout>
res.layout.manage_account.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="新建账户"
android:id="@+id/manage_accounts_newaccount"
></Button>
<ScrollView
android:id="@+id/ScrollView01"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
>
<TextView
android:text="@+id/TextView01"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:id="@+id/manage_accounts_accountlist"
></TextView>
</ScrollView>
</LinearLayout>
AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_08"
android:versionCode="1"
android:versionName="1.0"
>
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
>
<service //开启一个账户管理服务
android:name="SleepyAccountsService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" ></action>
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator">
</meta-data>
</service>
<activity
android:name=".auth.SleepyAccountAuthenticatorActivity"
>
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator"
></action>
</intent-filter>
</activity>
<activity
android:name="Activity01"
>
<intent-filter>
<action
android:name="android.intent.action.MAIN"
></action>
<category
android:name="android.intent.category.LAUNCHER"
></category>
</intent-filter>
</activity>
</application>
//因要对账户信息进行操作,需要在AndroidManifest.xml文件中对操作权限进行声明 确定API为5
<uses-sdk android:minSdkVersion="5"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.ACCOUNT_MANAGER"></uses-permission>
<uses-permission android:name="android.permission.GET_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"></uses-permission>
</manifest>
第四部分 桌面组件
一、快捷方式
在应用程序中通过代码来将一个应用程序添加到Android的Shortcuts列表中。
下例实现了添加一个发送邮件的应用到快捷方式列表中去。
public class Activity01 extends Activity{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent addShortcut; //要添加的快捷方式的Intent
if (getIntent().getAction().equals(Intent.ACTION_CREATE_SHORTCUT)){//判断是否要添加快捷方式
addShortcut = new Intent();
addShortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, "发送邮件");//设置快捷方式的名字
Parcelable icon = Intent.ShortcutIconResource.fromContext(this,R.drawable.mail_edit); //构建快捷方式中专门的图标
addShortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,icon);//添加快捷方式图标 其实android提供了Intent.ShortcutIconResource.fromContext来创建快捷方式的图标,
最后通过setResult来返回构建一个快捷方式
Intent mailto = new Intent(Intent.ACTION_SENDTO, Uri.parse( "mailto:[email protected]" )); //构建快捷方式执行的Intent
addShortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, mailto); //添加快捷方式Intent 对应快捷方式执行的事件
setResult(RESULT_OK,addShortcut); //通过setResult来返回构建一个快捷方式
}else{
setResult(RESULT_CANCELED); //取消
}
finish(); //关闭
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_05"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon" android:label="@string/app_name">
<activity
android:name=".Activity01"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.CREATE_SHORTCUT" /> //注册时添加一个Action为android.intent.action.CREATE_SHORTCUT的IntentFilter
添加之后Shortcuts列表中就会出现应用程序的图标和名字
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
最后可以在Shortcuts列表中找到所添加的快捷方式,并可将其添加到桌面。
二、实时文件夹
Live Folders就是一个查看你的手机中所有电子书、电子邮件、ress订阅、播放列表的快捷方式,并且这些内容是实时更新的。
Live Folders本身不存储任何信息,都是以映射的方式查看其ContentProvider所指向的数据信息,并可以自定义显示格式,所以LiveFold可以实时的更新显示内容。
在开发时要确保所指定数据信息URI的ContentProvider支持文件夹查询。
Live Folders的常用属性
EXTRA_LIVE_FOLDER_BASE_INTENT 选中选项后执行的事件
EXTRA_LIVE_FOLDER_NAME 实时文件夹的名字
EXTRA_LIVE_FOLDER_ICON 实时文件夹的图标
EXTRA_LIVE_FOLDER_DISPLAY_MODE 实时文件夹的显示模式(列表和宫格)
下例中通过Live Folders调用电话本中的信息,当点击其中一条信息时,便执行呼叫该联系人的动作。
public class Activity01 extends Activity{
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// setContentView(R.layout.main);
if (getIntent().getAction().equals(LiveFolders.ACTION_CREATE_LIVE_FOLDER)){// 判断是否创建实时文件夹
Intent intent = new Intent();//将实时文件夹的信息装入Intent对象
intent.setData(Uri.parse("content://contacts/live_folders/people"));// 设置数据地址
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_BASE_INTENT, new Intent(Intent.ACTION_CALL, ContactsContract.Contacts.CONTENT_URI));// 设置当我们单击之后的事件,这里单击一个联系人后,呼叫
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, "电话本");// 设置实时文件夹的名字
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON, Intent.ShortcutIconResource.fromContext(this, R.drawable.contacts));// 设置实施文件夹的图标
intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);// 设置显示模式为列表
setResult(RESULT_OK, intent);// 设置为Intent
}else{
setResult(RESULT_CANCELED);
}
finish();//结束Activity
}
}
AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_06"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon" android:label="@string/app_name">
<activity
android:name=".Activity01"
android:label="@string/app_name">
<intent-filter> //注册一个action动作为android.intent.action.CREATE_LIVE_FOLDER的Intentfilter
<action android:name= "android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name= "android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
三、Widget开发
作用:可以显示即将到来的日历事件,或者一首后台播放的歌曲的详细信息。
public class ExampleAppWidgetProvider extends AppWidgetProvider{//创建一个Widget
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){//周期更新时调用
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++){
int appWidgetId = appWidgetIds[i];
String titlePrefix = Activity01.loadTitlePref(context, appWidgetId);
updateAppWidget(context, appWidgetManager, appWidgetId, titlePrefix);
}
}
public void onDeleted(Context context, int[] appWidgetIds){//当桌面部件删除时调用
//删除appWidget
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++){
Activity01.deleteTitlePref(context, appWidgetIds[i]);
}
}
public void onEnabled(Context context){//当AppWidgetProvider提供的第一个部件被创建时调用
PackageManager pm = context.getPackageManager();
//用ComponentName来表示应用程序中某个组件的完整名字
pm.setComponentEnabledSetting(new ComponentName("com.yarin.android.Examples_09_07", ".ExampleBroadcastReceiver"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
public void onDisabled(Context context){//当AppWidgetProvider提供的最后一个部件被删除时调用
PackageManager pm = context.getPackageManager();
//用ComponentName来表示应用程序中某个组件的完整名字
pm.setComponentEnabledSetting(new ComponentName("com.yarin.android.Examples_09_07", ".ExampleBroadcastReceiver"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, String titlePrefix){//更新
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider);//构建RemoteViews对象来对桌面部件进行更新
views.setTextViewText(R.id.appwidget_text, titlePrefix);//更新文本内容,指定布局的组件
appWidgetManager.updateAppWidget(appWidgetId, views);//将RemoteViews的更新传入AppWidget进行更新
}
}
public class ExampleBroadcastReceiver extends BroadcastReceiver{//创建一个BroadcastReceiver类来接收更新的消息,在收到更新消息后就更新这个桌面的Widget组件
public void onReceive(Context context, Intent intent){
//通过BroadcastReceiver来更新AppWidget
String action = intent.getAction();
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED) || action.equals(Intent.ACTION_TIME_CHANGED)){
AppWidgetManager gm = AppWidgetManager.getInstance(context);
ArrayList<Integer> appWidgetIds = new ArrayList<Integer>();
ArrayList<String> texts = new ArrayList<String>();
Activity01.loadAllTitlePrefs(context, appWidgetIds, texts);
final int N = appWidgetIds.size();//更新所有AppWidget
for (int i = 0; i < N; i++){
ExampleAppWidgetProvider.updateAppWidget(context, gm, appWidgetIds.get(i), texts.get(i));
}
}
}
}
public class Activity01 extends Activity{//此类为android.configure指定的类,用来输入信息
private static final String PREFS_NAME = "com.yarin.android.Examples_09_07.ExampleAppWidgetProvider";
private static final String PREF_PREFIX_KEY = "prefix_";
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
EditText mAppWidgetPrefix;
public Activity01(){
super();
}
public void onCreate(Bundle icicle){
super.onCreate(icicle);
setResult(RESULT_CANCELED);
setContentView(R.layout.appwidget_configure);
mAppWidgetPrefix = (EditText) findViewById(R.id.appwidget_prefix);
findViewById(R.id.save_button).setOnClickListener(mOnClickListener);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null){
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID){
finish();
}
mAppWidgetPrefix.setText(loadTitlePref(Activity01.this, mAppWidgetId));
}
View.OnClickListener mOnClickListener = new View.OnClickListener() {
public void onClick(View v) {
final Context context = Activity01.this;
String titlePrefix = mAppWidgetPrefix.getText().toString();
saveTitlePref(context, mAppWidgetId, titlePrefix);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);//取得AppWidgetManager实例
ExampleAppWidgetProvider.updateAppWidget(context, appWidgetManager, mAppWidgetId, titlePrefix);//更新AppWidget
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
};
static void saveTitlePref(Context context, int appWidgetId, String text){
SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
prefs.putString(PREF_PREFIX_KEY + appWidgetId, text);
prefs.commit();
}
static String loadTitlePref(Context context, int appWidgetId){
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
String prefix = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
if (prefix != null){
return prefix;
}else{
return context.getString(R.string.appwidget_prefix_default);
}
}
static void deleteTitlePref(Context context, int appWidgetId){
}
static void loadAllTitlePrefs(Context context, ArrayList<Integer> appWidgetIds, ArrayList<String> texts){
}
}
rew.xml.appwidget_provider.xml//用于描述桌面属性的文件
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="100dp"//桌面组件的最小宽度和最小高度,其值可以用公式计算 .最小尺寸 = (单元格数*74)-2; 单元格数---期望的单元格数
android:minHeight="50dp"
android:updatePeriodMillis="86400000"//自动更新的时间间隔
android:initialLayout="@layout/appwidget_provider"//界面描述文件
android:configure="com.yarin.android.Examples_09_07.Activity01"//此条代码可选 如果你的Widget需要在启动前需要先启动一个Activity,则需要设定该项为你的Activity
>
</appwidget-provider>
res.layout.appwidget_configure.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/appwidget_configure_instructions"
/>
<EditText
android:id="@+id/appwidget_prefix"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/save_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@android:string/ok"
/>
</LinearLayout>
res.layout.appwidget_provider.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/appwidget_text"
android:textColor="#ff000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
AndroidManifest.xml//注册AppWidget、BroadcastReceiver和Activity
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_07"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<receiver
android:name=".ExampleAppWidgetProvider">
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/appwidget_provider" />
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
</receiver>
<activity
android:name=".Activity01">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<receiver
android:name=".ExampleBroadcastReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.ACTION_TIMEZONE_CHANGED" />
<action android:name="android.intent.ACTION_TIME" />
</intent-filter>
</receiver>
</application>
<uses-sdk android:minSdkVersion="5" />
</manifest>
将Widget添加到桌面上,和添加快捷方式一样。
第五部分 Google Map开发
在Android SDK 1.5预装的add-on中提供了一个Map拓展库com.google.android.maps加强了地图功能。这个库的位置"Android SDK路径"\add-ons\google_apis-3\libs 。这个库并不是标准
的Android sdk的内容,可以从这个位置下载,并放到你的SDK中,就可以为你新建的应用或者已有的应用加上地图功能了。
在使用Android Map API之前的准备工作
1、申请一个Android Map API Key步骤:
为了顺利的申请Android Map API Key,必须要准备Google的账号和系统的证明书。一般Google 发布Key都需要Google的账号,Google的账号是通用的,Gmail的账号就可以了
(可到http://www.google.com申请)。当一个应用程序发布时必须要证明书,证明书其实就是MD5. eclipse中测试可以使用Debug版的证明书。
步骤1:找到你的debug.keystore文件
证书的一般路径为:C:\Documents and Settins\当前用户\Local Settions\Application Data\Android\debug.keystore.
如果在Eclipse开发中,便可以通过Windows\Preference\Android\Build.其中Default debug keystore的值便是debug.keystore的路径。
步骤2:取得debug.keystore的MD5值
首先在命令提示符下进入debug.keystore文件所在路径,执行命令:keytool-list-keystore debug.keystore这时会提示你输入密码,这里输入默认的密码"android",即可
获得MD5值。
步骤3:申请Android Map的API Key
打开浏览器,输入网址:http://code.google.com/intl/zh-CN/android/maps-api-signup.html,登陆Google账号,在Google的Android Map API Key申请页面上输入步骤2
得到的MD5认证指纹,选中"I have read and agree with the terms and conditons"选项,然后选中"Generate API Key"按钮,即可以得到申请的API Key.
2、创建基于GoogleAPIs的AVD
在Eclipse中打开AVD管理界面,在"Create AVD"部分的Name处填写AVD的名字,在Target处选择"Google APIs-1.5".点击"Create AVD"完成创建。
3、创建基于Google APIs的工程
同创建一般工程一样,但需要在Build Target处需要选择Google APIs。在运行工程时也需要选择我们刚刚创建的基于Google APIs的AVD来运行。
下面将开始学习如何使用Google API来开发地图应用程序。
Google Map API的使用
Android中定义的包com.google.android.maps中几个重要的类:
MapActivity
这个类是用于显示Google Map的Activity类,它需要连接底层网络。MapActivity是一个抽象类,任何想要显示MapView的activity都需要派生自MapActivity,并且
在其派生类的onCreate()中,创建一个MapView的实例。
MapView
用于显示地图的View组件。它派生自android.view.ViewGroup.它必须和MapActivity配合使用,而且只能被MapActivity创建,这是因为MapView需要通过后台的线程来连接网络或者
文件系统,而这些线程要由MapActivity来管理。
MapController
MapController用于控制地图的移动,缩放等
Overlay
一个可显示于地图之上的可绘制的对象
GeoPoint
一个包含经纬度位置的对象
第一例实现了地图浏览程序
步骤1、创建工程,注意选择Builed Target为"Google APIs"
步骤2、修改AndroidManifest.xml文件
步骤3、创建MapView用于显示地图,即在xml文件中的布局,见例子
步骤4、实现MapActivity,见例子
步骤5、MapController的使用
步骤6、Overlay的使用
AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_03"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />//要从网络获取数据,需添加权限
<activity
android:name=".Activity01"
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>
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk android:minSdkVersion="5" />
</manifest>
main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.google.android.maps.MapView //当然也可以在程序中通过代码 MapView map = new MapView(this,"[Android Maps API Key]");
android:id="@+id/MapView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="0dYpmIXGIdwiVm-HEpzuUW2fjNYsFQ9EvYir1sg"/> //apiKey即是我们申请的Map API Key
</RelativeLayout>
public class Activity01 extends MapActivity{//实现了地图浏览程序
private MapView mMapView;
private MapController mMapController;
private GeoPoint mGeoPoint;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mMapView = (MapView) findViewById(R.id.MapView01);
//mMapView.setTraffic(true);//设置为交通模式
mMapView.setSatellite(true);//设置为卫星模式
//mMapView.setStreetView(false);//设置为街景模式
mMapController = mMapView.getController(); //取得MapController对象(控制MapView),用于设置地图显示的地点以及放大倍数
mMapView.setEnabled(true);
mMapView.setClickable(true);
mMapView.setBuiltInZoomControls(true); //设置地图支持缩放
mGeoPoint = new GeoPoint((int) (30.659259 * 1000000), (int) (104.065762 * 1000000));//构建一个GeoPoint来表示地点的经度和纬度,此设置起点为成都
mMapController.animateTo(mGeoPoint); //定位到成都,通过animateTo将地图定位到指定的GeoPoint上
mMapController.setZoom(12); //设置倍数(1-21)
//添加Overlay,用于显示标注信息。
MyLocationOverlay myLocationOverlay = new MyLocationOverlay();
List<Overlay> list = mMapView.getOverlays();
list.add(myLocationOverlay);
}
protected boolean isRouteDisplayed(){
return false;
}
class MyLocationOverlay extends Overlay{//如果需要在地图上标注一下图标文字等信息,可以使用Overlay。
首先要将地图上的经度和纬度转换成屏幕上实际的坐标,才能将信息绘制上去。
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when){
super.draw(canvas, mapView, shadow);//首先需要实现Overlay中的draw方法才能在地图上绘制信息
Paint paint = new Paint();
Point myScreenCoords = new Point();
mapView.getProjection().toPixels(mGeoPoint, myScreenCoords);// 可以通过toPixels(GeoPoint in,Point out)方法,将经纬度转换成实际屏幕坐标
paint.setStrokeWidth(1);
paint.setARGB(255, 255, 0, 0);
paint.setStyle(Paint.Style.STROKE);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.home);
canvas.drawBitmap(bmp, myScreenCoords.x, myScreenCoords.y, paint);
canvas.drawText("天府广场", myScreenCoords.x, myScreenCoords.y, paint);
return true;
}
}
}
第二例 实现了定位系统
全球定位系统(Global Positioning System,GPS)又称为全球卫星定位系统,是一个中距离圆型轨道卫星导航系统,它可以为地球表面的绝大部分地区(98%)提供准确的定位、
测速和高精度的时间标准。
Android中关于地理定位系统的API全部位于android.location包中。包括如下几个重要类:
LocationManager 此类包含了访问定位服务的功能,获取最佳定位提供者的功能,临近警报功能也可以借助该类来实现。
LocationProvider 该类是定位提供者的抽象类。定位提供者具备周期性报告设备地理位置的功能。
LocationListener 提供定位信息发生改变时的回调功能。必须先在定位管理器中注册监听器对象。
Criteria 该类使得应用能够通过在LocationProvider中设置的属性来选择合适的定位提供者。
Geocoder 用于处理地理编码和反向地理编码的类。地理编码是指将地址或其他描述转变为经度和纬度,反向地理编码则是将经度和纬度转变为地址或描述语言,其中包含
了两个构造函数,需要传入经度和纬度的坐标。getFromLoction方法可以得到一组关于地址的数组。
此例实现了自动通过定位系统获取用户当前的坐标,然后加载并显示地图,将坐标信息显示在一个TextView
main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/TextView01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<com.google.android.maps.MapView
android:id="@+id/MapView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:apiKey="0dYpmIXGIdwiVm-HEpzuUW2fjNYsFQ9EvYir1sg"/>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yarin.android.Examples_09_04"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<uses-library android:name="com.google.android.maps" />
<activity
android:name=".Activity01"
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>
<uses-permission android:name="android.permission.INTERNET"/>//要使用API 需添加下例权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-sdk android:minSdkVersion="5" />
</manifest>
public class Activity01 extends MapActivity {
public MapController mapController;
public MyLocationOverlay myPosition;
public MapView myMapView;
private static final int ZOOM_IN=Menu.FIRST;
private static final int ZOOM_OUT=Menu.FIRST+1;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LocationManager locationManager;//取得LocationManager位置管理器实例
String context=Context.LOCATION_SERVICE;
locationManager=(LocationManager)getSystemService(context);//要使用地理定位,首先需要取得LocationManager的实例,可以通过getSystemService()方法获得。
通过此实例我们可以获得一个位置提供者的列表。在一个真实的手持设备中,这个列表包含了一些GPS服务。
当然我们也可以得到更强大、更精确、不带有其他附件服务的GPS 代码如:
LocationManager locationManager = (LocationManager)getSystemService(Context.LOC-ATION_SERVICE);
myMapView=(MapView)findViewById(R.id.MapView01);
mapController=myMapView.getController();//取得MapController实例,控制地图
//设置显示模式
myMapView.setSatellite(true);
myMapView.setStreetView(true);
myMapView.displayZoomControls(false); //设置缩放控制,这里我们自己实现缩放菜单
//设置使用MyLocationOverlay来绘图
mapController.setZoom(17);
myPosition=new MyLocationOverlay();
List<Overlay> overlays=myMapView.getOverlays();
overlays.add(myPosition);
Criteria criteria =new Criteria();//设置Criteria(服务商)的信息
//经度要求
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(false);
criteria.setPowerRequirement(Criteria.POWER_LOW);
//取得效果最好的criteria
String provider=locationManager.getBestProvider(criteria, true);
//得到坐标相关的信息
Location location=locationManager.getLastKnownLocation(provider);
updateWithNewLocation(location);//更新坐标
locationManager.requestLocationUpdates(provider, 3000, 0,locationListener); //注册一个周期性的更新,第一参数设置服务提供者,第二个参数设置3000ms更新一次,
重点是第四个参数locationListener用来监听定位信息的改变 所以必须实现以下几个方法:
onLocationChanged(Location location);当坐标改变时触发此函数,如果Provider传进相同坐标,它就
不会触发。
onProviderDisabled(String provider);Provider禁用时触发此函数,比如GPS被关闭
onProviderEnabled(String provider);Provider启用时触发此函数,比如GPS被打开
onStatusChanged(String provider,int status,Bundle extras);Provider的转态在可用,暂时不可用和无服务
状态直接切换时触发此函数
}
private void updateWithNewLocation(Location location) {
String latLongString;
TextView myLocationText = (TextView)findViewById(R.id.TextView01);
String addressString="没有找到地址\n";
if(location!=null){
myPosition.setLocation(location);//为绘制标志的类设置坐标
Double geoLat=location.getLatitude()*1E6; //取得经度和纬度
Double geoLng=location.getLongitude()*1E6;
GeoPoint point=new GeoPoint(geoLat.intValue(),geoLng.intValue()); //将其转换为int型
//定位到指定坐标
mapController.animateTo(point);
double lat=location.getLatitude();
double lng=location.getLongitude();
latLongString="经度:"+lat+"\n纬度:"+lng;
double latitude=location.getLatitude();
double longitude=location.getLongitude();
Geocoder gc=new Geocoder(this,Locale.getDefault()); //更具地理环境来确定编码
try{
//取得地址相关的一些信息\经度、纬度
List<Address> addresses=gc.getFromLocation(latitude, longitude,1);//使用LocationManager和位置提供者进行getFromLocation的调用返回一个Location对象形式的快照
如果调用getFromLocationName方法可能返回一个表示一个地方名称的数据
StringBuilder sb=new StringBuilder();
if(addresses.size()>0){
Address address=addresses.get(0);
for(int i=0;i<address.getMaxAddressLineIndex();i++)
sb.append(address.getAddressLine(i)).append("\n");
sb.append(address.getLocality()).append("\n");
sb.append(address.getPostalCode()).append("\n");
sb.append(address.getCountryName());
addressString=sb.toString();
}
}catch(IOException e){}
}else{
latLongString="没有找到坐标.\n";
}
myLocationText.setText("你当前的坐标如下:\n"+latLongString+"\n"+addressString); //显示
}
private final LocationListener locationListener=new LocationListener(){
public void onLocationChanged(Location location){//当坐标改变时触发此函数
updateWithNewLocation(location);
}
public void onProviderDisabled(String provider){ //Provider被disable时触发此函数,比如GPS被关闭
updateWithNewLocation(null);
}
public void onProviderEnabled(String provider){ //Provider被enable时触发此函数,比如GPS被打开
}
public void onStatusChanged(String provider,int status,Bundle extras){ //Provider的转态在可用、暂时不可用和无服务三个状态直接切换时触发此函数
}
};
protected boolean isRouteDisplayed(){
return false;
}
public boolean onCreateOptionsMenu(Menu menu){ //为应用程序添加菜单
super.onCreateOptionsMenu(menu);
menu.add(0, ZOOM_IN, Menu.NONE, "放大");
menu.add(0, ZOOM_OUT, Menu.NONE, "缩小");
return true;
}
public boolean onOptionsItemSelected(MenuItem item){
super.onOptionsItemSelected(item);
switch (item.getItemId()){
case (ZOOM_IN):
mapController.zoomIn();//放大 通过MapController地图控制器来放大和缩小视图
return true;
case (ZOOM_OUT):
mapController.zoomOut();//缩小
return true;
}
return true;
}
class MyLocationOverlay extends Overlay{
Location mLocation;
public void setLocation(Location location){//在更新坐标时,设置该坐标,一边画图
mLocation = location;
}
public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when){
super.draw(canvas, mapView, shadow);
Paint paint = new Paint();
Point myScreenCoords = new Point();
// 将经纬度转换成实际屏幕坐标
GeoPoint tmpGeoPoint = new GeoPoint((int)(mLocation.getLatitude()*1E6),(int)(mLocation.getLongitude()*1E6));
mapView.getProjection().toPixels(tmpGeoPoint, myScreenCoords);
paint.setStrokeWidth(1);
paint.setARGB(255, 255, 0, 0);
paint.setStyle(Paint.Style.STROKE);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.home);
canvas.drawBitmap(bmp, myScreenCoords.x, myScreenCoords.y, paint);
canvas.drawText("Here am I", myScreenCoords.x, myScreenCoords.y, paint);
return true;
}
}
}
如果我们在模拟器上测试,要人为设置一个坐标。可用通过两种方法来设置一个模拟器的坐标值。
第一种方法通过DDMS,我们可以在Eclipse的ADT插件中使用这种方法。步骤:Window---Show View---Emulator Control ---然后手动或通过KML和GPX文件来设置一个坐标
第二种方法使用geo命令,需要telnet到本机的5554端口,然后输入类似于geo fix-121.45356 46.51119 4392这样的命令,后面3个参数代表了经度、纬度和(可选的)海拔。设置
后模拟器上便多出个标志。