diff --git a/README-zh.md b/README-zh.md index a185df0..92ff038 100644 --- a/README-zh.md +++ b/README-zh.md @@ -25,197 +25,50 @@

-## 1、简介 +更高级的 Android 图片压缩库,支持自定义: -本项目主要基于 Android 自带的图片压缩 API 进行实现,支持多种输入和输出类型,提供一个统一图片压缩框库的实现,支持自定义图片压缩算法,支持多种触发任务方式。 +- 图片类型 (Uri/File/bitmap/Byte 数组/自定义) +- 结果类型 (Bitmap/File) +- 任务执行模式 (阻塞/RxJava/Kotlin 协程/AsyncTask) +- 压缩算法 -## 2、功能和特性 - -目前,我们的库已经支持了下面的功能,在后面的介绍中,我们会介绍如何在项目中进行详细配置和使用: - -- **支持 Automatic 压缩方案**:据介绍这是微信逆推的压缩算法,它在我们的项目中只作为一种可选的压缩方案。(参考:[Luban](https://github.com/Curzibn/Luban)) - -- **支持 Concrete 压缩方案**:这种压缩方案综合了 Android 自带的三种压缩方式,可以对压缩结果的尺寸进行精确的控制。此外,在我们的项目中,我们对这种压缩方案的功能进行了拓展,不仅支持了颜色通道的选择,还提供了多种可选的策略,用来对尺寸进行更详细的配置。(参考:[Compressor](https://github.com/zetbaitsu/Compressor)) -- **支持 RxJava 的方式进行压缩**:使用 RxJava 的方式,您可以任意指定压缩任务和结果回调任务所在的线程,在我们的库中,我们提供了一个 Flowable 类型的对象,您可以用它来进行后续的处理。 -- **支持 AsyncTask + 回调的压缩方式**:这种方式通过使用 AsyncTask 在后台线程中执行压缩任务,当获取到压缩结果的时候通过 Handler 在主线程中返回压缩结果。 -- **支持 Kotlin 协程**:在 1.3.5 的版本上引入了 Kotlin 协程,你可以在这个版本上面使用 Kotlin 协程进行压缩并获取结果。 -- **提供同步获取结果的方法**:当然,有时候我们并不需要使用回调或者 RxJava 执行异步任务。比如,当我们本身已经处于后台线程的时候,我们希望的只是在当前线程中直接执行压缩任务并拿到压缩结果。因此,为了让我们的库适用于更多的应用场景,我们提出了这种压缩方案。 -- **支持 3 种数据源**:在上面的两款开源库中,要求传入的资源类型是 File。这就意味着,当我们从相机中获取到原始的图片信息(通常是字节数组)的时候,我们不得不先将其写入到文件系统中,然后获取到 File 的时候再进行压缩。这是没必要的,并且无疑地,会带来性能上的损耗。因此,为了能让我们的库应用到更多的场景中,我们支持了多种数据源。目前支持的数据源包括:文件类型 File,原始图片信息 byte[] 以及 Bitmap。 -- **支持 Bitmap 和 File 两种结果类型**:以上两款开源库还存在一个问题,即返回的结果的类型也只支持 File 类型。但很多时候,我们希望传入的是 Bitmap,处理之后传出的结果也是 Bitmap. 因此,为了让我们的库适用于这种场景,我们也支持 Bitmap 类型的返回结果。 -- **提供用户自定义压缩算法的接口**:我们希望设计的库可以允许用户自定义压缩策略。在想要替换图片压缩算法的时候,通过链式调用的一个方法直接更换策略即可。即,我们希望能够让用户以最低的成本替换项目中的图片压缩算法。 - -想要进一步了解该库的特性和功能,你还可以使用我们提供的示例 [APK](resources/app-debug.apk) - -[
示例程序预览图
](resources/app-debug.apk) - -## 3、使用 - -### 3.1 在 Gradle 中引用我们的库 - -在项目中接入我们的库是非常简单的。首先,在项目的 Gradle 中加入 jcenter仓库: +要使用该库,首先添加 maven central 仓库, ```gradle repositories { mavenCentral() } ``` -然后,在项目的依赖中添加该库的依赖: +然后添加该项目的依赖, ```gradle implementation 'com.github.Shouheng88:compressor:latest-version' ``` -### 3.2 使用我们库进行压缩 - -详细的用法你可以参考我们提供的 Sample 程序,这里我们介绍下使用我们库的几个要点: - -**1. 获取 Compress 实例** - -首先,你要使用 Compress 类的静态方法获取一个 `compress` 实例,这是所有配置的起点。针对不同的数据源,你可以根据自己的需求调用其对应的工厂方法。 +示例:以压缩 Uri 图片、获取 File 类型的结果、在 Kotlin 协程中启动为例。整个代码是, ```kotlin -// 使用文件 File 获取 Compress 实例 -val compress = Compress.with(this, file) - -// 使用图片的字节数组获取 Compress 实例 -val compress = Compress.with(this, byteArray) - -// 使用图片的 Bitmap 获取 Compress 实例 -val compress = Compress.with(this, bitmap) -``` - -然后,你可以调用 `compress` 的实例方法来对压缩的参数进行基本的配置: - -```kotlin -compress - // 指定要求的图片的质量 - .setQuality(60) - // 指定文件的输出目录(如果返回结果不是 File 的会,无效) - .setTargetDir(PathUtils.getExternalPicturesPath()) - // 指定压缩结果回调(如哦返回结果不是 File 则不会被回调到) - .setCompressListener(object : CompressListener { - override fun onStart() { - // 压缩开始的回调 - } - - override fun onSuccess(result: File?) { - // 压缩完成的回调 - } - - override fun onError(throwable: Throwable?) { - // 压缩失败的回调 - } - }) -``` - -以上是使用 Compress 进行基本的配置。Compress 的基本配置是通用的,你可以切换图片算法而无需更改这些配置。这也是我们的库可以轻松切换图片算法的原因。 - -**2. 指定压缩算法** - -根据上述配置,我们就得到了一个 Compress 对象。然后,我们需要指定一个图片压缩算法,并调用压缩算法的方法进行更详细的配置。以 concrete 为例,我们可以通过调用 `Strategies.compressor()` 方法获取它的实例并指定 Compressor 算法的配置: - -```kotlin -val compressor = compress - .strategy(Strategies.compressor()) - .setConfig(config) - .setMaxHeight(100f) - .setMaxWidth(100f) - .setScaleMode(scaleMode) -``` - -如果你试用 1.4.0 及以后的版本可以使用 Kotlin DSL 的形式定义自己的算法, - -```kotlin -compress - .concrete { - maxHeight = 100f - maxWidth = 120f - scaleMode = ScaleMode.SCALE_LARGER - } -``` - -下面就是触发图片压缩并获取压缩结果的过程了。 - -**3. 转换输出类型** - -上面我们也提到过,针对 File 类型和 Bitmap 类型的返回结果,我们提供了两个方案。默认的返回类型是 File,为了得到 Bitmap 类型的结果,你只需要调用一下 `asBitmap()` 方法,这样整个流程就“转换”到了 Bitmap 的构建中去了(就像 Glide 一样)。 - -```kotlin -compressor = compressor.asBitmap() -``` - -**4. 在多种环境下触发任务** - -最终触发图片压缩有 4 种选择, - -```kotlin -// 方式 1:使用 AsyncTask 压缩,此时只能通过之前的回调获取压缩结果 -compressor.launch() - -// 方式 2:将压缩任务转换成 Flowable,使用 RxJava 指定任务的线程和获取结果的线程 -val d = compressor - .asFlowable() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - ToastUtils.showShort("Success [Compressor,File,Flowable] $it") - displayResult(it.absolutePath) - }, { - ToastUtils.showShort("Error [Compressor,File,Flowable] : $it") - }) - -// 方式 3:直接在当前线程中获取返回结果(同步,阻塞) -val resultFile = compressor.get() - -// 方式 4:kotlin 协程中获取结果 GlobalScope.launch { - val resultFile = compressor.get(Dispatchers.IO) -} -``` - -对于 Luban 压缩方式的使用与之类似,只需要在指定压缩策略的那一步中将策略替换成 luban 即可。另外,对于自定义图片压缩的方式也是类似的,只需要在指定策略的那一步骤中指定即可。 - -**5. 最终的完整形式** - -因此,如果使用的是 RxJava 的方式获取压缩结果,并且输入类型是 File,输出类型是 Bitmap,整个压缩的流程将是下面这样: - -```kotlin -Compress.with(context, file) - .setQuality(60) - .setTargetDir(PathUtils.getExternalPicturesPath()) - .setCompressListener(getCompressListener("Concrete", compressorResultType)) - .concrete { // 对 Concrete 算法进行定制 - this.config = colorConfig - this.maxWidth = 100f - this.maxHeight = 100f - this.scaleMode = imageScaleMode + val result = Compress.with(context, file.uri(context)) + .setQuality(80) + .concrete { + withMaxWidth(100f) + withMaxHeight(100f) + withScaleMode(ScaleMode.SCALE_HEIGHT) + withIgnoreIfSmaller(true) + } + .get(Dispatchers.IO) + withContext(Dispatchers.Main) { + Glide.with(context).load(result).into(binding.iv6) } - .asBitmap() // 期望得到 Bitmap 类型的结果 - .asFlowable() // 转换为 Flowable,然后就可以使用 RxJava 了 - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( ... ) +} ``` -### 3.3 Concrete 算法配置说明 - -**1. ignoreIfSmaller** - -该字段用来指定当期望得到的图片尺寸大于图片实际尺寸时的行为,当该字段为 true 的时候,会忽略压缩逻辑并返回原始的图片,否则会将图片拉伸到期望大小的尺寸。 - -**2. scaleMode** - -- SCALE_LARGER:对高度和长度中较大的一个进行压缩,另一个自适应,因此压缩结果是 (W:100, H:50). 也就是说,因为原始图片宽高比 2:1,我们需要保持这个宽高比之后再压缩。而目标宽高比是 1:1. 而原图的宽度比较大,所以,我们选择将宽度作为压缩的基准,宽度缩小 10 倍,高度也缩小 10 倍。这是 Compressor 库的默认压缩策略,显然它只是优先使得到的图片更小。这在一般情景中没有问题,但是当你想把短边控制在 100 就无计可施了(需要计算之后再传参),此时可以使用 SCALE_SMALLER。 -- SCALE_SMALLER:对高度和长度中较大的一个进行压缩,另一个自适应,因此压缩结果是 (W:200, H:100). 也就是,高度缩小 5 倍之后,达到目标 100,然后宽度缩小 5 倍,达到 200. -- SCALE_WIDTH:对宽度进行压缩,高度自适应。因此得到的结果与 SCALE_LARGER 一致。 -- SCALE_HEIGHT:对高度进行压缩,宽度自适应,因此得到的结果与 SCALE_HEIGHT 一致。 - -## 4、项目资料 +该库允许你对多个地方进行自定义, -如果您对该项目感兴趣并且希望为该项目共享您的代码,那么您可以通过下面的一些资料来了解相关的内容: +- 了解更多使用方式,请阅读 [示例工程](sample/app/src/main/java/me/shouheng/sample/view/SampleActivity.kt). +- 了解如何自定义图片压缩算法,请阅读 [自定义算法示例](sample/app/src/main/java/me/shouheng/sample/custom/AlwaysHalfAlgorithm.kt). +- 了解如何自定义图片类型,请阅读 [自定义图片类型示例](sample/app/src/main/java/me/shouheng/sample/custom/AssetsResourceSource.kt). -1. 项目整体的架构设计:[https://www.processon.com/view/link/5cdfb769e4b00528648784b7](https://www.processon.com/view/link/5cdfb769e4b00528648784b7) -2. 我们提供的示例 APK:[app-debug.apk](resources/app-debug.apk) -3. [更新日志](CHANGELOG.md) ## License ``` diff --git a/README.md b/README.md index ffa3da2..33885ad 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

