您的位置:首页 > 其它

界面统一管理、观察者设计模式

2014-10-08 14:25 477 查看
界面统一管理

利用一个Activity去管理应用中所有的界面——速度快。

界面管理实现:

1)抽取标题管理

2)抽取底部导航管理

3)抽取中间内容部分管理,建立内容部分切换机制

4)完善用户提示机制

标题、底部导航等变化不多的部分分别写布局文件,然后在Activity引入的布局文件中用include方法包括这些布局文件。中间部分等变化多的先用一个相对布局占着位置。然后再写各自的管理者。标题管理者和底部导航管理者思路一致,主要功能描述:

①管理对象的创建(用单例模式)

②初始化各个标题容器及相关控件设置监听

③控制各个标题容器的显示和隐藏

④控制标题内容显示

如:

<span style="font-size:18px;">/**
* 标题容器的管理者,实现Observer接口是作为观察者
*
* @author HP1
*
*/
public class TitleManager implements Observer{
// ①管理对象的创建(用单例模式)
private static TitleManager instance = new TitleManager();
private TitleManager(){
}

public static TitleManager getInstance() {
return instance;
}

private RelativeLayout commonContainer;
private RelativeLayout unLoginContainer;
private RelativeLayout loginContainer;

private ImageView goback;// 返回
private ImageView help;// 帮助
private ImageView login;// 登录

private TextView titleContent;// 标题内容
private TextView userInfo;// 用户信息

public void init(Activity activity){// ②初始化各个标题容器及相关控件设置监听
commonContainer = (RelativeLayout) activity.findViewById(R.id.ii_common_container);
unLoginContainer = (RelativeLayout) activity.findViewById(R.id.ii_unlogin_title);
loginContainer = (RelativeLayout) activity.findViewById(R.id.ii_login_title);

goback = (ImageView) activity.findViewById(R.id.ii_title_goback);
help = (ImageView) activity.findViewById(R.id.ii_title_help);
login = (ImageView) activity.findViewById(R.id.ii_title_login);

titleContent = (TextView) activity.findViewById(R.id.ii_title_content);
userInfo = (TextView) activity.findViewById(R.id.ii_top_user_info);

setListener();
}

private void setListener() {
goback.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
System.out.println("返回键");

}
});
help.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
System.out.println("help");

}
});
login.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
System.out.println("login");
MiddleManager.getInstance().changeUI(SecondUI.class);
}
});
}
<span style="white-space:pre">	</span>// ③控制各个标题容器的显示和隐藏
private void initTitle(){
commonContainer.setVisibility(View.GONE);
unLoginContainer.setVisibility(View.GONE);
loginContainer.setVisibility(View.GONE);
}

/**
* 显示通用标题
*/
public void showCommonTitle() {
initTitle();
commonContainer.setVisibility(View.VISIBLE);
}

/**
* 显示未登录标题
*/
public void showUnloginTitle() {
initTitle();
unLoginContainer.setVisibility(View.VISIBLE);
}

/**
* 显示已登陆标题
*/
public void showLoginTitle() {
initTitle();
loginContainer.setVisibility(View.VISIBLE);
}
// ④控制标题内容显示
public void changeTitle(String title){
titleContent.setText(title);
}

/**
* 收到被观察者的通知时调用此方法,data是被观察者传递的信息,这里是界面的id值
*/
@Override
public void update(Observable observable, Object data) {
if(data != null){
Integer id = Integer.valueOf(data.toString());
switch (id) {
case ConstantValue.FIRSTUI:
showCommonTitle();
break;
case ConstantValue.SECONDUI:
showUnloginTitle();
break;
}
}
}
}</span>


由于该类中有findViewById的初始化操作,所以,在Activity的onCreate方法中必须调用上面的init()方法。如:

<span style="font-size:18px;">TitleManager tm = TitleManager.getInstance();
tm.init(this);
tm.showUnloginTitle();</span>


界面切换

界面切换的核心方法是ViewGroup的addView和removeView方法。

首先为中间部分的界面写一个抽象的基类BaseUI,并在类中写获得需要在中间容器中加载的内容的方法。中间部分的界面都继承该基类。

/**
* 所有界面的基类
*
* @author HP1
*
*/
public abstract class BaseUI implements OnClickListener {

protected Context context;
/**
* 显示在中间部分的布局
*/
protected ViewGroup view;

public BaseUI(Context context) {
super();
this.context = context;
init();
setListener();
}

/**
* 初始化布局
*/
public abstract void init();

/**
* 设置按钮的监听
*/
public abstract void setListener();

/**
* 默认的空白监听
*/
@Override
public void onClick(View v) {

}

/**
* 获取需要在中间部分加载的内容
* @return
*/
public View getChild(){
// 设置layout参数
if(view.getLayoutParams() == null){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, -1);
view.setLayoutParams(params);
}
return view;
}

/**
* 各界面的id(自定义的常量)
* @return
*/
public abstract int getID();
}


中间显示内容例子:

之前在类中用View.inflate(context, resource, root);使布局文件转换成View对象时,都是设置root为null,这次设置为null时,显示效果有问题,产生原因是:

