一种实现Android的流程页面方法

2022-06-02  本文已影响0人  无敌之神将

一种实现Android的流程页面方法

介绍

我们在应用开发的过程中,经常会遇到一整个模块,它里面比较完整的流程。就是这个流程中有很多的点(页面),有一个开始点(页面),多个结束点(页面),他是从开始点到结束点的实现方法。比如一个 常用的登陆流程,其实一个登陆流程也是比较复杂的包含好多的页面,首页A (登陆首页)包含验证码登陆,微信登录等选择;页面B(输入手机号或者用户名);页面C (输入密码);页面D:(更改用户);页面E(更改密码);页面F(更改手机号等),页面G (设置手势密码);页面H(登陆成功);页面I(登陆失败)。

比如下面的这张图(图1)是一个流程的模块,A点是开始G点和H点是两个结束的点,这个图是我随意画的。这个流程里面 A到H的页面可能都会展示到,我们开发应用的时候普通页面判断跳转也可以实现总感觉有点繁琐,今天介绍的就是一种比较简洁的一种方法去实现这种流程。


图1

方法的来源

这个方法是原生TvSetting里面的实现,因为最近QA 提个一些关于原生TVSetting的问题,问题里面有好多关于流程比如网络连接,蓝牙连接等流程的问题,它里面大量用到了一个State的Class,每个流程点都继承这个class,为了解问题,就了解了一下他怎么实现的。初看觉得复杂,后来觉得挺简洁的。

实现的原理

关键的核心class 有三个,1是一个interface名字是State,每个页面就是一个state,里面对应一个fragment;2 第二个class 是Transition,是定义两个State的关系的;第三个class 是StateMachine,用来管理这个流程的所有State的。
实现的原理是, 流程初始化的时候把所有的页面的对应的State(State的子类)初始化;然后把每两个State的关系Transition也都初始化;然后把所有的State和Transtion 都放到StateMachine里面,让他去管理所有的页面。当每个State达到一定条件的时候(比如点击)去调用StateMachine里面的对应的transation方法,就会跳转到新的State页面。

下面代码分析是我自己写的的demo。

State介绍

public interface State {

  /**
   * Process when moving forward.  前进跳到本页面会执行的方法
   */
  void processForward();

  /**
   * Process when moving backward. 后退跳到本页面 会执行的方法 。
   */
  void processBackward();

  /**
   * Listener for sending notification about state completion.
在StateMachine里面实现,state 里面 用来通知本state结束,通过event StateMachine 知道要去哪个界面。
   */
  interface StateCompleteListener {
      void onComplete(@StateMachine.Event int event);
  }

  /**
   * Listener for sending notification about fragment change. 这个方法的实现主要在Activity 里面 ,用来切换界面展示的。
   */
  interface FragmentChangeListener {
      void onFragmentChange(Fragment newFragment, boolean movingForward);
  }

  /**
   * @return the corresponding fragment for current state. If there is no corresponding fragment,
   * return null.
   */
  Fragment getFragment();
}

解读一下这几个方法

Transition 介绍

public class Transition {
    public State source;
     // event   source 经过 event 进入 destination
    public @StateMachine.Event int event;
    public State destination;

    public Transition(State source, @StateMachine.Event int event, State destination) {
        this.source = source;
        this.event = event;
        this.destination = destination;
    }
}

Transition 是用来设置 两个State之间的关系的。event 是两个之间的关系,state 里面的
void onComplete(@StateMachine.Event int event); 这个方法就是通知StateMachine,然后StateMachine遍历Transition, 让他根据 state 和 event 找另一个state。

StateMachine

这是个管理的State的class,也是最重要的一个class。

/**
 * State machine responsible for handling the logic between different states.
 */
public class StateMachine extends ViewModel {

    private Callback mCallback;
    private Map<State, List<Transition>> mTransitionMap = new HashMap<>();
    private LinkedList<State> mStatesList = new LinkedList<>();
    private State.StateCompleteListener mCompletionListener = this::updateState;
    //  登陆 流程  1 输入用户名 2 输入密码 3 成功 4 失败
    public static final int CONTINUE = 0;
    public static final int CANCEL = 1;
    public static final int FAIL = 2;
    public static final int EARLY_EXIT = 3;

    public static final int LOGIN_ENTER_PSD = 5;
    public static final int LOGIN_SUCCESS = 6;
    public static final int LOGIN_FAILED = 7;

    @IntDef({
            CONTINUE,
            CANCEL,
            FAIL,
            EARLY_EXIT,
            LOGIN_ENTER_PSD,
            LOGIN_SUCCESS,
            LOGIN_FAILED
           })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Event {
    }

    public StateMachine() {
    }

    public StateMachine(Callback callback) {
        mCallback = callback;
    }