An easy to use image compress library for Android

+

An advanced image compress library for Android

@@ -28,210 +28,49 @@ 中文版

-## 1 Introduction +An advanced image compress library for Android. Allow you to custom -This project is mainly designed based on the Android image compress API. It allow to use different types of image sources and result types. It provided sync and async API to meet more requirements. And it put forward the struture so that you can easily switch from different compress algorithms. +- Image source types (Uri/File/bitmap/Byte array/Custom...) +- Result image types (Bitmap/File) +- Compress task execution mode (Blocking/RxJava/Kotlin coroutines/AsyncTask) +- Image compress algorithms -## 2 Functions and features - -Now lets show you the functions and features of our library: - -- **Support automatic algorithm**: Calculate the in sample size according to image size, see [Luban](https://github.com/Curzibn/Luban). - -- **Support aoncrete algoruthm**: You are able to get an exact image size, see [Compressor](https://github.com/zetbaitsu/Compressor). - -- **Support RxJava callback**: We will return a Flowable object so you can use it as RxJava. - -- **Support AsyncTask callback**: Except RxJava, you can also use AsyncTask to run background task, and get the result in main thread from callback. - -- **Support kotlin coroutines**: Also, you can use the library in kotlin coroutines. - -- **Support synchronous APIs** - -- **Support to stretch images by width/height/longer side/smaller side** - -- **Support 3 image sources types**: Most of the liraries, the required image type was File. But when we got the image data from camera APIs, it turn out to be byte array. So in other libraries, you have to transfer data from byte array to File. That means you have to write data to file system, witch no doubt may lower the performance of your App.. Currently, our library support image source types include File, byte array, file path and Bitmap. - -- **Support 2 image result types**: Sometimes, when we got the compressed result, we have to process it later. In Android, we use Bitmap. In this circumstance, it's better to get Bitmap than File. So, to meet this requirement, we provided result type of Bitmap. - -- **Provided custom interfaces**: Except algorithms above, we also provided user custom interfaces. We built an structure so that user can easily and conveniently switch from different strategys. - -- **More**: To get more features and functions about this library, you can install our sample [APK](resources/app-debug.apk) to get more informations. - -[
示例程序预览图
](resources/app-debug.apk) - -## 3 Usage - -### 3.1 Introduce our library in Gradle - -It's convenient to use our lirary in your project. - -First, add jcenter repository in your project: +To use the library, to add maven central at first, ```gradle repositories { mavenCentral() } ``` -Then, add our library in your dependency: +then, add our library in your dependency: ```gradle implementation 'com.github.Shouheng88:compressor:latest-version' ``` -### 3.2 Use our library - -**1. Get compress object** - -First, you should use the static methods of Compress to get a an instance of it, which is the magic begins. It has three different factory methods correspond to three different type of image sources: - -```kotlin -// Factory 1: Use File to get Compress instance -val compress = Compress.with(this, file) - -// Factory 2: Use byte array to get Compress instance -val compress = Compress.with(this, byteArray) - -// Factory 3: Use Bitmap to get Compress instance -val compress = Compress.with(this, bitmap) -``` - -Then, you can call methods of `compress` instance to config basic image options. Basic options are those used in all strategies. That's why you can easily switch from different algorithms. +Sample: to compress an Uri image, try to get result as File and launch in kotlin coroutines. The code will be, ```kotlin -compress - // Sepcify image quality - .setQuality(60) - // Specify output directory - .setTargetDir(PathUtils.getExternalPicturesPath()) - // Specify callback of result - .setCompressListener(object : CompressListener { - override fun onStart() { - // callback when compress start - } - - override fun onSuccess(result: File?) { - // callback when compress succeed - } - - override fun onError(throwable: Throwable?) { - // callback when compress error +GlobalScope.launch { + val result = Compress.with(context, file.uri(context)) + .setQuality(80) + .concrete { + withMaxWidth(100f) + withMaxHeight(100f) + withScaleMode(ScaleMode.SCALE_HEIGHT) + withIgnoreIfSmaller(true) } - }) -``` - -**2. Specify algorithm** - -Then we need to specify compress strategy (algorithm). Take Compressor strategy as an example, we could use `Strategies.compressor()` to get instance of it. And set details of it by calling `setMaxHeight`, `setMaxWidth` etc. Different algorithm might have different configurations. - -```kotlin -val compressor = compress - .strategy(Strategies.compressor()) // Specify strategy - .setMaxHeight(100f) // Set desired output width and height - .setMaxWidth(100f) - .setScaleMode(scaleMode) // Set desiged output scale mode -``` - -On 1.4.0 and above, you can use the kotlin DSL to specify algorithm, - -```kotlin -compress - .setQuality(60) - .setTargetDir(PathUtils.getExternalPicturesPath()) - .setCompressListener(getCompressListener("Concrete", compressorResultType)) - .concrete { - this.config = colorConfig - this.maxWidth = 100f - this.maxHeight = 100f - this.scaleMode = imageScaleMode + .get(Dispatchers.IO) + withContext(Dispatchers.Main) { + Glide.with(context).load(result).into(binding.iv6) } -// --- or --- -Compress.with(context, byteArray) - .automatic { - // do nothing - } - .asBitmap() - .get() -``` - -**3. Change result type** - -Next, as mentioned above, if you want to get compressed image of Bitmap, you should use `asBitmap()`. Otherwise, the compressed result will be File type, - -```kotlin -compressor = compressor.asBitmap() -``` - -**4. Invoke the compress** - -To finally get the result you have 4 options correspond to 4 different ways async/sync apis: - -```kotlin -// Option 1: use AsyncTask to execute async task and to get result from callback -compressor.launch() - -// Option 2: use Flowable and RxJava to get result -compressor - .asFlowable() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( ... ) - -// Option 3: use sync and blocking API to get result in current thread -val resultFile = compressor.get() - -// Option 4: get the result by kotlin coroutines -GlobalScope.launch { - val resultFile = compressor.get(Dispatchers.IO) } ``` -**5. Complete code** - -If you want to use another strategy, you can simply use `Strategies.luban()` instead of `Strategies.compressor()`. Excpet these two strategies, you can also make a custom strategy. - -So, the full code will be: - -```kotlin -// Example of RxJava, also support other methods. -Compress.with(this@MainActivity, file) - .concrete { // config the concrete algorithm - this.config = colorConfig - this.maxWidth = 100f - this.maxHeight = 100f - this.scaleMode = imageScaleMode - } - .asBitmap() - .asFlowable() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( ... ) -``` - -### 3.3 Detail configurations about compressor - -**1. ignoreIfSmaller** - -This filed used to specifiy action when the current image size is smaller than required size. If it's true, the image will be ignored and the origin image will be returned, otherwise, the origin image will be stretched to required size. - -**2. scaleMode** - -The scale mode is used to specify image stretching ways while current image size ratio differs from desired image size ratio. It has 4 options: - -- SCALE_LARGER: Scale according to larger side, another will change according to original image width/height ratio. For example: 1). If the original image is (W:1000, H:500), destination is (W:100, H:100), then the result size will be (W:100, H:50). 2). If the original image is (W:500, H:1000), destination is (W:100, H:100), then the result size will be (W:50, H:100). - -- SCALE_SMALLER: Scale according to smaller, another side will change according to original image width/height ratio. For example: 1). If the original image is (W:1000, H:500), destination is (W:100, H:100), then the result size will be (W:200, H:100). 2). If the original image is (W:500, H:1000), destination is (W:100, H:100), then the result size will be (W:100, H:200). - -- SCALE_WIDTH: Scale the width, and the height will change according to the image ratio. For example: 1). If the original image is (W:1000, H:500), destination is (W:100, H:100). then the result size will be (W:100, H:50). 2). If the original image is (W:500, H:1000), destination is (W:100, H:100), then the result size will be (W:100, H:200). - -- SCALE_HEIGHT: Scale the width, and the height will change according to the image ratio. For example: 1). If the original image is (W:1000, H:500), destination is (W:100, H:100). then the result size will be (W:200, H:100). 2). If the original image is (W:500, H:1000), destination is (W:100, H:100), then the result size will be (W:50, H:100). - -## 3 More - -I'm glad if you could contribute to this project. Here, we provied more about our project to help you: +The library allows you to cusotm a lot. So, -1. Library structure: [https://www.processon.com/view/link/5cdfb769e4b00528648784b7](https://www.processon.com/view/link/5cdfb769e4b00528648784b7) -2. Sample APK: [app-debug.apk](resources/app-debug.apk) -3. [Release Log](CHANGELOG.md) +- To learn more usage, please read [Sample Codes](sample/app/src/main/java/me/shouheng/sample/view/SampleActivity.kt). +- To learn how to custom algorithm, please read [sample for custom algorithm](sample/app/src/main/java/me/shouheng/sample/custom/AlwaysHalfAlgorithm.kt). +- To learn how to custom image sources, please read [sample for custom image sources](sample/app/src/main/java/me/shouheng/sample/custom/AssetsResourceSource.kt). ## License diff --git a/library/build.gradle b/library/build.gradle index bf637d0..a89c916 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -54,7 +54,7 @@ publishing { mavenJava(MavenPublication) { groupId 'com.github.Shouheng88' artifactId 'compressor' - version '1.5.0' + version '1.6.0' // Two artifacts, the `aar` and the sources artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") artifact androidSourcesJar