Giter VIP home page Giter VIP logo

kmvvm's Introduction

Gitee 地址:kmvvm


Github 地址:kmvvm


技术要点

  • 支持Flow+Retrofit+OkHttp实现链式http请求

  • 支持Rxjava+Retrofit+OkHttp实现链式http请求

  • 封装基类:BaseActivity、BaseVMActivity、BaseFragment、BaseVMFragment、RecycleAdapter、BaseViewModel

  • 封装工具扩展类:CalendarExt、ContextExt、DateExt、EditTextExt、GsonExt、RxJavaExt、StringExt、SnackbarExt

  • 引入LifeCycle,将ViewModel和Activity的生命周期绑定在一起

  • 将在Application中初始化移至到ContentProvider中,从而不用封装BaseApplication

  • APT(编译时注解)封装注解:Title、OnClickFirstDrawable、OnClickFirstText、OnClickSecondDrawable、OnClickSecondText、Prefs、PrefsField、StatusBar、FlowError、Adapter、GlobalConfig、ServiceApi

最低兼容:21

Gradle

1. 在Project的build.gradle中添加

allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}

2. 在app的build.gradle的添加

apply plugin: 'kotlin-kapt' // 使用 kapt 注解处理工具
apply plugin: 'kotlinx-serialization' //序列化

3. 在app的build.gradle的android下添加

buildFeatures {
    viewBinding = true
}

4. 添加依赖

Gitee

implementation "com.gitee.catchpig.kmvvm:mvvm:last_version"
kapt "com.gitee.catchpig.kmvvm:compiler:last_version"

Github

implementation "com.github.catchpig.kmvvm:mvvm:last_version"
kapt "com.github.catchpig.kmvvm:compiler:last_version"

使用

1. 配置全部参数

interface IGlobalConfig {
    /**
     * 标题栏高度
     * @return Int
     */
    @DimenRes
    fun getTitleHeight(): Int

    /**
     * 标题栏的返回按钮资源
     * @return Int
     */
    @DrawableRes
    fun getTitleBackIcon(): Int

    /**
     * 标题栏背景颜色
     * @return Int
     */
    @ColorRes
    fun getTitleBackground(): Int

    /**
     * 标题栏文本颜色
     * @return Int
     */
    @ColorRes
    fun getTitleTextColor(): Int

    /**
     * 标题栏下方是否需要横线
     * @return Boolean
     */
    fun isShowTitleLine(): Boolean

    /**
     * 标题栏下方横线颜色
     * @return Int
     */
    @ColorRes
    fun getTitleLineColor(): Int

    /**
     * loading的颜色
     * @return Int
     */
    @ColorRes
    fun getLoadingColor(): Int

    /**
     * loading的背景颜色
     * @return Int
     */
    @ColorRes
    fun getLoadingBackground(): Int

    /**
     * RecyclerView的空页面ViewBinding
     * @param parent ViewGroup
     * @return ViewBinding
     */
    fun getRecyclerEmptyBanding(parent: ViewGroup): ViewBinding

    /**
     * 刷新每页加载个数
     * @return Int
     */
    fun getPageSize(): Int

    /**
     * 刷新起始页的index(有些后台设置的0,有些后台设置1)
     */
    fun getStartPageIndex(): Int
}

使用示例:

@GlobalConfig
class MvvmGlobalConfig : IGlobalConfig {
    override fun getTitleHeight(): Int {
        return R.dimen.title_bar_height
    }

    override fun getTitleBackIcon(): Int {
        return R.drawable.back_black
    }

    override fun getTitleBackground(): Int {
        return R.color.colorPrimary
    }

    override fun getTitleTextColor(): Int {
        return R.color.white
    }

    override fun isShowTitleLine(): Boolean {
        return true
    }

    override fun getTitleLineColor(): Int {
        return R.color.color_black
    }

    override fun getLoadingColor(): Int {
        return R.color.color_black
    }

    override fun getLoadingBackground(): Int {
        return R.color.white
    }

    override fun getRecyclerEmptyBanding(parent: ViewGroup): ViewBinding {
        return LayoutEmptyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    }

    override fun getPageSize(): Int {
        return 16
    }

