近期任务框(就是近期打开过的应用)其实也就是一个系统级别的对话框,就是长按手机的HOME键弹出的视图。
源码中的路径为:D:\tools\android4.0.1\frameworks\base\policy\src\com\android\internal\policy\impl\RecentApplicationsDialog.java
1,显示方式,该对话框在PhoneWindowManager (路径和RecentApplicationsDialog.java同样)类中的showRecentAppsDialog方法
/**
* Create (if necessary) and launch the recent apps dialog, or hide it if it is
* already shown.
*/
void showOrHideRecentAppsDialog(final int heldModifiers, final boolean dismissIfShown) {
mHandler.post(new Runnable() {
@Override
public void run() {
if (mRecentAppsDialog == null) {
mRecentAppsDialog = new RecentApplicationsDialog(mContext);
}
if (mRecentAppsDialog.isShowing()) {
if (dismissIfShown) {
mRecentAppsDialog.dismiss();
}
} else {
mRecentAppsDialog.setHeldModifiers(heldModifiers);
mRecentAppsDialog.show();
}
}
});
}
2,创建对话框时配置一些参数:比如填充父窗体,没有标题,更重要的一点就是设成系统级别的对话框。
/**
* 我们创建最近的应用程序对话框只是一次,它会留下(隐藏),直到用户激活。
*
* @see 调用显示是在这个类里(PhoneWindowManager#showRecentAppsDialog)
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getContext();
if (sStatusBar == null) {
sStatusBar = (StatusBarManager) context
.getSystemService(Context.STATUS_BAR_SERVICE);
}
//获取窗体,注意:window是整个手机屏幕的窗体,而不仅仅但是对话框的窗体
Window window = getWindow();
//设置无标题
window.requestFeature(Window.FEATURE_NO_TITLE);
//将该对话框设置成系统级别对话框
window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
window.setTitle("Recents");
setContentView(com.android.internal.R.layout.recent_apps_dialog);
//设置对话框的参数:宽、高为填充父窗体
final WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(params);
window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
mIcons[0] = (TextView) findViewById(com.android.internal.R.id.button0);
mIcons[1] = (TextView) findViewById(com.android.internal.R.id.button1);
mIcons[2] = (TextView) findViewById(com.android.internal.R.id.button2);
mIcons[3] = (TextView) findViewById(com.android.internal.R.id.button3);
mIcons[4] = (TextView) findViewById(com.android.internal.R.id.button4);
mIcons[5] = (TextView) findViewById(com.android.internal.R.id.button5);
mIcons[6] = (TextView) findViewById(com.android.internal.R.id.button6);
mIcons[7] = (TextView) findViewById(com.android.internal.R.id.button7);
mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
//为每个TextView设置点击事件
for (TextView b : mIcons) {
b.setOnClickListener(this);
}
}
3,为八个按钮载入最近的活动,如果最近打过的应用任务栈中超过8个也只能取八个
/**
* 为八个按钮载入最近的活动,如果最近打过的应用任务栈中超过8个也只能取八个,
*/
private void reloadButtons() {
final Context context = getContext();
final PackageManager pm = context.getPackageManager();
final ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
//这里是获取近期打开过的应用的RecentTaskInfo
final List<ActivityManager.RecentTaskInfo> recentTasks = am
.getRecentTasks(MAX_RECENT_TASKS,
ActivityManager.RECENT_IGNORE_UNAVAILABLE);
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(
Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
IconUtilities iconUtilities = new IconUtilities(getContext());
// 性能注意:android性能指南推荐迭代器来遍历集合,因为知道getRecentTasks()总是返回一个ArrayList
// < >,我们将使用一个简单的指数相反。
int index = 0;
int numTasks = recentTasks.size();
for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
// 目的用于调试,但不允许第一个结果创建一个空的列表
if (DBG_FORCE_EMPTY_LIST && (i == 0))
continue;
Intent intent = new Intent(info.baseIntent);
if (info.origActivity != null) {
intent.setComponent(info.origActivity);
}
// 跳过当前Launcher数据,就是不把当前launcher作为近期打开过的任务。
if (homeInfo != null) {
if (homeInfo.packageName.equals(intent.getComponent()
.getPackageName())
&& homeInfo.name.equals(intent.getComponent()
.getClassName())) {
continue;
}
}
//下面做的其实就是封装一些数据给每一个对应的TextView
intent.setFlags((intent.getFlags() & ~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
| Intent.FLAG_ACTIVITY_NEW_TASK);
final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
if (resolveInfo != null) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
final String title = activityInfo.loadLabel(pm).toString();
Drawable icon = activityInfo.loadIcon(pm);
if (title != null && title.length() > 0 && icon != null) {
final TextView tv = mIcons[index];
tv.setText(title);
icon = iconUtilities.createIconDrawable(icon);
tv.setCompoundDrawables(null, icon, null, null);
RecentTag tag = new RecentTag();
tag.info = info;
tag.intent = intent;
//关于setTag这个是view中集成的一个神奇的东西,后面我会专门表述一下我的见解
tv.setTag(tag);
tv.setVisibility(View.VISIBLE);
tv.setPressed(false);
tv.clearFocus();
++index;
}
}
}
// 处理没有图标的
mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);
// 隐藏其他的图标
for (; index < NUM_BUTTONS; ++index) {
mIcons[index].setVisibility(View.GONE);
}
}
4,设置和显示最近的活动对话框。
/**
* 设置和显示最近的活动对话框。
*/
@Override
public void onStart() {
super.onStart();
reloadButtons();
//禁止状态栏
if (sStatusBar != null) {
sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
}
// 注册接收广播
getContext().registerReceiver(mBroadcastReceiver,
mBroadcastIntentFilter);
mHandler.removeCallbacks(mCleanup);
}
5,处理手指按下的动作
/**
* 处理手指按下时的程序
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
6,处理手指离开的动作
/**
* 处理手指抬起时的程序
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
7,处理用户单击事件
/**
* 用户单击处理程序。如果一个按钮被单击时,启动相应的活动。
*/
@Override
public void onClick(View v) {
for (TextView b : mIcons) {
if (b == v) {
RecentTag tag = (RecentTag) b.getTag();
switchTo(tag);
break;
}
}
dismiss();
}
8,切换到所点击的应用
/**
* 切换到所点击的应用
*
* @param tag
*/
private void switchTo(RecentTag tag) {
if (tag.info.id >= 0) {
// 这是一个活跃的任务,所以把它移动到最近任务的前面
final ActivityManager am = (ActivityManager) getContext()
.getSystemService(Context.ACTIVITY_SERVICE);
am.moveTaskToFront(tag.info.id, ActivityManager.MOVE_TASK_WITH_HOME);
} else if (tag.intent != null) {
tag.intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME);
try {
getContext().startActivity(tag.intent);
} catch (ActivityNotFoundException e) {
Log.w("Recent", "Unable to launch recent task", e);
}
}
}
9,处理接收关闭对话框的广播
/**
* 这是监听关闭系统对话框意图的广播。收到广播后立即关闭自己,为了允许高优先级的UI来接管(如电话收到了)。
*/
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
String reason = intent
.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
if (!PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS
.equals(reason)) {
dismiss();
}
}
}
};
如果有需要从别的应用中关闭这个对话框,那么向系统发送一个广播即可:
Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
this.sendBroadcast(i);