About mvvm simple package (1)

foreword

It has been a long time since the emergence of mvvm, but bloggers have not paid too much attention to it, but because many of the recently contacted and new frameworks are developed based on the mvvm model, it took some time to look at it.
Before learning mvvm, you may need to understand databing first. Please go to Baidu yourself. There are a bunch of blogs introducing databing. Since the knowledge is not advanced enough, I will not guide you to interpret the source code. With the addition of databing to your project, you no longer need findViewById, nor do you need the butterknife plug-in, and the databing function is not only that, it can also bind events and data conversion. You can find this picture on the Internet to understand first.
insert image description here
Then start our simple packaging journey

  1. BaseActivity package
  2. BaseFragment encapsulation
  3. BaseView unified interface method encapsulation
  4. BaseViewModel encapsulation
  5. BaseException error exception definition class
  6. BaseModelEntity interface returns data unified processing class
  7. Encapsulate the network request framework, here we will take Rxjava2+Retrofit as an example
  8. Unified tool class encapsulation and api-related configuration encapsulation
    In this article, we start with BaseActivity, BaseFragment, BaseView, and BaseViewModel.
    The libraries that need to be introduced are
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    //放着没有及时回收造成RxJava内存泄漏
    implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'
    //Room的依赖引用
    implementation 'androidx.room:room-runtime:2.2.5'
    annotationProcessor 'androidx.room:room-compiler:2.2.5'

BaseActivity

When it comes to BaseActivity encapsulation, the first thing we think of is that there is no need to repeat findViewById and setContentView, so we can define an abstract method

    /**
     * 初始化布局
     * @return 布局id
     */
    protected abstract int getLayoutId();

Of course, we can also encapsulate a unified toolbar, and the remaining pages must introduce the toolbar. Of course, the original navigation bar must be removed before introducing the toolbar.

    <style name="AppBaseTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
        <!--<item name="android:statusBarColor">@color/theme_backgroung_color</item>-->
        <!--<item name="android:windowBackground">@color/window_background</item>-->
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/themeColor</item>
    </style>
    <style name="AppTheme" parent="AppBaseTheme"/>

Then we can introduce the theme theme under the application node.
At this time, we can try to refer to the two-way binding feature of databing. We create a new toolbar style class file

/**
 * Create by CherishTang on 2020/3/25 0025
 * describe:toolbar配置
 */
public class ToolbarConfig extends BaseObservable {
    
    
    private String title;
    private @DrawableRes int backIconRes = R.mipmap.icon_fh_black;//toolbar返回按钮资源样式
    private boolean defaultTheme = true;//toolbar的menu主题,默认主题为黑色
    private int textColor = R.color.black;//标题字体颜色
    private int bgColor = R.color.white;//标题背景色
    private boolean isShowBackButton = true;//是否显示返回按钮

    public ToolbarConfig() {
    
    
    }
    
    public String getTitle() {
    
    
        return title;
    }

    public void setTitle(String title) {
    
    
        this.title = title;
    }

    public int getBackIconRes() {
    
    
        return backIconRes;
    }

    public void setBackIconRes(int backIconRes) {
    
    
        this.backIconRes = backIconRes;
    }

    public boolean isDefaultTheme() {
    
    
        return defaultTheme;
    }

    public void setDefaultTheme(boolean defaultTheme) {
    
    
        this.defaultTheme = defaultTheme;
    }

    public int getTextColor() {
    
    
        return textColor;
    }

    public void setTextColor(int textColor) {
    
    
        this.textColor = textColor;
    }

    public int getBgColor() {
    
    
        return bgColor;
    }

    public void setBgColor(int bgColor) {
    
    
        this.bgColor = bgColor;
    }

    public boolean isShowBackButton() {
    
    
        return isShowBackButton;
    }

    public void setShowBackButton(boolean showBackButton) {
    
    
        isShowBackButton = showBackButton;
    }
}

Then create a base_activity.xml layout file and introduce a unified toolbar

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="toolbarConfig"
            type="com.jcloudsoft.demo.bean.base.ToolbarConfig" />

        <import type="android.view.View" />

        <import type="androidx.core.content.ContextCompat" />

        <import type="androidx.annotation.LayoutRes" />

        <import type="androidx.annotation.ColorRes" />
        <variable
            name="context"
            type="android.content.Context" />
    </data>

    <LinearLayout
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/main_bar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/y96"
            android:background="@{ContextCompat.getColor(context,toolbarConfig.bgColor)}"
            android:minHeight="?attr/actionBarSize"
            app:navigationIcon="@{ContextCompat.getDrawable(context,toolbarConfig.backIconRes)}"
            android:theme="@style/ToolBarStyle_black">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:ellipsize="end"
                android:maxLength="12"
                android:maxLines="1"
                android:text="@{toolbarConfig.title}"
                android:textColor="@{ContextCompat.getColor(context,toolbarConfig.textColor)}"
                android:textSize="@dimen/font_18"/>
        </androidx.appcompat.widget.Toolbar>

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </LinearLayout>
</layout>

