/fragmject

fragmject 是一个为初学者准备的上手项目。 通过对 Kotlin 和 Jetpack 的系统运用,实现的一个功能完备符合主流市场标准 App。 fragmject 没有复杂的业务和多余的封装, 完全依照 Android Developer 官方的写法。 代码简单,内容全面,快速上手,对理解其他项目设计**和封装技巧也很有帮助。

Primary LanguageKotlinApache License 2.0Apache-2.0

前言

刚开始学习 Kotlin 其实挺痛苦的,相关的书籍或视频偏向于知识点的讲解。
开源项目为了满足业务需求,代码层层封装,用来上手实在不合适,于是便有了 fragmject 项目。
在此感谢 玩Android 及其 开放API

简介

fragmject 是一个为初学者准备的上手项目。
通过对 KotlinJetpack 的系统运用,实现的一个功能完备符合主流市场标准应用。
fragmject 没有复杂的业务和多余的封装, 完全依照 Android Developer 官方的写法。
代码简单,内容全面,快速上手,对理解其他项目设计**和封装技巧也很有帮助。

学习本项目你将有如下收获:

  • Kotlin(函数进阶,泛型,反射,协程...)
  • MVVM(ViewModel,LiveData...)
  • 单 Activity 应用架构(Navigation...)
  • 常用控件封装(图片选择器、图片编辑器、日期控件、全面屏沉浸、屏幕录制...)
  • 字节码插桩(ASM...)
  • Compose

开发环境

为了您在运行本项目时获得最佳体验,请使用最新版本 Android Studio
Download Android Studio | Android Developer

前置知识

在学习前希望您能了解以下知识,这将帮助您更快的上手本项目。

截图展示

1.jpg 2.jpg 3.jpg

项目目录结构

├── app                                         app
|   └── src 
|       └── main 
|       |   └── java                            源码目录
|       |   |   ├── bean                        bean目录
|       |   |   ├── components                  自定义组件目录
|       |   |   ├── ui                          ui目录
|       |   |   |   └── main                    mian目录
|       |   |   |       └── home                home目录
|       |   |   |       |   ├── HomeScreen      
|       |   |   |       |   └── HomeViewModel   
|       |   |   |       └── MainScreen          
|       |   |   ├── utils                       工具类目录
|       |   |   ├── WanActivity                 唯一Activity
|       |   |   ├── WanApplication              Application
|       |   |   ├── WanTheme                    Theme
|       |   |   ├── WanNavGraph                 导航图
|       |   |   └── WanViewModel                ViewModel
|       |   |
|       |   └── res                             资源目录
|       |   └── AndroidManifest.xml             配置文件
|       |
|       ├── build.gradle                        模块构建配置
|       ├── dictionary                          自定义混淆字典
|       └── proguard-rules.pro                  代码混淆配置文件
| 
├── library-base                                基础library(library开头为公共库,任何项目都可使用)
|   └── src 
|       └── main 
|       |   ├── assets                          assets目录
|       |   └── java                            源码目录
|       |       ├── activity                    Activity目录
|       |       ├── adapter                     Adapter目录
|       |       ├── bus                         消息总线目录
|       |       ├── db                          Database目录
|       |       ├── dialog                      Dialog目录
|       |       ├── http                        网络请求目录
|       |       ├── provider                    ContentProvider目录
|       |       ├── service                     Service目录
|       |       ├── utils                       工具类目录
|       |       └── view                        自定义view目录
|       | 
|       └── build.gradle                        模块构建配置
| 
├── library-picture                             图片模块(目录同app,不再展开)
| 
├── library-plugin                      插件模块
|   └── src 
|       └── main 
|           ├── kotlin                          源码目录
|           └── resources                       配置目录
|               └── statistic.properties        插件配置
| 
├── repos                                       插件生成目录
|
├── build.gradle                                项目构建配置
├── config.properties                           项目配置
├── gradle.properties                           gradle配置
└── settings.gradle                             项目依赖配置

Jetpack Compose

如果你暂时不需要 Compose ,可以切换到 Tags v1.3.0

更少的代码

与使用 Android View 系统相比,Compose 可让我们用更少的代码实现更多的功能,这样需要测试和调试的代码会更少,出现 bug 的可能性也更小。对于审核人员或维护人员,需要阅读、理解、审核和维护的代码就更少。
Compose 的布局系统在概念上更简单,所有代码都使用同一种语言编写并且位于同一文件中,而不必在 Kotlin 和 XML 二者之间来回切换。

直观

Compose 使用声明性 API,这意味着您只需描述界面,Compose 会负责完成其余工作。
利用 Compose,您可以构建不与特定 activity 或 fragment 相关联的小型无状态组件。
在 Compose 中,状态是显式的,并且会传递给相应的可组合项。这样一来,状态便具有单一可信来源,因而是封装和分离的。然后,应用状态变化时,界面会自动更新。

相互兼容

Compose 与您所有的现有代码兼容:您可以从 View 调用 Compose 代码,也可以从 Compose 调用 View。大多数常用库(如 Navigation、ViewModel 和 Kotlin 协程)都适用于 Compose,因此您可以随时随地开始采用。

Jetpack Compose : 从改造你的登录页面开始

Jetpack Compose : WebView的使用与优化

Jetpack Compose : 一学就会的自定义下拉刷新&加载更多

Jetpack Compose : 一文学会嵌套滚动NestedScrollConnection

KVDatabase

通过对 RoomDatabase 进行封装,从而更方便的实现数据持久化。

快速使用

// 存储数据
KVDatabase.set(key: String, value: String)

