美文网首页自动化程序员
GUI应用程序架构的十年变迁(三)

GUI应用程序架构的十年变迁(三)

作者: 51reboot | 来源:发表于2018-04-24 09:54 被阅读15次

    MV* in Android

    此部分完整代码在这里 (https://github.com/ivacf/archi),笔者在这里节选出部分代码方便对照演示。Android中的Activity的功能很类似于iOS中的UIViewController,都可以看做MVC中的Controller。在2010年左右经典的Android程序大概是这样的:

    TextView mCounterText;
    
    Button mCounterIncrementButton;
    
    int mClicks = 0;
    
    public void onCreate(Bundle b) {
    
      super.onCreate(b);
    
      mCounterText = (TextView) findViewById(R.id.tv_clicks);
    
      mCounterIncrementButton = (Button) findViewById(R.id.btn_increment);
    
      mCounterIncrementButton.setOnClickListener(new View.OnClickListener() {
    
        public void onClick(View v) {
    
          mClicks++;
    
          mCounterText.setText(""+mClicks);
    
        }
    
      });
    
    }
    

    后来2013年左右出现了ButterKnife(https://github.com/JakeWharton/butterknife)这样的基于注解的控件绑定框架,此时的代码看上去是这样的:

    @Bind(R.id.tv_clicks) mCounterText;
    
    @OnClick(R.id.btn_increment)
    
    public void onSubmitClicked(View v) {
    
        mClicks++;
    
        mCounterText.setText("" + mClicks);
    
    }
    

    后来Google官方也推出了数据绑定的框架,从此MVVM模式在Android中也愈发流行:

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    
       <data>
    
           <variable name="counter" type="com.example.Counter"/>
    
           <variable name="counter" type="com.example.ClickHandler"/>
    
       </data>
    
       <LinearLayout
    
           android:orientation="vertical"
    
           android:layout_width="match_parent"
    
           android:layout_height="match_parent">
    
           <TextView android:layout_width="wrap_content"
    
               android:layout_height="wrap_content"
    
               android:text="@{counter.value}"/>
    
           <Buttonandroid:layout_width="wrap_content"
    
               android:layout_height="wrap_content"
    
               android:text="@{handlers.clickHandle}"/>
    
       </LinearLayout>
    
    </layout>
    

    后来Anvil(https://github.com/zserge/anvil)这样的受React启发的组件式框架以及Jedux这样借鉴了Redux全局状态管理的框架也将Unidirectional 架构引入了Android开发的世界。

    MVC

    • 声明View中的组件对象或者Model对象
        private Subscription subscription;
    
        private RecyclerView reposRecycleView;
    
        private Toolbar toolbar;
    
        private EditText editTextUsername;
    
        private ProgressBar progressBar;
    
        private TextView infoTextView;
    
        private ImageButton searchButton;
    
    • 将组件与Activity中对象绑定,并且声明用户响应处理函数
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
    
            progressBar = (ProgressBar) findViewById(R.id.progress);
    
            infoTextView = (TextView) findViewById(R.id.text_info);
    
            //Set up ToolBar
    
            toolbar = (Toolbar) findViewById(R.id.toolbar);
    
            setSupportActionBar(toolbar);
    
            //Set up RecyclerView
    
            reposRecycleView = (RecyclerView) findViewById(R.id.repos_recycler_view);
    
            setupRecyclerView(reposRecycleView);
    
            // Set up search button
    
            searchButton = (ImageButton) findViewById(R.id.button_search);
    
            searchButton.setOnClickListener(new View.OnClickListener() {
    
                @Override
    
                public void onClick(View v) {
    
                    loadGithubRepos(editTextUsername.getText().toString());
    
                }
    
            });
    
            //Set up username EditText
    
            editTextUsername = (EditText) findViewById(R.id.edit_text_username);
    
            editTextUsername.addTextChangedListener(mHideShowButtonTextWatcher);
    
            editTextUsername.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    
                @Override
    
                public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    
                    if (actionId == EditorInfo.IME_ACTION_SEARCH) {
    
                        String username = editTextUsername.getText().toString();
    
                        if (username.length() > 0) loadGithubRepos(username);
    
                        return true;
    
                    }
    
                    return false;
    
                }
    
    });
    
    • 用户输入之后的更新流程
            progressBar.setVisibility(View.VISIBLE);
    
            reposRecycleView.setVisibility(View.GONE);
    
            infoTextView.setVisibility(View.GONE);
    
            ArchiApplication application = ArchiApplication.get(this);
    
            GithubService githubService = application.getGithubService();
    
            subscription = githubService.publicRepositories(username)
    
                    .observeOn(AndroidSchedulers.mainThread())
    
                    .subscribeOn(application.defaultSubscribeScheduler())
    
                    .subscribe(new Subscriber<List<Repository>>() {
    
                        @Override
    
                        public void onCompleted() {
    
                            progressBar.setVisibility(View.GONE);
    
                            if (reposRecycleView.getAdapter().getItemCount() > 0) {
    
                                reposRecycleView.requestFocus();
    
                                hideSoftKeyboard();
    
                                reposRecycleView.setVisibility(View.VISIBLE);
    
                            } else {
    
                                infoTextView.setText(R.string.text_empty_repos);
    
                                infoTextView.setVisibility(View.VISIBLE);
    
                            }
    
                        }
    
                        @Override
    
                        public void onError(Throwable error) {
    
                            Log.e(TAG, "Error loading GitHub repos ", error);
    
                            progressBar.setVisibility(View.GONE);
    
                            if (error instanceof HttpException
    
                                    && ((HttpException) error).code() == 404) {
    
                                infoTextView.setText(R.string.error_username_not_found);
    
                            } else {
    
                                infoTextView.setText(R.string.error_loading_repos);
    
                            }
    
                            infoTextView.setVisibility(View.VISIBLE);
    
                        }
    
                        @Override
    
                        public void onNext(List<Repository> repositories) {
    
                            Log.i(TAG, "Repos loaded " + repositories);
    
                            RepositoryAdapter adapter =
    
                                    (RepositoryAdapter) reposRecycleView.getAdapter();
    
                            adapter.setRepositories(repositories);
    
                            adapter.notifyDataSetChanged();
    
                        }
    
    });
    

    MVP

    • 将Presenter与View绑定,并且将用户响应事件绑定到Presenter中
            //Set up presenter
    
            presenter = new MainPresenter();
    
            presenter.attachView(this);
    
            ...
    
            // Set up search button
    
            searchButton = (ImageButton) findViewById(R.id.button_search);
    
            searchButton.setOnClickListener(new View.OnClickListener() {
    
                @Override
    
                public void onClick(View v) {
    
                    presenter.loadRepositories(editTextUsername.getText().toString());
    
                }
    
            });
    
    • Presenter中调用Model更新数据,并且调用View中进行重新渲染
        public void loadRepositories(String usernameEntered) {
    
            String username = usernameEntered.trim();
    
            if (username.isEmpty()) return;
    
            mainMvpView.showProgressIndicator();
    
            if (subscription != null) subscription.unsubscribe();
    
            ArchiApplication application = ArchiApplication.get(mainMvpView.getContext());
    
            GithubService githubService = application.getGithubService();
    
            subscription = githubService.publicRepositories(username)
    
                    .observeOn(AndroidSchedulers.mainThread())
    
                    .subscribeOn(application.defaultSubscribeScheduler())
    
                    .subscribe(new Subscriber<List<Repository>>() {
    
                        @Override
    
                        public void onCompleted() {
    
                            Log.i(TAG, "Repos loaded " + repositories);
    
                            if (!repositories.isEmpty()) {
    
                                mainMvpView.showRepositories(repositories);
    
                            } else {
    
                                mainMvpView.showMessage(R.string.text_empty_repos);
    
                            }
    
                        }
    
                        @Override
    
                        public void onError(Throwable error) {
    
                            Log.e(TAG, "Error loading GitHub repos ", error);
    
                            if (isHttp404(error)) {
    
                                mainMvpView.showMessage(R.string.error_username_not_found);
    
                            } else {
    
                                mainMvpView.showMessage(R.string.error_loading_repos);
    
                            }
    
                        }
    
                        @Override
    
                        public void onNext(List<Repository> repositories) {
    
                            MainPresenter.this.repositories = repositories;
    
                        }
    
                    });
    
            }
    

    MVVM

    • XML中声明数据绑定
    <data>
    
            <variable
    
                name="viewModel"
    
                type="uk.ivanc.archimvvm.viewmodel.MainViewModel"/>
    
    </data>
    
    ...
    
                <EditText
    
                    android:id="@+id/edit_text_username"
    
                    android:layout_width="match_parent"
    
                    android:layout_height="wrap_content"
    
                    android:layout_toLeftOf="@id/button_search"
    
                    android:hint="@string/hit_username"
    
                    android:imeOptions="actionSearch"
    
                    android:inputType="text"
    
                    android:onEditorAction="@{viewModel.onSearchAction}"
    
                    android:textColor="@color/white"
    
                    android:theme="@style/LightEditText"
    
                    app:addTextChangedListener="@{viewModel.usernameEditTextWatcher}"/>
    
    • View中绑定ViewModel
            super.onCreate(savedInstanceState);
    
            binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
    
            mainViewModel = new MainViewModel(this, this);
    
            binding.setViewModel(mainViewModel);
    
            setSupportActionBar(binding.toolbar);
    
            setupRecyclerView(binding.reposRecyclerView);
    
    • ViewModel中进行数据操作
    public boolean onSearchAction(TextView view, int actionId, KeyEvent event) {
    
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
    
                String username = view.getText().toString();
    
                if (username.length() > 0) loadGithubRepos(username);
    
                return true;
    
            }
    
            return false;
    
        }
    
        public void onClickSearch(View view) {
    
            loadGithubRepos(editTextUsernameValue);
    
        }
    
        public TextWatcher getUsernameEditTextWatcher() {
    
            return new TextWatcher() {
    
                @Override
    
                public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
    
                }
    
                @Override
    
                public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
    
                    editTextUsernameValue = charSequence.toString();
    
                    searchButtonVisibility.set(charSequence.length() > 0 ? View.VISIBLE : View.GONE);
    
                }
    
                @Override
    
                public void afterTextChanged(Editable editable) {
    
                }
    
            };
    
    }
    

    精彩回顾:

    1、GUI应用程序架构的十年变迁(一)
    2、GUI应用程序架构的十年变迁(二)

    感谢作者:wxyyxc1992

    公告通知

    自动化运维班、架构师班、区块链正在招生中

    各位小伙伴们,欢迎试听和咨询:


    扫码添加小助手微信,备注"公开课,来源简书",进入分享群

    相关文章

      网友评论

      本文标题:GUI应用程序架构的十年变迁(三)

      本文链接:https://www.haomeiwen.com/subject/iuudlftx.html