    override fun getStartPageIndex(): Int {
        return 1
    }
}

2. Activity

  • 使用MVVM的继承BaseVMActivity
  • 不使用MVVM的继承BaseActivity'

2.1 标题注解使用

使用示例

Title其他注解参数,请看下方注解详情

//设置标题的文字
@Title(R.string.child_title)
class ChildActivity : BaseVMActivity<ActivityChildBinding, ChildViewModel>() 

如果标题栏文字要根据接口显示不同的文字,也有接口设置

class ChildActivity : BaseVMActivity<ActivityChildBinding, ChildViewModel>() {
    @OnClickFirstDrawable(R.drawable.more)
    fun clickFirstDrawable(v: View) {
        updateTitle("更改标题")
    }
}

2.2 状态栏注解使用

使用示例

StatusBar其他注解参数,请看下方注解详情

//弃用注解
@StatusBar(hide = true)
class FullScreenActivity : BaseActivity<ActivityFullScreenBinding>()

2.3 标题右侧文字或图标按钮注解使用

使用示例

注解修饰的方法只能可以带View参数,也可以不带View参数,看自身的需求

@Title(R.string.child_title)
class ChildActivity : BaseVMActivity<ActivityChildBinding, ChildViewModel>() {
    @OnClickFirstDrawable(R.drawable.more)
    fun clickFirstDrawable(v: View) {
        SnackbarManager.show(bodyBinding.root, "第一个图标按钮点击生效")
        updateTitle("nihao")
    }

    @OnClickFirstText(R.string.more)
    fun clickFirstText() {
        SnackbarManager.show(bodyBinding.root, "第一个文字按钮点击生效")
        updateTitle("12354")
    }

    @OnClickSecondDrawable(R.drawable.more)
    fun clickSecondDrawable(v: View) {
        SnackbarManager.show(bodyBinding.root, "第二个图标按钮点击生效")
        updateTitle("nihao")
    }

    @OnClickSecondText(R.string.more)
    fun clickSecondText() {
        SnackbarManager.show(bodyBinding.root, "第二个文字按钮点击生效")
        updateTitle("12354")
    }
}

2.4 提示框

  • Android 11 之后,Toast已经不支持自定义Toast,原生的Toast是很难看的
  • 本框架使用SnackBar做提示框

使用示例

@OnClickSecondDrawable(R.drawable.more)
fun clickSecondDrawable(v: View) {
    snackBar("第二个图标按钮点击生效")
}

3. Fragment

  • 使用MVVM的继承BaseVMFragment
  • 不使用MVVM的继承BaseFragment

3.1 提示框

  • Android 11 之后,Toast已经不支持自定义Toast,原生的Toast是很难看的
  • 本框架使用SnackBar做提示框

使用示例

snackbar.setOnClickListener {
    snackBar("提示框")
}

4. RecycleView

Adapter可以继承RecycleAdapter来使用,并在类上添加注解Adapter ,RecycleAdapter使用了ViewBanding,只需要实现以下一个方法

使用示例

@Adapter
class UserAdapter(iPageControl: IPageControl) :
    RecyclerAdapter<User, ItemUserBinding>(iPageControl) {

    override fun bindViewHolder(holder: CommonViewHolder<ItemUserBinding>, m: User, position: Int) {
        holder.viewBanding {
            name.text = m.name
        }
    }
}

5.刷新分页控件(RefreshRecyclerView)

  • RefreshRecyclerView集成了RefreshLayoutWrapper+RecyclerView

不用关心分页的逻辑,分页的刷新逻辑实现都在RefreshLayoutWrapper

  • 只需要设置LayoutManager和RecyclerAdapter,提供了setLayoutManager和setAdapter方法
  • 在获取到数据的时候调用updateData方法
  • 获取数据失败的时候调用updateError方法
  • 如果使用了lifecycleFlowRefresh方法,updateData方法和updateError方法都不用关心
  • 提供自定义属性recycler_background(设置RecyclerView的背景色)
<declare-styleable name="RefreshRecyclerView">
    <attr name="recycler_background" format="color" />
</declare-styleable>

使用示例

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
    android:layout_height="match_parent" android:orientation="vertical">

    <TextView android:layout_width="match_parent" android:layout_height="50dp"
        android:background="@color/colorPrimary" android:gravity="center" android:text="文章"
        android:textColor="@color/color_white" />

    <com.catchpig.mvvm.widget.refresh.RefreshRecyclerView android:id="@+id/refresh"
        android:layout_width="match_parent" android:layout_height="match_parent"
        app:recycler_background="#445467">

    </com.catchpig.mvvm.widget.refresh.RefreshRecyclerView>
</LinearLayout>
bodyBinding.refresh.run {
    setOnRefreshLoadMoreListener { nextPageIndex ->
        lifecycleFlowRefresh(viewModel.queryArticles(nextPageIndex), this)
    }
}

6. 网络请求

6.1只需要是接口类上加上注解ServiceApi,并使用NetManager.getService()获取对应的接口类

使用示例

@ServiceApi(
    baseUrl = "https://www.wanandroid.com/",
    responseConverter = ResponseBodyConverter::class,
    interceptors = [RequestInterceptor::class],
    debugInterceptors = [OkHttpProfilerInterceptor::class]
)
interface WanAndroidService {
    @GET("banner/json")
    suspend fun banner(): List<Banner>
}
object WanAndroidRepository {
    private val wanAndroidService = NetManager.getService(WanAndroidService::class.java)
    fun getBanners(): Flow<MutableList<Banner>> {
        //这里如果用flowOf的话,方法上面必须加上suspend关键字
        return flow {
            emit(wanAndroidService.queryBanner())
        }
    }
}
class IndexViewModel : BaseViewModel() {
    fun queryBanners(): Flow<MutableList<Banner>> {
        return WanAndroidRepository.getBanners()
    }
}
//Activity或者Fragment
lifecycleFlowLoadingView(viewModel.queryBanners()) {
    val images = mutableListOf<String>()
    this.forEach {
        images.add(it.imagePath)
    }
    bodyBinding.banner.run {
        setImages(images)
        start()
    }
}

6.2 Response转换器

6.2.1
  • 一般Response发返回结果会是如下
{
	code:"SUCCESS",
	errorMsg:"成功",
	data:...
}
  • 在code返回SUCEESSD的时候, 我们在Retrofit的Api接口里面只想拿到data的数据做返回,我们想在Converter里面处理掉code返回错误码的逻辑,就可以继承BaseResponseBodyConverter,内部已经实现了将response转化为data的逻辑

代码示例

class ResponseBodyConverter :
    BaseResponseBodyConverter() {
    override fun getResultClass(): KClass<out BaseResponseData<JsonElement>> {
        return Result::class
    }

    override fun handlerErrorCode(errorCode: String, msg: String): Exception {
        return NullPointerException()
    }
}
  • 再将实现了BaseResponseBodyConverter的类加到ServiceApi注解的responseConverter属性上
6.2.2

6.3 Activity和Fragment封装了网络请求方法(带lifecycleScope)

  • lifecycleFlowRefresh(flow: Flow<MutableList>,refresh: RefreshRecyclerView) -刷新+RecycleView的网络请求封装
  • lifecycleFlow(flow: Flow, callback: T.() -> Unit)-不带loading的网络请求封装
  • lifecycleFlowLoadingView(flow: Flow, callback: T.() -> Unit)-带loadingView的网络请求封装
  • lifecycleFlowLoadingDialog(flow: Flow, callback: T.() -> Unit)-带loadingDialog的网络请求封装

7. 注解使用

7.1 Title-标题

属性 类型 必须 默认 说明
value StringRes 标题内容
backgroundColor ColorRes 全局标题背景色 标题背景色
textColor ColorRes 全局标题文字颜色 标题文字颜色
backIcon DrawableRes 全局标题返回按钮图标 标题返回按钮图标

7.2 OnClickFirstDrawable-标题上第一个图标按钮的点击事件

属性 类型 必须 默认 说明
value DrawableRes 按钮图片资源

7.3 OnClickFirstText-标题上第一个文字按钮的点击事件

属性 类型 必须 默认 说明
value StringRes 按钮文字内容