    /**
     * Set the callback for the things need to done when the state machine leaves end state.
     */
    public void setCallback(Callback callback) {
        mCallback = callback;
    }

    /**
     * Add state with transition.
     *
     * @param state       start state.
     * @param event       transition between two states.
     * @param destination destination state.
     */
    public void addState(State state, @Event int event, State destination) {
        if (!mTransitionMap.containsKey(state)) {
            mTransitionMap.put(state, new ArrayList<>());
        }
        mTransitionMap.get(state).add(new Transition(state, event, destination));
    }

    /**
     * Add a state that has no outward transitions, but will end the state machine flow.
     */
    public void addTerminalState(State state) {
        mTransitionMap.put(state, new ArrayList<>());
    }

    /**
     * Enables the activity to be notified when state machine enter end state.
     */
    public interface Callback {
        /**
         * Implement this to define what to do when the activity is finished.
         *
         * @param result the activity result.
         */
        void onFinish(int result);
    }

    /**
     * Set the start state of state machine/
     *
     * @param startState start state.
     */
    public void setStartState(State startState) {
        mStatesList.addLast(startState);
    }

    /**
     * Start the state machine.
     */
    public void start(boolean movingForward) {
        if (mStatesList.isEmpty()) {
            throw new IllegalArgumentException("Start state not set");
        }
        State currentState = getCurrentState();
        if (movingForward) {
            currentState.processForward();
        } else {
            currentState.processBackward();
        }
    }

    /**
     * Initialize the states list.
     */
    public void reset() {
        mStatesList = new LinkedList<>();
    }

    /**
     * Make the state machine go back to the previous state.
     */
    public void back() {
        updateState(CANCEL);
    }

    /**
     * Return the current state of state machine.
     */
    public State getCurrentState() {
        if (!mStatesList.isEmpty()) {
            return mStatesList.getLast();
        } else {
            return null;
        }
    }

    /**
     * Notify state machine that current activity is finished.
     *
     * @param result the result of activity.
     */
    public void finish(int result) {
        mCallback.onFinish(result);
    }

    private void updateState(@Event int event) {
        // Handle early exits first.
        if (event == EARLY_EXIT) {
            finish(Activity.RESULT_OK);
            return;
        } else if (event == FAIL) {
            finish(Activity.RESULT_CANCELED);
            return;
        }

        // Handle Event.CANCEL, it happens when the back button is pressed.
        if (event == CANCEL) {
            if (mStatesList.size() < 2) {
                mCallback.onFinish(Activity.RESULT_CANCELED);
            } else {
                mStatesList.removeLast();
                State prev = mStatesList.getLast();
                prev.processBackward();
            }
            return;
        }

        State next = null;
        State currentState = getCurrentState();

        List<Transition> list = mTransitionMap.get(currentState);
        if (list != null) {
            for (Transition transition : mTransitionMap.get(currentState)) {
                if (transition.event == event) {
                    next = transition.destination;
                }
            }
        }

        if (next == null) {
            if (event == CONTINUE) {
                mCallback.onFinish(Activity.RESULT_OK);
                return;
            }
            throw new IllegalArgumentException(
                    getCurrentState().getClass() + "Invalid transition " + event);
        }

        addToStack(next);
        next.processForward();
    }

    private void addToStack(State state) {
        for (int i = mStatesList.size() - 1; i >= 0; i--) {
            if (equal(state, mStatesList.get(i))) {
                for (int j = mStatesList.size() - 1; j >= i; j--) {
                    mStatesList.removeLast();
                }
            }
        }
        mStatesList.addLast(state);
    }

    private boolean equal(State s1, State s2) {
        if (!s1.getClass().equals(s2.getClass())) {
            return false;
        }
        return true;
    }

    public State.StateCompleteListener getListener() {
        return mCompletionListener;
    }
}

先看一下重要的方法和成员变量

+mCallback
mCallback 一般是在activity实现的,用来通知activity 你该finish了,并且把流程结果告诉activity。

登陆流程例子

登陆流程一共有 有输入用户名,输入密码,登陆成功,登陆失败四个页面,也就是四个state。我们接下来看一下Activity的代码和state的实现来分析一下。

Activity的代码如下

