Android中設計模式--狀態模式(將動作委託到當前狀態,狀態之間可以互相轉換)
阿新 • • 發佈:2019-01-08
狀態模式:將狀態封裝成為獨立類,並將動作委託到當前狀態;狀態之間可以相互轉換,因為實現了相同的介面;狀態改變,則動作會跟著改變。
理解:
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.根據需要,定義不同的狀態類,但都要實現統一的介面
3. 狀態之間可以相互替換,因為都實現了介面,所以不需要關心具體狀態,直接呼叫方法。這點和策略模式非常類似。策略模式請參考我的上一篇文章:http://blog.csdn.net/adayabetter/article/details/45580841/** * 選擇頁,實現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 } }
與策略模式不同之處在於,狀態模式執行了相應動作後,可以改變當前狀態。
舉個例子,進行狀態動作呼叫和狀態切換的應該由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
學習一種模式,一定要自己認真的想一遍,再根據自己的想法,通過程式碼驗證一下,當測試結果和自己想的一致時,再把那種感覺記錄下來,以便日後其他專案用作參考。 謝謝閱讀~