There may be several questions about the above layout file:
1. The context object, we cannot return the context object in the xml layout, but we can use databing two-way binding to pass this object to the xml layout, or you can directly Customize a method to return the same return data, the effect is the same

bind.setVariable(BR.context, this);

2. The problem of style @style/ToolBarStyle_black, if you don’t understand, you can read the blog post of custom toolbar and paste the code directly, without explaining too much, if you want to make the toolbar style more cool, of course you can use this idea Add more related configuration on

    <style name="ToolBarStyle_black" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <item name="actionMenuTextColor">@android:color/black</item> <!-- menu 敲定颜色-->
        <item name="android:textSize">@dimen/font_14</item> <!--  搞掂字体大小-->
        <item name="android:textStyle">normal</item>
        <item name="colorControlNormal">@color/black</item><!--图标颜色-->
        <item name="overlapAnchor">false</item>
    </style>

Introduce the layout in the activity, but the problem comes, what if some of our pages do not need the toolbar? Can’t setVisibility, it’s too low, we can first define an abstract method for sub-activity to control the display and hide of toolbar

    /**
     * 是否引用toolbar
     * @return 默认显示
     */
    protected boolean hasToolBar() {
    
    
        return true;
    }

Define another title text method

    /**
     * 设置toolbar的title
     * @return 标题
     */
    public abstract String setTitleBar();

Then how do we refer to these methods? Of course, using the two-way binding feature of databing,

//toolbarConfig是xml定义的name,setToolbarStyle()是我们刚才新建的toolbar配置类
bind.setVariable(BR.toolbarConfig, setToolbarStyle())

Of course we need to expose an interface for sub-Activities to modify the toolbar style

    /**
     * 设置toolbar默认样式
     *
     * @return toolbar配置
     */
    public ToolbarConfig setToolbarStyle() {
    
    
        return new ToolBarSet().build();
    }

Customize the toolbar style, you can add your own methods according to your needs

   /**
     * 自定义toolbar样式类
     */
    public class ToolBarSet {
    
    
        private ToolbarConfig toolbarConfig;

        public ToolBarSet() {
    
    
            if (toolbarConfig == null) {
    
    
                this.toolbarConfig = new ToolbarConfig();
            }
            toolbarConfig.setTitle(setTitleBar());
        }

        public ToolbarConfig getConfig(){
    
    
            return toolbarConfig;
        }

        public ToolbarConfig build() {
    
    
            return toolbarConfig;
        }

        public ToolBarSet setTitleTextColor(@ColorRes int colorRes) {
    
    
            toolbarConfig.setTextColor(colorRes);
            return this;
        }

        public ToolBarSet setBackIconRes(@DrawableRes int imgRes) {
    
    
            toolbarConfig.setBackIconRes(imgRes);
            return this;
        }

        public ToolBarSet setTitle(String title) {
    
    
            toolbarConfig.setTitle(title);
            return this;
        }

        public ToolBarSet setBgColor(@ColorRes int colorRes) {
    
    
            toolbarConfig.setBgColor(colorRes);
            return this;
        }

        public ToolBarSet setDefaultTheme(boolean defaultTheme) {
    
    
            toolbarConfig.setDefaultTheme(defaultTheme);
            return this;
        }

        public ToolBarSet setShowBackButton(boolean isShow) {
    
    
            toolbarConfig.setShowBackButton(isShow);
            return this;
        }
    }

Then the custom style of the toolbar is bound, so the problem of whether we introduce the toolbar or not has not been resolved yet? The binding layout file in databing is written as follows,

ViewDataBinding VDB = DataBindingUtil.setContentView(this, R.layout.base_activity)

