android程序中最基本的组成部分是activity,一个程序有多个activity,当启动一个新的activity的时候,新的activity会出现在屏幕上覆盖旧的activity;当这个activity finish()的时候,之前的activity重新显示出来.
简单的说,这是一个类似于栈的结构,即原有activity A1:[A1]
现在新开activity A2:[A1 A2]
当A2finish的时候,再显示A1:[A1]
这符合栈的特点,即后入先出,也符合一般人的思考逻辑,及返回键返回到之前的页面.
但是这样的结构有一个局限性,这样启动的activity都是我们通过Activity.startActivity(intent)这种方式启动的,是属于全屏的.
当我们只是想在一个页面的某个部分去启动一个activity,而且还保持这种层次结构,应该如何进行呢?
在ios平台上最基本的元素是ViewController和Controller对应的View,因此ios平台上提供了一个以UIVIEW为元素的栈来时限这样的功能,称为UINavigationController.这里不讨论它的使用,有兴趣的同学可以自己去学习.这里重点说明一下如何用ActivityGroup来实现类似的功能.
package com.zz;
import java.util.Stack;
import java.util.UUID;
import android.app.ActivityGroup;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.widget.LinearLayout;
/**
* 各个Tab对应的Activity的父类
*
* @author zhouzhe@lenovo-cw.com
*/
public abstract class ActivityStack extends ActivityGroup {
/** 一个activity的栈用来存放缓存的View */
Stack<StackElement> stack;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stack = new Stack<StackElement>();
}
/**
* 一个抽象方法,返回一个LinearLayout实际上是一个区域用来展示栈内的view
*
* @return
* @author zhouzhe@lenovo-cw.com
* @time 2012-2-20
*/
abstract LinearLayout getMainView();
/**
* 用来向栈顶添加一个元素并显示
*
* @param intent
* 跟启动activity的intent一样
* @author zhouzhe@lenovo-cw.com
* @time 2012-2-20
*/
void addView(Intent intent) {
// 生成一个字符串,作为启动activity的key,后面在移除了view以后,要通过这个key来finish对应的activity
String str = UUID.randomUUID().toString();
getMainView().removeAllViews();
View view = getLocalActivityManager().startActivity(str, intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();
// 注意layoutParams,否则会造成无法充满的问题
getMainView().addView(view, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
getMainView().requestFocus();
// 生成一个栈内缓存对象,主要包括activity对应的view和对应的key,并压入栈中
StackElement component = new StackElement(str, view);
stack.add(component);
}
/**
* 弹栈操作
*
* @return
* @author zhouzhe@lenovo-cw.com
* @time 2012-2-20
*/
int pop() {
// 先判断是否已经是栈顶元素了
int size = stack.size();
StackElement element = null;
if (size > 1) {
// 如果不是,获取到栈顶元素,并销毁.然后再获取当前的栈顶元素,显示到activityGroup
element = stack.pop();
getLocalActivityManager().destroyActivity(element.getTag(), true);
element = stack.peek();
} else if (size == 1) {
// 如果是栈顶元素,直接显示
element = stack.peek();
}
getMainView().removeAllViews();
getMainView().addView(element.getView());
getMainView().requestFocus();
return stack.size();
}
/**
* 清空Stack,仅保留最底层的view
*/
void clearStack() {
int size = stack.size();
if (size > 1) {
for (int i = 0; i < size - 1; i++) {
stack.pop();
}
}
}
void clearAll() {
int size = stack.size();
if (size > 0) {
for (int i = 0; i < size; i++) {
StackElement element = stack.pop();
getLocalActivityManager().destroyActivity(element.getTag(), true);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// 对键盘事件的处理,如果当前activityGroup中已经载入了activity,则分发给子activity处理,如果没有则自己处理.
if (getCurrentActivity() != null) {
return getCurrentActivity().dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}
}
这是一个比较简单的类,简单说明一下流程.
activityGroup载入activity,A1的时候,使用addView方法,压入栈中.在加载A2的时候,还是调用activityStack的addView方法,移除A1,加入A2并压入栈中.在finish A2的时候,不能直接finish,这样整个activityGroup都被finish掉了.要调用activityStack的pop方法,移除栈顶元素,销毁,显示移除后的栈顶元素.同时要注意分发键盘的back事件,同样是要用pop方法取代默认的finish方法,这样即可完成一个类似于uinavigationcontroller的栈.
有兴趣的同学看一下吧.
源代码 http://download.youkuaiyun.com/detail/zz880329/4076318