├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── UpdateLog.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── goldze │ │ └── mvvmhabit │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── goldze │ │ │ └── mvvmhabit │ │ │ ├── app │ │ │ ├── AppApplication.java │ │ │ ├── AppViewModelFactory.java │ │ │ └── Injection.java │ │ │ ├── binding │ │ │ └── twinklingrefreshlayout │ │ │ │ └── ViewAdapter.java │ │ │ ├── data │ │ │ ├── DemoRepository.java │ │ │ └── source │ │ │ │ ├── HttpDataSource.java │ │ │ │ ├── LocalDataSource.java │ │ │ │ ├── http │ │ │ │ ├── HttpDataSourceImpl.java │ │ │ │ └── service │ │ │ │ │ └── DemoApiService.java │ │ │ │ └── local │ │ │ │ └── LocalDataSourceImpl.java │ │ │ ├── entity │ │ │ ├── DemoEntity.java │ │ │ ├── FormEntity.java │ │ │ └── SpinnerItemData.java │ │ │ ├── ui │ │ │ ├── base │ │ │ │ ├── adapter │ │ │ │ │ └── BaseFragmentPagerAdapter.java │ │ │ │ ├── fragment │ │ │ │ │ └── BasePagerFragment.java │ │ │ │ └── viewmodel │ │ │ │ │ └── ToolbarViewModel.java │ │ │ ├── form │ │ │ │ ├── FormFragment.java │ │ │ │ └── FormViewModel.java │ │ │ ├── login │ │ │ │ ├── LoginActivity.java │ │ │ │ └── LoginViewModel.java │ │ │ ├── main │ │ │ │ ├── DemoActivity.java │ │ │ │ └── DemoViewModel.java │ │ │ ├── network │ │ │ │ ├── NetWorkFragment.java │ │ │ │ ├── NetWorkItemViewModel.java │ │ │ │ ├── NetWorkViewModel.java │ │ │ │ └── detail │ │ │ │ │ ├── DetailFragment.java │ │ │ │ │ └── DetailViewModel.java │ │ │ ├── rv_multi │ │ │ │ ├── MultiRecycleHeadViewModel.java │ │ │ │ ├── MultiRecycleLeftItemViewModel.java │ │ │ │ ├── MultiRecycleRightItemViewModel.java │ │ │ │ ├── MultiRecycleViewFragment.java │ │ │ │ └── MultiRecycleViewModel.java │ │ │ ├── tab_bar │ │ │ │ ├── activity │ │ │ │ │ └── TabBarActivity.java │ │ │ │ └── fragment │ │ │ │ │ ├── TabBar1Fragment.java │ │ │ │ │ ├── TabBar2Fragment.java │ │ │ │ │ ├── TabBar3Fragment.java │ │ │ │ │ └── TabBar4Fragment.java │ │ │ ├── viewpager │ │ │ │ ├── activity │ │ │ │ │ └── ViewPagerActivity.java │ │ │ │ ├── adapter │ │ │ │ │ └── ViewPagerBindingAdapter.java │ │ │ │ └── vm │ │ │ │ │ ├── ViewPagerItemViewModel.java │ │ │ │ │ └── ViewPagerViewModel.java │ │ │ └── vp_frg │ │ │ │ └── ViewPagerGroupFragment.java │ │ │ └── utils │ │ │ ├── HttpsUtils.java │ │ │ └── RetrofitClient.java │ └── res │ │ ├── drawable │ │ └── login_clear_input.xml │ │ ├── layout │ │ ├── activity_demo.xml │ │ ├── activity_login.xml │ │ ├── activity_tab_bar.xml │ │ ├── fragment_base_pager.xml │ │ ├── fragment_detail.xml │ │ ├── fragment_form.xml │ │ ├── fragment_multi_rv.xml │ │ ├── fragment_network.xml │ │ ├── fragment_tab_bar_1.xml │ │ ├── fragment_tab_bar_2.xml │ │ ├── fragment_tab_bar_3.xml │ │ ├── fragment_tab_bar_4.xml │ │ ├── fragment_viewpager.xml │ │ ├── item_multi_head.xml │ │ ├── item_multi_rv_left.xml │ │ ├── item_multi_rv_right.xml │ │ ├── item_network.xml │ │ ├── item_viewpager.xml │ │ └── layout_toolbar.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── back.png │ │ ├── btn_login.png │ │ ├── clean_edit.png │ │ ├── huanzhe.png │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ ├── login_back.png │ │ ├── logo.png │ │ ├── password_icon.png │ │ ├── registered_delete_bule.png │ │ ├── registered_delete_grey.png │ │ ├── registered_show_blue.png │ │ ├── registered_show_white.png │ │ ├── show_psw.png │ │ ├── show_psw_press.png │ │ ├── toolbar_more.png │ │ ├── user_edit.png │ │ ├── user_icon.png │ │ ├── wode_select.png │ │ ├── xiaoxi_select.png │ │ └── yingyong.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── goldze │ └── mvvmhabit │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── error1.png ├── error2.png ├── error3.png └── fc.png ├── mvvmhabit ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── goldze │ │ └── mvvmhabit │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── me │ │ │ └── goldze │ │ │ └── mvvmhabit │ │ │ ├── base │ │ │ ├── AppManager.java │ │ │ ├── BaseActivity.java │ │ │ ├── BaseApplication.java │ │ │ ├── BaseFragment.java │ │ │ ├── BaseModel.java │ │ │ ├── BaseViewModel.java │ │ │ ├── ContainerActivity.java │ │ │ ├── IBaseView.java │ │ │ ├── IBaseViewModel.java │ │ │ ├── IModel.java │ │ │ ├── ItemViewModel.java │ │ │ ├── MultiItemViewModel.java │ │ │ └── ViewModelFactory.java │ │ │ ├── binding │ │ │ ├── command │ │ │ │ ├── BindingAction.java │ │ │ │ ├── BindingCommand.java │ │ │ │ ├── BindingConsumer.java │ │ │ │ ├── BindingFunction.java │ │ │ │ └── ResponseCommand.java │ │ │ └── viewadapter │ │ │ │ ├── checkbox │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── edittext │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── image │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── listview │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── mswitch │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── radiogroup │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── recyclerview │ │ │ │ ├── DividerLine.java │ │ │ │ ├── LayoutManagers.java │ │ │ │ ├── LineManagers.java │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── scrollview │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── spinner │ │ │ │ ├── IKeyAndValue.java │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── swiperefresh │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── view │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── viewgroup │ │ │ │ ├── IBindingItemViewModel.java │ │ │ │ └── ViewAdapter.java │ │ │ │ ├── viewpager │ │ │ │ └── ViewAdapter.java │ │ │ │ └── webview │ │ │ │ └── ViewAdapter.java │ │ │ ├── bus │ │ │ ├── Messenger.java │ │ │ ├── RxBus.java │ │ │ ├── RxBusSubscriber.java │ │ │ ├── RxSubscriptions.java │ │ │ ├── WeakAction.java │ │ │ └── event │ │ │ │ ├── SingleLiveEvent.java │ │ │ │ └── SnackbarMessage.java │ │ │ ├── crash │ │ │ ├── CaocConfig.java │ │ │ ├── CaocInitProvider.java │ │ │ ├── CustomActivityOnCrash.java │ │ │ └── DefaultErrorActivity.java │ │ │ ├── http │ │ │ ├── ApiDisposableObserver.java │ │ │ ├── BaseResponse.java │ │ │ ├── DownLoadManager.java │ │ │ ├── ExceptionHandle.java │ │ │ ├── NetworkUtil.java │ │ │ ├── ResponseThrowable.java │ │ │ ├── cookie │ │ │ │ ├── CookieJarImpl.java │ │ │ │ └── store │ │ │ │ │ ├── CookieStore.java │ │ │ │ │ ├── MemoryCookieStore.java │ │ │ │ │ ├── PersistentCookieStore.java │ │ │ │ │ └── SerializableHttpCookie.java │ │ │ ├── download │ │ │ │ ├── DownLoadStateBean.java │ │ │ │ ├── DownLoadSubscriber.java │ │ │ │ ├── ProgressCallBack.java │ │ │ │ └── ProgressResponseBody.java │ │ │ └── interceptor │ │ │ │ ├── BaseInterceptor.java │ │ │ │ ├── CacheInterceptor.java │ │ │ │ ├── ProgressInterceptor.java │ │ │ │ └── logging │ │ │ │ ├── I.java │ │ │ │ ├── Level.java │ │ │ │ ├── Logger.java │ │ │ │ ├── LoggingInterceptor.java │ │ │ │ └── Printer.java │ │ │ ├── utils │ │ │ ├── CloseUtils.java │ │ │ ├── ConvertUtils.java │ │ │ ├── ImageUtils.java │ │ │ ├── KLog.java │ │ │ ├── MaterialDialogUtils.java │ │ │ ├── RegexUtils.java │ │ │ ├── RxUtils.java │ │ │ ├── SDCardUtils.java │ │ │ ├── SPUtils.java │ │ │ ├── StringUtils.java │ │ │ ├── ToastUtils.java │ │ │ ├── Utils.java │ │ │ ├── compression │ │ │ │ ├── Luban.java │ │ │ │ ├── OnCompressListener.java │ │ │ │ └── Preconditions.java │ │ │ └── constant │ │ │ │ ├── MemoryConstants.java │ │ │ │ ├── RegexConstants.java │ │ │ │ └── TimeConstants.java │ │ │ └── widget │ │ │ └── ControlDistributeLinearLayout.java │ └── res │ │ ├── drawable-hdpi │ │ └── customactivityoncrash_error_image.png │ │ ├── drawable-mdpi │ │ └── customactivityoncrash_error_image.png │ │ ├── drawable-xhdpi │ │ └── customactivityoncrash_error_image.png │ │ ├── drawable-xxhdpi │ │ └── customactivityoncrash_error_image.png │ │ ├── drawable-xxxhdpi │ │ └── customactivityoncrash_error_image.png │ │ ├── layout │ │ ├── activity_container.xml │ │ └── customactivityoncrash_default_error_activity.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ └── test │ └── java │ └── me │ └── goldze │ └── mvvmhabit │ └── ExampleUnitTest.java └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | key.jks 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | /.idea 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | /app/release 13 | /mvvmhabit/bintray.gradle 14 | -------------------------------------------------------------------------------- /UpdateLog.md: -------------------------------------------------------------------------------- 1 | ## 更新日志 2 | **v4.0.0:2021年07月16日** 3 | 4 | - 迁移AndroidX分支作为主线分支; 5 | - 升级第三方框架依赖版本; 6 | - 升级gradle插件版本支持; 7 | - 优化框架代码,解决已知Bug; 8 | - 修改文档说明。 9 | *** 10 | **v3.0.7:2019年1月25日** 11 | 12 | - 优化框架代码,解决已知Bug; 13 | - 新增ViewPager+Fragment例子; 14 | - 新增RecycleView多布局例子; 15 | - 升级第三方依赖库; 16 | - 修改文档说明。 17 | *** 18 | **v3.0.0:2018年10月8日** 19 | 20 | - 全面升级AAC,引入谷歌lifecycle组件; 21 | - 修改Base基类,满足新一套模式; 22 | - 升级第三方依赖库; 23 | - 修改例子程序; 24 | - 修改文档说明。 25 | *** 26 | **v2.0.6:2018年7月19日** 27 | 28 | - 优化框架性能、基类逻辑,新增绑定命令; 29 | - 补充例子程序及注释; 30 | - 升级/修改第三方依赖库; 31 | - 补充文档说明。 32 | *** 33 | **v2.0.0:2018年4月10日** 34 | 35 | - 全面升级RxJava2; 36 | - 优化绑定回调方式; 37 | - 升级第三方依赖库; 38 | - 微调例子程序。 39 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.android.compileSdkVersion 5 | defaultConfig { 6 | applicationId rootProject.ext.android.applicationId 7 | minSdkVersion rootProject.ext.android.minSdkVersion 8 | targetSdkVersion rootProject.ext.android.targetSdkVersion 9 | versionCode rootProject.ext.android.versionCode 10 | versionName rootProject.ext.android.versionName 11 | } 12 | buildTypes { 13 | release { 14 | minifyEnabled true 15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 16 | } 17 | debug { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | dataBinding { 23 | enabled true 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(include: ['*.jar'], dir: 'libs') 29 | //support 30 | implementation rootProject.ext.support["design"] 31 | //下拉刷新,上拉加载 32 | implementation 'com.lcodecorex:tkrefreshlayout:1.0.7' 33 | //底部tabBar 34 | implementation('me.majiajie:pager-bottom-tab-strip:2.2.5') { 35 | exclude group: 'com.android.support' 36 | } 37 | //MVVMHabit 38 | implementation project(':mvvmhabit') 39 | // implementation rootProject.ext.dependencies.MVVMHabit 40 | //内存泄漏测试 41 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3' 42 | debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3' 43 | } 44 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/goldze/mvvmhabit/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.goldze.mvvmhabit", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/app/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.app; 2 | 3 | import com.goldze.mvvmhabit.BuildConfig; 4 | import com.goldze.mvvmhabit.R; 5 | import com.goldze.mvvmhabit.ui.login.LoginActivity; 6 | import com.squareup.leakcanary.LeakCanary; 7 | 8 | import me.goldze.mvvmhabit.base.BaseApplication; 9 | import me.goldze.mvvmhabit.crash.CaocConfig; 10 | import me.goldze.mvvmhabit.utils.KLog; 11 | 12 | /** 13 | * Created by goldze on 2017/7/16. 14 | */ 15 | 16 | public class AppApplication extends BaseApplication { 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | //是否开启打印日志 21 | KLog.init(BuildConfig.DEBUG); 22 | //初始化全局异常崩溃 23 | initCrash(); 24 | //内存泄漏检测 25 | if (!LeakCanary.isInAnalyzerProcess(this)) { 26 | LeakCanary.install(this); 27 | } 28 | } 29 | 30 | private void initCrash() { 31 | CaocConfig.Builder.create() 32 | .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) //背景模式,开启沉浸式 33 | .enabled(true) //是否启动全局异常捕获 34 | .showErrorDetails(true) //是否显示错误详细信息 35 | .showRestartButton(true) //是否显示重启按钮 36 | .trackActivities(true) //是否跟踪Activity 37 | .minTimeBetweenCrashesMs(2000) //崩溃的间隔时间(毫秒) 38 | .errorDrawable(R.mipmap.ic_launcher) //错误图标 39 | .restartActivity(LoginActivity.class) //重新启动后的activity 40 | // .errorActivity(YourCustomErrorActivity.class) //崩溃后的错误activity 41 | // .eventListener(new YourCustomEventListener()) //崩溃后的错误监听 42 | .apply(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/app/AppViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.app; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Application; 5 | 6 | import com.goldze.mvvmhabit.data.DemoRepository; 7 | import com.goldze.mvvmhabit.ui.login.LoginViewModel; 8 | import com.goldze.mvvmhabit.ui.network.NetWorkViewModel; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.annotation.VisibleForTesting; 12 | import androidx.lifecycle.ViewModel; 13 | import androidx.lifecycle.ViewModelProvider; 14 | 15 | /** 16 | * Created by goldze on 2019/3/26. 17 | */ 18 | public class AppViewModelFactory extends ViewModelProvider.NewInstanceFactory { 19 | @SuppressLint("StaticFieldLeak") 20 | private static volatile AppViewModelFactory INSTANCE; 21 | private final Application mApplication; 22 | private final DemoRepository mRepository; 23 | 24 | public static AppViewModelFactory getInstance(Application application) { 25 | if (INSTANCE == null) { 26 | synchronized (AppViewModelFactory.class) { 27 | if (INSTANCE == null) { 28 | INSTANCE = new AppViewModelFactory(application, Injection.provideDemoRepository()); 29 | } 30 | } 31 | } 32 | return INSTANCE; 33 | } 34 | 35 | @VisibleForTesting 36 | public static void destroyInstance() { 37 | INSTANCE = null; 38 | } 39 | 40 | private AppViewModelFactory(Application application, DemoRepository repository) { 41 | this.mApplication = application; 42 | this.mRepository = repository; 43 | } 44 | 45 | @NonNull 46 | @Override 47 | public T create(@NonNull Class modelClass) { 48 | if (modelClass.isAssignableFrom(NetWorkViewModel.class)) { 49 | return (T) new NetWorkViewModel(mApplication, mRepository); 50 | } else if (modelClass.isAssignableFrom(LoginViewModel.class)) { 51 | return (T) new LoginViewModel(mApplication, mRepository); 52 | } 53 | throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass.getName()); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/app/Injection.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.app; 2 | 3 | import com.goldze.mvvmhabit.data.DemoRepository; 4 | import com.goldze.mvvmhabit.data.source.HttpDataSource; 5 | import com.goldze.mvvmhabit.data.source.LocalDataSource; 6 | import com.goldze.mvvmhabit.data.source.http.HttpDataSourceImpl; 7 | import com.goldze.mvvmhabit.data.source.http.service.DemoApiService; 8 | import com.goldze.mvvmhabit.data.source.local.LocalDataSourceImpl; 9 | import com.goldze.mvvmhabit.utils.RetrofitClient; 10 | 11 | 12 | /** 13 | * 注入全局的数据仓库,可以考虑使用Dagger2。(根据项目实际情况搭建,千万不要为了架构而架构) 14 | * Created by goldze on 2019/3/26. 15 | */ 16 | public class Injection { 17 | public static DemoRepository provideDemoRepository() { 18 | //网络API服务 19 | DemoApiService apiService = RetrofitClient.getInstance().create(DemoApiService.class); 20 | //网络数据源 21 | HttpDataSource httpDataSource = HttpDataSourceImpl.getInstance(apiService); 22 | //本地数据源 23 | LocalDataSource localDataSource = LocalDataSourceImpl.getInstance(); 24 | //两条分支组成一个数据仓库 25 | return DemoRepository.getInstance(httpDataSource, localDataSource); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/binding/twinklingrefreshlayout/ViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.binding.twinklingrefreshlayout; 2 | 3 | import com.lcodecore.tkrefreshlayout.RefreshListenerAdapter; 4 | import com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout; 5 | 6 | import androidx.databinding.BindingAdapter; 7 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 8 | 9 | 10 | /** 11 | * Created by goldze on 2017/6/16. 12 | * TwinklingRefreshLayout列表刷新的绑定适配器 13 | */ 14 | public class ViewAdapter { 15 | 16 | @BindingAdapter(value = {"onRefreshCommand", "onLoadMoreCommand"}, requireAll = false) 17 | public static void onRefreshAndLoadMoreCommand(TwinklingRefreshLayout layout, final BindingCommand onRefreshCommand, final BindingCommand onLoadMoreCommand) { 18 | layout.setOnRefreshListener(new RefreshListenerAdapter() { 19 | @Override 20 | public void onRefresh(TwinklingRefreshLayout refreshLayout) { 21 | super.onRefresh(refreshLayout); 22 | if (onRefreshCommand != null) { 23 | onRefreshCommand.execute(); 24 | } 25 | } 26 | 27 | @Override 28 | public void onLoadMore(TwinklingRefreshLayout refreshLayout) { 29 | super.onLoadMore(refreshLayout); 30 | if (onLoadMoreCommand != null) { 31 | onLoadMoreCommand.execute(); 32 | } 33 | } 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/DemoRepository.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data; 2 | 3 | import com.goldze.mvvmhabit.data.source.HttpDataSource; 4 | import com.goldze.mvvmhabit.data.source.LocalDataSource; 5 | import com.goldze.mvvmhabit.entity.DemoEntity; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.VisibleForTesting; 9 | import io.reactivex.Observable; 10 | import me.goldze.mvvmhabit.base.BaseModel; 11 | import me.goldze.mvvmhabit.http.BaseResponse; 12 | 13 | /** 14 | * MVVM的Model层,统一模块的数据仓库,包含网络数据和本地数据(一个应用可以有多个Repositor) 15 | * Created by goldze on 2019/3/26. 16 | */ 17 | public class DemoRepository extends BaseModel implements HttpDataSource, LocalDataSource { 18 | private volatile static DemoRepository INSTANCE = null; 19 | private final HttpDataSource mHttpDataSource; 20 | 21 | private final LocalDataSource mLocalDataSource; 22 | 23 | private DemoRepository(@NonNull HttpDataSource httpDataSource, 24 | @NonNull LocalDataSource localDataSource) { 25 | this.mHttpDataSource = httpDataSource; 26 | this.mLocalDataSource = localDataSource; 27 | } 28 | 29 | public static DemoRepository getInstance(HttpDataSource httpDataSource, 30 | LocalDataSource localDataSource) { 31 | if (INSTANCE == null) { 32 | synchronized (DemoRepository.class) { 33 | if (INSTANCE == null) { 34 | INSTANCE = new DemoRepository(httpDataSource, localDataSource); 35 | } 36 | } 37 | } 38 | return INSTANCE; 39 | } 40 | 41 | @VisibleForTesting 42 | public static void destroyInstance() { 43 | INSTANCE = null; 44 | } 45 | 46 | 47 | @Override 48 | public Observable login() { 49 | return mHttpDataSource.login(); 50 | } 51 | 52 | @Override 53 | public Observable loadMore() { 54 | return mHttpDataSource.loadMore(); 55 | } 56 | 57 | @Override 58 | public Observable> demoGet() { 59 | return mHttpDataSource.demoGet(); 60 | } 61 | 62 | @Override 63 | public Observable> demoPost(String catalog) { 64 | return mHttpDataSource.demoPost(catalog); 65 | } 66 | 67 | @Override 68 | public void saveUserName(String userName) { 69 | mLocalDataSource.saveUserName(userName); 70 | } 71 | 72 | @Override 73 | public void savePassword(String password) { 74 | mLocalDataSource.savePassword(password); 75 | } 76 | 77 | @Override 78 | public String getUserName() { 79 | return mLocalDataSource.getUserName(); 80 | } 81 | 82 | @Override 83 | public String getPassword() { 84 | return mLocalDataSource.getPassword(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/source/HttpDataSource.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data.source; 2 | 3 | import com.goldze.mvvmhabit.entity.DemoEntity; 4 | 5 | import io.reactivex.Observable; 6 | import me.goldze.mvvmhabit.http.BaseResponse; 7 | 8 | /** 9 | * Created by goldze on 2019/3/26. 10 | */ 11 | public interface HttpDataSource { 12 | //模拟登录 13 | Observable login(); 14 | 15 | //模拟上拉加载 16 | Observable loadMore(); 17 | 18 | Observable> demoGet(); 19 | 20 | Observable> demoPost(String catalog); 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/source/LocalDataSource.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data.source; 2 | 3 | /** 4 | * Created by goldze on 2019/3/26. 5 | */ 6 | public interface LocalDataSource { 7 | /** 8 | * 保存用户名 9 | */ 10 | void saveUserName(String userName); 11 | 12 | /** 13 | * 保存用户密码 14 | */ 15 | 16 | void savePassword(String password); 17 | 18 | /** 19 | * 获取用户名 20 | */ 21 | String getUserName(); 22 | 23 | /** 24 | * 获取用户密码 25 | */ 26 | String getPassword(); 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/source/http/HttpDataSourceImpl.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data.source.http; 2 | 3 | import com.goldze.mvvmhabit.data.source.HttpDataSource; 4 | import com.goldze.mvvmhabit.data.source.http.service.DemoApiService; 5 | import com.goldze.mvvmhabit.entity.DemoEntity; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import io.reactivex.Observable; 12 | import io.reactivex.ObservableEmitter; 13 | import io.reactivex.ObservableOnSubscribe; 14 | import me.goldze.mvvmhabit.http.BaseResponse; 15 | 16 | /** 17 | * Created by goldze on 2019/3/26. 18 | */ 19 | public class HttpDataSourceImpl implements HttpDataSource { 20 | private DemoApiService apiService; 21 | private volatile static HttpDataSourceImpl INSTANCE = null; 22 | 23 | public static HttpDataSourceImpl getInstance(DemoApiService apiService) { 24 | if (INSTANCE == null) { 25 | synchronized (HttpDataSourceImpl.class) { 26 | if (INSTANCE == null) { 27 | INSTANCE = new HttpDataSourceImpl(apiService); 28 | } 29 | } 30 | } 31 | return INSTANCE; 32 | } 33 | 34 | public static void destroyInstance() { 35 | INSTANCE = null; 36 | } 37 | 38 | private HttpDataSourceImpl(DemoApiService apiService) { 39 | this.apiService = apiService; 40 | } 41 | 42 | @Override 43 | public Observable login() { 44 | return Observable.just(new Object()).delay(3, TimeUnit.SECONDS); //延迟3秒 45 | } 46 | 47 | @Override 48 | public Observable loadMore() { 49 | return Observable.create(new ObservableOnSubscribe() { 50 | @Override 51 | public void subscribe(ObservableEmitter observableEmitter) throws Exception { 52 | DemoEntity entity = new DemoEntity(); 53 | List itemsEntities = new ArrayList<>(); 54 | //模拟一部分假数据 55 | for (int i = 0; i < 10; i++) { 56 | DemoEntity.ItemsEntity item = new DemoEntity.ItemsEntity(); 57 | item.setId(-1); 58 | item.setName("模拟条目"); 59 | itemsEntities.add(item); 60 | } 61 | entity.setItems(itemsEntities); 62 | observableEmitter.onNext(entity); 63 | } 64 | }).delay(3, TimeUnit.SECONDS); //延迟3秒 65 | } 66 | 67 | @Override 68 | public Observable> demoGet() { 69 | return apiService.demoGet(); 70 | } 71 | 72 | @Override 73 | public Observable> demoPost(String catalog) { 74 | return apiService.demoPost(catalog); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/source/http/service/DemoApiService.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data.source.http.service; 2 | 3 | import com.goldze.mvvmhabit.entity.DemoEntity; 4 | 5 | import io.reactivex.Observable; 6 | import me.goldze.mvvmhabit.http.BaseResponse; 7 | import retrofit2.http.Field; 8 | import retrofit2.http.FormUrlEncoded; 9 | import retrofit2.http.GET; 10 | import retrofit2.http.POST; 11 | 12 | /** 13 | * Created by goldze on 2017/6/15. 14 | */ 15 | 16 | public interface DemoApiService { 17 | @GET("action/apiv2/banner?catalog=1") 18 | Observable> demoGet(); 19 | 20 | @FormUrlEncoded 21 | @POST("action/apiv2/banner") 22 | Observable> demoPost(@Field("catalog") String catalog); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/data/source/local/LocalDataSourceImpl.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.data.source.local; 2 | 3 | import com.goldze.mvvmhabit.data.source.LocalDataSource; 4 | 5 | import me.goldze.mvvmhabit.utils.SPUtils; 6 | 7 | /** 8 | * 本地数据源,可配合Room框架使用 9 | * Created by goldze on 2019/3/26. 10 | */ 11 | public class LocalDataSourceImpl implements LocalDataSource { 12 | private volatile static LocalDataSourceImpl INSTANCE = null; 13 | 14 | public static LocalDataSourceImpl getInstance() { 15 | if (INSTANCE == null) { 16 | synchronized (LocalDataSourceImpl.class) { 17 | if (INSTANCE == null) { 18 | INSTANCE = new LocalDataSourceImpl(); 19 | } 20 | } 21 | } 22 | return INSTANCE; 23 | } 24 | 25 | public static void destroyInstance() { 26 | INSTANCE = null; 27 | } 28 | 29 | private LocalDataSourceImpl() { 30 | //数据库Helper构建 31 | } 32 | 33 | @Override 34 | public void saveUserName(String userName) { 35 | SPUtils.getInstance().put("UserName", userName); 36 | } 37 | 38 | @Override 39 | public void savePassword(String password) { 40 | SPUtils.getInstance().put("password", password); 41 | } 42 | 43 | @Override 44 | public String getUserName() { 45 | return SPUtils.getInstance().getString("UserName"); 46 | } 47 | 48 | @Override 49 | public String getPassword() { 50 | return SPUtils.getInstance().getString("password"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/entity/FormEntity.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import androidx.databinding.BaseObservable; 7 | 8 | /** 9 | * Created by goldze on 2017/7/17. 10 | */ 11 | 12 | public class FormEntity extends BaseObservable implements Parcelable { 13 | private String id; 14 | private String name; 15 | private String sex; 16 | private String Bir; 17 | private String hobby; 18 | private Boolean isMarry; 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getSex() { 37 | return sex; 38 | } 39 | 40 | public void setSex(String sex) { 41 | this.sex = sex; 42 | } 43 | 44 | public String getBir() { 45 | return Bir; 46 | } 47 | 48 | public void setBir(String bir) { 49 | Bir = bir; 50 | } 51 | 52 | public String getHobby() { 53 | return hobby; 54 | } 55 | 56 | public void setHobby(String hobby) { 57 | this.hobby = hobby; 58 | } 59 | 60 | public FormEntity() { 61 | } 62 | 63 | public Boolean getMarry() { 64 | return isMarry; 65 | } 66 | 67 | public void setMarry(Boolean marry) { 68 | isMarry = marry; 69 | } 70 | 71 | @Override 72 | public int describeContents() { 73 | return 0; 74 | } 75 | 76 | @Override 77 | public void writeToParcel(Parcel dest, int flags) { 78 | dest.writeString(this.id); 79 | dest.writeString(this.name); 80 | dest.writeString(this.sex); 81 | dest.writeString(this.Bir); 82 | dest.writeString(this.hobby); 83 | dest.writeValue(this.isMarry); 84 | } 85 | 86 | protected FormEntity(Parcel in) { 87 | this.id = in.readString(); 88 | this.name = in.readString(); 89 | this.sex = in.readString(); 90 | this.Bir = in.readString(); 91 | this.hobby = in.readString(); 92 | this.isMarry = (Boolean) in.readValue(Boolean.class.getClassLoader()); 93 | } 94 | 95 | public static final Creator CREATOR = new Creator() { 96 | @Override 97 | public FormEntity createFromParcel(Parcel source) { 98 | return new FormEntity(source); 99 | } 100 | 101 | @Override 102 | public FormEntity[] newArray(int size) { 103 | return new FormEntity[size]; 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/entity/SpinnerItemData.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.entity; 2 | 3 | import me.goldze.mvvmhabit.binding.viewadapter.spinner.IKeyAndValue; 4 | 5 | /** 6 | * Created by goldze on 2017/7/17. 7 | * Spinner条目数据实体 8 | * 该实体类可以自定义,比如该类是数据库实体类. 或者是数据字典实体类, 但需要实现IKeyAndValue接口, 返回key和value两个值就可以在Spinner中绑定使用了 9 | */ 10 | 11 | public class SpinnerItemData implements IKeyAndValue { 12 | //key是下拉显示的文字 13 | private String key; 14 | //value是对应需要上传给后台的值, 这个可以根据具体业务具体定义 15 | private String value; 16 | 17 | public SpinnerItemData(String key, String value) { 18 | this.key = key; 19 | this.value = value; 20 | } 21 | 22 | @Override 23 | public String getKey() { 24 | return key; 25 | } 26 | 27 | @Override 28 | public String getValue() { 29 | return value; 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/base/adapter/BaseFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.base.adapter; 2 | 3 | import java.util.List; 4 | 5 | import androidx.fragment.app.Fragment; 6 | import androidx.fragment.app.FragmentManager; 7 | import androidx.fragment.app.FragmentPagerAdapter; 8 | 9 | /** 10 | * Created by goldze on 2017/7/17. 11 | * FragmentPager适配器 12 | */ 13 | 14 | public class BaseFragmentPagerAdapter extends FragmentPagerAdapter { 15 | private List list;//ViewPager要填充的fragment列表 16 | private List title;//tab中的title文字列表 17 | 18 | //使用构造方法来将数据传进去 19 | public BaseFragmentPagerAdapter(FragmentManager fm, List list, List title) { 20 | super(fm); 21 | this.list = list; 22 | this.title = title; 23 | } 24 | 25 | @Override 26 | public Fragment getItem(int position) {//获得position中的fragment来填充 27 | return list.get(position); 28 | } 29 | 30 | @Override 31 | public int getCount() {//返回FragmentPager的个数 32 | return list.size(); 33 | } 34 | 35 | //FragmentPager的标题,如果重写这个方法就显示不出tab的标题内容 36 | @Override 37 | public CharSequence getPageTitle(int position) { 38 | return title.get(position); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/base/fragment/BasePagerFragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.base.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | import com.goldze.mvvmhabit.databinding.FragmentBasePagerBinding; 10 | import com.goldze.mvvmhabit.ui.base.adapter.BaseFragmentPagerAdapter; 11 | import com.google.android.material.tabs.TabLayout; 12 | 13 | import java.util.List; 14 | 15 | import androidx.annotation.Nullable; 16 | import androidx.fragment.app.Fragment; 17 | import me.goldze.mvvmhabit.base.BaseFragment; 18 | import me.goldze.mvvmhabit.base.BaseViewModel; 19 | 20 | /** 21 | * Created by goldze on 2017/7/17. 22 | * 抽取的二级BasePagerFragment 23 | */ 24 | 25 | public abstract class BasePagerFragment extends BaseFragment { 26 | 27 | private List mFragments; 28 | private List titlePager; 29 | 30 | protected abstract List pagerFragment(); 31 | 32 | protected abstract List pagerTitleString(); 33 | 34 | @Override 35 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 36 | return R.layout.fragment_base_pager; 37 | } 38 | 39 | @Override 40 | public int initVariableId() { 41 | return BR.viewModel; 42 | } 43 | 44 | @Override 45 | public void initData() { 46 | mFragments = pagerFragment(); 47 | titlePager = pagerTitleString(); 48 | //设置Adapter 49 | BaseFragmentPagerAdapter pagerAdapter = new BaseFragmentPagerAdapter(getChildFragmentManager(), mFragments, titlePager); 50 | binding.viewPager.setAdapter(pagerAdapter); 51 | binding.tabs.setupWithViewPager(binding.viewPager); 52 | binding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.tabs)); 53 | } 54 | 55 | @Override 56 | public void initViewObservable() { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/base/viewmodel/ToolbarViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.base.viewmodel; 2 | 3 | import android.app.Application; 4 | import android.view.View; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.databinding.ObservableField; 8 | import androidx.databinding.ObservableInt; 9 | import me.goldze.mvvmhabit.base.BaseModel; 10 | import me.goldze.mvvmhabit.base.BaseViewModel; 11 | import me.goldze.mvvmhabit.binding.command.BindingAction; 12 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 13 | 14 | /** 15 | * Create Author:goldze 16 | * Create Date:2019/01/03 17 | * Description: 对应include标题的ToolbarViewModel 18 | * Toolbar的封装方式有很多种,具体封装需根据项目实际业务和习惯来编写 19 | * 所有例子仅做参考,业务多种多样,可能我这里写的例子和你的需求不同,理解如何使用才最重要。 20 | */ 21 | 22 | public class ToolbarViewModel extends BaseViewModel { 23 | //标题文字 24 | public ObservableField titleText = new ObservableField<>(""); 25 | //右边文字 26 | public ObservableField rightText = new ObservableField<>("更多"); 27 | //右边文字的观察者 28 | public ObservableInt rightTextVisibleObservable = new ObservableInt(View.GONE); 29 | //右边图标的观察者 30 | public ObservableInt rightIconVisibleObservable = new ObservableInt(View.GONE); 31 | //兼容databinding,去泛型化 32 | public ToolbarViewModel toolbarViewModel; 33 | 34 | public ToolbarViewModel(@NonNull Application application) { 35 | this(application, null); 36 | } 37 | 38 | public ToolbarViewModel(@NonNull Application application, M model) { 39 | super(application, model); 40 | toolbarViewModel = this; 41 | } 42 | 43 | /** 44 | * 设置标题 45 | * 46 | * @param text 标题文字 47 | */ 48 | public void setTitleText(String text) { 49 | titleText.set(text); 50 | } 51 | 52 | /** 53 | * 设置右边文字 54 | * 55 | * @param text 右边文字 56 | */ 57 | public void setRightText(String text) { 58 | rightText.set(text); 59 | } 60 | 61 | /** 62 | * 设置右边文字的显示和隐藏 63 | * 64 | * @param visibility 65 | */ 66 | public void setRightTextVisible(int visibility) { 67 | rightTextVisibleObservable.set(visibility); 68 | } 69 | 70 | /** 71 | * 设置右边图标的显示和隐藏 72 | * 73 | * @param visibility 74 | */ 75 | public void setRightIconVisible(int visibility) { 76 | rightIconVisibleObservable.set(visibility); 77 | } 78 | 79 | /** 80 | * 返回按钮的点击事件 81 | */ 82 | public final BindingCommand backOnClick = new BindingCommand(new BindingAction() { 83 | @Override 84 | public void call() { 85 | finish(); 86 | } 87 | }); 88 | 89 | public BindingCommand rightTextOnClick = new BindingCommand(new BindingAction() { 90 | @Override 91 | public void call() { 92 | rightTextOnClick(); 93 | } 94 | }); 95 | public BindingCommand rightIconOnClick = new BindingCommand(new BindingAction() { 96 | @Override 97 | public void call() { 98 | rightIconOnClick(); 99 | } 100 | }); 101 | 102 | /** 103 | * 右边文字的点击事件,子类可重写 104 | */ 105 | protected void rightTextOnClick() { 106 | } 107 | 108 | /** 109 | * 右边图标的点击事件,子类可重写 110 | */ 111 | protected void rightIconOnClick() { 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/form/FormFragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.form; 2 | 3 | import android.app.DatePickerDialog; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | import android.widget.DatePicker; 8 | 9 | import com.goldze.mvvmhabit.BR; 10 | import com.goldze.mvvmhabit.R; 11 | import com.goldze.mvvmhabit.databinding.FragmentFormBinding; 12 | import com.goldze.mvvmhabit.entity.FormEntity; 13 | 14 | import java.util.Calendar; 15 | 16 | import androidx.annotation.Nullable; 17 | import androidx.appcompat.app.AppCompatActivity; 18 | import androidx.databinding.Observable; 19 | import androidx.lifecycle.Observer; 20 | import me.goldze.mvvmhabit.base.BaseFragment; 21 | import me.goldze.mvvmhabit.utils.MaterialDialogUtils; 22 | 23 | /** 24 | * Created by goldze on 2017/7/17. 25 | * 表单提交/编辑界面 26 | */ 27 | 28 | public class FormFragment extends BaseFragment { 29 | 30 | private FormEntity entity = new FormEntity(); 31 | 32 | @Override 33 | public void initParam() { 34 | //获取列表传入的实体 35 | Bundle mBundle = getArguments(); 36 | if (mBundle != null) { 37 | entity = mBundle.getParcelable("entity"); 38 | } 39 | } 40 | 41 | @Override 42 | public int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 43 | return R.layout.fragment_form; 44 | } 45 | 46 | @Override 47 | public int initVariableId() { 48 | return BR.viewModel; 49 | } 50 | 51 | @Override 52 | public void initData() { 53 | //通过binding拿到toolbar控件, 设置给Activity 54 | ((AppCompatActivity) getActivity()).setSupportActionBar(binding.include.toolbar); 55 | //View层传参到ViewModel层 56 | viewModel.setFormEntity(entity); 57 | //初始化标题 58 | viewModel.initToolbar(); 59 | } 60 | 61 | @Override 62 | public void initViewObservable() { 63 | //监听日期选择 64 | viewModel.uc.showDateDialogObservable.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { 65 | @Override 66 | public void onPropertyChanged(Observable sender, int propertyId) { 67 | final Calendar calendar = Calendar.getInstance(); 68 | int year = calendar.get(Calendar.YEAR); 69 | int month = calendar.get(Calendar.MONTH); 70 | int day = calendar.get(Calendar.DAY_OF_MONTH); 71 | DatePickerDialog datePickerDialog = new DatePickerDialog(getContext(), new DatePickerDialog.OnDateSetListener() { 72 | @Override 73 | public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) { 74 | viewModel.setBir(year, month, dayOfMonth); 75 | } 76 | }, year, month, day); 77 | datePickerDialog.setMessage("生日选择"); 78 | datePickerDialog.show(); 79 | } 80 | }); 81 | viewModel.entityJsonLiveData.observe(this, new Observer() { 82 | @Override 83 | public void onChanged(@Nullable String submitJson) { 84 | MaterialDialogUtils.showBasicDialog(getContext(), "提交的json实体数据:\r\n" + submitJson).show(); 85 | } 86 | }); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/login/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.login; 2 | 3 | import android.os.Bundle; 4 | import android.text.method.HideReturnsTransformationMethod; 5 | import android.text.method.PasswordTransformationMethod; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | import com.goldze.mvvmhabit.app.AppViewModelFactory; 10 | import com.goldze.mvvmhabit.databinding.ActivityLoginBinding; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.lifecycle.Observer; 14 | import androidx.lifecycle.ViewModelProviders; 15 | import me.goldze.mvvmhabit.base.BaseActivity; 16 | 17 | /** 18 | * 一个MVVM模式的登陆界面 19 | */ 20 | public class LoginActivity extends BaseActivity { 21 | //ActivityLoginBinding类是databinding框架自定生成的,对应activity_login.xml 22 | @Override 23 | public int initContentView(Bundle savedInstanceState) { 24 | return R.layout.activity_login; 25 | } 26 | 27 | @Override 28 | public int initVariableId() { 29 | return BR.viewModel; 30 | } 31 | 32 | @Override 33 | public LoginViewModel initViewModel() { 34 | //使用自定义的ViewModelFactory来创建ViewModel,如果不重写该方法,则默认会调用LoginViewModel(@NonNull Application application)构造方法 35 | AppViewModelFactory factory = AppViewModelFactory.getInstance(getApplication()); 36 | return ViewModelProviders.of(this, factory).get(LoginViewModel.class); 37 | } 38 | 39 | @Override 40 | public void initViewObservable() { 41 | //监听ViewModel中pSwitchObservable的变化, 当ViewModel中执行【uc.pSwitchObservable.set(!uc.pSwitchObservable.get());】时会回调该方法 42 | viewModel.uc.pSwitchEvent.observe(this, new Observer() { 43 | @Override 44 | public void onChanged(@Nullable Boolean aBoolean) { 45 | //pSwitchObservable是boolean类型的观察者,所以可以直接使用它的值改变密码开关的图标 46 | if (viewModel.uc.pSwitchEvent.getValue()) { 47 | //密码可见 48 | //在xml中定义id后,使用binding可以直接拿到这个view的引用,不再需要findViewById去找控件了 49 | binding.ivSwichPasswrod.setImageResource(R.mipmap.show_psw); 50 | binding.etPassword.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); 51 | } else { 52 | //密码不可见 53 | binding.ivSwichPasswrod.setImageResource(R.mipmap.show_psw_press); 54 | binding.etPassword.setTransformationMethod(PasswordTransformationMethod.getInstance()); 55 | } 56 | } 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/network/NetWorkItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.network; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.os.Bundle; 5 | 6 | import com.goldze.mvvmhabit.R; 7 | import com.goldze.mvvmhabit.entity.DemoEntity; 8 | import com.goldze.mvvmhabit.ui.network.detail.DetailFragment; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.core.content.ContextCompat; 12 | import androidx.databinding.ObservableField; 13 | import me.goldze.mvvmhabit.base.ItemViewModel; 14 | import me.goldze.mvvmhabit.binding.command.BindingAction; 15 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 16 | import me.goldze.mvvmhabit.utils.ToastUtils; 17 | 18 | /** 19 | * Created by goldze on 2017/7/17. 20 | */ 21 | 22 | public class NetWorkItemViewModel extends ItemViewModel { 23 | public ObservableField entity = new ObservableField<>(); 24 | public Drawable drawableImg; 25 | 26 | public NetWorkItemViewModel(@NonNull NetWorkViewModel viewModel, DemoEntity.ItemsEntity entity) { 27 | super(viewModel); 28 | this.entity.set(entity); 29 | //ImageView的占位图片,可以解决RecyclerView中图片错误问题 30 | drawableImg = ContextCompat.getDrawable(viewModel.getApplication(), R.mipmap.ic_launcher); 31 | } 32 | 33 | /** 34 | * 获取position的方式有很多种,indexOf是其中一种,常见的还有在Adapter中、ItemBinding.of回调里 35 | * 36 | * @return 37 | */ 38 | public int getPosition() { 39 | return viewModel.getItemPosition(this); 40 | } 41 | 42 | //条目的点击事件 43 | public BindingCommand itemClick = new BindingCommand(new BindingAction() { 44 | @Override 45 | public void call() { 46 | //这里可以通过一个标识,做出判断,已达到跳入不同界面的逻辑 47 | if (entity.get().getId() == -1) { 48 | viewModel.deleteItemLiveData.setValue(NetWorkItemViewModel.this); 49 | } else { 50 | //跳转到详情界面,传入条目的实体对象 51 | Bundle mBundle = new Bundle(); 52 | mBundle.putParcelable("entity", entity.get()); 53 | viewModel.startContainerActivity(DetailFragment.class.getCanonicalName(), mBundle); 54 | } 55 | } 56 | }); 57 | //条目的长按事件 58 | public BindingCommand itemLongClick = new BindingCommand(new BindingAction() { 59 | @Override 60 | public void call() { 61 | //以前是使用Messenger发送事件,在NetWorkViewModel中完成删除逻辑 62 | // Messenger.getDefault().send(NetWorkItemViewModel.this, NetWorkViewModel.TOKEN_NETWORKVIEWMODEL_DELTE_ITEM); 63 | //现在ItemViewModel中存在ViewModel引用,可以直接拿到LiveData去做删除 64 | ToastUtils.showShort(entity.get().getName()); 65 | } 66 | }); 67 | // /** 68 | // * 可以在xml中使用binding:currentView="@{viewModel.titleTextView}" 拿到这个控件的引用, 但是强烈不推荐这样做,避免内存泄漏 69 | // **/ 70 | // private TextView tv; 71 | // //将标题TextView控件回调到ViewModel中 72 | // public BindingCommand titleTextView = new BindingCommand(new BindingConsumer() { 73 | // @Override 74 | // public void call(TextView tv) { 75 | // NetWorkItemViewModel.this.tv = tv; 76 | // } 77 | // }); 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/network/detail/DetailFragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.network.detail; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | import com.goldze.mvvmhabit.databinding.FragmentDetailBinding; 10 | import com.goldze.mvvmhabit.entity.DemoEntity; 11 | 12 | import me.goldze.mvvmhabit.base.BaseFragment; 13 | 14 | /** 15 | * Created by goldze on 2017/7/17. 16 | * 详情界面 17 | */ 18 | 19 | public class DetailFragment extends BaseFragment { 20 | 21 | private DemoEntity.ItemsEntity entity; 22 | 23 | @Override 24 | public void initParam() { 25 | //获取列表传入的实体 26 | Bundle mBundle = getArguments(); 27 | if (mBundle != null) { 28 | entity = mBundle.getParcelable("entity"); 29 | } 30 | } 31 | 32 | @Override 33 | public int initContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 34 | return R.layout.fragment_detail; 35 | } 36 | 37 | @Override 38 | public int initVariableId() { 39 | return BR.viewModel; 40 | } 41 | 42 | @Override 43 | public void initData() { 44 | viewModel.setDemoEntity(entity); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/network/detail/DetailViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.network.detail; 2 | 3 | import android.app.Application; 4 | 5 | import com.goldze.mvvmhabit.entity.DemoEntity; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.databinding.ObservableField; 9 | import me.goldze.mvvmhabit.base.BaseViewModel; 10 | 11 | /** 12 | * Created by goldze on 2017/7/17. 13 | */ 14 | 15 | public class DetailViewModel extends BaseViewModel { 16 | public ObservableField entity = new ObservableField<>(); 17 | 18 | public DetailViewModel(@NonNull Application application) { 19 | super(application); 20 | } 21 | 22 | public void setDemoEntity(DemoEntity.ItemsEntity entity) { 23 | this.entity.set(entity); 24 | } 25 | 26 | @Override 27 | public void onDestroy() { 28 | super.onDestroy(); 29 | entity = null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/rv_multi/MultiRecycleHeadViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.rv_multi; 2 | 3 | import androidx.annotation.NonNull; 4 | import me.goldze.mvvmhabit.base.BaseViewModel; 5 | import me.goldze.mvvmhabit.base.MultiItemViewModel; 6 | import me.goldze.mvvmhabit.binding.command.BindingAction; 7 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 8 | import me.goldze.mvvmhabit.utils.ToastUtils; 9 | 10 | /** 11 | * Create Author:goldze 12 | * Create Date:2019/01/25 13 | * Description: 14 | */ 15 | 16 | public class MultiRecycleHeadViewModel extends MultiItemViewModel { 17 | 18 | public MultiRecycleHeadViewModel(@NonNull BaseViewModel viewModel) { 19 | super(viewModel); 20 | } 21 | 22 | //条目的点击事件 23 | public BindingCommand itemClick = new BindingCommand(new BindingAction() { 24 | @Override 25 | public void call() { 26 | ToastUtils.showShort("我是头布局"); 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/rv_multi/MultiRecycleLeftItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.rv_multi; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.databinding.ObservableField; 5 | import me.goldze.mvvmhabit.base.MultiItemViewModel; 6 | 7 | import me.goldze.mvvmhabit.binding.command.BindingAction; 8 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 9 | import me.goldze.mvvmhabit.utils.ToastUtils; 10 | 11 | /** 12 | * Create Author:goldze 13 | * Create Date:2019/01/25 14 | * Description: 15 | */ 16 | 17 | public class MultiRecycleLeftItemViewModel extends MultiItemViewModel { 18 | public ObservableField text = new ObservableField<>(""); 19 | 20 | public MultiRecycleLeftItemViewModel(@NonNull MultiRecycleViewModel viewModel, String text) { 21 | super(viewModel); 22 | this.text.set(text); 23 | } 24 | 25 | //条目的点击事件 26 | public BindingCommand itemClick = new BindingCommand(new BindingAction() { 27 | @Override 28 | public void call() { 29 | //拿到position 30 | int position = viewModel.observableList.indexOf(MultiRecycleLeftItemViewModel.this); 31 | ToastUtils.showShort("position:" + position); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/rv_multi/MultiRecycleRightItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.rv_multi; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.databinding.ObservableField; 5 | import me.goldze.mvvmhabit.base.MultiItemViewModel; 6 | 7 | import me.goldze.mvvmhabit.binding.command.BindingAction; 8 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 9 | import me.goldze.mvvmhabit.utils.ToastUtils; 10 | 11 | /** 12 | * Create Author:goldze 13 | * Create Date:2019/01/25 14 | * Description: 15 | */ 16 | 17 | public class MultiRecycleRightItemViewModel extends MultiItemViewModel { 18 | public ObservableField text = new ObservableField<>(""); 19 | 20 | public MultiRecycleRightItemViewModel(@NonNull MultiRecycleViewModel viewModel, String text) { 21 | super(viewModel); 22 | this.text.set(text); 23 | } 24 | 25 | //条目的点击事件 26 | public BindingCommand itemClick = new BindingCommand(new BindingAction() { 27 | @Override 28 | public void call() { 29 | //拿到position 30 | int position = viewModel.observableList.indexOf(MultiRecycleRightItemViewModel.this); 31 | ToastUtils.showShort("position:" + position); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/rv_multi/MultiRecycleViewFragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.rv_multi; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | import com.goldze.mvvmhabit.databinding.FragmentMultiRvBinding; 10 | 11 | import androidx.annotation.Nullable; 12 | import me.goldze.mvvmhabit.base.BaseFragment; 13 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter; 14 | 15 | /** 16 | * Create Author:goldze 17 | * Create Date:2019/01/25 18 | * Description:RecycleView多布局实现 19 | */ 20 | 21 | public class MultiRecycleViewFragment extends BaseFragment { 22 | @Override 23 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 24 | return R.layout.fragment_multi_rv; 25 | } 26 | 27 | @Override 28 | public int initVariableId() { 29 | return BR.viewModel; 30 | } 31 | 32 | @Override 33 | public void initData() { 34 | super.initData(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/rv_multi/MultiRecycleViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.rv_multi; 2 | 3 | import android.app.Application; 4 | 5 | import com.goldze.mvvmhabit.BR; 6 | import com.goldze.mvvmhabit.R; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.databinding.ObservableArrayList; 10 | import androidx.databinding.ObservableList; 11 | import me.goldze.mvvmhabit.base.BaseViewModel; 12 | import me.goldze.mvvmhabit.base.MultiItemViewModel; 13 | import me.tatarka.bindingcollectionadapter2.ItemBinding; 14 | import me.tatarka.bindingcollectionadapter2.OnItemBind; 15 | 16 | /** 17 | * Create Author:goldze 18 | * Create Date:2019/01/25 19 | * Description: 20 | */ 21 | 22 | public class MultiRecycleViewModel extends BaseViewModel { 23 | private static final String MultiRecycleType_Head = "head"; 24 | private static final String MultiRecycleType_Left = "left"; 25 | private static final String MultiRecycleType_Right = "right"; 26 | 27 | public MultiRecycleViewModel(@NonNull Application application) { 28 | super(application); 29 | //模拟10个条目,数据源可以来自网络 30 | for (int i = 0; i < 20; i++) { 31 | if (i == 0) { 32 | MultiItemViewModel item = new MultiRecycleHeadViewModel(this); 33 | //条目类型为头布局 34 | item.multiItemType(MultiRecycleType_Head); 35 | observableList.add(item); 36 | } else { 37 | String text = "我是第" + i + "条"; 38 | if (i % 2 == 0) { 39 | MultiItemViewModel item = new MultiRecycleLeftItemViewModel(this, text); 40 | //条目类型为左布局 41 | item.multiItemType(MultiRecycleType_Left); 42 | observableList.add(item); 43 | } else { 44 | MultiItemViewModel item = new MultiRecycleRightItemViewModel(this, text); 45 | //条目类型为右布局 46 | item.multiItemType(MultiRecycleType_Right); 47 | observableList.add(item); 48 | } 49 | } 50 | } 51 | } 52 | 53 | //给RecyclerView添加ObservableList 54 | public ObservableList observableList = new ObservableArrayList<>(); 55 | //RecyclerView多布局添加ItemBinding 56 | public ItemBinding itemBinding = ItemBinding.of(new OnItemBind() { 57 | @Override 58 | public void onItemBind(ItemBinding itemBinding, int position, MultiItemViewModel item) { 59 | //通过item的类型, 动态设置Item加载的布局 60 | String itemType = (String) item.getItemType(); 61 | if (MultiRecycleType_Head.equals(itemType)) { 62 | //设置头布局 63 | itemBinding.set(BR.viewModel, R.layout.item_multi_head); 64 | } else if (MultiRecycleType_Left.equals(itemType)) { 65 | //设置左布局 66 | itemBinding.set(BR.viewModel, R.layout.item_multi_rv_left); 67 | } else if (MultiRecycleType_Right.equals(itemType)) { 68 | //设置右布局 69 | itemBinding.set(BR.viewModel, R.layout.item_multi_rv_right); 70 | } 71 | } 72 | }); 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/tab_bar/fragment/TabBar1Fragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.tab_bar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | 10 | import androidx.annotation.Nullable; 11 | import me.goldze.mvvmhabit.base.BaseFragment; 12 | 13 | /** 14 | * Created by goldze on 2018/7/18. 15 | */ 16 | 17 | public class TabBar1Fragment extends BaseFragment { 18 | @Override 19 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 20 | return R.layout.fragment_tab_bar_1; 21 | } 22 | 23 | @Override 24 | public int initVariableId() { 25 | return BR.viewModel; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/tab_bar/fragment/TabBar2Fragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.tab_bar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | 10 | import androidx.annotation.Nullable; 11 | import me.goldze.mvvmhabit.base.BaseFragment; 12 | 13 | /** 14 | * Created by goldze on 2018/7/18. 15 | */ 16 | 17 | public class TabBar2Fragment extends BaseFragment { 18 | @Override 19 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 20 | return R.layout.fragment_tab_bar_2; 21 | } 22 | 23 | @Override 24 | public int initVariableId() { 25 | return BR.viewModel; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/tab_bar/fragment/TabBar3Fragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.tab_bar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | 10 | import androidx.annotation.Nullable; 11 | import me.goldze.mvvmhabit.base.BaseFragment; 12 | 13 | /** 14 | * Created by goldze on 2018/7/18. 15 | */ 16 | 17 | public class TabBar3Fragment extends BaseFragment{ 18 | @Override 19 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 20 | return R.layout.fragment_tab_bar_3; 21 | } 22 | 23 | @Override 24 | public int initVariableId() { 25 | return BR.viewModel; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/tab_bar/fragment/TabBar4Fragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.tab_bar.fragment; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.goldze.mvvmhabit.BR; 8 | import com.goldze.mvvmhabit.R; 9 | 10 | import androidx.annotation.Nullable; 11 | import me.goldze.mvvmhabit.base.BaseFragment; 12 | 13 | /** 14 | * Created by goldze on 2018/7/18. 15 | */ 16 | 17 | public class TabBar4Fragment extends BaseFragment { 18 | @Override 19 | public int initContentView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 20 | return R.layout.fragment_tab_bar_4; 21 | } 22 | 23 | @Override 24 | public int initVariableId() { 25 | return BR.viewModel; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/viewpager/activity/ViewPagerActivity.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.viewpager.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.goldze.mvvmhabit.BR; 6 | import com.goldze.mvvmhabit.R; 7 | import com.goldze.mvvmhabit.databinding.FragmentViewpagerBinding; 8 | import com.goldze.mvvmhabit.ui.viewpager.adapter.ViewPagerBindingAdapter; 9 | import com.goldze.mvvmhabit.ui.viewpager.vm.ViewPagerViewModel; 10 | import com.google.android.material.tabs.TabLayout; 11 | 12 | import androidx.annotation.Nullable; 13 | import androidx.lifecycle.Observer; 14 | import me.goldze.mvvmhabit.base.BaseActivity; 15 | import me.goldze.mvvmhabit.utils.ToastUtils; 16 | 17 | /** 18 | * ViewPager绑定的例子, 更多绑定方式,请参考 https://github.com/evant/binding-collection-adapter 19 | * 所有例子仅做参考,千万不要把它当成一种标准,毕竟主打的不是例子,业务场景繁多,理解如何使用才最重要。 20 | * Created by goldze on 2018/7/18. 21 | */ 22 | 23 | public class ViewPagerActivity extends BaseActivity { 24 | 25 | @Override 26 | public int initContentView(Bundle savedInstanceState) { 27 | return R.layout.fragment_viewpager; 28 | } 29 | 30 | @Override 31 | public int initVariableId() { 32 | return BR.viewModel; 33 | } 34 | 35 | 36 | @Override 37 | public void initData() { 38 | // 使用 TabLayout 和 ViewPager 相关联 39 | binding.tabs.setupWithViewPager(binding.viewPager); 40 | binding.viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.tabs)); 41 | //给ViewPager设置adapter 42 | binding.setAdapter(new ViewPagerBindingAdapter()); 43 | } 44 | 45 | @Override 46 | public void initViewObservable() { 47 | viewModel.itemClickEvent.observe(this, new Observer() { 48 | @Override 49 | public void onChanged(@Nullable String text) { 50 | ToastUtils.showShort("position:" + text); 51 | } 52 | }); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/viewpager/adapter/ViewPagerBindingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.viewpager.adapter; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import com.goldze.mvvmhabit.databinding.ItemViewpagerBinding; 6 | import com.goldze.mvvmhabit.ui.viewpager.vm.ViewPagerItemViewModel; 7 | 8 | import androidx.databinding.ViewDataBinding; 9 | import me.tatarka.bindingcollectionadapter2.BindingViewPagerAdapter; 10 | 11 | /** 12 | * Created by goldze on 2018/6/21. 13 | */ 14 | 15 | public class ViewPagerBindingAdapter extends BindingViewPagerAdapter { 16 | 17 | @Override 18 | public void onBindBinding(final ViewDataBinding binding, int variableId, int layoutRes, final int position, ViewPagerItemViewModel item) { 19 | super.onBindBinding(binding, variableId, layoutRes, position, item); 20 | //这里可以强转成ViewPagerItemViewModel对应的ViewDataBinding, 21 | ItemViewpagerBinding _binding = (ItemViewpagerBinding) binding; 22 | } 23 | 24 | @Override 25 | public void destroyItem(ViewGroup container, int position, Object object) { 26 | super.destroyItem(container, position, object); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/viewpager/vm/ViewPagerItemViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.viewpager.vm; 2 | 3 | import androidx.annotation.NonNull; 4 | import me.goldze.mvvmhabit.base.ItemViewModel; 5 | import me.goldze.mvvmhabit.binding.command.BindingAction; 6 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 7 | 8 | /** 9 | * 所有例子仅做参考,千万不要把它当成一种标准,毕竟主打的不是例子,业务场景繁多,理解如何使用才最重要。 10 | * Created by goldze on 2018/7/18. 11 | */ 12 | 13 | public class ViewPagerItemViewModel extends ItemViewModel { 14 | public String text; 15 | 16 | public ViewPagerItemViewModel(@NonNull ViewPagerViewModel viewModel, String text) { 17 | super(viewModel); 18 | this.text = text; 19 | } 20 | 21 | public BindingCommand onItemClick = new BindingCommand(new BindingAction() { 22 | @Override 23 | public void call() { 24 | //点击之后将逻辑转到activity中处理 25 | viewModel.itemClickEvent.setValue(text); 26 | } 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/viewpager/vm/ViewPagerViewModel.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.viewpager.vm; 2 | 3 | import android.app.Application; 4 | 5 | import com.goldze.mvvmhabit.BR; 6 | import com.goldze.mvvmhabit.R; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.databinding.ObservableArrayList; 10 | import androidx.databinding.ObservableList; 11 | import me.goldze.mvvmhabit.base.BaseViewModel; 12 | import me.goldze.mvvmhabit.binding.command.BindingCommand; 13 | import me.goldze.mvvmhabit.binding.command.BindingConsumer; 14 | import me.goldze.mvvmhabit.bus.event.SingleLiveEvent; 15 | import me.goldze.mvvmhabit.utils.ToastUtils; 16 | import me.tatarka.bindingcollectionadapter2.BindingViewPagerAdapter; 17 | import me.tatarka.bindingcollectionadapter2.ItemBinding; 18 | 19 | /** 20 | * 所有例子仅做参考,千万不要把它当成一种标准,毕竟主打的不是例子,业务场景繁多,理解如何使用才最重要。 21 | * Created by goldze on 2018/7/18. 22 | */ 23 | 24 | public class ViewPagerViewModel extends BaseViewModel { 25 | public SingleLiveEvent itemClickEvent = new SingleLiveEvent<>(); 26 | public ViewPagerViewModel(@NonNull Application application) { 27 | super(application); 28 | //模拟3个ViewPager页面 29 | for (int i = 1; i <= 3; i++) { 30 | ViewPagerItemViewModel itemViewModel = new ViewPagerItemViewModel(this, "第" + i + "个页面"); 31 | items.add(itemViewModel); 32 | } 33 | } 34 | 35 | //给ViewPager添加ObservableList 36 | public ObservableList items = new ObservableArrayList<>(); 37 | //给ViewPager添加ItemBinding 38 | public ItemBinding itemBinding = ItemBinding.of(BR.viewModel, R.layout.item_viewpager); 39 | //给ViewPager添加PageTitle 40 | public final BindingViewPagerAdapter.PageTitles pageTitles = new BindingViewPagerAdapter.PageTitles() { 41 | @Override 42 | public CharSequence getPageTitle(int position, ViewPagerItemViewModel item) { 43 | return "条目" + position; 44 | } 45 | }; 46 | //ViewPager切换监听 47 | public BindingCommand onPageSelectedCommand = new BindingCommand<>(new BindingConsumer() { 48 | @Override 49 | public void call(Integer index) { 50 | ToastUtils.showShort("ViewPager切换:" + index); 51 | } 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/goldze/mvvmhabit/ui/vp_frg/ViewPagerGroupFragment.java: -------------------------------------------------------------------------------- 1 | package com.goldze.mvvmhabit.ui.vp_frg; 2 | 3 | import com.goldze.mvvmhabit.ui.base.fragment.BasePagerFragment; 4 | import com.goldze.mvvmhabit.ui.tab_bar.fragment.TabBar1Fragment; 5 | import com.goldze.mvvmhabit.ui.tab_bar.fragment.TabBar2Fragment; 6 | import com.goldze.mvvmhabit.ui.tab_bar.fragment.TabBar3Fragment; 7 | import com.goldze.mvvmhabit.ui.tab_bar.fragment.TabBar4Fragment; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import androidx.fragment.app.Fragment; 13 | 14 | /** 15 | * Create Author:goldze 16 | * Create Date:2019/01/25 17 | * Description:ViewPager+Fragment的实现 18 | */ 19 | 20 | public class ViewPagerGroupFragment extends BasePagerFragment { 21 | @Override 22 | protected List pagerFragment() { 23 | List list = new ArrayList<>(); 24 | list.add(new TabBar1Fragment()); 25 | list.add(new TabBar2Fragment()); 26 | list.add(new TabBar3Fragment()); 27 | list.add(new TabBar4Fragment()); 28 | return list; 29 | } 30 | 31 | @Override 32 | protected List pagerTitleString() { 33 | List list = new ArrayList<>(); 34 | list.add("page1"); 35 | list.add("page2"); 36 | list.add("page3"); 37 | list.add("page4"); 38 | return list; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/login_clear_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 19 | 20 |