So look at this, do you have inspiration? We need to do the following:
1. The layout we bind in the UI page cannot be base_activity, it needs to be the layout id returned by our getLayoutId()
2. Use the hasToolBar() method to Judging the hidden display of the toolbar
Binding the layout in databing is somewhat different from the original one, but they all use the system's setContentView method, then we can rewrite the system's setContentView method to achieve different binding relationships

    @Override
    public void setContentView(int layoutResID) {
    
    
        if (hasToolBar()) {
    
    //如果是引用toolbar布局的话我们根布局重写一下,需要引入base_activity作为根布局文件,然后把各ui页面的getLayoutId()定义的布局资源添加到根布局文件中去
            super.setContentView(R.layout.base_activity);
            FrameLayout container = findViewById(R.id.container);
            mToolbar = findViewById(R.id.main_bar);
            binding = DataBindingUtil.inflate(LayoutInflater.from(this), getLayoutId(), container, true);
            mToolbar.setNavigationOnClickListener(v -> finish());
        } else {
    
    //如果不需要toolbar的话,我们直接就以getLayoutId()的布局资源id作为根布局
            super.setContentView(layoutResID);
        }
    }

After doing this, we can implement it in onCreate

   @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        if (hasToolBar()) {
    
    
            toolbarBind = DataBindingUtil.setContentView(this, R.layout.base_activity);//我们需要定义一个属于toolbar的bind来控制toolbar的样式
            toolbarBind.setVariable(BR.context, this);//给view传递context对象
            toolbarBind.setVariable(BR.toolbarConfig, setToolbarStyle());
        } else {
    
    
            binding = DataBindingUtil.setContentView(this, getLayoutId());
        }
    }

So here our toolbar style is packaged. Of course, our Activity package is not finished yet, we can add some public methods

    /**
     * 初始化布局
     */
    public abstract void initView();

    /**
     * 设置数据
     */
    public abstract void initData(Bundle bundle);

But after talking for a long time, we haven't introduced ViewModel yet, don't worry! Let's introduce the fragment package first, and I will post the source code of the activity in the article below

BaseFragment

With the precedent of activity encapsulation, our fragment encapsulation is much simpler. First, we also define some public methods we need

    /**
     * 该抽象方法就是 onCreateView中需要的layoutID
     *
     * @return
     */
    protected abstract int getLayoutId();

    /**
     * 该抽象方法就是 初始化view
     *
     * @param view
     * @param savedInstanceState
     */
    protected abstract void initView(View view, Bundle savedInstanceState);

    /**
     * 执行数据的加载
     */
    protected abstract void initData(Bundle bundle);

Binding resource files in fragments is slightly different from activity

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
    
    
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        return binding.getRoot();
    }

The View layout bound in the fragment may be useful later, so we define a View to accept it

    protected View mContentView;
        @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
    
    
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        return mContentView;
    }

Then go to implement the abstract method we just customized

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    
    
        super.onActivityCreated(savedInstanceState);
        initData(getArguments() == null ? new Bundle() : getArguments());
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
    
    
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }

Then the simple encapsulation of fragment is much simpler.

BaseView

After encapsulating Activity and Fragment, we found that many public methods are missing, such as: Toast, loading box, etc. Because these methods are shared by Fragment and Activity, we can define an interface, and then let Activity and Fragment implement his method body. Can

public interface BaseView {
    
    
    /**
     * 显示dialog
     */
    void showLoading(String dialogMessage);
    /**
     * 更新dialog
     */
    void refreshLoading(String dialogMessage);

    /**
     * 隐藏 dialog
     */

    void hideLoading();

    /**
     * 显示错误信息
     *
     * @param msg
     */
    void showToast(String msg);

    /**
     * 错误码
     */
    void onErrorCode(BaseModelEntity model);

}

Then we only need to implement their method bodies in BaseActivity and BaseFragment

BaseViewModel

Having said so much, let's really talk about the ViewModel.
Before talking about this, some people may say that LifeCycle should be introduced. I can answer: no need! ! ! ! ! The LifeCycle interface has been implemented by default in androidx.
If it is not a special requirement, the above

    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

This library can not be imported, because Google has already done it for you

public class BaseViewModel extends AndroidViewModel {
    
    
    public BaseViewModel(@NonNull Application application) {
    
    
        super(application);
    }
}

Of course we need to introduce the interface baseView just now

public class BaseViewModel<V extends BaseView> extends AndroidViewModel {
    
    
    protected V baseView;
    public BaseViewModel(@NonNull Application application) {
    
    
        super(application);
    }
    protected void setBaseView(V baseView) {
    
    
        this.baseView = baseView;
    }
    public V getBaseView() {
    
    
        return baseView;
    }
}

At this time, we still don’t know what we need in ViewModel. Don’t write blindly. You will know it when you use it. It’s better to write so much in our viewModel. What we need to do now is to introduce BaseViewModel in BaseActivity. Of course, we have different activities. There will be different ViewModels, so we might as well let all ViewModels inherit from BaseViewModel and introduce them as generics in BaseActivity;

public abstract class BaseActivity<VM extends BaseViewModel, VDB extends ViewDataBinding> extends AppCompatActivity implements BaseView {
    
    
    public Context context;
    Toolbar mToolbar;
    public VM mViewModel;
    protected VDB binding;
    private BaseActivityBinding toolbarBind;
    private CustomProgressDialog customProgressDialog;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        context = this;
        if (hasToolBar()) {
    
    
            toolbarBind = DataBindingUtil.setContentView(this, R.layout.base_activity);
            toolbarBind.setVariable(BR.context, this);//给view传递context对象
            toolbarBind.setVariable(BR.toolbarConfig, setToolbarStyle());
        } else {
    
    
            binding = DataBindingUtil.setContentView(this, getLayoutId());
        }
        createViewModel();
        initView();
        initData((getIntent() == null || getIntent().getExtras() == null) ? new Bundle() : getIntent().getExtras());
    }
    
    private void closeLoadingDialog() {
    
    
        if (customProgressDialog != null && customProgressDialog.isShowing()) {
    
    
            customProgressDialog.dismiss();
        }
    }

    private void showLoadingDialog(String dialogMessage) {
    
    
        if (customProgressDialog == null || !customProgressDialog.isShowing()) {
    
    
            customProgressDialog = new CustomProgressDialog(context);
            customProgressDialog.isShowBg(StringUtil.isEmpty(dialogMessage));
            customProgressDialog.setMessage(dialogMessage);
            customProgressDialog.show();
        }
    }

    @Override
    public void showLoading(String dialogMessage) {
    
    
        runOnUiThread(() -> showLoadingDialog(dialogMessage));
    }

    @Override
    public void refreshLoading(String dialogMessage) {
    
    
        runOnUiThread(() -> {
    
    
            if (customProgressDialog != null && customProgressDialog.isShowing()) {
    
    
                customProgressDialog.setMessage(dialogMessage);
            }
        });
    }

    @Override
    public void hideLoading() {
    
    
        runOnUiThread(this::closeLoadingDialog);
    }

    @Override
    public void showToast(String msg) {
    
    
        runOnUiThread(() -> {
    
    
            if (StringUtil.isEmpty(msg)) return;
            ToastUtils.showShort(this, msg);
        });
    }

    @Override
    public void onErrorCode(BaseModelEntity model) {
    
    
        if (model != null && (UserAccountHelper.isLoginPast(model.getCode()) || UserAccountHelper.isNoPermission(model.getCode()))) {
    
    
            LoginActivity.show(this, new Bundle());
        }
    }

}

We now create the viewModel. If you want to add a constructor to the ViewModel, you can implement ViewModelProvider.Factory through its factory class

    /**
     * 创建viewModel
     */
    public void createViewModel() {
    
    
        if (mViewModel == null) {
    
    
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
    
    
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
    
    
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

Here our setBaseView method binds activity and ViewModel

    protected BaseView createBaseView() {
    
    
        return this;
    }

The CustomProgressDialog.java here can be written by yourself. A global loading bullet box dialog
is here, and our BaseActivity is basically encapsulated. Next, we continue to reference ViewModel in BaseFragment; similarly, we introduce it in a generic form

public abstract class BaseFragment<VM extends BaseViewModel, VDB extends ViewDataBinding> extends Fragment{
    
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
    
    
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        createViewModel();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }
}

Creating the ViewModel object here is the same as in the activity

    public void createViewModel() {
    
    
        if (mViewModel == null) {
    
    
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
    
    
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
    
    
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

    protected BaseView createBaseView(){
    
    
        return this;
    }

Then we implement the BaseView interface class in BaseFragment

public abstract class BaseFragment<VM extends BaseViewModel, VDB extends ViewDataBinding> extends Fragment implements BaseView {
    
    
    protected Activity mActivity;

    public CustomProgressDialog customProgressDialog;
    protected VM mViewModel;
    protected View mContentView;
    protected VDB binding;
    /**
     * 获得全局的,防止使用getActivity()为空
     *
     * @param context
     */
    @Override
    public void onAttach(@NotNull Context context) {
    
    
        super.onAttach(context);
        this.mActivity = (Activity) context;
    }
    
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
    
    
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mContentView = binding.getRoot();
        createViewModel();
        initView(mContentView, savedInstanceState);
        return mContentView;
    }

    public void createViewModel() {
    
    
        if (mViewModel == null) {
    
    
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
    
    
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
    
    
                //如果没有指定泛型参数,则默认使用BaseViewModel
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) new ViewModelProvider(this).get(modelClass);
            mViewModel.setBaseView(createBaseView());
        }
    }

    protected BaseView createBaseView(){
    
    
        return this;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    
    
        super.onActivityCreated(savedInstanceState);
        initData(getArguments() == null ? new Bundle() : getArguments());
    }

    /**
     * 该抽象方法就是 onCreateView中需要的layoutID
     *
     * @return
     */
    protected abstract int getLayoutId();

    /**
     * 该抽象方法就是 初始化view
     *
     * @param view
     * @param savedInstanceState
     */
    protected abstract void initView(View view, Bundle savedInstanceState);

    /**
     * 执行数据的加载
     */
    protected abstract void initData(Bundle bundle);

    /**
     * 关闭弹框
     */
    private void closeLoadingDialog() {
    
    
        if (customProgressDialog != null && customProgressDialog.isShowing()) {
    
    
            customProgressDialog.dismiss();
        }
    }

    /**
     * 显示加载弹框
     *
     * @param dialogMessage 弹框内容,如果内容为空则不展示文字部分
     */
    private void showLoadingDialog(String dialogMessage) {
    
    
        if (customProgressDialog == null || !customProgressDialog.isShowing()) {
    
    
            customProgressDialog = new CustomProgressDialog(getActivity());
            customProgressDialog.isShowBg(StringUtil.isEmpty(dialogMessage));
            customProgressDialog.setMessage(dialogMessage);
            customProgressDialog.show();
        }
    }

    @Override
    public void showLoading(String dialogMessage) {
    
    
        if (getActivity() != null && !getActivity().isFinishing()) {
    
    
            getActivity().runOnUiThread(() -> showLoadingDialog(dialogMessage));
        }
    }

    @Override
    public void hideLoading() {
    
    
        if (getActivity() != null && !getActivity().isFinishing()) {
    
    
            getActivity().runOnUiThread(this::closeLoadingDialog);
        }
    }

    @Override
    public void refreshLoading(String dialogMessage) {
    
    
        if (getActivity() != null && !getActivity().isFinishing()) {
    
    
            getActivity().runOnUiThread(() -> {
    
    
                if (customProgressDialog != null && customProgressDialog.isShowing()) {
    
    
                    customProgressDialog.setMessage(dialogMessage);
                }
            });
        }
    }

    @Override
    public void showToast(String msg) {
    
    
        ToastUtils.showShort(getActivity(), msg);
    }

    @Override
    public void onErrorCode(BaseModelEntity model) {
    
    
        if (model != null && (UserAccountHelper.isLoginPast(model.getCode())||UserAccountHelper.isNoPermission(model.getCode()))) {
    
    
            Bundle bundle = new Bundle();
            LoginActivity.show(this, bundle);
        }
    }
}

In this way, we can easily write code

public class TestActivity extends BaseActivity<MainViewModel, BaseFragmentContainerBinding> {
    
    

    @Override
    protected int getLayoutId() {
    
    
        return R.layout.base_fragment_container;
    }

    @Override
    public String setTitleBar() {
    
    
        return "测试";
    }

    @Override
    public void initView() {
    
    
        
    }

    @Override
    public void initData(Bundle bundle) {
    
    

    }

}

fragment

public class TestFragment extends BaseFragment<TestViewModel, ActivityTestBinding> {
    
    
    @Override
    protected int getLayoutId() {
    
    
        return R.layout.activity_test;
    }

    @Override
    protected void initView(View view, Bundle savedInstanceState) {
    
    
    }

    @Override
    protected void initData(Bundle bundle) {
    
    
        binding.tvTest.setText("这是"+bundle.getInt(MainViewModel.ARG_PAGE)+"个页面");
    }
}

Written at the end, the demo will be uploaded later,
pay attention to stepping on the pit here! ! ! If you introduce other generics in the base class, you need to write the generics at the end and not before ViewDataBinding and BaseViewModel, which is wrong.

BaseActivity<T extends BeseBean,VM extends BaseViewModel, VDB extends ViewDataBinding>

correct spelling

BaseActivity<VM extends BaseViewModel, VDB extends ViewDataBinding,T extends BeseBean>

In the future, we will continue to package

  1. Unified exception error handling BaseException;
  2. Encapsulation of network framework Rxjava2+Retrofit
  3. Interface Data Unified Data
  4. Application and related api parameter configuration
  5. Tool class encapsulation
    For network requests and unified exception handling, you can check this blog post about mvvm simple encapsulation (2)

Guess you like

Origin blog.csdn.net/fzkf9225/article/details/105197996