10.6.3. Service开关
10.6.3.Service开关
在这个地方分别提供开/关两个按钮,不如只留一个按钮做开关。因此需要修改菜单,并为BaseActivity添加一个onMenuOpened()方法来动态控制按钮文本以及图标的变化。
首先修改munu.xml文件,添加新的菜单项也就是开关按钮。这时原先启动/关闭Service的两个按钮已经没必要了,删除即可。
例 10.10. res/menu/menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/itemStatus" android:title="@string/titleStatus"
android:icon="@android:drawable/ic_menu_edit"></item>
<item android:title="@string/titleTimeline" android:id="@+id/itemTimeline"
android:icon="@android:drawable/ic_menu_sort_by_size"></item>
<item android:id="@+id/itemPrefs" android:title="@string/titlePrefs"
android:icon="@android:drawable/ic_menu_preferences"></item>
<item android:icon="@android:drawable/ic_menu_delete"
android:title="@string/titlePurge" android:id="@+id/itemPurge"></item>
<!-- -->
<item android:id="@+id/itemToggleService" android:title="@string/titleServiceStart"
android:icon="@android:drawable/ic_media_play"></item>
</menu>
- 将原先的itemServiceStart与itemServiceStop替换为itemToggleService。
例 10.11. BaseActivity.java
package com.marakana.yamba5;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
/**
* The base activity with common features shared by TimelineActivity and
* StatusActivity
*/
public class BaseActivity extends Activity { //
YambaApplication yamba; //
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
yamba = (YambaApplication) getApplication(); //
}
// Called only once first time menu is clicked on
@Override
public boolean onCreateOptionsMenu(Menu menu) { //
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
// Called every time user clicks on a menu item
@Override
public boolean onOptionsItemSelected(MenuItem item) { //
switch (item.getItemId()) {
case R.id.itemPrefs:
startActivity(new Intent(this, PrefsActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
break;
case R.id.itemToggleService:
if (yamba.isServiceRunning()) {
stopService(new Intent(this, UpdaterService.class));
} else {
startService(new Intent(this, UpdaterService.class));
}
break;
case R.id.itemPurge:
((YambaApplication) getApplication()).getStatusData().delete();
Toast.makeText(this, R.string.msgAllDataPurged, Toast.LENGTH_LONG).show();
break;
case R.id.itemTimeline:
startActivity(new Intent(this, TimelineActivity.class).addFlags(
Intent.FLAG_ACTIVITY_SINGLE_TOP).addFlags(
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
break;
case R.id.itemStatus:
startActivity(new Intent(this, StatusActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
break;
}
return true;
}
// Called every time menu is opened
@Override
public boolean onMenuOpened(int featureId, Menu menu) { //
MenuItem toggleItem = menu.findItem(R.id.itemToggleService); //
if (yamba.isServiceRunning()) { //
toggleItem.setTitle(R.string.titleServiceStop);
toggleItem.setIcon(android.R.drawable.ic_media_pause);
} else { //
toggleItem.setTitle(R.string.titleServiceStart);
toggleItem.setIcon(android.R.drawable.ic_media_play);
}
return true;
}
}
- BaseActivity是个Activity。
- 声明一个共享的YambaApplication对象,使之可为所有子类访问。
- 在onCreate()中获得yamba的引用。
- 将StatusActivity的onCreateOptionsMenu挪到了这里。
- onOptionsItemSelected也是同样来自StatusActivity。不过留意下这里的变动:它不再检查启动/停止Service的两个条目,改为检查itemToggleService一个条目。通过yamba中的标志变量我们可以得到Service的运行状态,基于此,决定启动还是关闭。
- onMenuOpened()是个新加入的方法,它在菜单打开时为系统所调用,参数menu即我们的选项菜单。我们可以在这里控制开关的显示。
- 在menu对象中找到我们新建的开关条目。
- 检查Service是否正在运行,如果是,则设置对应的标题与图标。留意我们在这里是通过Java的API手工地修改GUI的内容,而无关xml。
- 如果Service没有运行,则设置对应的标题与图标,用户点击它即可启动Service。这样我们就实现了开关按钮随Service状态的不同而变化。
好,已经有了BaseActivity类,接下来修改TimelineActivity也使用它。如下为TimelineActivity的完整实现:
例 10.12. TimelineActivity.java, final version
package com.marakana.yamba5;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.SimpleCursorAdapter.ViewBinder;
public class TimelineActivity extends BaseActivity { //
Cursor cursor;
ListView listTimeline;
SimpleCursorAdapter adapter;
static final String[] FROM = { DbHelper.C_CREATED_AT, DbHelper.C_USER,
DbHelper.C_TEXT };
static final int[] TO = { R.id.textCreatedAt, R.id.textUser, R.id.textText };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timeline);
// Check if preferences have been set
if (yamba.getPrefs().getString("username", null) == null) { //
startActivity(new Intent(this, PrefsActivity.class));
Toast.makeText(this, R.string.msgSetupPrefs, Toast.LENGTH_LONG).show();
}
// Find your views
listTimeline = (ListView) findViewById(R.id.listTimeline);
}
@Override
protected void onResume() {
super.onResume();
// Setup List
this.setupList(); //
}
@Override
public void onDestroy() {
super.onDestroy();
// Close the database
yamba.getStatusData().close(); //
}
// Responsible for fetching data and setting up the list and the adapter
private void setupList() { //
// Get the data
cursor = yamba.getStatusData().getStatusUpdates();
startManagingCursor(cursor);
// Setup Adapter
adapter = new SimpleCursorAdapter(this, R.layout.row, cursor, FROM, TO);
adapter.setViewBinder(VIEW_BINDER); //
listTimeline.setAdapter(adapter);
}
// View binder constant to inject business logic for timestamp to relative
// time conversion
static final ViewBinder VIEW_BINDER = new ViewBinder() { //
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if (view.getId() != R.id.textCreatedAt)
return false;
// Update the created at text to relative time
long timestamp = cursor.getLong(columnIndex);
CharSequence relTime = DateUtils.getRelativeTimeSpanString(view
.getContext(), timestamp);
((TextView) view).setText(relTime);
return true;
}
};
}
- 首先将基类由系统提供的Activity改为我们的BaseActivity。由此也就继承来了yamba对象,以及选项菜单的相关支持。
- 在这里检查用户的个人选项是否存在,若不存在,则切换到选项界面。
- 在界面显示时,初始化消息列表。这是个私有方法,定义在后面。
- 我们希望在界面关闭时,关闭数据库并释放资源。数据库是在yamba对象中的getStatusUpdates()方法中打开的。
- 辅助方法setupList()用以获取数据、设置Adapter并将其绑定于ListView。
- 在这里将ViewBinder绑定于List。参见"ViewBinder:TimelineAdapter之外的更好选择"一节。
- ViewBinder的定义。
到这里,我们已经将TimelineActivity重构的差不多了。按照如上的步骤,我们可以进一步简化StatusActivity,将选项菜单相关的代码清掉,从而使得BaseActivity、StatusDate、TimelineActivity的职责更加分明。
图10.3 "TimelineActivity"展示了Timeline界面的成品图。
图10.3. TimelineActivity