7.4 OnClickSecondDrawable-标题上第二个图标按钮的点击事件

属性 类型 必须 默认 说明
value DrawableRes 按钮图片资源

7.5 OnClickSecondText-标题上第二个文字按钮的点击事件

属性 类型 必须 默认 说明
value StringRes 按钮文字内容

7.6 StatusBar-状态栏

属性 类型 必须 默认 说明
hide boolean false 隐藏状态栏
enabled boolean false 状态栏是否可用
transparent boolean false 状态栏透明

7.7 Prefs-SharedPreferences注解生成器

属性 类型 必须 默认 说明
value String "" 别名
mode PrefsMode PrefsMode.MODE_PRIVATE 模式,对应PreferencesMode

7.8 PrefsField-SharedPreferences字段注解

属性 类型 必须 默认 说明
value String "" 字段别名,如果为空则取修饰字段的参数名称

7.9 FlowError-Activity和Fragment中的Flow的onError方法统一处理

7.10 Adapter-RecyclerAdapter的继承类注解,加上此注解之后可以自动找到对应的layout资源

7.11 GlobalConfig-全局参数配置

  • 注解在IGlobalConfig接口的实现类上面

7.12 ServiceApi-网络请求接口注解类

属性 类型 必须 默认 说明
baseUrl String retrofit的baseurl
responseConverter Converter 接收数据转换器
connectTimeout Long 5000 http的超时时间
readTimeout Long 5000 http读取超时时间
interceptors Interceptor Interceptor http拦截器
debugInterceptors Interceptor Interceptor debug模式下的http拦截器,只有NetManager.setDebug(true),才会生效

8. 文件下载器(DownloadManager))

DownloadManager.download(downloadUrl, {
        //下载完成回调
    }, { downloadProgress ->
        //下载进度回调
    })
  • DownloadCallback
interface DownloadCallback {
        /**
         * 开始下载
         */
        fun onStart()
    
        /**
         * 下载成功
         * @param path 本地保存的地址
         */
        fun onSuccess(path:String)
    
        /**
         * 下载完成
         */
        fun onComplete()
    
		/**
     	 * 下载进度
         * @param downloadProgress DownloadProgress
         */
        fun onProgress(downloadProgress: DownloadProgress)
    
        /**
         * 下载错误
         * @param t 错误信息
         */
        fun onError(t:Throwable)
    }
DownloadManager.multiDownload(downloadUrls, {
         //下载完成回调
    }, { downloadProgress ->
        //下载进度回调
    })
  • MultiDownloadCallback
interface MultiDownloadCallback {
    /**
    * 开始下载
    */
    fun onStart()
      
    /**
    * 下载成功
    * @param paths 本地保存的地址集
    */
    fun onSuccess(paths:MutableList<String>)
    
    /**
     * 文件下载进度
     * @param downloadProgress DownloadProgress
     */
    fun onProgress(downloadProgress: DownloadProgress)
  
    /**
    * 下载完成
    */
    fun onComplete()
      
    /**
    * 下载错误
    * @param t 错误信息
    */
    fun onError(t:Throwable)
}

9. 工具库

utils

混淆

-keep class com.catchpig.annotation.enums.**

-keep class com.google.android.material.snackbar.Snackbar {*;}

-keep @com.catchpig.annotation.Adapter class * {*;}
-keep @com.catchpig.annotation.ServiceApi class * {*;}

-keep public class **.databinding.*Binding {*;}

-keep class **.*_Compiler {*;}

-keep class com.gyf.immersionbar.* {*;}
-dontwarn com.gyf.immersionbar.**

#序列化混淆
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
    static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
    static **$* *;
}
-keepclassmembers class <1>$<3> {
    kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
    public static ** INSTANCE;
}
-keepclassmembers class <1> {
    public static <1> INSTANCE;
    kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

第三方库

SmartRefreshLayout-刷新控件

Immersionbar-状态栏

kotlinpoet - kt代码生成工具

AndroidUtilKTX - 工具类

LoadingView - Loading动画

coroutines - 协程

serialization-序列化

其他

欢迎大家在issue上提出问题,我这边会不定时的看issue,解决问题

kmvvm's People

Contributors

catchpig avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.