root为null时,view.getLayoutParams()为null,源码中布局参数为空时,没有设置view的宽高信息,所以这里需要我们自己设置view的宽高信息。

之前在Listview中设置root为null,却没有出现显示效果的问题,是因为Listview的源码中给自己的子孩子都设置了宽高信息。

/**
* 购彩大厅
*
* @author HP1
*
*/
public class Hall extends BaseUI implements OnClickListener {

private TextView ssq_summary;
private ImageView ssq_bet;

public Hall(Context context) {
super(context);
init();
setListener();
}

@Override
public void init() {
view = (LinearLayout) View.inflate(context, R.layout.il_hall1, null);

ssq_summary = (TextView) view.findViewById(R.id.ii_hall_ssq_summary);
ssq_bet = (ImageView) view.findViewById(R.id.ii_hall_ssq_bet);
}

@Override
public void setListener() {
ssq_summary.setOnClickListener(this);
ssq_bet.setOnClickListener(this);
}

@Override
public void onClick(View v) {
super.onClick(v);
}

@Override
public int getID() {
return ConstantValue.HALL;
}
}


在中间部分管理者中写changeUI()的方法。在该方法中用removeView()(或removeAllViews())和addView()方法切换界面。

<span style="font-size:18px;">/**
* 中间容器的管理者,继承了Observable,作为被观察者
* @author HP1
*
*/
public class MiddleManager extends Observable{
// 单例模式
private static MiddleManager instance = new MiddleManager();
public void MiddleManager(){}

public static MiddleManager getInstance() {
return instance;
}

private RelativeLayout ui_middle;

public void setUi_middle(RelativeLayout ui_middle) {
this.ui_middle = ui_middle;
}

public Context getContext(){
return ui_middle.getContext();
}

/**
* 用来存储创建过的界面。用手机内存,换应用的运行速度
* key是界面的简单名称,value是界面
*/
private Map<String,BaseUI> VIEWCACHE = new HashMap<String,BaseUI>();

private BaseUI currentUI;

/**
* 用户操作的历史记录,类似于任务栈
*/
private LinkedList<String> HISTORY = new LinkedList<String>();

/**
* 切换界面的核心方法
* @param targetClazz 参数是目标界面的字节码,可以避免每次切换界面时都创建一个目标界面
*/
public void changeUI(Class<? extends BaseUI> targetClazz) {

// 判断当前界面和目标界面是否为同一个界面,若为同一个界面,直接return
if(currentUI != null && currentUI.getClass() == targetClazz){
return;
}

BaseUI targetUI = null;
String key = targetClazz.getSimpleName();
// 判断目标界面是否已经创建过--已经创建过的界面需要用map存储起来
if(VIEWCACHE.containsKey(key)){
// 若已经创建过,则重用
targetUI = VIEWCACHE.get(key);
}else{
try {
// 没有创建过,则创建
// 根据界面的字节码获得有参的构造器
Constructor<? extends BaseUI> constructor = targetClazz.getConstructor(Context.class);
targetUI = constructor.newInstance(getContext());
VIEWCACHE.put(key, targetUI);
} catch (Exception e) {
// 由于下面代码要用到targetUI,若出异常会崩溃,所以,这里需要抛出异常。
throw new RuntimeException("构造器创建目标界面实例时出错!");
}
}

ui_middle.removeAllViews();
View child = targetUI.getChild();
ui_middle.addView(child);
currentUI = targetUI;
// 将当前显示的界面放到栈顶
HISTORY.addFirst(key);
// 执行切换动画
Animation animation = AnimationUtils.loadAnimation(getContext(),R.anim.ua_view_change);
child.startAnimation(animation);

// 当中间容器切换成功时,通知另外两个容器变化
notifyTitleAndBottom();
}

/**
* 三个容器联动
* 通知所有的观察者,被观察者切换到了id为currentUI.getID()的界面
*/
private void notifyTitleAndBottom() {
setChanged();
notifyObservers(currentUI.getID());
}

public boolean goBack() {
if(HISTORY.size() > 1){
// 移除栈顶元素
HISTORY.removeFirst();
if(HISTORY.size() > 0){
// 得到新的栈顶元素
String key = HISTORY.getFirst();
// 获得对应栈顶元素的界面并展示
BaseUI targetUI = VIEWCACHE.get(key);
ui_middle.removeAllViews();
View child = targetUI.getChild();
ui_middle.addView(child);
currentUI = targetUI;
notifyTitleAndBottom();
return true;
}
}
return false;
}
}</span>


然后在Activity中findViewById找到中间部分占着位置的相对布局,并把该布局传递给管理者。

<span style="font-size:18px;">ui_middle = (RelativeLayout) findViewById(R.id.ui_middle);
MiddleManager.getInstance().setUi_middle(ui_middle);</span>


观察者设计模式

①被观察者继承Observable,观察者实现Observer接口。

②Activity初始化时建立观察者和被观察者之间的关系。

<span style="font-size:18px;">MiddleManager.getInstance().addObserver(TitleManager.getInstance());
MiddleManager.getInstance().addObserver(BottomManager.getInstrance());</span>


③当观察者变化时通知被观察者。被观察者做出相应的响应。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: