├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── mvvm_develop │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── mvvm_develop │ │ │ ├── App.kt │ │ │ ├── activity │ │ │ ├── ImagePreviewActivity.kt │ │ │ ├── MainActivity.kt │ │ │ └── PuppetActivity.kt │ │ │ ├── adapter │ │ │ └── ImagePreviewAdapter.kt │ │ │ ├── ui │ │ │ └── MainFragment.kt │ │ │ └── vm │ │ │ ├── CommonRepo.kt │ │ │ └── CommonViewModel.kt │ └── res │ │ ├── drawable-v24 │ │ ├── bottom_navigation_back.xml │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_launcher_background.xml │ │ ├── menu_tab1.xml │ │ ├── menu_tab2.xml │ │ ├── menu_tab3.xml │ │ ├── tab1_select.xml │ │ ├── tab1_unselect.xml │ │ ├── tab2_select.xml │ │ ├── tab2_unselect.xml │ │ ├── tab3_select.xml │ │ └── tab3_unselect.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_test.xml │ │ ├── fragment_image_preview.xml │ │ └── fragment_main.xml │ │ ├── menu │ │ └── bottom_nav_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ ├── nav_main.xml │ │ └── nav_module.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── style.xml │ └── test │ └── java │ └── com │ └── example │ └── mvvm_develop │ └── ExampleUnitTest.kt ├── base.gradle ├── base ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro ├── schemas │ └── com.example.baselibrary.db.BaseDatabase │ │ └── 1.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── baselibrary │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── baselibrary │ │ │ ├── AppInit.kt │ │ │ ├── BaseApp.kt │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseDialog.kt │ │ │ ├── BaseDialogFragment.kt │ │ │ └── BaseFragment.kt │ │ │ ├── base_databinding │ │ │ ├── BaseBindingActivity.kt │ │ │ └── BaseBindingFragment.kt │ │ │ ├── base_swipeback │ │ │ ├── SwipeBackActivity.kt │ │ │ ├── SwipeBackFragment.kt │ │ │ └── SwipeBackLayout.kt │ │ │ ├── bus │ │ │ ├── LiveDataBus.kt │ │ │ └── SingleLiveData.kt │ │ │ ├── db │ │ │ ├── BaseDatabase.kt │ │ │ ├── MMKVUtil.kt │ │ │ ├── dao │ │ │ │ └── NetCacheDao.kt │ │ │ └── entity │ │ │ │ └── NetCache.kt │ │ │ ├── http │ │ │ ├── ApiException.kt │ │ │ ├── ApiResponse.kt │ │ │ ├── NetState.kt │ │ │ ├── ResultLiveData.kt │ │ │ ├── RetrofitFactory.kt │ │ │ ├── RetrofitManager.kt │ │ │ └── SingleLiveData.kt │ │ │ ├── lifecycle │ │ │ ├── ActivityStack.kt │ │ │ ├── ApplicationLifecycle.kt │ │ │ ├── InitDepend.kt │ │ │ └── LoadModuleProxy.kt │ │ │ ├── navigation │ │ │ ├── DialogFragmentNavigator.java │ │ │ ├── FragmentNavigator.java │ │ │ └── NavHostFragment.java │ │ │ ├── recyclerview │ │ │ ├── BaseAdapter.kt │ │ │ ├── BaseBRVAdapter.kt │ │ │ ├── BaseDataBindAdapter.kt │ │ │ ├── BindingHolderUtil.kt │ │ │ ├── BindingViewHolder.kt │ │ │ ├── CommonInterface.kt │ │ │ ├── GridSpaceItemDecoration.java │ │ │ ├── LayoutManger.kt │ │ │ └── test │ │ │ │ ├── AdapterDemo1.kt │ │ │ │ ├── AdapterDemo2.kt │ │ │ │ ├── AdapterDemo3.kt │ │ │ │ ├── AdapterDemo4.kt │ │ │ │ └── Book.kt │ │ │ ├── utils │ │ │ ├── CommonUtils.kt │ │ │ ├── activity │ │ │ │ ├── Activity.kt │ │ │ │ ├── Application.kt │ │ │ │ └── Lifecycle.kt │ │ │ ├── design │ │ │ │ ├── Dialogs.kt │ │ │ │ ├── RecyclerView.kt │ │ │ │ ├── Snackbar.kt │ │ │ │ ├── TabLayout.kt │ │ │ │ └── ViewPager2.kt │ │ │ ├── file │ │ │ │ ├── File.kt │ │ │ │ ├── FilePath.kt │ │ │ │ └── FileSizeUtil.java │ │ │ ├── log │ │ │ │ └── xLog.kt │ │ │ ├── longan │ │ │ │ ├── Bundle.kt │ │ │ │ ├── DeviceInfo.kt │ │ │ │ └── Encode.kt │ │ │ ├── media │ │ │ │ ├── MediaStoreUtil.kt │ │ │ │ ├── PDFUtils.kt │ │ │ │ ├── PicturesUtil.kt │ │ │ │ ├── SavePicUtil.kt │ │ │ │ └── TextFileUtil.kt │ │ │ ├── net │ │ │ │ ├── DownloadUtil.kt │ │ │ │ └── Network.kt │ │ │ ├── other │ │ │ │ ├── AssertMangerUtil.kt │ │ │ │ ├── BitmapUtil.kt │ │ │ │ ├── ByteUtil.kt │ │ │ │ ├── Color.kt │ │ │ │ ├── JsonUtil.kt │ │ │ │ ├── Md5Util.kt │ │ │ │ ├── NameLengthFilter.java │ │ │ │ ├── Resoures.kt │ │ │ │ ├── Screen.kt │ │ │ │ ├── Share.kt │ │ │ │ ├── String.kt │ │ │ │ ├── ThreadPoolManager.kt │ │ │ │ ├── Threads.kt │ │ │ │ └── TimeUtil.kt │ │ │ ├── transformation │ │ │ │ ├── CenterBlurTransformation.java │ │ │ │ ├── DepthPageTransformer.java │ │ │ │ ├── GlideRoundTransform.java │ │ │ │ ├── MZScaleInTransformer.java │ │ │ │ └── ZoomOutPageTransformer.java │ │ │ └── view │ │ │ │ ├── AnimationBindingAdapters.kt │ │ │ │ ├── BindAdapter.kt │ │ │ │ ├── Canvas.kt │ │ │ │ ├── ClickListener.kt │ │ │ │ ├── Dimensions.kt │ │ │ │ ├── EditText.kt │ │ │ │ ├── ImageView.kt │ │ │ │ ├── Internal.kt │ │ │ │ ├── Keyboard.kt │ │ │ │ ├── TextView.kt │ │ │ │ └── ViewExt.kt │ │ │ ├── view │ │ │ ├── ZoomImageView.java │ │ │ └── photoscontentview │ │ │ │ ├── PhotoImageView.kt │ │ │ │ ├── PhotosContentView.kt │ │ │ │ └── PhotosContentViewAdapter.kt │ │ │ ├── viewbinding │ │ │ └── ViewBindingProperty.kt │ │ │ └── vm │ │ │ ├── BaseRepository.kt │ │ │ └── BaseViewModel.kt │ └── res │ │ ├── anim │ │ ├── dialog_enter_anim.xml │ │ ├── dialog_exit_anim.xml │ │ ├── dialog_in.xml │ │ ├── dialog_out.xml │ │ └── no_anim.xml │ │ ├── drawable-xhdpi │ │ ├── shadow_bottom.png │ │ ├── shadow_left.png │ │ ├── shadow_right.png │ │ └── shadow_top.png │ │ ├── drawable │ │ ├── icon_errorload.xml │ │ ├── shape_radius_10dp.xml │ │ ├── shape_radius_13dp.xml │ │ └── shape_radius_5dp.xml │ │ ├── font │ │ ├── domine_bold.ttf │ │ ├── domine_regular.ttf │ │ ├── montserrat_medium.ttf │ │ ├── montserrat_regular.ttf │ │ └── montserrat_semibold.ttf │ │ ├── layout │ │ └── list_item.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── styles.xml │ │ └── tags.xml │ └── test │ └── java │ └── com │ └── example │ └── baselibrary │ └── ExampleUnitTest.kt ├── build.gradle ├── buildSrc ├── .gitignore ├── build.gradle.kts ├── consumer-rules.pro ├── hs_err_pid6324.log ├── proguard-rules.pro ├── replay_pid6324.log └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xlu │ │ └── buildsrc │ │ └── ExampleInstrumentedTest.kt │ └── main │ └── java │ └── com │ └── xlu │ └── buildsrc │ └── Dependency.kt ├── common ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── xlu │ │ └── common │ │ ├── CommonUtil.kt │ │ ├── Router.kt │ │ ├── api │ │ └── LoginItf.kt │ │ ├── bean │ │ ├── BannerData.kt │ │ ├── RouterBean.kt │ │ ├── RouterBeanParcable.kt │ │ └── RouterBeanSerializable.kt │ │ ├── constants │ │ ├── ConstantARouter.kt │ │ ├── ConstantEvent.kt │ │ ├── ConstantParams.kt │ │ └── ConstantSP.kt │ │ └── server │ │ └── ServerUtil.kt │ └── res │ └── anim │ ├── all.xml │ ├── roate_360.xml │ ├── scale_in_center.xml │ ├── scale_in_scroll.xml │ ├── slide_in_left.xml │ ├── slide_in_right.xml │ ├── slide_out_left.xml │ └── slide_out_right.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── jitpack.yml ├── local.properties ├── module_1 ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro ├── schemas │ └── com.xlu.module_tab1.db.HomeDatabase │ │ └── 2.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xlu │ │ └── module_tab1 │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xlu │ │ │ └── module_tab1 │ │ │ ├── FragmentHome.kt │ │ │ ├── HomeRepo.kt │ │ │ ├── HomeViewModel.kt │ │ │ ├── adapter │ │ │ ├── CardAdapter.kt │ │ │ └── QuickBiningBRVAdapter.kt │ │ │ ├── api │ │ │ └── HomeApi.kt │ │ │ ├── bean │ │ │ └── Article.kt │ │ │ ├── db │ │ │ ├── ArticleDao.kt │ │ │ ├── HomeDatabase.kt │ │ │ └── convertor │ │ │ │ ├── ArticleInfoTypeConvertor.kt │ │ │ │ └── TagTypeConverter.kt │ │ │ ├── dialog │ │ │ └── TestDialog.kt │ │ │ ├── swipe │ │ │ ├── SwipeBackTestActivity.kt │ │ │ └── SwipeBackTestFragment.kt │ │ │ └── ui │ │ │ ├── ARouterFragment.kt │ │ │ ├── BindingFragment.kt │ │ │ ├── BindingFragmentRoom.kt │ │ │ ├── BrvaFragment.kt │ │ │ ├── FragmentHomeMain.kt │ │ │ ├── FragmentMMKV.kt │ │ │ ├── FragmentNet.kt │ │ │ ├── FragmentSwipe.kt │ │ │ └── FragmentTab.kt │ └── res │ │ ├── drawable │ │ ├── tab_selector.xml │ │ ├── tab_selector_back.xml │ │ ├── tab_selector_indicator.xml │ │ └── tab_selector_indicator_2.xml │ │ ├── layout │ │ ├── activity_swipe_back_test.xml │ │ ├── dialog_test.xml │ │ ├── fragment_arouter.xml │ │ ├── fragment_brva.xml │ │ ├── fragment_brva_list.xml │ │ ├── fragment_container.xml │ │ ├── fragment_databind.xml │ │ ├── fragment_home.xml │ │ ├── fragment_mmkv.xml │ │ ├── fragment_net.xml │ │ ├── fragment_room.xml │ │ ├── fragment_swipe.xml │ │ ├── fragment_swipe_back_test.xml │ │ ├── fragment_tablayout.xml │ │ └── item_vp.xml │ │ ├── navigation │ │ └── navi_home.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── xlu │ └── module_tab1 │ └── ExampleUnitTest.kt ├── module_2 ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xlu │ │ └── module_collection │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xlu │ │ │ └── module_collection │ │ │ ├── CollectionRepo.kt │ │ │ ├── CollectionViewModel.kt │ │ │ ├── FragmentCollection.kt │ │ │ └── ui │ │ │ └── FragmentFlow.kt │ └── res │ │ ├── layout │ │ ├── fragment_collection.xml │ │ └── fragment_flow.xml │ │ ├── navigation │ │ └── navi_collection.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── xlu │ └── module_collection │ └── ExampleUnitTest.kt ├── module_3 ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xlu │ │ │ └── module_center │ │ │ └── FragmentCenter.kt │ └── res │ │ ├── layout │ │ └── fragment_center.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── xlu │ └── module_center │ └── ExampleUnitTest.kt ├── module_app_test ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xlu │ │ └── test │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xlu │ │ │ └── test │ │ │ ├── AppHome.kt │ │ │ └── MainActivity.kt │ ├── manifest │ │ └── AndroidManifest.xml │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── xlu │ └── test │ └── ExampleUnitTest.kt ├── module_community ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── module_community │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── url.txt │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── module_community │ │ │ ├── CommunityActivity.kt │ │ │ ├── FragmentCoil.kt │ │ │ ├── ShareElementsFragment.kt │ │ │ ├── adapter │ │ │ ├── ImageListAdapter.kt │ │ │ ├── ImageListAdapter2.kt │ │ │ ├── ImageListAdapter3.kt │ │ │ ├── ShareAdapter.kt │ │ │ └── ShareAdapter2.kt │ │ │ ├── bean │ │ │ ├── Common.kt │ │ │ └── Image.kt │ │ │ └── vm │ │ │ └── CommunityViewModel.kt │ └── res │ │ ├── drawable │ │ ├── icon_community_tab1.xml │ │ ├── icon_community_tab2.xml │ │ ├── icon_community_tab3.xml │ │ ├── icon_focus.xml │ │ ├── icon_focused.xml │ │ └── selected_focus.xml │ │ ├── layout │ │ ├── fragment_coil.xml │ │ ├── fragment_coil_item.xml │ │ ├── fragment_community.xml │ │ ├── fragment_recycler_list.xml │ │ ├── item_1.xml │ │ ├── item_2.xml │ │ └── nine_grid_image.xml │ │ ├── menu │ │ └── community_bottom_bar.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── example │ └── module_community │ └── ExampleUnitTest.kt ├── module_login ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xlu │ │ └── module_login │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xlu │ │ │ └── module_login │ │ │ ├── AppLogin.kt │ │ │ ├── LoginActivity.kt │ │ │ └── LoginImpl.kt │ └── res │ │ ├── layout │ │ └── activity_login.xml │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── xlu │ └── module_login │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /.gradle/ 3 | /app/.cxx/ 4 | /base/build/ 5 | /base/build/intermediates/javac/debug/classes/ 6 | .gradle 7 | .idea 8 | .gradle/ 9 | .idea/ 10 | *.iml 11 | *.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## mvvm_develop 2 | 3 | 这是一个MVVM快速开发框架,是对目前一些较热门的技术实践,在学习某个新技术的时候,有一个测试环境。日常的Demo也会方在其中,并整理了日常开发中的常用工具,在有新的需求的时候能够快速进行开发。 4 | 5 | 本库主要采用Kotlin+Jetpack组件+组件化+主流第三方库,目标是搭建一个自己顺手的快速开发框架。 6 | 7 | ### 相关模块 8 | module名称 | 作用 9 | ---|--- 10 | app | 宿主app 11 | base | 基础功能封装 12 | common | 业务基础功能封装 13 | buildSrc | 统一依赖管理 14 | module_tab1 | UI相关功能测试 15 | module_tab2 | 数据相关功能测试 16 | module_tab3 | 其他测试 17 | module_login | 业务module(登录) 18 | module_app_test | 独立app模块测试 19 | 20 | - base 21 | > `base`模块是基础功能封装,不依赖任何模块,提供基础工具使用 22 | 23 | - common 24 | > `common`主要是业务基础封装,仅依赖`base`模块 25 | 26 | - buildSrc 27 | > 主流的依赖管理方法,`Google Demo`中大多采用此方案 28 | 29 | - app 30 | > 壳`app`,集成所有业务`module`,本项目采用单`Activity+多Fragment`模式,主体`Activity`在此。 31 | 32 | - module_community 33 | > 社区模块测试,`RecyclerView长`列表,多类型支持,元素共享 34 | 35 | 36 | 37 | ## 相关参考文章 38 | 39 | ### 基础功能封装 40 | 41 | > 这里包含了对BaseActivity、BaseFragment封装设计,比如viewBidning和databinding的处理。 42 | 43 | 参考:[写一个MVVM快速开发框架(一)基础类封装](https://juejin.cn/post/6989918599007698957) 44 | 45 | 46 | ### 组件化搭建 47 | > 各个module采用统一依赖管理,页面跳转采用ARouter,页面通信采用接口编程思想,module对外提供服务,application初始化采用@AutoService 48 | 49 | 参考:[写一个MVVM快速开发框架(二)组件化改造](https://juejin.cn/post/6995082240287850527) 50 | 51 | 52 | 53 | ### 单Activity+多Fragment模式 54 | > 将Activity作为容器,使用Navigation作为导航,可以有效提高页面跳转效率。 55 | 56 | 参考:[谈一谈“单Activity+多Fragment”模式](https://juejin.cn/post/6997422487654891533) 57 | 58 | 59 | ### 数据处理 60 | > 使用LiveData进行事件通信,Room+mmkv数据处理,网络缓存设计 61 | 62 | 参考:[写一个MVVM快速开发框架(四)优雅的数据处理](https://juejin.cn/post/7000627451575566373) 63 | 64 | 65 | ## Star 66 | 67 | 欢迎大家补充... -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/mvvm_develop/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.mvvm_develop 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.mvvm_develop", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 18 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/mvvm_develop/App.kt: -------------------------------------------------------------------------------- 1 | package com.example.mvvm_develop 2 | 3 | import com.alibaba.android.arouter.launcher.ARouter 4 | import com.example.baselibrary.BaseApp 5 | import com.example.baselibrary.db.MMKVUtil 6 | import com.example.baselibrary.utils.log.xLog 7 | import com.example.xlulibrary.ToastBox 8 | 9 | /** 10 | * @ClassName App 11 | * @Description 12 | * @Author AlexLu_1406496344@qq.com 13 | * @Date 2021/7/26 17:09 14 | */ 15 | class App : BaseApp(){ 16 | 17 | 18 | override fun onCreate() { 19 | super.onCreate() 20 | xLog.d("App_init") 21 | 22 | MMKVUtil.init(this) 23 | initARouter() 24 | 25 | ToastBox.init( 26 | x = 0, y= 250, 27 | anim = R.style.ToastAnim_MIUI 28 | ) 29 | } 30 | 31 | 32 | private fun initARouter() { 33 | //if (DEBUG) return 34 | // 这两行必须写在init之前,否则这些配置在init过程中将无效 35 | ARouter.openLog(); // 打印日志 36 | ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) 37 | ARouter.init(this) 38 | } 39 | 40 | 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/mvvm_develop/vm/CommonRepo.kt: -------------------------------------------------------------------------------- 1 | package com.example.mvvm_develop.vm 2 | 3 | import com.example.baselibrary.vm.BaseRepository 4 | import kotlinx.coroutines.CoroutineScope 5 | 6 | /** 7 | * @ClassName CommonRepo 8 | * @Description 9 | * @Author AlexLu_1406496344@qq.com 10 | * @Date 2021/5/12 10:18 11 | */ 12 | class CommonRepo(scope: CoroutineScope) : BaseRepository(scope) { 13 | 14 | companion object{ 15 | @Volatile 16 | private var instance: CommonRepo?= null 17 | 18 | fun getInstance(scope: CoroutineScope) = instance ?: synchronized(this) { 19 | instance ?: CommonRepo(scope).also { instance = it } 20 | } 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/mvvm_develop/vm/CommonViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.mvvm_develop.vm 2 | 3 | import com.example.baselibrary.vm.BaseViewModel 4 | 5 | class CommonViewModel : BaseViewModel() { 6 | 7 | 8 | fun test(){ 9 | launchMain { 10 | println("test launch main") 11 | } 12 | launchIO { 13 | println("test launch IO") 14 | } 15 | } 16 | 17 | 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/bottom_navigation_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/menu_tab1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/menu_tab2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/menu_tab3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab1_select.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab1_unselect.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab2_select.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab2_unselect.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab3_select.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tab3_unselect.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | 13 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 22 | 23 | 24 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_image_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 18 | 19 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 18 | 19 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/menu/bottom_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_module.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 8dp 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | mvvm_develop 3 | 4 | Hello blank fragment 5 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/mvvm_develop/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.mvvm_develop 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /base.gradle: -------------------------------------------------------------------------------- 1 | //**************************************** 2 | //********* lib 模块的公共脚本配置 ********** 3 | //**************************************** 4 | 5 | import com.xlu.buildsrc.* 6 | 7 | apply plugin: 'com.android.library' 8 | apply plugin: 'kotlin-android' 9 | apply plugin: 'kotlin-kapt' 10 | 11 | android { 12 | compileSdkVersion Dependency.ProjectConfig.compileSdkVersion 13 | buildToolsVersion Dependency.ProjectConfig.buildToolsVersion 14 | 15 | defaultConfig { 16 | minSdkVersion Dependency.ProjectConfig.minSdkVersion 17 | targetSdkVersion Dependency.ProjectConfig.targetSdkVersion 18 | versionCode Dependency.ProjectConfig.versionCode 19 | versionName Dependency.ProjectConfig.versionName 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "consumer-rules.pro" 23 | kapt { 24 | arguments { 25 | arg("AROUTER_MODULE_NAME", project.getName()) 26 | arg("room.schemaLocation", "$projectDir/schemas") 27 | } 28 | } 29 | } 30 | 31 | compileOptions { 32 | sourceCompatibility = JavaVersion.VERSION_11 33 | targetCompatibility = JavaVersion.VERSION_11 34 | } 35 | kotlinOptions { 36 | jvmTarget = '11' 37 | useIR = true 38 | } 39 | buildFeatures { 40 | viewBinding = true 41 | dataBinding = true 42 | } 43 | 44 | buildTypes { 45 | debug { 46 | minifyEnabled false 47 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 48 | } 49 | 50 | release { 51 | minifyEnabled false 52 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 53 | } 54 | } 55 | } 56 | 57 | //kapt { 58 | // arguments { 59 | // arg("AROUTER_MODULE_NAME", project.getName()) 60 | // } 61 | //} -------------------------------------------------------------------------------- /base/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /base/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/consumer-rules.pro -------------------------------------------------------------------------------- /base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /base/schemas/com.example.baselibrary.db.BaseDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "dc6c0ed9502c3b26d0e0706702721623", 6 | "entities": [ 7 | { 8 | "tableName": "NetCache", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`md` TEXT NOT NULL, `response` TEXT NOT NULL, PRIMARY KEY(`md`))", 10 | "fields": [ 11 | { 12 | "fieldPath": "md", 13 | "columnName": "md", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "response", 19 | "columnName": "response", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | } 23 | ], 24 | "primaryKey": { 25 | "columnNames": [ 26 | "md" 27 | ], 28 | "autoGenerate": false 29 | }, 30 | "indices": [], 31 | "foreignKeys": [] 32 | } 33 | ], 34 | "views": [], 35 | "setupQueries": [ 36 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 37 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dc6c0ed9502c3b26d0e0706702721623')" 38 | ] 39 | } 40 | } -------------------------------------------------------------------------------- /base/src/androidTest/java/com/example/baselibrary/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.baselibrary.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/base/BaseDialog.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.base 2 | 3 | import android.app.Dialog 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import androidx.viewbinding.ViewBinding 8 | 9 | abstract class BaseDialog( 10 | context: Context, 11 | themeResId: Int, 12 | private val inflate: (LayoutInflater) -> VB 13 | ) : Dialog(context, themeResId) { 14 | 15 | lateinit var binding: VB 16 | 17 | constructor(context: Context, inflate: (LayoutInflater) -> VB) : this(context, 0, inflate) 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | binding = inflate(layoutInflater) 22 | setContentView(binding.root) 23 | } 24 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/base_databinding/BaseBindingActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.base_databinding 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.LayoutRes 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.databinding.DataBindingUtil 7 | import androidx.databinding.ViewDataBinding 8 | import com.example.baselibrary.base.BaseActivity 9 | 10 | abstract class BaseBindingActivity(@LayoutRes private val layout: Int) : BaseActivity() { 11 | 12 | private var _mBinding: T ?= null 13 | val mBinding get() = _mBinding!! 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | _mBinding = DataBindingUtil.setContentView(this, layout) 18 | } 19 | 20 | override fun onDestroy() { 21 | super.onDestroy() 22 | _mBinding?.unbind() 23 | } 24 | 25 | 26 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/base_databinding/BaseBindingFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.base_databinding 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.annotation.LayoutRes 8 | import androidx.databinding.DataBindingUtil 9 | import androidx.databinding.ViewDataBinding 10 | import com.example.baselibrary.base.BaseFragment 11 | 12 | abstract class BaseBindingFragment(@LayoutRes private val layout: Int, lazyInit:Boolean = false) : BaseFragment(layout = layout,lazyInit = lazyInit) { 13 | 14 | private var _mBinding: T? = null 15 | val mBinding get() = _mBinding!! 16 | 17 | 18 | override fun onCreateView( 19 | inflater: LayoutInflater, 20 | container: ViewGroup?, 21 | savedInstanceState: Bundle? 22 | ): View? { 23 | _mBinding = DataBindingUtil.inflate(inflater, layout, container, false) 24 | return mBinding.root 25 | } 26 | 27 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 28 | super.onViewCreated(view, savedInstanceState) 29 | //LiveData needs the lifecycle owner 30 | mBinding.lifecycleOwner = requireActivity() 31 | } 32 | 33 | override fun onDestroyView() { 34 | super.onDestroyView() 35 | _mBinding?.unbind() 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/bus/SingleLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.bus 2 | 3 | import android.util.Log 4 | import androidx.annotation.MainThread 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.MutableLiveData 7 | import androidx.lifecycle.Observer 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | 10 | /** 11 | * @ClassName SingleLiveData 12 | * @Description 单事件响应的liveData 13 | * @Author AlexLu_1406496344@qq.com 14 | * @Date 2021/5/12 11:08 15 | */ 16 | class SingleLiveData : MutableLiveData() { 17 | 18 | private val mPending = AtomicBoolean(false) 19 | 20 | @MainThread 21 | override fun observe(owner: LifecycleOwner, observer: Observer) { 22 | 23 | if (hasActiveObservers()) { 24 | Log.w(TAG, "多个观察者存在的时候,只会有一个被通知到数据更新") 25 | } 26 | 27 | //比较AtomicBoolean和expect的值,如果一致,执行方法内的语句。其实就是一个if语句 28 | //把AtomicBoolean的值设成update 29 | 30 | super.observe(owner, Observer { t -> 31 | if (mPending.compareAndSet(true, false)) { 32 | observer.onChanged(t) 33 | } 34 | }) 35 | } 36 | 37 | override fun setValue(value: T?) { 38 | mPending.set(true) 39 | super.setValue(value) 40 | } 41 | 42 | companion object { 43 | private const val TAG = "SingleLiveData" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/db/BaseDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.db 2 | 3 | import androidx.room.Room 4 | import androidx.room.RoomDatabase 5 | import com.example.baselibrary.db.dao.NetCacheDao 6 | import com.example.baselibrary.db.entity.NetCache 7 | import com.example.baselibrary.utils.activity.application 8 | 9 | 10 | /** 11 | * @ClassName Database 12 | * @Description TODO 13 | * @Author AlexLu_1406496344@qq.com 14 | * @Date 2021/8/24 14:52 15 | */ 16 | @androidx.room.Database(entities = arrayOf(NetCache::class),version = 1) 17 | abstract class BaseDatabase : RoomDatabase(){ 18 | 19 | companion object { 20 | 21 | const val dbName = "Base.db" 22 | 23 | @Volatile 24 | private var INSTANCE: BaseDatabase? = null 25 | 26 | fun getInstance(): BaseDatabase = INSTANCE ?: synchronized(this) { 27 | buildDatabase().also { INSTANCE = it } 28 | } 29 | 30 | private fun buildDatabase() = 31 | Room.databaseBuilder(application, BaseDatabase::class.java, dbName).build() 32 | } 33 | 34 | 35 | abstract fun netCacheDao():NetCacheDao 36 | 37 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/db/dao/NetCacheDao.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.db.dao 2 | 3 | import androidx.room.* 4 | import com.example.baselibrary.db.entity.NetCache 5 | /** 6 | * @ClassName ArticleDao 7 | * @Description TODO 8 | * @Author AlexLu_1406496344@qq.com 9 | * @Date 2021/8/24 14:50 10 | */ 11 | @Dao 12 | interface NetCacheDao { 13 | 14 | /*获取全部数据*/ 15 | @Query("select * from NetCache") 16 | fun getAllData():List? 17 | 18 | /*删除全部数据*/ 19 | @Query("DELETE FROM NetCache") 20 | fun deleteAll() 21 | 22 | /*删除单条、多条数据*/ 23 | @Delete() 24 | fun delete(vararg netCache: NetCache):Int 25 | 26 | /*根据id删除*/ 27 | @Query("DELETE FROM NetCache where md =:md") 28 | fun delete(md:String) 29 | 30 | /*查询单组数据*/ 31 | @Query("SELECT * FROM NetCache WHERE md = :md") 32 | fun query(md:String):NetCache? 33 | 34 | /*查询多组数据*/ 35 | @Query("select * from NetCache where md in (:mds)") 36 | fun query(mds: List): List? 37 | 38 | /*插入单组数据*/ 39 | @Insert(onConflict = OnConflictStrategy.REPLACE) 40 | fun insert(netCache: NetCache) 41 | 42 | 43 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/db/entity/NetCache.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.db.entity 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | /** 7 | * @ClassName NetCache 8 | * @Description TODO 9 | * @Author AlexLu_1406496344@qq.com 10 | * @Date 2021/8/25 14:50 11 | */ 12 | @Entity(tableName = "NetCache") 13 | data class NetCache( 14 | 15 | @PrimaryKey 16 | val md:String, 17 | 18 | val response:String 19 | ) 20 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/ApiException.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | /** 4 | * 用来封装业务错误信息 5 | */ 6 | class ApiException(val errorMessage: String, val errorCode: Int) : Throwable() -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/ApiResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | import java.io.Serializable 4 | 5 | 6 | class ApiResponse : Serializable { 7 | 8 | var code : Int = -1 9 | 10 | var data : T ?= null 11 | get() { 12 | when (code) { 13 | 0, 200 -> { 14 | //请求成功 15 | return field 16 | } 17 | 401 -> { 18 | 19 | } 20 | 500 ->{ 21 | 22 | } 23 | else -> { 24 | 25 | } 26 | } 27 | throw ApiException(message as String, code) 28 | } 29 | set(value) { 30 | field = value 31 | } 32 | 33 | 34 | 35 | var message : Any ?= null 36 | 37 | var netState : NetState = NetState.STATE_UNSTART 38 | 39 | var state:Int ?= 0 40 | 41 | var error : String ?= null 42 | 43 | } 44 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/NetState.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | /** 4 | * @ClassName NetState 5 | * @Description 赋值LiveData的网络状态 6 | * @Author AlexLu_1406496344@qq.com 7 | * @Date 2021/5/12 11:21 8 | */ 9 | enum class NetState { 10 | STATE_UNSTART,//未知 11 | STATE_LOADING,//加载中 12 | STATE_SUCCESS,//成功 13 | STATE_EMPTY,//数据为null 14 | STATE_FAILED,//接口请求成功但是服务器返回error 15 | STATE_ERROR//请求失败 16 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/ResultLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | 5 | /** 6 | * @ClassName ResultLiveData 7 | * @Description 8 | * @Author AlexLu_1406496344@qq.com 9 | * @Date 2021/5/18 17:01 10 | */ 11 | class ResultLiveData : MutableLiveData>() { 12 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/RetrofitManager.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | import retrofit2.Retrofit 4 | 5 | object RetrofitManager { 6 | 7 | /** 8 | * 用于存储ApiService 9 | */ 10 | private val mapClass = mutableMapOf, Any>() 11 | 12 | private val mapRetrofit = mutableMapOf() 13 | 14 | private var baseUrl = "https://www.wanandroid.com/" 15 | 16 | private var retrofit :Retrofit ?= null 17 | 18 | 19 | @Synchronized 20 | fun changeBase(url:String) : RetrofitManager = apply { 21 | baseUrl = url 22 | if (mapRetrofit[url] == null){ 23 | retrofit = RetrofitFactory.factory(url) 24 | retrofit?.let { 25 | mapRetrofit[url] = it 26 | } 27 | }else{ 28 | retrofit = mapRetrofit[url] 29 | } 30 | } 31 | 32 | @Synchronized 33 | fun getApiService(apiClass: Class): T { 34 | if (retrofit == null){ 35 | retrofit = RetrofitFactory.factory(baseUrl) 36 | } 37 | return getService(apiClass) 38 | } 39 | 40 | private fun getService(apiClass: Class): T{ 41 | return if (mapClass[apiClass] == null) { 42 | val t = retrofit!!.create(apiClass) 43 | if (mapClass[apiClass] == null) { 44 | mapClass[apiClass] = t 45 | } 46 | t 47 | } else { 48 | mapClass[apiClass] as T 49 | } 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/http/SingleLiveData.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.http 2 | 3 | import androidx.annotation.MainThread 4 | import androidx.lifecycle.LifecycleOwner 5 | import androidx.lifecycle.MutableLiveData 6 | import androidx.lifecycle.Observer 7 | import com.example.baselibrary.utils.log.xLog 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | 10 | /** 11 | * @ClassName SingleLiveData 12 | * @Description 单事件响应的liveData 13 | * @Author AlexLu_1406496344@qq.com 14 | * @Date 2021/5/12 11:08 15 | */ 16 | class SingleLiveData : MutableLiveData() { 17 | 18 | private val mPending = AtomicBoolean(false) 19 | 20 | @MainThread 21 | override fun observe(owner: LifecycleOwner, observer: Observer) { 22 | 23 | if (hasActiveObservers()) { 24 | xLog.w(TAG, "多个观察者存在的时候,只会有一个被通知到数据更新") 25 | } 26 | 27 | super.observe(owner, Observer { t -> 28 | if (mPending.compareAndSet(true, false)) { 29 | observer.onChanged(t) 30 | } 31 | }) 32 | } 33 | 34 | override fun setValue(value: T?) { 35 | mPending.set(true) 36 | super.setValue(value) 37 | } 38 | 39 | @MainThread 40 | fun call() { 41 | value = null 42 | } 43 | 44 | companion object { 45 | private const val TAG = "SingleLiveData" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/lifecycle/ApplicationLifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.lifecycle 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | 6 | /** 7 | * Application 生命周期 用于初始化各个组件 8 | * 9 | * @author Qu Yunshuo 10 | * @since 4/23/21 5:22 PM 11 | */ 12 | interface ApplicationLifecycle { 13 | 14 | /** 15 | * 同[Application.attachBaseContext] 16 | * @param context Context 17 | */ 18 | fun onAttachBaseContext(context: Context) 19 | 20 | /** 21 | * 同[Application.onCreate] 22 | * @param application Application 23 | */ 24 | fun onCreate(application: Application) 25 | 26 | /** 27 | * 同[Application.onTerminate] 28 | * @param application Application 29 | */ 30 | fun onTerminate(application: Application) 31 | 32 | /** 33 | * 需要立即进行初始化的放在这里进行并行初始化 34 | * 需要必须在主线程初始化的放在[InitDepend.mainThreadDepends],反之放在[InitDepend.workerThreadDepends] 35 | * @return InitDepend 初始化方法集合 36 | */ 37 | fun initByFrontDesk(): InitDepend 38 | 39 | /** 40 | * 不需要立即初始化的放在这里进行后台初始化 41 | */ 42 | fun initByBackstage() 43 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/lifecycle/InitDepend.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.lifecycle 2 | 3 | /** 4 | * 需要立即进行初始化的依赖列表 有的依赖可能必须要在主线程进行初始化,就放在[mainThreadDepends]里面就可以 5 | * 其余的非必须要在主线程进行初始化的放在[workerThreadDepends]里面,这部分依赖会被后台线程并行初始化 6 | * 7 | * @property mainThreadDepends MutableList<() -> String> 必须要在主线程初始化的依赖 8 | * @property workerThreadDepends MutableList<() -> String> 非必须要在主线程初始化的依赖 9 | * 10 | * @author Qu Yunshuo 11 | * @since 4/24/21 5:20 PM 12 | */ 13 | data class InitDepend( 14 | val mainThreadDepends: MutableList<() -> Any>, 15 | val workerThreadDepends: MutableList<() -> Any> 16 | ) -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/BaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.viewbinding.ViewBinding 7 | import com.example.baselibrary.utils.log.xLog 8 | 9 | 10 | /** 11 | * @ClassName BaseAdapter 12 | * @Description 基础Adapter封装,使用ViewBinding 13 | * @Author AlexLu_1406496344@qq.com 14 | * @Date 2021/11/16 11:48 15 | */ 16 | abstract class BaseAdapter( 17 | private val list: List, 18 | private val inflate: (LayoutInflater, ViewGroup, Boolean) -> VB, 19 | ) : RecyclerView.Adapter>() { 20 | 21 | override fun onCreateViewHolder( 22 | parent: ViewGroup, 23 | viewType: Int 24 | ): BindingViewHolder { 25 | xLog.d( content = "onCreateViewHolder: BaseAdapter") 26 | return BindingViewHolder(parent, inflate) 27 | } 28 | 29 | var listener : AdapterClickListener ?= null 30 | 31 | open fun setClickListener(listener: AdapterClickListener){ 32 | this.listener = listener 33 | } 34 | 35 | override fun getItemCount(): Int = list.size 36 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/BaseBRVAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.viewbinding.ViewBinding 7 | import com.chad.library.adapter.base.BaseQuickAdapter 8 | import com.chad.library.adapter.base.viewholder.BaseViewHolder 9 | 10 | 11 | /** 12 | * 使用 ViewBinding 和 BaseRecyclerViewAdapter的基础Adapter封装 13 | */ 14 | abstract class BaseBRVAdapter( 15 | private val inflate: (LayoutInflater, ViewGroup, Boolean) -> VB, 16 | layoutResId: Int = -1 17 | ) : BaseQuickAdapter(layoutResId) { 18 | 19 | var listener : AdapterClickListener ?= null 20 | 21 | override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int) = 22 | BaseBindingHolder(inflate(LayoutInflater.from(parent.context), parent, false)) 23 | 24 | class BaseBindingHolder(private val binding: ViewBinding) : BaseViewHolder(binding.root) { 25 | constructor(itemView: View) : this(ViewBinding { itemView }) 26 | 27 | @Suppress("UNCHECKED_CAST") 28 | fun getViewBinding() = binding as VB 29 | } 30 | 31 | open fun setClickListener(listener: AdapterClickListener){ 32 | this.listener = listener 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/BaseDataBindAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | 4 | import androidx.annotation.LayoutRes 5 | import androidx.databinding.ViewDataBinding 6 | import com.chad.library.adapter.base.BaseQuickAdapter 7 | import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder 8 | 9 | /** 10 | * 使用 DataBinding 和 BaseRecyclerViewAdapter的基础Adapter封装,建议使用 11 | */ 12 | abstract class BaseDataBindAdapter(@LayoutRes layout: Int) : BaseQuickAdapter>(layout) { 13 | 14 | var listener : AdapterClickListener ?= null 15 | 16 | open fun setClickListener(listener: AdapterClickListener){ 17 | this.listener = listener 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/BindingHolderUtil.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | import android.view.View 4 | import androidx.viewbinding.ViewBinding 5 | import com.chad.library.adapter.base.viewholder.BaseViewHolder 6 | 7 | 8 | @JvmName("bind") 9 | fun BaseViewHolder.withBinding(bind: (View) -> VB): BaseViewHolder = 10 | BaseViewHolderWithBinding(bind(itemView)) 11 | 12 | @JvmName("getBinding") 13 | @Suppress("UNCHECKED_CAST") 14 | fun BaseViewHolder.getViewBinding(): VB { 15 | if (this is BaseViewHolderWithBinding<*>) { 16 | return binding as VB 17 | } else { 18 | throw IllegalStateException("The binding could not be found.") 19 | } 20 | } 21 | 22 | class BaseViewHolderWithBinding(val binding: VB) : BaseViewHolder(binding.root) -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/CommonInterface.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | import android.view.View 4 | 5 | /** 6 | * @ClassName CommonInterface 7 | * @Description TODO 移花接木 8 | * @Author AlexLu_1406496344@qq.com 9 | * @Date 2021/11/26 15:38 10 | */ 11 | 12 | 13 | 14 | interface AdapterClickListener{ 15 | fun click(position:Int, view: View, data:T) 16 | fun longClick(position:Int, view: View, data:T) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/GridSpaceItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview; 2 | 3 | import android.graphics.Rect; 4 | import android.view.View; 5 | 6 | import androidx.annotation.NonNull; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | /** 10 | * 描述 : RecyclerView GridLayoutManager 等间距。 11 | *

