美文网首页自动化程序员
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