// 获取数据
KVDatabase.get(key: String)

SharedFlowBus

SharedFlowBus:30行代码实现消息总线你确定不看吗

快速使用

// 发送消息
SharedFlowBus.with(objectKey: Class<T>).tryEmit(value: T)

// 发送粘性消息
SharedFlowBus.withSticky(objectKey: Class<T>).tryEmit(value: T)

// 订阅消息
SharedFlowBus.on(objectKey: Class<T>).observe(owner){ it ->
    println(it)
}

// 订阅粘性消息
SharedFlowBus.onSticky(objectKey: Class<T>).observe(owner){ it ->
    println(it)
}

字节码插桩

最通俗易懂的字节码插桩实战 —— 优雅的打印方法执行时间

最通俗易懂的字节码插桩实战 —— 自动埋点

隐私合规 ———— 替换目标字段或方法(library-plugin)

一文学会字节码替换,再也不用担心隐私合规审核

快速使用

源代码在 library-plugin 目录下 在 MiaowPlugin 添加 ScanBean 并配置目标字段或方法以及对应的替换字段或方法

ScanBean(
    owner = "android/os/Build",
    name = "BRAND",
    desc = "Ljava/lang/String;",
    replaceOpcode = Opcodes.INVOKESTATIC,
    replaceOwner = "com/example/fragment/library/common/utils/BuildUtils",
    replaceName = "getBrand",
    "()Ljava/lang/String;"
)

耗时扫描 ———— 打印方法执行时间

MiaowPlugin 添加 TimeBean 并配置打印目标或范围

TimeBean( //以包名和执行时间为条件
    "com/example/fragment/library/base",
    time = 50L
)

埋点统计 ———— 自动埋点

MiaowPlugin 添加 TraceBean 并配置埋点目标以及对应埋点方法

TraceBean(
    owner = "Landroid/view/View\$OnClickListener;",
    name = "onClick",
    desc = "(Landroid/view/View;)V",
    traceOwner = "com/example/fragment/library/common/utils/StatisticHelper",
    traceName = "viewOnClick",
    traceDesc = "(Landroid/view/View;)V" //参数应在desc范围之内
)

配置完成后 gradle 执行 publish 任务生成插件 在根目录 setting.gradle 添加本地插件源

pluginManagement {
    repositories {
        maven {
            url uri('repo')
        }
    }
}

在根目录 build.gradle 添加插件依赖

buildscript {
    dependencies {
        classpath 'com.example.miaow:plugin:1.0.0'
    }
}

在app目录 build.gradle apply插件

plugins {
    id 'miaow'
}

图片编辑器(library-picture)

自己动手撸一个图片编辑器(支持长图)

截图展示

5.gif 6.gif 7.gif

快速使用

源代码在 library-picture 目录下

PictureEditorDialog.newInstance()
    .setBitmapPath(path)
    .setEditorFinishCallback(object : EditorFinishCallback {
        override fun onFinish(path: String) {
            val bitmap = BitmapFactory.decodeFile(path, BitmapFactory.Options())
        }
    })
    .show(childFragmentManager)

如上所示:

  1. 通过 PictureEditorDialog 调用图片编辑器
  2. 通过 setBitmapPath(path) 传入图片路径
  3. 通过 setEditorFinishCallback(callback) 获取编辑后的图片地址

如果觉得 PictureEditorDialog 不能满足需求,还可以通过 PictureEditorView 来自定义样式

自定义使用

<com.example.miaow.picture.editor.PictureEditorView
    android:id="@+id/pic_editor"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
picEditor.setBitmapPath(path)
picEditor.setMode(PictureEditorView.Mode.STICKER)
picEditor.setGraffitiColor(Color.parseColor("#ffffff"))
picEditor.setSticker(StickerAttrs(bitmap))
picEditor.graffitiUndo()
picEditor.mosaicUndo()
picEditor.saveBitmap()

如上所示:

  1. 通过 setBitmapPath(path) 传入图片路径
  2. 通过 setMode(mode) 设置编辑模式,分别有:涂鸦,橡皮擦,马赛克,贴纸
  3. 通过 setGraffitiColor(color) 设置涂鸦画笔颜色
  4. 通过 setSticker(StickerAttrs(bitmap)) 设置贴纸
  5. 通过 graffitiUndo() 涂鸦撤销
  6. 通过 mosaicUndo() 马赛克撤销
  7. 通过 saveBitmap() 保存编辑图片

PictureEditorView 就介绍到这里,具体使用请查看 PictureEditorDialog

图片裁剪

<com.example.miaow.picture.editor.PictureClipView
    android:id="@+id/clip"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
clip.setBitmapResource(bitmap)
clip.rotate()
clip.reset()
clip.saveBitmap()

如上所示:

  1. 通过 setBitmapResource(bitmap) 传入裁剪图片
  2. 通过 clip.rotate() 图片旋转
  3. 通过 clip.reset() 图片重置
  4. 通过 clip.saveBitmap() 保存裁剪框内图片

PictureClipView 就介绍到这里,具体使用请查看 PictureClipDialog

图片选择

if (context is AppCompatActivity) {
    PictureSelectorDialog.newInstance()
        ...省略部分代码
        .show(context.supportFragmentManager)
}

WebView 优化及 H5 秒开实践

满满的 WebView 优化干货,让你的 H5 实现秒开体验

主要开源框架

Gitee镜像

About me

Thanks

感谢所有优秀的开源项目 ^^
如果喜欢的话希望给个 Star 或 Fork ^
^
谢谢~~