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

界面统一管理

利用一个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>

③当观察者变化时通知被观察者。被观察者做出相应的响应。

你可能感兴趣的:(界面统一管理、观察者设计模式)