public class LoginActivity extends AppCompatActivity  implements State.FragmentChangeListener{
    private static final String TAG = "LoginActivity";
    private StateMachine mStateMachine;
    // finish  set result
    private final StateMachine.Callback mStateMachineCallback = result -> {
        setResult(result);
        finish();
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        // 初始化
        mStateMachine = new ViewModelProvider(this).get(StateMachine.class);
        // set result callback
        mStateMachine.setCallback(mStateMachineCallback);
        State mEnterNameState = new EnterNameState(this);
        State mEnterPsdState = new EnterPsdState(this);
        State mSuccessState = new LoginSuccessState(this);
        State mFailedState = new LoginFailedState(this);

        mStateMachine.addState(mEnterNameState,StateMachine.LOGIN_ENTER_PSD, mEnterPsdState);
        mStateMachine.addState(mEnterPsdState,StateMachine.LOGIN_SUCCESS, mSuccessState);
        mStateMachine.addState(mEnterPsdState,StateMachine.LOGIN_FAILED, mFailedState);
        mStateMachine.setStartState(mEnterNameState);
        mStateMachine.start(true);
    }

    @Override
    public void onBackPressed() {
        mStateMachine.back();
    }
    @Override
    public void onFragmentChange(Fragment newFragment, boolean movingForward) {
        updateView(newFragment,movingForward);
    }
    /**
     *
     * @param fragment
     * @param movingForward 前进还是后退 设置 FragmentTransaction 设置 展示的动画
     */
    private void updateView(Fragment fragment, boolean movingForward) {
        if (fragment != null) {
            FragmentTransaction updateTransaction = getSupportFragmentManager().beginTransaction();
            if (movingForward) {
                updateTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            } else {
                updateTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
            }
            updateTransaction.replace(R.id.fragment_container, fragment, TAG);
            updateTransaction.commit();
        }
    }

activity 里面主要有两个重要的小块功能;

输入密码的State 代码如下

public class EnterPsdState implements State{
    FragmentActivity mActivity;

    public EnterPsdState(FragmentActivity activity) {
        this.mActivity = activity;
    }

    private Fragment mFragment;

    @Override
    public void processForward() {
        mFragment = EnterPsdFragment.newInstance("processForward");
        FragmentChangeListener listener = (FragmentChangeListener) mActivity;
        listener.onFragmentChange(mFragment,true);
    }

    @Override
    public void processBackward() {
        mFragment = EnterPsdFragment.newInstance("processForward");
        FragmentChangeListener listener = (FragmentChangeListener) mActivity;
        listener.onFragmentChange(mFragment,false);
    }

    @Override
    public Fragment getFragment() {
        return mFragment;
    }



    public static class EnterPsdFragment extends Fragment implements View.OnClickListener {

        public static EnterPsdFragment newInstance(String param){
            EnterPsdFragment enterNameFragment = new EnterPsdFragment();
            Bundle args = new Bundle();
            args.putString("arg1",param);
            enterNameFragment.setArguments(args);
            return enterNameFragment;
        }

        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View inflate = inflater.inflate(R.layout.state_enter_psd, null);
            return inflate;
        }

        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            initView(view);
            super.onViewCreated(view, savedInstanceState);
        }

        private void initView(View rootView){
            rootView.findViewById(R.id.success).setOnClickListener(this);
            rootView.findViewById(R.id.failed).setOnClickListener(this);
            rootView.findViewById(R.id.exit).setOnClickListener(this);
        }

        @SuppressLint("NonConstantResourceId")
        @Override
        public void onClick(View view) {
            StateMachine stateMachine = new ViewModelProvider(requireActivity()).get(StateMachine.class);
            switch (view.getId()) {
                case R.id.success:
                    stateMachine.getListener().onComplete(StateMachine.LOGIN_SUCCESS);
                    break;
                case R.id.failed:
                    stateMachine.getListener().onComplete(StateMachine.LOGIN_FAILED);
                    break;
                case R.id.exit:
                    stateMachine.getListener().onComplete(StateMachine.EARLY_EXIT);
                    break;

            }
        }
    }
}

输入密码页面 EnterPsdState里面有两个模块比较重要

对于这个流程我画了一个流程图 如下图。


登陆流程图.png

上面的图里面是一个输入用户面,输入密码 ,登陆成功三个过程中Activity 和State 和 Statemachine之间的调用关系,看图的流程的时候可以同时看上面的代码部分。
流程1 初始化 state 并 给StateMachine 填充数据
流程2 StateMachine 添加一个state 并展示EnterNameState 调用state的processForward 方法
流程3 EnterNameState 获取fragment 调用activity updatefragment 展示页面
流程4 EnterNameState 的fragment 满足条件 输入完名字 调用 Statemachine的updatestate 寻找接下来的页面。
流程5 寻找到输入密码的PsdState,调用state的processForward 方法
流程6 PsdState 获取fragment 调用activity updatefragment 展示页面
流程7 PsdState 的fragment 满足条件 登录成功 调用 Statemachine的updatestate 寻找接下来的页面 。
流程8 寻找到登陆成功的SuccessState,调用state的processForward 方法
流程9 SuccessState 获取fragment 调用activity updatefragment 展示页面

上一篇下一篇

猜你喜欢

热点阅读