状态模式:将状态封装成为独立类,并将动作委托到当前状态;状态之间可以相互转换,因为实现了相同的接口;状态改变,则动作会跟着改变。
理解:
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
学习一种模式,一定要自己认真的想一遍,再根据自己的想法,通过代码验证一下,当测试结果和自己想的一致时,再把那种感觉记录下来,以便日后其他项目用作参考。 谢谢阅读~