12 | * 等间距需满足两个条件: 13 | * 1.各个模块的大小相等,即 各列的left+right 值相等; 14 | * 2.各列的间距相等,即 前列的right + 后列的left = 列间距; 15 | */ 16 | public class GridSpaceItemDecoration extends RecyclerView.ItemDecoration { 17 | 18 | private final String TAG = "GridSpaceItemDecoration"; 19 | 20 | private int mSpanCount;//横条目数量 21 | private int mRowSpacing;//行间距 22 | private int mColumnSpacing;// 列间距 23 | 24 | /** 25 | * @param spanCount 列数 26 | * @param rowSpacing 行间距 27 | * @param columnSpacing 列间距 28 | */ 29 | public GridSpaceItemDecoration(int spanCount, int rowSpacing, int columnSpacing) { 30 | this.mSpanCount = spanCount; 31 | this.mRowSpacing = rowSpacing; 32 | this.mColumnSpacing = columnSpacing; 33 | } 34 | 35 | @Override 36 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { 37 | int position = parent.getChildAdapterPosition(view); // 获取view 在adapter中的位置。 38 | int column = position % mSpanCount; // view 所在的列 39 | 40 | outRect.left = column * mColumnSpacing / mSpanCount; // column * (列间距 * (1f / 列数)) 41 | outRect.right = mColumnSpacing - (column + 1) * mColumnSpacing / mSpanCount; // 列间距 - (column + 1) * (列间距 * (1f /列数)) 42 | 43 | // 如果position > 行数,说明不是在第一行,则不指定行高,其他行的上间距为 top=mRowSpacing 44 | if (position >= mSpanCount) { 45 | outRect.top = mRowSpacing; // item top 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/LayoutManger.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview 2 | 3 | /** 4 | * @ClassName LayoutManger 5 | * @Description TODO 6 | * @Author AlexLu_1406496344@qq.com 7 | * @Date 2021/11/26 14:04 8 | */ 9 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/test/AdapterDemo1.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview.test 2 | 3 | import android.view.ViewGroup 4 | import com.chad.library.adapter.base.BaseQuickAdapter 5 | import com.chad.library.adapter.base.viewholder.BaseViewHolder 6 | import com.example.baselibrary.R 7 | import com.example.baselibrary.databinding.ListItemBinding 8 | import com.example.baselibrary.recyclerview.getViewBinding 9 | import com.example.baselibrary.recyclerview.withBinding 10 | 11 | 12 | /** 13 | * 直接使用BRVA中的BaseQuickAdapter,通过扩展类BindingHolderUtil使用binding 14 | */ 15 | class AdapterDemo1 : BaseQuickAdapter(R.layout.list_item) { 16 | 17 | override fun onCreateDefViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { 18 | return super.onCreateDefViewHolder(parent, viewType).withBinding { 19 | ListItemBinding.bind(it) 20 | } 21 | } 22 | 23 | override fun convert(holder: BaseViewHolder, item: Book) { 24 | val binding = holder.getViewBinding() 25 | binding.apply { 26 | textView.text = item.name 27 | button.text = item.id.toString() 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/test/AdapterDemo2.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview.test 2 | 3 | import com.example.baselibrary.databinding.ListItemBinding 4 | import com.example.baselibrary.recyclerview.BaseBRVAdapter 5 | 6 | 7 | /** 8 | * 继承BaseBRVAdapter,简单快捷,建议使用 9 | */ 10 | class AdapterDemo2 : BaseBRVAdapter(ListItemBinding::inflate) { 11 | 12 | override fun convert(holder: BaseBindingHolder, item: Book) { 13 | val binding = holder.getViewBinding() 14 | binding.apply { 15 | textView.text = item.name 16 | button.text = item.id.toString() 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/test/AdapterDemo3.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview.test 2 | 3 | import com.example.baselibrary.databinding.ListItemBinding 4 | import com.example.baselibrary.recyclerview.BaseAdapter 5 | import com.example.baselibrary.recyclerview.BindingViewHolder 6 | 7 | 8 | /** 9 | * 自定义ReycllerView.Adapter,对于简单列表也可以适用 10 | */ 11 | class AdapterDemo3(private val list: List) : BaseAdapter(list,ListItemBinding::inflate) { 12 | 13 | override fun onBindViewHolder(holder: BindingViewHolder, position: Int) { 14 | val binding = holder.binding 15 | val data = list[position] 16 | binding.apply { 17 | textView.text = data.name 18 | button.text = data.id.toString() 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/test/AdapterDemo4.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview.test 2 | 3 | import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder 4 | import com.example.baselibrary.R 5 | import com.example.baselibrary.databinding.ListItemBinding 6 | import com.example.baselibrary.recyclerview.BaseDataBindAdapter 7 | 8 | /** 9 | * @ClassName AdapterDemo4 10 | * @Description TODO 11 | * @Author AlexLu_1406496344@qq.com 12 | * @Date 2021/11/16 14:01 13 | */ 14 | class AdapterDemo4 : BaseDataBindAdapter(R.layout.list_item) { 15 | 16 | override fun convert(holder: BaseDataBindingHolder, item: Book) { 17 | val binding = holder.dataBinding 18 | binding?.data = item 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/recyclerview/test/Book.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.recyclerview.test 2 | 3 | /** 4 | * @ClassName Book 5 | * @Description 测试数据类 6 | * @Author AlexLu_1406496344@qq.com 7 | * @Date 2021/11/16 11:11 8 | */ 9 | data class Book( 10 | val id:Int, 11 | val name:String 12 | ) 13 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/activity/Application.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.activity 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.pm.ApplicationInfo 6 | import android.content.pm.PackageInfo 7 | import android.content.pm.PackageManager 8 | import android.content.res.Configuration.UI_MODE_NIGHT_MASK 9 | import android.content.res.Configuration.UI_MODE_NIGHT_YES 10 | import androidx.core.content.pm.PackageInfoCompat 11 | import com.example.baselibrary.AppInit 12 | import com.example.baselibrary.lifecycle.ActivityStack 13 | 14 | val application: Application get() = AppInit.application 15 | 16 | val context:Context get() = ActivityStack.currentActivity?.context ?: application 17 | 18 | inline val packageName: String get() = application.packageName 19 | 20 | inline val activitiesPackageInfo: PackageInfo get() = application.packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) 21 | 22 | inline val appName: String get() = application.applicationInfo.loadLabel(application.packageManager).toString() 23 | 24 | inline val appVersionName: String get() = activitiesPackageInfo.versionName 25 | 26 | inline val appVersionCode: Long get() = PackageInfoCompat.getLongVersionCode(activitiesPackageInfo) 27 | 28 | inline val isAppDebug: Boolean get() = application.packageManager.getApplicationInfo(packageName, 0).flags and ApplicationInfo.FLAG_DEBUGGABLE != 0 29 | 30 | inline val isAppDarkMode: Boolean get() = (application.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES 31 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/activity/Lifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.activity 2 | 3 | import androidx.fragment.app.Fragment 4 | import androidx.lifecycle.DefaultLifecycleObserver 5 | import androidx.lifecycle.LifecycleOwner 6 | import androidx.lifecycle.lifecycleScope 7 | 8 | 9 | inline fun Fragment.doOnViewLifecycle( 10 | crossinline onCreateView: () -> Unit = {}, 11 | crossinline onStart: () -> Unit = {}, 12 | crossinline onResume: () -> Unit = {}, 13 | crossinline onPause: () -> Unit = {}, 14 | crossinline onStop: () -> Unit = {}, 15 | crossinline onDestroyView: () -> Unit = {}, 16 | ) = viewLifecycleOwner.doOnLifecycle(onCreateView, onStart, onResume, onPause, onStop, onDestroyView) 17 | 18 | inline fun LifecycleOwner.doOnLifecycle( 19 | crossinline onCreate: () -> Unit = {}, 20 | crossinline onStart: () -> Unit = {}, 21 | crossinline onResume: () -> Unit = {}, 22 | crossinline onPause: () -> Unit = {}, 23 | crossinline onStop: () -> Unit = {}, 24 | crossinline onDestroy: () -> Unit = {}, 25 | ) = 26 | lifecycle.addObserver(object : DefaultLifecycleObserver { 27 | fun onCreate() = onCreate() 28 | fun onStart() = onStart() 29 | fun onResume() = onResume() 30 | fun onPause() = onPause() 31 | fun onStop() = onStop() 32 | fun onDestroy() = onDestroy() 33 | }) 34 | 35 | val Fragment.viewLifecycleScope get() = viewLifecycleOwner.lifecycleScope 36 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/longan/Bundle.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.longan 2 | 3 | import android.os.Bundle 4 | 5 | 6 | inline operator fun Bundle?.get(key: String): T? = 7 | this?.get(key).let { if (it is T) it else null } 8 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/longan/DeviceInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021. Dylan Cai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.baselibrary.utils.longan 18 | 19 | import android.os.Build 20 | 21 | /** 22 | * @author Dylan Cai 23 | */ 24 | 25 | inline val sdkVersionName: String get() = Build.VERSION.RELEASE 26 | 27 | inline val sdkVersionCode: Int get() = Build.VERSION.SDK_INT 28 | 29 | inline val deviceManufacturer: String get() = Build.MANUFACTURER 30 | 31 | inline val deviceModel: String get() = Build.MODEL 32 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/longan/Encode.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.longan 2 | 3 | import android.util.Base64 4 | import java.net.URLDecoder 5 | import java.net.URLEncoder 6 | 7 | 8 | 9 | inline fun ByteArray.base64Encode(flag: Int = Base64.DEFAULT): ByteArray = 10 | Base64.encode(this, flag) 11 | 12 | inline fun ByteArray.base64EncodeToString(flag: Int = Base64.DEFAULT): String = 13 | Base64.encodeToString(this, flag) 14 | 15 | inline fun String.base64Decode(flag: Int = Base64.DEFAULT): ByteArray = 16 | Base64.decode(this, flag) 17 | 18 | inline fun String.urlEncode(enc: String): String = 19 | URLEncoder.encode(this, enc) 20 | 21 | inline fun String.urlDecode(enc: String): String = 22 | URLDecoder.decode(this, enc) 23 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/JsonUtil.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.other 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.reflect.TypeToken 5 | import java.lang.reflect.Type 6 | 7 | /** 8 | * @ClassName JsonUtil 9 | * @Description TODO 10 | * @Author AlexLu_1406496344@qq.com 11 | * @Date 2021/8/26 14:20 12 | */ 13 | object JsonUtil { 14 | 15 | //val response = Gson().fromJson(json,ApiResponse

().javaClass) 16 | // val type: Type = object : TypeToken>() {}.type 17 | // val response: ApiResponse
= Gson().fromJson(json, type) 18 | 19 | //不可用 20 | private fun decode(json: String): T { 21 | val type: Type = object : TypeToken() {}.type 22 | return Gson().fromJson(json, type) 23 | } 24 | 25 | fun encode(t: Any):String{ 26 | return Gson().toJson(t) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/Md5Util.kt: -------------------------------------------------------------------------------- 1 | 2 | import java.io.File 3 | import java.io.FileInputStream 4 | import java.io.IOException 5 | import java.math.BigInteger 6 | import java.security.MessageDigest 7 | import java.security.NoSuchAlgorithmException 8 | 9 | 10 | object Md5Util { 11 | 12 | 13 | /** 14 | * 计算文件MD5值 15 | */ 16 | fun getMD5(f: File): String? { 17 | var bi: BigInteger? = null 18 | try { 19 | val buffer = ByteArray(8192) 20 | var len = 0 21 | val md = MessageDigest.getInstance("MD5") 22 | val fis = FileInputStream(f) 23 | while (fis.read(buffer).also { len = it } != -1) { 24 | md.update(buffer, 0, len) 25 | } 26 | fis.close() 27 | val b = md.digest() 28 | bi = BigInteger(1, b) 29 | } catch (e: NoSuchAlgorithmException) { 30 | e.printStackTrace() 31 | } catch (e: IOException) { 32 | e.printStackTrace() 33 | } 34 | return bi?.toString(16) 35 | } 36 | 37 | 38 | } 39 | 40 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/NameLengthFilter.java: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.other; 2 | 3 | import android.content.Context; 4 | import android.text.InputFilter; 5 | import android.text.Spanned; 6 | import android.widget.Toast; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * edittext字符长度限制 13 | * etName.setFilters(arrayOf(NameLengthFilter(30))); 14 | */ 15 | public class NameLengthFilter implements InputFilter { 16 | 17 | int MAX_EN;// 最大英文/数字长度 一个汉字算两个字母 18 | String regEx = "[\\u4e00-\\u9fa5]"; // unicode编码,判断是否为汉字 19 | Context context; 20 | 21 | public NameLengthFilter(int mAX_EN, Context context) { 22 | super(); 23 | MAX_EN = mAX_EN; 24 | this.context = context; 25 | } 26 | 27 | @Override 28 | public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { 29 | int destCount = dest.toString().length()+ getChineseCount(dest.toString()); 30 | int sourceCount = source.toString().length() + getChineseCount(source.toString()); 31 | if (destCount + sourceCount > MAX_EN) { 32 | Toast.makeText(context,"最大长度为"+MAX_EN+"字符",Toast.LENGTH_SHORT).show(); 33 | return ""; 34 | } else { 35 | return source; 36 | } 37 | } 38 | 39 | private int getChineseCount(String str) { 40 | int count = 0; 41 | Pattern p = Pattern.compile(regEx); 42 | Matcher m = p.matcher(str); 43 | while (m.find()) { 44 | for (int i = 0; i <= m.groupCount(); i++) { 45 | count = count + 1; 46 | } 47 | } 48 | return count; 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/Share.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.other 2 | 3 | import android.net.Uri 4 | import androidx.core.app.ShareCompat 5 | import com.example.baselibrary.lifecycle.ActivityStack 6 | 7 | 8 | 9 | inline fun shareText(content: String, title: String? = null) = 10 | share("text/plain") { 11 | setText(content) 12 | setChooserTitle(title) 13 | } 14 | 15 | inline fun shareImage(imageUri: Uri, title: String? = null) = 16 | shareTextAndImage(null, imageUri, title) 17 | 18 | inline fun shareImages(imageUris: List, title: String? = null) = 19 | shareTextAndImages(null, imageUris, title) 20 | 21 | inline fun shareTextAndImage(content: String?, imageUri: Uri, title: String? = null) = 22 | share("image/*") { 23 | setText(content) 24 | setStream(imageUri) 25 | setChooserTitle(title) 26 | } 27 | 28 | inline fun shareTextAndImages(content: String?, imageUris: List, title: String? = null) = 29 | share("image/*") { 30 | setText(content) 31 | imageUris.forEach { addStream(it) } 32 | setChooserTitle(title) 33 | } 34 | 35 | 36 | inline fun share(mimeType: String, crossinline block: ShareCompat.IntentBuilder.() -> Unit) = 37 | ActivityStack.currentActivity?.let { 38 | ShareCompat.IntentBuilder(it).apply { setType(mimeType) }.apply(block).startChooser() 39 | } 40 | 41 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/String.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.other 2 | 3 | import android.text.format.Formatter 4 | import android.util.Patterns 5 | import androidx.core.util.PatternsCompat 6 | import com.example.baselibrary.utils.activity.application 7 | import org.json.JSONObject 8 | import java.util.* 9 | 10 | 11 | 12 | const val REGEX_ID_CARD_15: String = 13 | "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$" 14 | 15 | const val REGEX_ID_CARD_18: String = 16 | "^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$" 17 | 18 | inline val randomUUIDString: String 19 | get() = UUID.randomUUID().toString() 20 | 21 | fun Long.toFileSizeString(): String = 22 | Formatter.formatFileSize(application, this) 23 | 24 | fun Long.toShortFileSizeString(): String = 25 | Formatter.formatShortFileSize(application, this) 26 | 27 | fun String.limitLength(length: Int): String = 28 | if (this.length <= length) this else substring(0, length) 29 | 30 | fun String.isPhone(): Boolean = 31 | Patterns.PHONE.matcher(this).matches() 32 | 33 | fun String.isDomainName(): Boolean = 34 | PatternsCompat.DOMAIN_NAME.matcher(this).matches() 35 | 36 | fun String.isEmail(): Boolean = 37 | PatternsCompat.EMAIL_ADDRESS.matcher(this).matches() 38 | 39 | fun String.isIP(): Boolean = 40 | PatternsCompat.IP_ADDRESS.matcher(this).matches() 41 | 42 | /** 43 | * Regular expression pattern to match most part of RFC 3987 44 | * Internationalized URLs, aka IRIs. 45 | */ 46 | fun String.isWebUrl(): Boolean = 47 | PatternsCompat.WEB_URL.matcher(this).matches() 48 | 49 | fun String.isIDCard15(): Boolean = 50 | REGEX_ID_CARD_15.toRegex().matches(this) 51 | 52 | fun String.isIDCard18(): Boolean = 53 | REGEX_ID_CARD_18.toRegex().matches(this) 54 | 55 | fun String.isJson(): Boolean = 56 | try { 57 | JSONObject(this) 58 | true 59 | } catch (e: Exception) { 60 | false 61 | } 62 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/Threads.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021. Dylan Cai 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | @file:Suppress("unused", "NOTHING_TO_INLINE") 18 | 19 | package com.example.baselibrary.utils.other 20 | 21 | import android.os.Handler 22 | import android.os.Looper 23 | 24 | /** 25 | * @author Dylan Cai 26 | */ 27 | 28 | val mainHandler = lazy { Handler(Looper.getMainLooper()) } 29 | 30 | inline fun mainThread(noinline block: () -> Unit) = 31 | mainHandler.value.post(block) 32 | 33 | inline fun mainThread(delayMillis: Long, noinline block: () -> Unit) = 34 | mainHandler.value.postDelayed(block, delayMillis) 35 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/other/TimeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.other 2 | 3 | import java.text.SimpleDateFormat 4 | import java.util.* 5 | 6 | /** 7 | * @ClassName TimeUtil 8 | * @Description 时间工具类 9 | * @Author AlexLu_1406496344@qq.com 10 | * @Date 2021/10/20 15:20 11 | */ 12 | object TimeUtil { 13 | 14 | fun getCurrentTime(): String { 15 | val timeStamp = System.currentTimeMillis() //获取当前时间戳 16 | //SimpleDateFormat("yyyy 年 MM 月 dd 日 HH 时 mm 分 ss 秒") 17 | val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") 18 | return sdf.format(Date(timeStamp.toString().toLong())) // 时间戳转换成时间 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/transformation/DepthPageTransformer.java: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.transformation; 2 | 3 | import android.view.View; 4 | 5 | import androidx.viewpager2.widget.ViewPager2; 6 | 7 | public class DepthPageTransformer implements ViewPager2.PageTransformer { 8 | private static final float MIN_SCALE = 0.75f; 9 | 10 | public void transformPage(View view, float position) { 11 | int pageWidth = view.getWidth(); 12 | 13 | if (position < -1) { // [-Infinity,-1) 14 | // This page is way off-screen to the left. 15 | view.setAlpha(0f); 16 | 17 | } else if (position <= 0) { // [-1,0] 18 | // Use the default slide transition when moving to the left page 19 | view.setAlpha(1f); 20 | view.setTranslationX(0f); 21 | view.setScaleX(1f); 22 | view.setScaleY(1f); 23 | 24 | } else if (position <= 1) { // (0,1] 25 | // Fade the page out. 26 | view.setAlpha(1 - position); 27 | 28 | // Counteract the default slide transition 29 | view.setTranslationX(pageWidth * -position); 30 | 31 | // Scale the page down (between MIN_SCALE and 1) 32 | float scaleFactor = MIN_SCALE 33 | + (1 - MIN_SCALE) * (1 - Math.abs(position)); 34 | view.setScaleX(scaleFactor); 35 | view.setScaleY(scaleFactor); 36 | 37 | } else { // (1,+Infinity] 38 | // This page is way off-screen to the right. 39 | view.setAlpha(0f); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/transformation/ZoomOutPageTransformer.java: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.transformation; 2 | 3 | import android.view.View; 4 | 5 | import androidx.viewpager2.widget.ViewPager2; 6 | 7 | public class ZoomOutPageTransformer implements ViewPager2.PageTransformer { 8 | private static final float MIN_SCALE = 0.85f; 9 | private static final float MIN_ALPHA = 0.5f; 10 | 11 | public void transformPage(View view, float position) { 12 | int pageWidth = view.getWidth(); 13 | int pageHeight = view.getHeight(); 14 | 15 | if (position < -1) { // [-Infinity,-1) 16 | // This page is way off-screen to the left. 17 | view.setAlpha(0f); 18 | 19 | } else if (position <= 1) { // [-1,1] 20 | // Modify the default slide transition to shrink the page as well 21 | float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); 22 | float vertMargin = pageHeight * (1 - scaleFactor) / 2; 23 | float horzMargin = pageWidth * (1 - scaleFactor) / 2; 24 | if (position < 0) { 25 | view.setTranslationX(horzMargin - vertMargin / 2); 26 | } else { 27 | view.setTranslationX(-horzMargin + vertMargin / 2); 28 | } 29 | 30 | // Scale the page down (between MIN_SCALE and 1) 31 | view.setScaleX(scaleFactor); 32 | view.setScaleY(scaleFactor); 33 | 34 | // Fade the page relative to its size. 35 | view.setAlpha(MIN_ALPHA + 36 | (scaleFactor - MIN_SCALE) / 37 | (1 - MIN_SCALE) * (1 - MIN_ALPHA)); 38 | 39 | } else { // (1,+Infinity] 40 | // This page is way off-screen to the right. 41 | view.setAlpha(0f); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/Canvas.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.graphics.Canvas 4 | import android.graphics.Paint 5 | 6 | 7 | inline fun Canvas.drawCenterVerticalText(text: String, centerX: Float, centerY: Float, paint: Paint) = 8 | drawCenterText(text, centerX, centerY, paint, Paint.Align.LEFT) 9 | 10 | inline fun Canvas.drawCenterText( 11 | text: String, 12 | centerX: Float, 13 | centerY: Float, 14 | paint: Paint, 15 | textAlign: Paint.Align = Paint.Align.CENTER 16 | ) { 17 | val textAlignTemp = paint.textAlign 18 | paint.textAlign = textAlign 19 | val fontMetrics = paint.fontMetrics 20 | val baseline = centerY + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom 21 | drawText(text, centerX, baseline, paint) 22 | paint.textAlign = textAlignTemp 23 | } 24 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/Dimensions.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.content.res.Resources 4 | import android.util.TypedValue 5 | 6 | 7 | /*------dp转换--------*/ 8 | inline val Int.dp: Float get() = toFloat().dp 9 | 10 | inline val Long.dp: Float get() = toFloat().dp 11 | 12 | inline val Double.dp: Float get() = toFloat().dp 13 | 14 | inline val Float.dp: Float get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics) 15 | 16 | 17 | 18 | /*------sp转换--------*/ 19 | inline val Int.sp: Float get() = toFloat().sp 20 | 21 | inline val Long.sp: Float get() = toFloat().sp 22 | 23 | inline val Double.sp: Float get() = toFloat().sp 24 | 25 | inline val Float.sp: Float get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this, Resources.getSystem().displayMetrics) 26 | 27 | 28 | 29 | /*------px-dp转换--------*/ 30 | inline fun Int.pxToDp(): Int = toFloat().pxToDp() 31 | 32 | inline fun Long.pxToDp(): Int = toFloat().pxToDp() 33 | 34 | inline fun Double.pxToDp(): Int = toFloat().pxToDp() 35 | 36 | inline fun Float.pxToDp(): Int = (this / Resources.getSystem().displayMetrics.density + 0.5f).toInt() 37 | 38 | 39 | 40 | /*------px-sp转换--------*/ 41 | inline fun Int.pxToSp(): Int = toFloat().pxToSp() 42 | 43 | inline fun Long.pxToSp(): Int = toFloat().pxToSp() 44 | 45 | inline fun Double.pxToSp(): Int = toFloat().pxToSp() 46 | 47 | inline fun Float.pxToSp(): Int = (this / Resources.getSystem().displayMetrics.scaledDensity + 0.5f).toInt() 48 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/EditText.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.widget.EditText 4 | import androidx.core.widget.addTextChangedListener 5 | 6 | /** 7 | * @ClassName EditText 8 | * @Description TODO 9 | * @Author AlexLu_1406496344@qq.com 10 | * @Date 2021/11/17 9:59 11 | */ 12 | 13 | 14 | fun T.onChange(block: (T) -> Unit){ 15 | addTextChangedListener { 16 | block(this) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/Internal.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.view.View 4 | 5 | internal const val NO_GETTER: String = "Property does not have a getter" 6 | 7 | 8 | internal fun noGetter(): Nothing = throw NotImplementedError(NO_GETTER) 9 | 10 | internal var View.isAddedMarginTop: Boolean? by viewTags(-101) 11 | internal var View.isAddedPaddingTop: Boolean? by viewTags(-102) 12 | internal var View.isAddedMarginBottom: Boolean? by viewTags(-103) 13 | internal var View.lastClickTime: Long? by viewTags(-104) 14 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/Keyboard.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.widget.EditText 4 | import androidx.core.view.WindowInsetsCompat.Type 5 | 6 | 7 | inline fun EditText.showKeyboard() = windowInsetsControllerCompat?.show(Type.ime()) 8 | 9 | inline fun EditText.hideKeyboard() = windowInsetsControllerCompat?.hide(Type.ime()) 10 | 11 | inline fun EditText.toggleKeyboard() = if (isKeyboardVisible) hideKeyboard() else showKeyboard() 12 | 13 | inline val EditText.isKeyboardVisible: Boolean get() = rootWindowInsetsCompat?.isVisible(Type.ime()) == true 14 | 15 | inline val EditText.keyboardHeight: Int get() = rootWindowInsetsCompat?.getInsets(Type.ime())?.bottom ?: -1 16 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/TextView.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.graphics.Paint 4 | import android.os.CountDownTimer 5 | import android.text.method.HideReturnsTransformationMethod 6 | import android.text.method.PasswordTransformationMethod 7 | import android.widget.TextView 8 | import androidx.core.widget.doAfterTextChanged 9 | import androidx.lifecycle.Lifecycle 10 | import androidx.lifecycle.LifecycleObserver 11 | import androidx.lifecycle.LifecycleOwner 12 | import androidx.lifecycle.OnLifecycleEvent 13 | import kotlin.math.roundToInt 14 | 15 | 16 | inline val TextView.content : String get() = text.toString() 17 | 18 | inline fun TextView.isTextEmpty(): Boolean = content.isEmpty() 19 | 20 | inline fun TextView.isTextNotEmpty(): Boolean = content.isNotEmpty() 21 | 22 | inline fun TextView.addUnderline(){ 23 | paint.flags = Paint.UNDERLINE_TEXT_FLAG 24 | } 25 | 26 | fun TextView.startCountDown( 27 | lifecycleOwner: LifecycleOwner, 28 | secondInFuture: Int = 60, 29 | onTick: TextView.(secondUntilFinished: Int) -> Unit, 30 | onFinish: TextView.() -> Unit, 31 | ) { 32 | val countDownTimer = object : CountDownTimer(secondInFuture * 1000L, 1000) { 33 | override fun onTick(millisUntilFinished: Long) { 34 | isEnabled = false 35 | onTick((millisUntilFinished / 1000f).roundToInt()) 36 | } 37 | 38 | override fun onFinish() { 39 | isEnabled = true 40 | this@startCountDown.onFinish() 41 | } 42 | } 43 | countDownTimer.start() 44 | lifecycleOwner.lifecycle.addObserver(object : LifecycleObserver { 45 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 46 | fun onDestroy() { 47 | countDownTimer.cancel() 48 | } 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/utils/view/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.utils.view 2 | 3 | import android.app.Service 4 | import android.view.View 5 | import android.view.inputmethod.InputMethodManager 6 | import androidx.core.view.ViewCompat 7 | import androidx.core.view.WindowInsetsCompat 8 | import androidx.core.view.WindowInsetsControllerCompat 9 | import kotlin.properties.ReadWriteProperty 10 | import kotlin.reflect.KProperty 11 | 12 | 13 | fun View.showKeyboard() { 14 | (this.context.getSystemService(Service.INPUT_METHOD_SERVICE) as? InputMethodManager)?.showSoftInput(this, 0) 15 | } 16 | 17 | fun View.hideKeyboard() { 18 | (this.context.getSystemService(Service.INPUT_METHOD_SERVICE) as? InputMethodManager)?.hideSoftInputFromWindow(this.windowToken, 0) 19 | } 20 | 21 | fun View.toVisible() { 22 | this.visibility = View.VISIBLE 23 | } 24 | 25 | fun View.toGone() { 26 | this.visibility = View.GONE 27 | } 28 | 29 | fun View.toInvisible() { 30 | this.visibility = View.GONE 31 | } 32 | 33 | 34 | inline val View.rootWindowInsetsCompat: WindowInsetsCompat? get() = ViewCompat.getRootWindowInsets(this) 35 | 36 | inline val View.windowInsetsControllerCompat: WindowInsetsControllerCompat? get() = ViewCompat.getWindowInsetsController(this) 37 | 38 | inline fun View.doOnApplyWindowInsets(noinline action: (View, WindowInsetsCompat) -> WindowInsetsCompat) = ViewCompat.setOnApplyWindowInsetsListener(this, action) 39 | 40 | 41 | fun viewTags(key: Int) = object : ReadWriteProperty { 42 | @Suppress("UNCHECKED_CAST") 43 | override fun getValue(thisRef: View, property: KProperty<*>) = 44 | thisRef.getTag(key) as T? 45 | 46 | override fun setValue(thisRef: View, property: KProperty<*>, value: T?) = 47 | thisRef.setTag(key, value) 48 | } 49 | -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/view/photoscontentview/PhotoImageView.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.view.photoscontentview 2 | 3 | import android.content.Context 4 | import android.graphics.BlendMode 5 | import android.graphics.BlendModeColorFilter 6 | import android.graphics.Color 7 | import android.graphics.PorterDuff 8 | import android.os.Build 9 | import android.util.AttributeSet 10 | import android.view.MotionEvent 11 | import androidx.appcompat.widget.AppCompatImageView 12 | 13 | class PhotoImageView @JvmOverloads constructor( 14 | context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 15 | ) : AppCompatImageView(context, attrs, defStyleAttr) { 16 | override fun onTouchEvent(event: MotionEvent): Boolean { 17 | when (event.action) { 18 | MotionEvent.ACTION_DOWN -> { 19 | val drawable = drawable 20 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 21 | drawable?.mutate()?.colorFilter = BlendModeColorFilter(Color.GRAY, BlendMode.MULTIPLY) 22 | } else { 23 | drawable?.mutate()?.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY) 24 | } 25 | } 26 | MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { 27 | val drawableUp = drawable 28 | drawableUp?.mutate()?.clearColorFilter() 29 | } 30 | } 31 | return super.onTouchEvent(event) 32 | } 33 | 34 | override fun onDetachedFromWindow() { 35 | super.onDetachedFromWindow() 36 | setImageDrawable(null) 37 | } 38 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/view/photoscontentview/PhotosContentViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.view.photoscontentview 2 | 3 | import android.content.Context 4 | import android.widget.ImageView 5 | 6 | abstract class PhotosContentViewAdapter { 7 | 8 | abstract fun onDisplayImage(context: Context, imageView: ImageView, data: T) 9 | 10 | open fun onItemImageClick( 11 | context: Context, 12 | imageView: ImageView, 13 | index: Int, 14 | list: MutableList 15 | ) { 16 | } 17 | 18 | open fun onItemImageLongClick( 19 | context: Context, 20 | imageView: ImageView, 21 | index: Int, 22 | list: MutableList 23 | ): Boolean { 24 | return false 25 | } 26 | 27 | open fun generateImageView(context: Context): ImageView { 28 | return PhotoImageView(context) 29 | } 30 | } -------------------------------------------------------------------------------- /base/src/main/java/com/example/baselibrary/vm/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.example.baselibrary.vm 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import kotlinx.coroutines.CoroutineScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.launch 8 | 9 | 10 | open class BaseViewModel : ViewModel() { 11 | 12 | fun launchMain(block: suspend CoroutineScope.() -> Unit) { 13 | viewModelScope.launch(Dispatchers.Main){ 14 | block 15 | } 16 | } 17 | 18 | fun launchIO(block: suspend CoroutineScope.() -> Unit) { 19 | viewModelScope.launch(Dispatchers.IO){ 20 | block 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /base/src/main/res/anim/dialog_enter_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /base/src/main/res/anim/dialog_exit_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /base/src/main/res/anim/dialog_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /base/src/main/res/anim/dialog_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /base/src/main/res/anim/no_anim.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /base/src/main/res/drawable-xhdpi/shadow_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/drawable-xhdpi/shadow_bottom.png -------------------------------------------------------------------------------- /base/src/main/res/drawable-xhdpi/shadow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/drawable-xhdpi/shadow_left.png -------------------------------------------------------------------------------- /base/src/main/res/drawable-xhdpi/shadow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/drawable-xhdpi/shadow_right.png -------------------------------------------------------------------------------- /base/src/main/res/drawable-xhdpi/shadow_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/drawable-xhdpi/shadow_top.png -------------------------------------------------------------------------------- /base/src/main/res/drawable/shape_radius_10dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/shape_radius_13dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /base/src/main/res/drawable/shape_radius_5dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /base/src/main/res/font/domine_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/font/domine_bold.ttf -------------------------------------------------------------------------------- /base/src/main/res/font/domine_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/font/domine_regular.ttf -------------------------------------------------------------------------------- /base/src/main/res/font/montserrat_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/font/montserrat_medium.ttf -------------------------------------------------------------------------------- /base/src/main/res/font/montserrat_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/font/montserrat_regular.ttf -------------------------------------------------------------------------------- /base/src/main/res/font/montserrat_semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xluu233/mvvm_develop/ed575f4068de5c1c40781e723493485012113fba/base/src/main/res/font/montserrat_semibold.ttf -------------------------------------------------------------------------------- /base/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 16 | 17 |