Android中设计模式--状态模式(将动作委托到当前状态,状态之间可以互相转换)

   状态模式:将状态封装成为独立类,并将动作委托到当前状态;状态之间可以相互转换,因为实现了相同的接口;状态改变,则动作会跟着改变。


 理解

1.定义状态接口,所有的状态均实现该接口,这样对于客户(调用者)来说,状态是可以替换的,客户不关心具体的状态是什么,只是调用接口中的方法即可。

 2.状态之间可以互相转换,如:去自动售货机买饮料,先投入硬币,那么这时,售货机会将状态从“请投币”,转换为“请选择饮料”,这就是状态的转换。


 总结

 找出代码中整体逻辑相同,执行动作略有差异的地方,考虑用状态模式来实现。定义统一的接口,不同的状态实现相同的接口,接口方法中每种状态有自己的特色,状态之间可以相互影响和转换。


 状态模式图如下:



例子

1.先定义接口,用于所有的状态来实现该接口

public interface State {
	void doOK(); // 处理确定键
	void doBack(); // 处理返回键
	void doLeft(); // 处理左键
	void doRight(); // 处理右键
	MAbsoluteLayout getLayout(); //获取布局
	boolean showLeftIcon(); // 左侧icon是否显示
	boolean showRightIcon(); // 右侧icon是否显示
	void animationEndCallBack(); // 动画结束回调(用于光纤、耳机口、亮点动画效果显示)
	boolean dispatchKeyEvent(KeyEvent event);  // 按键处理

}

2.根据需要,定义不同的状态类,但都要实现统一的接口

/**
 * 选择页,实现State接口
 *
 */
public class SelectView implements State {
	private MAbsoluteLayout mLayout;
	private Context mContext;
	private SelectAdapter mAdapter;
	private MListView mViewList;
	private changeViewCallBack mCallBack; // view切换回调

	@Override
	public void doOK() {
		return;
	}

	@Override
	public void doBack() {
		StaticFunction.getPageHelper().finishPage(KEY_PAGEID.PAGE_HOME);
		
	}

	@Override
	public void doLeft() {
		return;
	}

	@Override
	public void doRight() {
		return;
	}

	
	public SelectView(changeViewCallBack cb, Context context){
		mCallBack = cb;
		mContext = context;		
		initView();		
	}
	
	void initView(){
		View rootView = LayoutInflater.from(mContext).inflate(R.layout.view_select, null);
		mViewList = (MListView)rootView.findViewById(R.id.activity_home_list);
        mAdapter = new SelectAdapter(mCallBack, mContext);
		
		mViewList.setAdapter(mAdapter);
		mViewList.setFocusView(new FocusView(mContext));
		mViewList.setMFocus(true);
		if(null != mViewList.getTopCover()){
			mViewList.getTopCover().setVisibility(View.GONE);
		}
		mLayout = (MAbsoluteLayout)rootView.findViewById(R.id.view_select);
	}
	
	@Override
	public boolean dispatchKeyEvent(KeyEvent event){
		if(KeyEvent.ACTION_DOWN == event.getAction() && KeyCode.BACK == KeyCode.getKeyCode(event)){
			this.doBack();
			return true;
		}
		if (null != mViewList ) {
			mViewList.dispatchKeyEvent(event);
			return true;
		}
		return false;

	}

	@Override
	public MAbsoluteLayout getLayout() {
		return mLayout;
	}

	@Override
	public boolean showLeftIcon() {
		return false;
	}

	@Override
	public boolean showRightIcon() {
		return true;
	}

	@Override
	public void animationEndCallBack() {
		// TODO Auto-generated method stub
		
	}

}
3. 状态之间可以相互替换,因为都实现了接口,所以不需要关心具体状态,直接调用方法。这点和策略模式非常类似。 策略模式请参考我的上一篇文章http://blog.csdn.net/adayabetter/article/details/45580841

与策略模式不同之处在于,状态模式执行了相应动作后,可以改变当前状态

举个例子,进行状态动作调用和状态切换的应该由Activity负责,而具体状态实现应该是View实现,代码如下:

public class GuideHomeActivity extends BaseActivity implements changeViewCallBack{
	private static final String TAG = "GuideHomeActivity";
	private State mNowState;
	private State mBeforeState;
	private MAbsoluteLayout mLayout;
	private MImageView mLeftIcon;
	private MImageView mRightIcon;
	private boolean canTranslate = true; // 是否可以移动
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		LogHelper.releaseLog(TAG, "onCreate");
		setContentView(R.layout.activity_home);				
		mLayout   = getViewById(R.id.container);
		mLeftIcon = getViewById(R.id.left_icon);
		mRightIcon = getViewById(R.id.right_icon);
		SelectView mView = new SelectView(this, getApplicationContext());
		addPage(mView);
		
	}
	
	@Override
	public boolean dispatchKeyEvent(KeyEvent event) {
		if(canTranslate == true){
			super.dispatchKeyEvent(event);			
			if (KeyEvent.ACTION_DOWN == event.getAction()) {
				if(null != mNowState && mNowState.dispatchKeyEvent(event)){
					return true;
				}
				switch (KeyCode.getKeyCode(event)) {
				case KeyCode.BACK:
					LogHelper.releaseLog(TAG, "dispatchKeyEvent--"+mNowState.getClass().getSimpleName()+"--"+"doBack()");
					mNowState.doBack();
					
					break;
				case KeyCode.LEFT:
					LogHelper.releaseLog(TAG, "dispatchKeyEvent--"+mNowState.getClass().getSimpleName()+"--"+"doLeft()");
					mNowState.doLeft();
					break;
				case KeyCode.RIGHT:
					LogHelper.releaseLog(TAG, "dispatchKeyEvent--"+mNowState.getClass().getSimpleName()+"--"+"doRight()");
					mNowState.doRight();
					break;
				case KeyCode.OK:
					LogHelper.releaseLog(TAG, "dispatchKeyEvent--"+mNowState.getClass().getSimpleName()+"--"+"doOK()");
					mNowState.doOK();
					break;
				default:
					return false;
					
				}
				return true;
			}
			return false;
		}
		return true;
	}
	
	
	
	/**
	 * 设置左右箭头显示隐藏
	 * @param showleft true: 显示,false:隐藏
	 * @param showright
	 */
	public void setLeftRightIcon(boolean showleft, boolean showright){
		if(null != mLeftIcon){ // 左icon
			if(showleft){
				mLeftIcon.setVisibility(View.VISIBLE);
			}
			else{
				mLeftIcon.setVisibility(View.INVISIBLE);
			}
		}
		if(null != mRightIcon){ // 右icon
			if(showright){
				mRightIcon.setVisibility(View.VISIBLE);
			}
			else{
				mRightIcon.setVisibility(View.INVISIBLE);
			}
		}
	}
	
	public void addPage(State state){
		if(null != mPageList && null != state ){   
			if(mPageList.size() > 0){
				mBeforeState = mPageList.get(mPageList.size() - 1);
			}
			mPageList.add(state);
			mNowState = state;
			if(null != mBeforeState){
				animationEnter(mBeforeState.getLayout(), mNowState.getLayout());
			}else{
				mLayout.addView(mNowState.getLayout());
			}
			setLeftRightIcon(mNowState.showLeftIcon(), mNowState.showRightIcon());
		}
	}
	
	public void finishPage(){
		if(null != mPageList && mPageList.size() > 0){
			if(mPageList.size() >1){
				mBeforeState = mPageList.remove(mPageList.size() - 1);
			}
			mNowState = mPageList.get(mPageList.size() - 1);
			if(null != mBeforeState){
				animationBack(mBeforeState.getLayout(), mNowState.getLayout());
			}
			setLeftRightIcon(mNowState.showLeftIcon(), mNowState.showRightIcon());
		}
	}
	
	private List<State> mPageList = new ArrayList<State>(); 
	/**
	 * 进入动画,左移
	 * @param layoutNow 当前layout
	 * @param layoutRight 右方layout 
	 * 窗口不变,移动mLayout
	 */
	public void animationEnter(MAbsoluteLayout layoutNow,MAbsoluteLayout layoutRight){
		int transDistance = 0;
		if(null != mLayout){
			clearView(layoutNow, layoutRight);
			AbsoluteLayout.LayoutParams nowParams  = (LayoutParams) layoutNow.getMLayoutParams();
			nowParams.x += 1920;
			transDistance = - nowParams.x;
			if(null == layoutRight.getParent() || mLayout != layoutRight.getParent()){
				mLayout.addIView(layoutRight,nowParams);
			}
		}
		ViewPropertyAnimator.animate(mLayout).translationX(transDistance).setListener(new AnimatorListener() {
			
			@Override
			public void onAnimationStart(Animator arg0) {
				canTranslate = false;
			}
			
			@Override
			public void onAnimationRepeat(Animator arg0) {
				
			}
			
			@Override
			public void onAnimationEnd(Animator arg0) {
				canTranslate = true;
				if(null != mNowState){
					mNowState.animationEndCallBack();
				}
			}
			
			@Override
			public void onAnimationCancel(Animator arg0) {
				canTranslate = true;
				
			}
		}).setDuration(500).start();
		
	}
	/**
	 * 退出动画,右移
	 * @param layoutNow 当前layout
	 * @param layoutRight 左方layout
	 * 窗口不变,移动mLayout
	 */
	public void animationBack(MAbsoluteLayout layoutNow, MAbsoluteLayout layoutLeft){
		int transDistance = 0;
		if(null != mLayout){
			clearView(layoutNow,layoutLeft);
			AbsoluteLayout.LayoutParams nowParams  = (LayoutParams) layoutNow.getMLayoutParams();
			nowParams.x -= 1920;
			transDistance = - nowParams.x;
			
			if(null == layoutLeft.getParent() || mLayout != layoutLeft.getParent()){
				mLayout.addIView(layoutLeft,nowParams);
			}
		}
		ViewPropertyAnimator.animate(mLayout).translationX(transDistance).setListener(new AnimatorListener() {
			
			@Override
			public void onAnimationStart(Animator arg0) {
				canTranslate = false;
			}
			
			@Override
			public void onAnimationRepeat(Animator arg0) {
				
			}
			
			@Override
			public void onAnimationEnd(Animator arg0) {
				canTranslate = true;
			}
			
			@Override
			public void onAnimationCancel(Animator arg0) {
				canTranslate = true;
			}
		}).setDuration(500).start();
	}
	

	/**
	 * 
	 * @param id 页面标识 
	 * @param duration  0:left,1:right
	 */
	@Override
	public void changeState(StateID id, int direction, KEY_MODE mode) {
		State tmpState = null;
		switch (id) {
		case SELECT_PAGE_BLUETOOTH:
			tmpState = new LightView(this, getApplicationContext(), KEY_MODE.MODE_BLUETOOTH);
			this.addPage(tmpState);	
			break;
		case SELECT_PAGE_OPTICALFIBER:
			tmpState = new SocketView(this, getApplicationContext(), KEY_MODE.MODE_OPTICALFIBER);
			this.addPage(tmpState);	
			break;
		case SELECT_PAGE_HEADSET:
			tmpState = new SocketView(this, getApplicationContext(), KEY_MODE.MODE_HEADSET);
			this.addPage(tmpState);	
			break;
		case SOCKECT_PAGE:
			if(DIRECTION.DIRECTION_RIGHT == direction){
				tmpState = new LightView(this, getApplicationContext(), mode);
				this.addPage(tmpState);
			}
			else if(DIRECTION.DIRECTION_LEFT == direction){
				this.finishPage();
			}
			
			break;
		case LIGHT_PAGE:
			if(DIRECTION.DIRECTION_RIGHT == direction){
				tmpState = new SuccessView(this, getApplicationContext(), mode);
				this.addPage(tmpState);
			}
			else if(DIRECTION.DIRECTION_LEFT == direction){
				this.finishPage();
			}
			
			break;
		case SUCCESS_PAGE:
			this.finishPage();
			break;

		default:
			break;
		}
		
	}
}

从Activity代码中可以看到,引用了State全局变量,执行动作时,直接调用State的相应方法,而State状态值是在Activity中通过changeState方法变化的,通过回调设置到不同的状态View中,View是知道何时做出改变,但具体的改变要通过回调在Activity中实现。关于回调的理解,请参考我的另一篇文章: http://blog.csdn.net/adayabetter/article/details/44116993


 学习一种模式,一定要自己认真的想一遍,再根据自己的想法,通过代码验证一下,当测试结果和自己想的一致时,再把那种感觉记录下来,以便日后其他项目用作参考。 谢谢阅读~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值