diff --git a/README.md b/README.md index 88075fa325..9794adedb1 100644 --- a/README.md +++ b/README.md @@ -134,10 +134,10 @@ sketchImageView.displayImage("http://t.cn/RShdS1f"); * [使用 ImageShaper 在绘制时改变图片的形状][image_shaper] * [使用 ImageProcessor 在解码后改变图片][image_processor] * [使用 ImageDisplayer 以动画的方式显示图片][image_displayer] -* [使用 MaxSize 限制读取到内存的图片大小][max_size] +* [使用 MaxSize 读取合适尺寸的缩略图,节省内存][max_size] * [使用 Resize 精确修改图片的尺寸][resize] * [使用 StateImage 设置占位图片和状态图片][state_image] -* [监听开始加载、成功、失败以及下载进度事件][listener] +* [监听准备加载、成功、失败以及下载进度事件][listener] 提升用户体验: * [使用 TransitionImageDisplayer 以自然过渡渐的变方式显示图片][transition_image_displayer] @@ -159,12 +159,11 @@ sketchImageView.displayImage("http://t.cn/RShdS1f"); * [在内存中缓存 Bitmap 提升显示速度][memory_cache] * [在磁盘上缓存图片原文件,避免重复下载][disk_cache] * [发送 HTTP 请求][http_stack] -* [取消显示图片][cancel_request] +* [取消请求][cancel_request] * [监控 Sketch 的异常][error_tracker] * [日志][log] * [延迟并统一配置 Sketch][initializer] * [配置混淆(Proguard)][proguard_config] -* [其它知识点][other] ### 特别感谢 @@ -256,7 +255,6 @@ sketchImageView.displayImage("http://t.cn/RShdS1f"); [log]: docs/wiki/log.md [initializer]: docs/wiki/initializer.md [proguard_config]: docs/wiki/proguard_config.md -[other]: docs/wiki/other.md [options_filter]: docs/wiki/options_filter.md [koral--]: https://github.com/koral-- diff --git a/README_EN.md b/README_EN.md index 05ea6e0d4b..03a76b4beb 100644 --- a/README_EN.md +++ b/README_EN.md @@ -134,10 +134,10 @@ Basic functions: * [Use ImageShaper to change the shape of a picture when drawing][image_shaper] * [Use ImageProcessor to change the picture after decoding][image_processor] * [Use ImageDisplayer to display images in an animated manner][image_displayer] -* [Use MaxSize to limit the size of the image to read to memory][max_size] +* [Use MaxSize to read thumbnails of the right size to save memory][max_size] * [Use Resize to precisely modify the size of the image][resize] * [Use StateImage to set the placeholder picture and status picture][state_image] -* [Listen to start loading, success, failure, and download progress events][listener] +* [Listen to ready load, success, failure, and download progress events][listener] To further enhance the user experience: * [Use the TransitionImageDisplayer to display the image in a natural transition gradient][transition_image_displayer] @@ -159,12 +159,11 @@ More: * [Cache Bitmap in memory to increase the display speed][memory_cache] * [Cache the original file on the disk, to avoid duplication of the download][disk_cache] * [Send HTTP request][http_stack] -* [Cancel display image][cancel_request] +* [Cancel request][cancel_request] * [Monitor Sketch's exception][error_tracker] * [Log][log] * [Delay and configure Sketch][initializer] * [Config proguard][proguard_config] -* [Other knowledge points][other] ### Thanks @@ -256,7 +255,6 @@ More: [log]: docs/wiki/log.md [initializer]: docs/wiki/initializer.md [proguard_config]: docs/wiki/proguard_config.md -[other]: docs/wiki/other.md [options_filter]: docs/wiki/options_filter.md [koral--]: https://github.com/koral-- diff --git a/docs/logs/log_2.5.0_p2_temp.md b/docs/logs/log_2.5.0_p2_temp.md index 8432c47fc7..47e2c2c78c 100644 --- a/docs/logs/log_2.5.0_p2_temp.md +++ b/docs/logs/log_2.5.0_p2_temp.md @@ -3,6 +3,16 @@ bug 修复版 bugs: * :bug: 修复 2.5.0-p1版本改出来的 无法显示 content chunked 的图片 +变更: +* :hammer: Listener.onStartLoad() 方法名字改为 onReadyLoad() + +删除: +* :fire: 删除 RequestLevelFrom ,因此取消原因中不再区分 REQUEST_LEVEL_IS_LOCAL 和 REQUEST_LEVEL_IS_MEMORY +* :fire: 删除 CancelCause.REQUEST_LEVEL_IS_LOCAL 和 REQUEST_LEVEL_IS_MEMORY +* :fire: 删除 SketchUtils.makeRequestKey(String, UriModel, DownloadOptions) 方法,makeRequestKey(String, UriModel, String) 方法代替之 +* :fire: 删除 SketchUtils.makeRequestKey(String, String) 方法,makeRequestKey(String, UriModel, String) 方法代替之 +* :fire: 删除 SketchUtils.makeStateImageMemoryCacheKey(String, DownloadOptions) 方法,makeRequestKey(String, UriModel, String) 方法代替之 + 新功能: * :sparkles: 新增 OptionsFilter 可统一过滤修改 Options diff --git a/docs/wiki/bitmap_pool.md b/docs/wiki/bitmap_pool.md index 3397972eb3..992aeb5c1f 100644 --- a/docs/wiki/bitmap_pool.md +++ b/docs/wiki/bitmap_pool.md @@ -12,53 +12,90 @@ #### 在 BitmapFactory 中使用 \>= 3.0 && <= 4.3 ->* 只能是 jpeg 或 png图片 ->* Options.inSampleSize 只能是1`(默认是0,即使不需要缩小也必须改成1)` ->* inBitmap 的宽高必须和新图片的宽高一样`(宽==宽&&高==高)` ->* inBitmap 的 config 必须同新的 Options.inPreferredConfig 一样 ->* inBitmap.isMutable() == true +* 只能是 jpeg 或 png图片 +* Options.inSampleSize 只能是1`(默认是0,即使不需要缩小也必须改成1)` +* inBitmap 的宽高必须和新图片的宽高一样`(宽==宽&&高==高)` +* inBitmap 的 config 必须同新的 Options.inPreferredConfig 一样 +* inBitmap.isMutable() == true \>= 4.4 ->* inBitmap 的字节数大于等于新图片除以 inSampleSize 后所占的字节数即可 ->* inBitmap 的 config 同新的 Options.inPreferredConfig 最好一样,如果不一样将以 inBitmap 的 config 为准 ->* inBitmap.isMutable() == true +* inBitmap 的字节数大于等于新图片除以 inSampleSize 后所占的字节数即可 +* inBitmap 的 config 同新的 Options.inPreferredConfig 最好一样,如果不一样将以 inBitmap 的 config 为准 +* inBitmap.isMutable() == true #### 在 BitmapRegionDecoder 中使用 \>= 4.1 ->* inBitmap 的字节数大于等于新图片除以 inSampleSize 后所占的字节数即可 ->* inBitmap 的 config 同新的 Options.inPreferredConfig 最好一样,如果不一样将以 inBitmap 的 config 为准 ->* inBitmap.isMutable() == true +* inBitmap 的字节数大于等于新图片除以 inSampleSize 后所占的字节数即可 +* inBitmap 的 config 同新的 Options.inPreferredConfig 最好一样,如果不一样将以 inBitmap 的 config 为准 +* inBitmap.isMutable() == true `如果 inBitmap 不为空的时候解码失败或 inBitmap 不满足使用条件都将抛出 IllegalArgumentException 异常` ### BitmapPool -BitmapPool 用来存储已经不再使用的 Bitmap,Sketch 在解码之前会根据 width、height、Bitmap.Config 从 BitmapPool 中寻找可复用的 Bitmap 并设置给 Options.inBitmap +[BitmapPool] 用来存储已经不再使用的 Bitmap,Sketch 在解码之前会根据 width、height、Bitmap.Config 从 BitmapPool 中寻找可复用的 Bitmap 并设置给 Options.inBitmap -BitmapPool 的默认实现是 [LruBitmapPool](`来自 Glide`),默认最大容量是 3 个屏幕大小 +[BitmapPool] 的默认实现是 [LruBitmapPool](`来自 Glide`) -#### 使用 BitmapPool +#### 最大容量 -你可以通过 Configuration 拿到 BitmapPool,如下: +默认最大容量是 3 个屏幕像素数: ```java -Context context = ...; -BitmapPool bitmapPool = Sketch.with(context).getConfiguration().getBitmapPool(); +final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); +final int screenSize = displayMetrics.widthPixels * displayMetrics.heightPixels * 4; + +final int bitmapPoolMaxSize = screenSize * 3; ``` -然后根据宽、高、Bitmap.Config 寻找可复用的 Bitmap +由于最大容量一旦创建就不能修改,因此想要修改的话就只能重新创建 [BitmapPool] ```java +// 最大容量为 APP 最大可用内存的十分之一 +int newBitmapPoolMaxSize = (int) (Runtime.getRuntime().maxMemory() / 10); +Sketch.with(context).getConfiguration().setBitmapPool(new LruBitmapPool(context, newBitmapPoolMaxSize)); +``` + +#### 使用 BitmapPool + +Sketch 默认开启了 BitmapPool,但如果你有编辑 Bitmap 的需求也可以通过 [BitmapPool] 寻找可复用的 Bitmap,如下: + +```java +BitmapPool bitmapPool = Sketch.with(context).getConfiguration().getBitmapPool(); + +// 根据宽、高、Bitmap.Config 寻找可复用的 Bitmap Bitmap bitmap = bitmapPool.get(100, 100, Bitmap.Config.ARGB_8888); ``` -Bitmap 使用完毕后通过 BitmapPoolUtils.freeBitmapToPool(Bitmap, BitmapPool) 方法回收 Bitmap +Bitmap 使用完毕后通过 [BitmapPoolUtils].freeBitmapToPool(Bitmap, BitmapPool) 方法回收 Bitmap ```java BitmapPoolUtils.freeBitmapToPool(bitmap, bitmapPool); ``` -BitmapPoolUtils 会先尝试将 Bitmap 放进 BitmapPool 中,如果 BitmapPool 已经满了或 Bitmap 不可复用的话就会执行 recycle() 回收掉 Bitmap +[BitmapPoolUtils] 会先尝试将 Bitmap 放进 [BitmapPool] 中,如果 [BitmapPool] 已经满了或 Bitmap 不可复用的话就会执行 recycle() 回收掉 Bitmap + +#### 开关 BitmapPool + +```java +BitmapPool bitmapPool = Sketch.with(context).getConfiguration().getBitmapPool(); + +// 禁用 BitmapPool +bitmapPool.setDisabled(true); + +// 恢复 BitmapPool +bitmapPool.setDisabled(false); +``` + +### 释放缓存 + +bitmap 的释放是全自动的,使用者无需关心,总结一下会在如下时机自动释放: + +* 达到最大容量时自动释放较旧的 bitmap +* 设备可用内存较低触发了 Application 的 onLowMemory() 方法 +* 系统整理内存触发了 Application 的 onTrimMemory(int) 方法 +[BitmapPool]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/BitmapPool.java [LruBitmapPool]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/LruBitmapPool.java +[BitmapPoolUtils]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/BitmapPoolUtils.java diff --git a/docs/wiki/cache_processed_image_in_disk.md b/docs/wiki/cache_processed_image_in_disk.md index 79b5167205..fcabce8004 100644 --- a/docs/wiki/cache_processed_image_in_disk.md +++ b/docs/wiki/cache_processed_image_in_disk.md @@ -1,8 +1,8 @@ # 使用 cacheProcessedImageInDisk 属性缓存需要复杂处理的图片,提升显示速度 -一张图片要经过读取(或缩略图模式读取)和ImageProcessor处理才能显示在页面上,这两步通常也是整个显示过程中最耗时的,为了加快显示速度,减少用户等待时间我们可以将最终经过inSampleSize缩小的、缩略图模式读取的或ImageProcessor处理过的图片缓存在磁盘中,下次就可以直接读取 +一张图片要经过读取(或缩略图模式读取)和 ImageProcessor 处理才能显示在页面上,这两步通常也是整个显示过程中最耗时的,为了加快显示速度,减少用户等待时间我们可以将最终经过 inSampleSize 缩小的、缩略图模式读取的或 [ImageProcessor] 处理过的图片缓存在磁盘中,下次就可以读取后直接使用 -#### 如何开启: +### 如何开启: ```java DisplayOptions displayOptions = new DisplayOptions(); @@ -10,18 +10,19 @@ DisplayOptions displayOptions = new DisplayOptions(); displayOptions.setCacheProcessedImageInDisk(true); ``` -#### 使用条件 +### 使用条件 并不是你只要开启了就一定会将最终的图片缓存在磁盘缓存中,还需要满足以下任一条件: ->* 有maxSize并且最终计算得出的inSampleSize大于等于8 ->* 有resize ->* 有ImageProcessor,并且确实生成了一张新的图片 ->* thumbnailMode为true并且resize不为null +* 有 maxSize 并且最终计算得出的 inSampleSize 大于等于 8 +* 有 resize +* 有 ImageProcessor,并且确实生成了一张新的图片 +* thumbnailMode 为 true 并且 resize 不为 null -#### 存在的问题 +### 存在的问题 -由于Android天然存在的BUG,导致读到内存里的图片,再保存到磁盘后图片会发生轻微的色彩变化(通常是发黄),因此在使用此功能时还是要慎重考虑此因素带来的影响 +由于 Android 天然存在的 BUG,导致读到内存里的图片,再保存到磁盘后图片会发生轻微的色彩变化(通常是发黄并丢失一些细节),因此在使用此功能时还是要慎重考虑此因素带来的影响,参考文章 [Android 中 decode JPG 时建议使用 inPreferQualityOverSpeed][reference_article] -参考文章:http://www.cnblogs.com/zhucai/p/inPreferQualityOverSpeed.html +`此功能读取图片时已强制设置 inPreferQualityOverSpeed 为 true` -`此功能读取图片时已强制设置inPreferQualityOverSpeed为true` +[ImageProcessor]:../../sketch/src/main/java/me/xiaopan/sketch/process/ImageProcessor.java +[reference_article]: http://www.cnblogs.com/zhucai/p/inPreferQualityOverSpeed.html diff --git a/docs/wiki/cancel_request.md b/docs/wiki/cancel_request.md index 81d17e9bc5..76817797ed 100644 --- a/docs/wiki/cancel_request.md +++ b/docs/wiki/cancel_request.md @@ -1,14 +1,32 @@ -# 取消显示图片 +# 取消请求 -Sketch会自动取消请求,因此你不必刻意关注怎么去取消一个请求,或者该在什么时候去取消一个请求 +### display 显示请求 -#### 什么时候会自动取消请求? ->* SketchImageView重用的时候会自动取消之前的请求 ->* SketchImageView在onDetachedFromWindow的时候也会自动取消请求 +display 显示请求 [Sketch] 会在合适的时候自动取消,如下: +* [SketchImageView] 重用的时候会自动取消旧的未完成的请求 +* [SketchImageView] 在 onDetachedFromWindow 的时候自动取消未完成的请求 -#### 如何主动取消请求? ->* 方法1:在执行commit()或SketchImageView.display***Image()方法之后你会得到一个Request, -你可以通过Request的isCanceled()方法查看请求是否结束会或通过Request的cancel()方法取消请求 ->* 方法2:你可以通过Sketch.cancel(SketchView)方法来取消请求 +因此你不必刻意关注怎么去取消一个显示请求 -``取消请求的时候如果正在读取数据,就会立马停止读取,已经读取的数据就算浪费了`` +但有时候你还是想知道如何主动取消显示请求? +1. 在执行 [DisplayHelper].commit() 或 [SketchImageView].display***Image() 方法之后你会得到一个 [DisplayRequest],通过其 cancel() 方法可以取消显示请求 +2. 你还可以通过 [Sketch].cancel(SketchView) 方法来取消显示请求 + +``取消请求的时候如果正在下载图片,就会立马停止下载,已经下载的数据就算浪费了`` + +### load 加载请求 + +在执行 [LoadHelper].commit() 方法之后你会得到一个 [LoadRequest],通过其 cancel() 方法可以取消显示请求 + +### download 下载请求 + +在执行 [DownloadHelper].commit() 方法之后你会得到一个 [DownloadRequest],通过其 cancel() 方法可以取消显示请求 + +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java +[SketchImageView]: ../../sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java +[DisplayHelper]:../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java +[DisplayRequest]:../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayRequest.java +[LoadHelper]:../../sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java +[LoadRequest]:../../sketch/src/main/java/me/xiaopan/sketch/request/LoadRequest.java +[DownloadHelper]:../../sketch/src/main/java/me/xiaopan/sketch/request/DownloadHelper.java +[DownloadRequest]:../../sketch/src/main/java/me/xiaopan/sketch/request/DownloadRequest.java diff --git a/docs/wiki/correct_image_orientation.md b/docs/wiki/correct_image_orientation.md index 8c4cbe821f..63103ccbc7 100644 --- a/docs/wiki/correct_image_orientation.md +++ b/docs/wiki/correct_image_orientation.md @@ -2,9 +2,9 @@ 有的时候碰到被旋转的图片,看着很费劲,图片查看器支持了旋转还好说,要是不支持可就蛋疼了。 -现在Sketch支持自动识别图片方向并且纠正,看起来就跟图片原本就是正常的一样,并且分块显示超大图功能也能自动纠正 +现在 [Sketch] 支持自动识别图片方向并且纠正,看起来就跟图片原本就是正常的一样,并且分块显示超大图功能也能自动纠正 -`仅支持jpeg类型的图片`,因为目前只有jpeg类型的图片才会有exif信息 +`仅支持 jpeg 类型的图片,因为目前只有 jpeg 类型的图片才会有 exif 信息` ### 关闭自动纠正功能: @@ -39,6 +39,7 @@ Sketch.with(context).display(...).disableCorrectImageOrientation().commit(); ### 获取图片方向 1.第一种方法:监听显示完成 + ```java SketchImageView sketchImageView = ...; sketchImageView.setDisplayListener(new DisplayListener(){ @@ -51,7 +52,8 @@ sketchImageView.setDisplayListener(new DisplayListener(){ }); ``` -2.第二种方法:直接通过Drawable获取 +2.第二种方法:直接通过 Drawable 获取 + ```java SketchImageView sketchImageView = ...; Drawable drawable = sketchImageView.getDrawable(); @@ -61,13 +63,17 @@ if (drawable != null && drawable instanceof SketchDrawable){ } ``` -这里返回的方向是未经修饰的exif信息里记载的方向,你可以通过ImageOrientationCorrector类提供的工具方法解析这个方向,如下: +这里返回的方向是未经修饰的 exif 信息里记载的方向,你可以通过 [ImageOrientationCorrector] 类提供的工具方法解析方向,如下: + ``` int exifOrientation = ...; // 纠正方向时顺时针方向需要旋转的度数 int degrees = ImageOrientationCorrector.getExifOrientationDegrees(exifOrientation); -// 纠正方向时X轴需要是的缩放倍数,通常是1(不需要翻转)或-1(横向翻转) +// 纠正方向时X轴需要是的缩放倍数,通常是 1(不需要翻转)或 -1(横向翻转) int xScale = ImageOrientationCorrector.getExifOrientationTranslation(exifOrientation); ``` + +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java +[ImageOrientationCorrector]: ../../sketch/src/main/java/me/xiaopan/sketch/decode/ImageOrientationCorrector.java diff --git a/docs/wiki/disk_cache.md b/docs/wiki/disk_cache.md index 83e8ed480c..1ef566c040 100644 --- a/docs/wiki/disk_cache.md +++ b/docs/wiki/disk_cache.md @@ -1,27 +1,24 @@ # 在磁盘上缓存图片原文件,避免重复下载 -DiskCache用来在磁盘上缓存图片,默认实现是LruDiskCache,其核心是DiskLruCache - -#### 相关方法 ->* boolean exist(String):判断缓存是否存在 ->* DiskCache.Entry get(String):获取缓存 ->* DiskCache.Editor edit(String):编辑缓存 ->* File getCacheDir():获取缓存目录 ->* long getMaxSize():获取最大容量 ->* long getSize():获取当前缓存大小 ->* void clear():清除缓存 ->* void close():关闭 ->* ReentrantLock getEditorLock(String):获取编辑同步锁 - -#### 配置最大容量 +[DiskCache] 用来在磁盘上缓存图片,默认实现是 [LruDiskCache],根据最少使用原则释放缓存 + +### 最大容量 + +默认最大容量是 100MB + +由于最大容量一旦创建就不能修改,因此想要修改的话就只能重新创建 [LruDiskCache] + ```java +// 最大容量为 50MB +int newDiskCacheMaxSize = (int) (Runtime.getRuntime().maxMemory() / 10); Configuration configuration = Sketch.with(context).getConfiguration(); -configuration.setDiskCache(new LruDiskCache(context, 1, 50 * 1024 * 1024)); +configuration.setDiskCache(new LruDiskCache(context, 1, newDiskCacheMaxSize)); ``` -#### 编辑缓存 +### 编辑缓存 + +如果你要通过 edit(String) 方法编辑磁盘缓存,那么你需要加同步锁,如下: -如果你要通过edit(String)方法编辑磁盘缓存,那么你需要加同步锁,如下: ```java DiskCache diskCache = Sketch.with(context).getConfiguration().getDiskCache(); String diskCacheKey = ...; @@ -30,11 +27,46 @@ String diskCacheKey = ...; ReentrantLock lock = diskCache.getEditorLock(diskCacheKey); lock.lock(); -// 编辑 -DiskCache.Entry diskCacheEntry = diskCache.edit(diskCacheKey); -... -diskCacheEntry.commit(); +try { + // 编辑 + DiskCache.Entry diskCacheEntry = diskCache.edit(diskCacheKey); + ... + diskCacheEntry.commit(); +} finally { + // 解锁 + lock.unlock(); +} +``` + +### 缓存目录 + +缓存目录默认是 sdcard/Android/data/APP_PACKAGE_NAME/cache/sketch -// 解锁 -lock.unlock(); +另外为了兼容多进程,当在非主进程使用 [Sketch] 时缓存目录名称后会加上进程名,例如 "sketch:push" + +#### 开关 DiskCache + +```java +DiskCache diskCache = Sketch.with(context).getConfiguration().getDiskCache(); + +// 禁用 DiskCache +diskCache.setDisabled(true); + +// 恢复 DiskCache +diskCache.setDisabled(false); ``` + +### 其它方法 +* boolean exist(String):判断缓存是否存在 +* DiskCache.Entry get(String):获取缓存 +* DiskCache.Editor edit(String):编辑缓存 +* File getCacheDir():获取缓存目录 +* long getMaxSize():获取最大容量 +* long getSize():获取当前缓存大小 +* void clear():清除缓存 +* ReentrantLock getEditorLock(String):获取编辑同步锁 + +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java +[DiskCache]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/DiskCache.java +[LruDiskCache]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/LruDiskCache.java +[DiskLruCache]: ../../sketch/src/main/java/me/xiaopan/sketch/util/DiskLruCache.java diff --git a/docs/wiki/display_apk_or_app_icon.md b/docs/wiki/display_apk_or_app_icon.md index 787bf07e83..e2282081dc 100644 --- a/docs/wiki/display_apk_or_app_icon.md +++ b/docs/wiki/display_apk_or_app_icon.md @@ -2,7 +2,7 @@ ### 显示 APK 的图标 -Sketch 支持显示 APK 文件的图标,是通过 [ApkIconUriModel] 实现的此功能,uri 如下: +[Sketch] 支持显示 APK 文件的图标,是通过 [ApkIconUriModel] 实现的此功能,uri 如下: ``` apk.icon:///sdcard/file.apk @@ -26,7 +26,7 @@ Sketch.with(context).load(apkIconUri, new LoadListener(){...}).commit(); ### 显示 APP 的图标 -Sketch 还支持显示已安装 APP 的图标,是通过 [AppIconUriModel] 实现的此功能,uri 如下: +[Sketch] 还支持显示已安装 APP 的图标,是通过 [AppIconUriModel] 实现的此功能,uri 如下: ``` app.icon://com.xiaopan.sketchsample/241 @@ -48,6 +48,8 @@ Sketch.with(context).display(appIconUri, sketchImageView).commit(); Sketch.with(context).load(appIconUri, new LoadListener(){...}).commit(); ``` +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java [ApkIconUriModel]: ../../sketch/src/main/java/me/xiaopan/sketch/uri/ApkIconUriModel.java +[AppIconUriModel]: ../../sketch/src/main/java/me/xiaopan/sketch/uri/AppIconUriModel.java [SketchImageView]: ../../sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java [Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java diff --git a/docs/wiki/display_video_thumbnail.md b/docs/wiki/display_video_thumbnail.md index 366216dd4c..495322c011 100644 --- a/docs/wiki/display_video_thumbnail.md +++ b/docs/wiki/display_video_thumbnail.md @@ -1,12 +1,14 @@ # 显示视频缩略图 -Sketch 本身并没有提供显示视频缩略图的功能,但是你可以通过自定义 [UriModel] 轻松实现显示视频缩略图的功能,在源码中就有一个叫 [sample-video-thumbnail] 示例 module 展示了如何显示视频缩略图 +[Sketch] 本身并没有提供显示视频缩略图的功能,但是你可以通过自定义 [UriModel] 轻松实现显示视频缩略图的功能,在源码中就有一个叫 [sample-video-thumbnail] 示例 module 展示了如何显示视频缩略图 + ### 为什么不内置显示视频缩略图的功能 -首先读取视频缩略图的库通常都比较大,像 [FFmpegMediaMetadataRetriever] 每一种平台的 so 文件都有 6MB,如果集成到 Sketch 中将大大增加 Sketch 包的体积 +首先读取视频缩略图的库通常都比较大,像 [FFmpegMediaMetadataRetriever] 每一种平台的 so 文件都有 6MB,如果集成到 [Sketch] 中将大大增加 AAR 包的体积 + +其次有视频播放或编辑需求的 APP 都会用特定或自己改造的视频库,如果单为了显示视频缩略图而引入 [FFmpegMediaMetadataRetriever] 会得不偿失 -其次读取视频缩略图的库可用的比较多,一般有视频播放或编辑需求的 APP 都会用特定或自己改造的视频库,很难形成统一 ### 集成 [sample-video-thumbnail] 到你的 APP 中 @@ -19,12 +21,14 @@ compile 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14' 2.拷贝 [VideoThumbnailUriModel.java] 到你的 app 中 3.在 Application 中将 [VideoThumbnailUriModel.java] 加入到 [UriModelRegistry] 中,如下: + ```java Configuration configuration = Sketch.with(context).getConfiguration(); configuration.getUriModelRegistry().add(new VideoThumbnailUriModel()); ``` 4.显示视频缩略图 + ```java SketchImageView sketchImageView = ...; @@ -38,6 +42,7 @@ sketchImageView.displayImage(videoFileUri); 5.如果你想要使用其它的视频处理库,只需将 [VideoThumbnailUriModel.java] 的 getContent(Context, String) 方法的实现替换掉即可 +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java [UriModel]: ../../sketch/src/main/java/me/xiaopan/sketch/uri/UriModel.java [sample-video-thumbnail]: ../../sample-video-thumbnail/ [FFmpegMediaMetadataRetriever]: https://github.com/wseemann/FFmpegMediaMetadataRetriever diff --git a/docs/wiki/error_tracker.md b/docs/wiki/error_tracker.md index a5f364371a..eb858cad7c 100644 --- a/docs/wiki/error_tracker.md +++ b/docs/wiki/error_tracker.md @@ -1,10 +1,11 @@ # 通过 ErrorTracker 监控 Sketch 的异常 -Sketch在运行的过程中会有各种各样的异常,你可以通过ErrorTracker收到这些异常,然后将这些异常记录下来帮助解决问题 +[Sketch] 在运行的过程中会有各种各样的异常,你可以通过 [ErrorTracker] 收到这些异常,然后将这些异常记录下来帮助解决问题 -#### 使用 +### 使用 + +[ErrorTracker] 默认实现只是将收到的异常打印在 log cat 中,因此首先你要继承 [ErrorTracker] 并重写你要记录的异常对应的方法,如下: -ErrorTracker默认实现只是将收到的异常信息打印在log cat中,你要记录异常信息的话首先你需要继承ErrorTracker修改 ```java public class MyErrorTracker extends ErrorTracker { @@ -21,7 +22,11 @@ public class MyErrorTracker extends ErrorTracker { } ``` -然后通过Configuration设置即可,如下: +然后通过 [Configuration] 使用即可,如下: ```java Sketch.with(context).getConfiguration().setErrorTracker(new MyErrorTracker()); ``` + +[Sketch]: ../../sketch/src/main/java/me/xiaopan/sketch/Sketch.java +[Configuration]: ../../sketch/src/main/java/me/xiaopan/sketch/Configuration.java +[ErrorTracker]: ../../sketch/src/main/java/me/xiaopan/sketch/ErrorTracker.java \ No newline at end of file diff --git a/docs/wiki/http_stack.md b/docs/wiki/http_stack.md index 7a4157b55b..945616fb8a 100644 --- a/docs/wiki/http_stack.md +++ b/docs/wiki/http_stack.md @@ -1,21 +1,26 @@ # 发送 HTTP 请求 -HttpStack用来发起网络请求,然后返回响应,默认的实现是HurlStack +[HttpStack] 用来发起网络请求,然后返回响应,默认的实现是 [HurlStack] #### 相关配置: ->* setMaxRetryCount(int maxRetryCount):设置连接超时后重试次数,默认0(不重试) ->* setConnectTimeout(int connectTimeout):设置连接超时时间,默认7秒 ->* setReadTimeout(int readTimeout):设置读取超时时间,默认7秒 ->* setUserAgent(String userAgent):设置User-Agent,为空的话不设置 ->* setExtraHeaders(Map extraHeaders):设置一些通用的请求头属性 ->* addExtraHeaders(Map extraHeaders):添加一些通用的请求头属性 +* setMaxRetryCount(int maxRetryCount):设置连接超时后重试次数,默认0(不重试) +* setConnectTimeout(int connectTimeout):设置连接超时时间,默认 7 秒 +* setReadTimeout(int readTimeout):设置读取超时时间,默认 7 秒 +* setUserAgent(String userAgent):设置 User-Agent,为空的话不设置 +* setExtraHeaders(Map extraHeaders):设置一些通用的请求头属性,不可重复 +* addExtraHeaders(Map extraHeaders):添加一些通用的请求头属性,可重复 -#### 自定义: +#### 自定义: -你可以实现HttpStack接口实现自定义HttpStack +首先实现 [HttpStack] 接口定义自己的 [HttpStack] + +然后通过 [Configuration] 使用即可,如下: -然后通过Configuration设置即可,如下 ```java Sketch.with(context).getConfiguration().setHttpStack(new MyHttpStack()); ``` + +[HttpStack]: ../../sketch/src/main/java/me/xiaopan/sketch/http/HttpStack.java +[HurlStack]: ../../sketch/src/main/java/me/xiaopan/sketch/http/HurlStack.java +[Configuration]: ../../sketch/src/main/java/me/xiaopan/sketch/Configuration.java \ No newline at end of file diff --git a/docs/wiki/huge_image.md b/docs/wiki/huge_image.md index 93ef19e0d1..b04cfe5a46 100644 --- a/docs/wiki/huge_image.md +++ b/docs/wiki/huge_image.md @@ -1,33 +1,48 @@ # 分块显示超大图片 -Sketch 通过 HugeImageViewer 可以让 SketchImageView 支持分块显示超级大图,现在你可以替换掉 SubsamplingScaleImageView 了 +### 背景 +超大图片一直都是所有图片显示控件的噩梦,它们通常尺寸巨大,要想完整读取肯定会让 APP 因内存不足而崩掉 -### 如何开启 +然后 Android 官方并没有提供现成可用的控件来解决这个问题,仅在 API 10 之后提供了 [BitmapRegionDecoder] 可以让我们读取完整图片的部分区域 + +纵观其它几款流行的图片加载器 [Fresco]、[Glide]、[Picasso] 都没有提供超大图片支持,而单独支持超大图片的 View 倒是有几款,[Subsampling Scale Image View]、[WorldMap]、[LargeImage] 但都做的不够好或者没法跟现有的图片加载框架集成,做的不好还好说,不能跟现有图片加载框架集成用起来就很恶心了 + +下面用 [Glide] 代指现有的图片框架,用 [Subsampling Scale Image View] 代指单独的分块显示超大图控件来举例说明两者不能集成时的问题: + +1. 图片详情页必须准备两个 ImageView,一个 [Glide] 是用的,一个是 [Subsampling Scale Image View]。先用 [Glide] 加载完图片,然后根据结果(如果返回了原始图片尺寸的话,没有的话你还要自己去解析并判断)判断这张图片需不需要用 [Subsampling Scale Image View],如果需要的话再将 [Subsampling Scale Image View] 显示出来遮盖住 [Glide] 用的 ImageView,并初始化 [Subsampling Scale Image View] +2. [Subsampling Scale Image View] 要继续优化的话,还会涉及到内存缓存和 bitmap 复用池,如果 [Subsampling Scale Image View] 和 [Glide] 分别单独维护一套的话,APP 的可用内存就剩不了多少了,因此这两者必须能共用一套内存缓存和 bitmap 复用池 +3. [Glide] 支持的 uri,[Subsampling Scale Image View] 未必支持 + +### HugeImageViewer + +Sketch 是目前唯一提供了超大图片支持的图片加载器,核心类是 [HugeImageViewer] + +#### 支持的图片类型和系统版本 +* jpeg、png:API 10(2.3.3)及其以上 +* webp:API 14(4.0)及其以上 + +#### 使用条件 + +只要满足上述对图片类型和系统版本的要求并且读到内存的图片比原始图片小就可以使用超大图功能 + +#### 开启 ```java SketchImageView sketchImageView = ...; sketchImageView.setHugeImageEnabled(true); ``` 注意: -* HugeImageViewer 需要依赖 ImageZoomer,因此 HugeImageViewer 会自动开启 ImageZoomer -* 在关闭 HugeImageViewer 的时候如果检测到 ImageZoomer 是被 HugeImageViewer 开启的也会一并关闭 ImageZoomer +* [HugeImageViewer] 需要依赖手势缩放功能,因此当手势缩放功能未开启时 [HugeImageViewer] 会自动开启,同理在关闭 [HugeImageViewer] 的时候如果检测到手势缩放功能是被 [HugeImageViewer] 开启的也会一并关闭手势缩放功能 -### 支持的图片类型和系统版本 ->* jpeg、png:API 10(2.3.3)及其以上 ->* webp:API 14(4.0)及其以上 +#### 旋转 -### 使用条件 +[HugeImageViewer] 支持跟随手势缩放功旋转,但只支持 90°、180°、270° 旋转 -只要满足上述对图片类型和系统版本的要求并且读到内存的图片比原始图片小就可以使用超大图功能 +#### 在 ViewPager 中使用 -### 旋转 +由于 ViewPager 会至少缓存三个页面,所以至少会有三个 [HugeImageViewer] 同时工作,这样对内存的消耗是非常大的 -HugeImageViewer 支持跟随 ImageZoomer 旋转,但只支持 90°、180°、270° 旋转 - -### 在 ViewPager 中使用 -由于 ViewPager 会至少缓存三个页面,所以至少会有三个 HugeImageViewer 同时工作,这样对内存的消耗是非常大的 - -因此 HugeImageViewer 特地提供了 setPause(boolean) 方法来减少在 ViewPager 中的内存消耗,如下: +因此 [HugeImageViewer] 特地提供了 setPause(boolean) 方法来减少在 ViewPager 中的内存消耗,如下: ```java public class MyFragment extends Fragment { @@ -81,7 +96,7 @@ public class MyFragment extends Fragment { } ``` -### 其它方法 +#### 其它方法 ```java // 显示碎片范围 @@ -96,3 +111,12 @@ long tilesByteCount = hugeImageViewer.getTilesAllocationByteCount(); // 设置碎片变化监听器 hugeImageViewer.setOnTileChangedListener(HugeImageViewer.OnTileChangedListener) ``` + +[BitmapRegionDecoder]: https://developer.android.google.cn/reference/android/graphics/BitmapRegionDecoder.html +[Fresco]: https://github.com/facebook/fresco +[Glide]: https://github.com/bumptech/glide +[Picasso]: https://github.com/square/picasso +[WorldMap]: https://github.com/johnnylambada/WorldMap +[Subsampling Scale Image View]: https://github.com/davemorrissey/subsampling-scale-image-view +[LargeImage]: https://github.com/LuckyJayce/LargeImage +[HugeImageViewer]: ../../sketch/src/main/java/me/xiaopan/sketch/viewfun/huge/HugeImageViewer.java diff --git a/docs/wiki/image_displayer.md b/docs/wiki/image_displayer.md index d7d6a7c9b4..267ee2e2c3 100644 --- a/docs/wiki/image_displayer.md +++ b/docs/wiki/image_displayer.md @@ -1,20 +1,58 @@ # 使用 ImageDisplayer 以动画的方式显示图片 -ImageDisplayer是最后用来显示图片的,通过ImageDisplayer可以以更炫酷的方式显示图片 +通常显示 Bitmap 都是通过 setBitmapDrawable(Drawable) 方法,但是这个方法默认没有任何动画,会显的很突兀 -目前内置了以下几种ImageDisplayer: ->* DefaultImageDisplayer: 没有任何动画效果,默认的图片显示器 ->* TransitionImageDisplayer: 通过TransitionDrawable用当前图片(没有的话就创建一张透明的drawable代替)和新图片以过渡渐变的方式显示 [点击查看详细介绍以及注意事项](transition_image_displayer.md) ->* ZoomInImageDisplayer:由小到大的显示图片,缩放比例是从0.5f到1.0f ->* ZoomOutImageDisplayer:由大到小的显示图片,缩放比例是从1.5f到1.0f ->* ColorTransitionImageDisplayer:用指定的颜色创建一个Drawable同新图片以过渡效果显示 ->* FadeInImageDisplayer:已渐入效果显示图片 +现在已经不是 Android 2.x 的时代了,那时候只要你的 APP 能正确的显示图片就可以了,现在是体验为王的时代,因此我们要提升图片的显示体验 -ImageDisplayer还可以通过setAlwaysUse(boolean)方法设置只要涉及到显示图片就得使用ImageDisplayer(显示从内存里取出的缓存图片时也不例外) +[ImageDisplayer] 就是最后用来显示图片的,通过 [ImageDisplayer] 可以以更自然、更炫酷的方式显示图片,提升用户体验 -#### 自定义 -你还可以自定义ImageDisplayer,用你喜欢的方式显示图片,有以下几点需要注意: +### 使用 -1. 要先过滤一下bitmap为null以及已经回收的情况 -2. 调用startAnimation()执行动画之前要下调用clearAnimation()清理一下 -3. 尽量使用ImageDisplayer.DEFAULT_ANIMATION_DURATION作为动画持续时间 +可在 [DisplayOptions] 和 [DisplayHelper] 中使用,例如: + +```java +// SketchImageView +SketchImageView sketchImageView = ...; +sketchImageView.getOptions().setImageDisplayer(new TransitionImageDisplayer()); + +// DisplayHelper +Sketch.with(context).display(uri, sketchImageView) + .displayer(new TransitionImageDisplayer()) + .commit(); +``` + +目前内置了以下几种 [ImageDisplayer]: + +* [DefaultImageDisplayer]: 没有任何动画效果,默认的图片显示器 +* [TransitionImageDisplayer]: 通过 TransitionDrawable 用当前图片(没有的话就创建一张透明的 Drawable代替)和新图片以过渡渐变的方式显示图片,请参考 [使用 TransitionImageDisplayer 以自然过渡渐变的方式显示图片](transition_image_displayer.md) +* [ZoomInImageDisplayer]:由小到大的显示图片,缩放比例是从 0.5f 到 1.0f +* [ZoomOutImageDisplayer]:由大到小的显示图片,缩放比例是从 1.5f 到 1.0f +* [ColorTransitionImageDisplayer]:用指定的颜色创建一个 Drawable 同新图片以过渡效果显示 +* [FadeInImageDisplayer]:以渐入效果显示图片 + +### setAlwaysUse(boolean) + +默认的显示从内存里取出的缓存图片时不使用 [ImageDisplayer],但在两张图片来回交替显示在同一个 ImageView 的场景下,如果两张图片都已经存在于内存缓存中了,这时候就会因没有过渡动画而显得不自然 + +对于这样的场景你可以通过 [ImageDisplayer].setAlwaysUse(true) 方法设置只要涉及到显示图片就必须使用 [ImageDisplayer] + +### 自定义 + +你还可以自定义 [ImageDisplayer],用你喜欢的方式显示图片,但有几点需要注意: + +1. 要先过滤一下 bitmap 为 null 或已经回收的情况 +2. 调用 startAnimation() 执行动画之前要下调用 clearAnimation() 清理一下 +3. 尽量使用 [ImageDisplayer].DEFAULT_ANIMATION_DURATION 作为动画持续时间 + +[ImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/ImageDisplayer.java +[DefaultImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/DefaultImageDisplayer.java +[TransitionImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/TransitionImageDisplayer.java +[ZoomInImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/ZoomInImageDisplayer.java +[ZoomOutImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/ZoomOutImageDisplayer.java +[ColorTransitionImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/ColorTransitionImageDisplayer.java +[FadeInImageDisplayer]: ../../sketch/src/main/java/me/xiaopan/sketch/display/FadeInImageDisplayer.java +[transition_image_displayer]: transition_image_displayer.md +[LoadOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java +[DisplayOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayOptions.java +[LoadHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java +[DisplayHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java diff --git a/docs/wiki/image_processor.md b/docs/wiki/image_processor.md index 32ce6ad497..6890039cac 100644 --- a/docs/wiki/image_processor.md +++ b/docs/wiki/image_processor.md @@ -1,28 +1,60 @@ # 使用 ImageProcessor 在解码后改变图片 -ImageProcessor 是用来在ImageDecoder解码完图片之后在对图片进行处理的 - -目前内置了以下几种 ImageProcessor: ->* ResizeImageProcessor:默认的图片处理器,主要根据resize调整图片尺寸 ->* CircleImageProcessor:可以将图片处理成圆形的,同时也会根据resize调整图片尺寸 ->* ReflectionImageProcessor:可以将图片处理成倒影效果的,同时也会根据resize调整图片尺寸 ->* RoundedCornerImageProcessor:可以将图片处理成圆角的,同时也会根据resize调整图片尺寸 ->* GaussianBlurImageProcessor:可以对图片进行高斯模糊处理,同时也会根据resize调整图片尺寸 - -ImageProcessor 的职责不仅要将图片处理成各种效果的还要负责根据resize调整图片的尺寸,具体原则是: ->* 首先保证最终返回的图片的宽高比同resize一致 ->* 如果原图尺寸大于resize,那么就以resize为最终尺寸 ->* 如果原图尺寸小于resize,那么就以原图尺寸为准缩小resize ->* 如果resize.getMode()==Resize.Mode.EXACTLY_SAME,那么必须以resize为最终尺寸 ->* 如果resize为null,就不必改变尺寸 ->* 根据ScaleType选择原图片上对应的区域绘制到新图片上 +[ImageProcessor] 用来将图片读取到内存之后对图片进行处理 + +### 使用 + +可在 [LoadOptions]/[DisplayOptions] 和 [LoadHelper]/[DisplayHelper] 中使用,例如: + +```java +// SketchImageView +SketchImageView sketchImageView = ...; +sketchImageView.getOptions().setImageProcessor(new RoundRectImageProcessor(10)); + +// LoadHelper +Sketch.with(context).load(uri, listener) + .processor(new RoundRectImageProcessor(10)) + .commit(); + +// DisplayHelper +Sketch.with(context).display(uri, sketchImageView) + .processor(new RoundRectImageProcessor(10)) + .commit(); +``` + +目前内置了以下几种 [ImageProcessor]: + +* [ResizeImageProcessor]:默认的图片处理器,只会根据 resize 调整图片尺寸 +* [CircleImageProcessor]:将图片处理成圆形的,同时也会根据 resize 调整图片尺寸 +* [RoundRectImageProcessor]:将图片处理成圆角的,同时也会根据 resize 调整图片尺寸 +* [ReflectionImageProcessor]:将图片处理成倒影效果的,同时也会根据 resize 调整图片尺寸 +* [GaussianBlurImageProcessor]:对图片进行高斯模糊处理,同时也会根据 resize 调整图片尺寸 + +注意: +* [ImageProcessor] 的职责不仅要将图片处理成各种效果的还要根据 [Resize] 调整图片的尺寸,有关 [Resize] 具体规则请参考 [使用 Resize 精确修改图片的尺寸][resize] +* [ImageProcessor] 执行成功后,会返回一个新的 bitmap #### 自定义 -自定义ImageProcessor有以下几点需要注意: -1. 你需要处理resize,你可以调用sketch.getConfiguration().getResizeCalculator().calculator(bitmap.getWidth(), bitmap.getHeight(), resize.getWidth(), resize.getHeight(), resize.getScaleType(), resize != null && resize.getMode() == Resize.Mode.EXACTLY_SAME)来计算新图的大小、srcRect、destRect等,然后根据返回的ResizeCalculator.Result来创建新的图片 -2. 通过ImageProcessor的process()方法传进去的Bitmap在你处理完之后你无需回收它,Sketch会去处理 -3. 创建新的bitmap之前,先从BitmapPool中查找可复用bitmap,是在没有再创建新的bitmap -4. 在处理的过程中产生的过渡Bitmap在用完之后一定要调用BitmapPoolUtils.freeBitmapToPool(Bitmap, BitmapPool)回收掉 +自定义 [ImageProcessor] 时有以下几点需要注意: + +1. 需要处理 [Resize],你可以调用 Sketch.getConfiguration().getResizeCalculator().calculator(bitmap.getWidth(), bitmap.getHeight(), resize.getWidth(), resize.getHeight(), resize.getScaleType(), resize != null && resize.getMode() == Resize.Mode.EXACTLY_SAME) 来计算新图片的大小、srcRect、destRect等,然后根据返回的 ResizeCalculator.Result 来创建新的图片,请参考 [RoundRectImageProcessor] +2. 通过 [ImageProcessor] 的 process() 方法传进去的 Bitmap 在处理完之后无需回收它,Sketch 会去回收 +3. 创建新的 bitmap 之前,先从 BitmapPool 中查找可复用 bitmap,实在没有再创建新的 bitmap +4. 在处理的过程中产生的过渡 Bitmap 在用完之后一定要调用 BitmapPoolUtils.freeBitmapToPool(Bitmap, BitmapPool) 回收掉 + +自定义的 [ImageProcessor] 写好后通过 [LoadOptions]/[DisplayOptions] 的 setImageProcessor(ImageProcessor) 方法或 [LoadHelper]/[DisplayHelper] 的 processor(ImageProcessor) 方法使用即可 -自定义完后调用LoadOptions、DisplayOptions.setImageProcessor(ImageProcessor)方法或LoadHelper、DisplayHelper的processor(ImageProcessor)方法设置即可 +[ImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/ImageProcessor.java +[ImageDecoder]: ../../sketch/src/main/java/me/xiaopan/sketch/decode/ImageDecoder.java +[ResizeImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/ResizeImageProcessor.java +[CircleImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/CircleImageProcessor.java +[ReflectionImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/ReflectionImageProcessor.java +[RoundRectImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/RoundRectImageProcessor.java +[GaussianBlurImageProcessor]: ../../sketch/src/main/java/me/xiaopan/sketch/process/GaussianBlurImageProcessor.java +[resize]: resize.md +[Resize]: resize.md +[LoadOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java +[DisplayOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayOptions.java +[LoadHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java +[DisplayHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java diff --git a/docs/wiki/image_shaper.md b/docs/wiki/image_shaper.md index 7da8ccb9d8..b50bd4e704 100644 --- a/docs/wiki/image_shaper.md +++ b/docs/wiki/image_shaper.md @@ -1,25 +1,36 @@ # 通过 ImageShaper 在绘制时改变图片的形状 -ImageShaper 用来在绘制时修改图片的形状, +[ImageShaper] 用来在绘制时修改图片的形状,特点如下: ->* 是通过BitmapShader实现的,所以不需要创建新的图片 ->* loadingImage、errorImage、pauseDownloadImage以及加载的图片都会被修改 +* 通过 BitmapShader 实现,不会创建新的 Bitmap,节省内存 +* 会应用于 loadingImage、errorImage 以及 pauseDownloadImage -在DisplayOptions中设置即可使用: +### 使用 -```java -DisplayOptions options = new DisplayOptions(); -... -// 以20度圆角矩形的形状显示图片 -options.setImageShaper(new RoundRectImageShaper(20)); +可在 [DisplayOptions] 和 [DisplayHelper] 中使用,例如: +```java +// SketchImageView SketchImageView sketchImageView = ...; -sketchImageView.setOptions(options); -sketchImageView.displayImage(R.drawable.sample); +sketchImageView.getOptions().setImageShaper(new RoundRectImageShaper(10)); + +// DisplayHelper +Sketch.with(context).display(uri, sketchImageView) + .shaper(new RoundRectImageShaper(10)) + .commit(); ``` -目前内置了两种ImageShaper: ->* RoundRectImageShaper:圆角矩形,还支持描边 ->* CircleImageShaper:圆形,还支持描边 +目前内置了两种 [ImageShaper]: + +* RoundRectImageShaper:圆角矩形,还支持描边 +* CircleImageShaper:圆形,还支持描边 + +如果需要在绘制时同时改变图片的尺寸就要用到 [ShapeSize] 了 -如果需要在绘制时同时改变图片的尺寸就要用到ShapeSize了,[点击查看ShapeSize介绍](shape_size.md) +[ImageShaper]: ../../sketch/src/main/java/me/xiaopan/sketch/shaper/ImageShaper.java +[ImageProcessor]: image_processor.md +[LoadOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java +[DisplayOptions]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayOptions.java +[LoadHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java +[DisplayHelper]: ../../sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java +[ShapeSize]: shape_size.md diff --git a/docs/wiki/initializer.md b/docs/wiki/initializer.md index ed61b13d73..0eaa8dc600 100644 --- a/docs/wiki/initializer.md +++ b/docs/wiki/initializer.md @@ -1,10 +1,10 @@ # 通过 Initializer 延迟并统一配置 Sketch -Initializer 用来延迟配置 Sketch,具体会延迟到你第一次使用Sketch的时候 +[Initializer] 用来延迟配置 Sketch,具体会延迟到你第一次使用 Sketch 的时候 -#### 设计的初衷 +### 设计的初衷 -通常我们在开发一款app的时候会依赖很多的第三方 sdk 或 library ,大部分的 sdk 或 library 都需要你在 Application 的 onCreate() 方法中初始化它,这样一来就会增加 onCreate() 方法的耗时,进而影响app的启动速度,如下: +通常我们在开发 APP 的时候会依赖很多的第三方 sdk 或 library ,大部分的 sdk 或 library 都需要你在 Application 的 onCreate() 方法中初始化它,这样一来就会增加 onCreate() 方法的耗时,进而影响 APP 的冷启动速度,如下: ```java public class MyApplication extends Application { @@ -19,26 +19,26 @@ public class MyApplication extends Application { } ``` -那么最理想的初始化 sdk 或 library 的时机就应该是用到某个 sdk 或 library 的时候 +最理想的初始化 sdk 或 library 的时机就应该是在真正需要的时候 -虽然 Sketch 本身不需要任何初始化操作就可以使用,但仍免不了用户会有些自定义的配置需要第一时间设置生效,因此 Sketch 就提供了 Initializer 用来实现延迟配置 +虽然 Sketch 本身不需要任何初始化操作就可以使用,但仍免不了用户会有些自定义的配置需要第一时间设置生效,因此 Sketch 提供了 [Initializer] 用来实现延迟配置 #### 使用方式 -1.定义你自己的初始化类,并实现Initializer接口,如下: +1.定义你自己的初始化类,并实现 [Initializer] 接口,如下: ```java public class MyInitializer implements Initializer { @Override - public void onInitialize(Context context, Sketch sketch, Configuration configuration) { - configuration.getImagePreprocessor().addPreprocessor(new VideoIconPreprocessor()); + public void onInitialize(Context context, Configuration configuration) { + configuration.getUriModelRegistry().add(new XpkIconUriModel()); configuration.setErrorTracker(new MyErrorTracker(context)); } } ``` -2.在AndroidManifest.xml中定义meta-data,如下: +2.在 AndroidManifest.xml 中定义 meta-data,如下: ```xml @@ -54,6 +54,9 @@ public class MyInitializer implements Initializer { ``` 注意: -* 类名要定义在 name 属性上 +* 类名要定义在 name 属性上,这样 IDE 才能检测到类被使用了,不会被混淆删掉,并且也能被 IDE 的重构覆盖到 * value 属性值固定是 SKETCH_INITIALIZER -* sketch 的 aar 中已经包含了对所有 Initializer 子类的混淆忽略配置 +* sketch 的 aar 中已经包含了对所有 [Initializer] 子类的混淆忽略配置 + + +[Initializer]: ../../sketch/src/main/java/me/xiaopan/sketch/Initializer.java diff --git a/docs/wiki/listener.md b/docs/wiki/listener.md index 28007c4f65..c2cc6b2a4f 100644 --- a/docs/wiki/listener.md +++ b/docs/wiki/listener.md @@ -1,14 +1,21 @@ -# 监听开始加载、成功、失败以及下载进度事件 +# 监听准备加载、成功、失败以及下载进度事件 -Sketch支持对``开始加载``、``完成``、``失败``、``取消``以及``下载进度``进行监听 +Sketch 支持对 `准备加载`、`完成`、`失败`、`取消` 以及 `下载进度` 进行监听 + +注意: +* listener 默认在主线程回调,但是当 Sketch.load() 和 Sketch.download() 开启了同步后其 listener 就在运行线程回调,可能是主线程,也可能是非主线程 +* onReadyLoad() 方法只有在需要进入非主线程加载图片时才会被回调,因此有可能不回调 onReadyLoad() 方法而直接回调其它方法 + +#### SketchImageView -SketchImageView ```java SketchImageView sketchImageView = ...; + +// setDisplayListener() 一定要在 displayImage() 之前 sketchImageView.setDisplayListener(new DisplayListener() { @Override - public void onStartLoad() { - // 只有在需要进入异步线程加载数据时才会回调 onStartLoad() 方法 + public void onReadyLoad() { + // 只有在需要进入非主线程加载图片时才会回调 onReadyLoad() 方法 } @Override @@ -27,7 +34,7 @@ sketchImageView.setDisplayListener(new DisplayListener() { } }); -// setDownloadProgressListener()一定要在displayImage之前执行 +// setDownloadProgressListener() 一定要在 displayImage() 之前 sketchImageView.setDownloadProgressListener(new DownloadProgressListener() { @Override public void onUpdateDownloadProgress(int totalLength, int completedLength) { @@ -38,14 +45,15 @@ sketchImageView.setDownloadProgressListener(new DownloadProgressListener() { sketchImageView.displayImage("http://b.zol-img.com.cn/desk/bizhi/image/4/1366x768/1387347695254.jpg"); ``` -``display()不支持设置listener和downloadProgressListener`` +``Sketch.display() 不支持设置 listener 和 downloadProgressListener`` + +#### Sketch.load() -load() ```java Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { @Override - public void onStartLoad() { - // 只有在需要进入异步线程加载数据时才会回调 onStartLoad() 方法 + public void onReadyLoad() { + // 只有在需要进入非主线程加载图片时才会回调 onReadyLoad() 方法 } @Override @@ -70,12 +78,13 @@ Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { }).maxSize(100, 100).commit(); ``` -download() +#### Sketch.download() + ```java Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { @Override - public void onStartLoad() { - // 只有在需要进入异步线程加载数据时才会回调 onStartLoad() 方法 + public void onReadyLoad() { + // 只有在需要进入非主线程加载图片时才会回调 onReadyLoad() 方法 } @Override @@ -99,5 +108,3 @@ Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { } }).commit(); ``` - -listener 默认都是异步回调的,并且都会在主线程回调,但是当 load() 和 download() 开启了同步后其 listener 就在当前线程回调 diff --git a/docs/wiki/load_and_download.md b/docs/wiki/load_and_download.md index e5ece8bc4e..74c17dbeb9 100644 --- a/docs/wiki/load_and_download.md +++ b/docs/wiki/load_and_download.md @@ -1,17 +1,17 @@ # 只加载或下载图片 Sketch 共有三个方法可供使用,你可以根据你的需求选择合适的方法 ->* download():下载图片到本地,并实现本地缓存 ->* load():在download()方法的基础上,加载图片到内存中,并对图片进行处理 ->* display():在load()方法的基础上,将图片缓存在内存中并显示在ImageView上 +* download():下载图片到本地,并实现本地缓存 +* load():在 download() 方法的基础上,加载图片到内存中,并对图片进行处理 +* display():在 load() 方法的基础上,将图片缓存在内存中并显示在 ImageView 上,[SketchImageView] 的 displayImage() 方法就是对 Sketch.display() 方法的一层封装 -SketchImageView就是使用的 Sketch.display() 方法 +### Options & Listener & Helper -每一种方法都有专用的Options、Listener、Helper +每一种方法都有专用的 Options、Listener、Helper -* Options:用来定制请求,[点击了解更多](options_and_helper.md) -* Listener:用来监控请求的状态,[点击了解更多](listener.md) -* Helper:用来组织请求并提交请求 +* Options:用来定制请求,更详细的介绍请参考 [Options & Helper][options_and_helper] +* Listener:用来监控请求的结果,更详细的介绍请参考 [监听准备加载、成功、失败以及下载进度事件][listener.md] +* Helper:用来组织请求并提交 |Method|Options|Listener|Helper| |:---|:---|:---|:---| @@ -19,9 +19,9 @@ SketchImageView就是使用的 Sketch.display() 方法 |load()|LoadOptions|LoadListener|LoadHelper| |display()|DisplayOptions|DisplayListener|DisplayHelper| -#### 示例: +### 示例: -显示一张图片到 SketchImageView 上并设置加载中占位图 +1.显示一张图片到 SketchImageView 上并设置加载中占位图 ```java Sketch.with(context).display("http://t.cn/RShdS1f", sketchImageView) @@ -29,9 +29,9 @@ Sketch.with(context).display("http://t.cn/RShdS1f", sketchImageView) .commit(); ``` -显示图片时我们可以直接使用 SketchImageView 的 displayImage() 方法,其内部也是调的这个方法,[点我了解更多 SketchImageView 的用法](sketch_image_view.md) +显示图片时我们可以直接使用 SketchImageView 的 displayImage() 方法,其内部也是调的这个方法,更多内容请参考[SketchImageView 使用指南][sketch_image_view.md] -加载图片到内存中,并限制图片尺寸不能超过100x100 +2.加载图片到内存中,并限制图片尺寸不能超过 100x100 ```java Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { @@ -45,7 +45,8 @@ Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { }).maxSize(100, 100).commit(); ``` -下载图片到本地 +3.下载图片到本地 + ```java Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { @Override @@ -59,11 +60,12 @@ Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { }).commit(); ``` -#### 同步执行 +### 同步执行 -load() 和 download() 方法还支持同步执行,这在非主线程中加载图片时非常有用,只需调用sync()方法开启即可: +load() 和 download() 方法还支持同步执行,这在非主线程中加载图片时非常有用,只需调用 sync() 方法开启即可: ```java +// load Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { @Override public void onCompleted(LoadResult loadResult) { @@ -74,7 +76,7 @@ Sketch.with(context).load("http://t.cn/RShdS1f", new LoadListener() { ... }).maxSize(100, 100).sync().commit(); - +// download Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { @Override public void onCompleted(DownloadResult downloadResult) { @@ -88,5 +90,10 @@ Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener() { ``` 注意: ->* 不能在主线程开启同步,否则会运行时抛异常 ->* 不仅下载、加载的主体过程会在当前调用线程执行,listener 回调也会在当前调用线程执行 +* 不能在主线程开启同步,否则会运行时抛异常 +* 不仅下载、加载的主体过程会在当前调用线程执行,listener 回调也会在当前调用线程执行,更多listener内容请参考 [监听准备加载、成功、失败以及下载进度事件][listener.md] + +[SketchImageView]: ../../sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java +[options_and_helper]: options_and_helper.md +[listener]: listener.md +[sketch_image_view]: sketch_image_view.md diff --git a/docs/wiki/log.md b/docs/wiki/log.md index 513b84a44e..d47ce3ce0e 100644 --- a/docs/wiki/log.md +++ b/docs/wiki/log.md @@ -34,7 +34,7 @@ SLog.setLoggable(SLog.LEVEL_DEBUG); SLog.isLoggable(SLog.LEVEL_DEBUG) == true // 获取当前日志级别 -SLog.getLevel() +SLog.getLevel(); ``` ### 日志分类 @@ -60,7 +60,7 @@ SLog.setLoggable(SLog.TYPE_CACHE); SLog.isLoggable(SLog.TYPE_CACHE) == true // 关闭 CACHE 类型日志(此方法只能删除日志分类) -SLog.removeLoggable(SLog.TYPE_CACHE) +SLog.removeLoggable(SLog.TYPE_CACHE); ``` [SLog]: ../../sketch/src/main/java/me/xiaopan/sketch/SLog.java diff --git a/docs/wiki/max_size.md b/docs/wiki/max_size.md index 09883af71c..12dd0bb4d7 100644 --- a/docs/wiki/max_size.md +++ b/docs/wiki/max_size.md @@ -1,29 +1,51 @@ -# 使用 MaxSize 限制读取到内存的图片大小 +# 使用 MaxSize 读取合适尺寸的缩略图,节省内存 -maxSize用来计算inSampleSize,防止加载过大的图片到内存中 +### 背景 -#### 背景 -我们在移动端显示图片的时候都需要面对加载大尺寸图片的问题 -目前主流的Android旗舰机内存一般在2G左右,而APP最多可用内存为200M左右 -现在手机都是800万像素的,随便拍一张照片都是3264x1840的,那么加载到内存中就需要3264x1840x4/1024/1024=22M,照这么看加载9张3264x1840的图片内存就耗完了,显然我们不能这样搞 +我们在 APP 中显示图片的时候都需要面对加载大尺寸图片的问题,假如手机内存为2GB,APP 最多可用内存为 200MB ,手机摄像头像素为800万,那么随便拍一张照片都是 3264x1840 的,完整加载到内存中就需要 3264x1840x4/1024/1024=22MB,照这么看加载 9 张 3264x1840 的图片内存就耗完了 -#### 何时使用 -ImageDecoder会在读取图片的时候根据maxSize计算出合适的inSampleSize,然后再读取图片 +如果 APP 中一个页面只有一个 ImageView 还好,但如果是九宫格要显示 9 个呢,一下子加载 9 张完整尺寸的图片 APP 一下就崩掉了,因此必须要根据 ImageView 的尺寸加载合适尺寸的缩略图 -#### 关于inSampleSize -BitmapFactory提供了一个Options来配置读取图片的相关选项,其中一个参数就是inSampleSize,其作用是设置缩放倍数。 +### 关于 inSampleSize -例如: - 原图尺寸3264x1840 - inSampleSize=2 -那么最终读到内存的图片的尺寸就是1632x920,宽高都缩小了2倍,其所占内存从23M缩小到1632x920x4/1024/1024=5.7M,缩小了4倍。 +inSampleSize 是用来读取图片的完整缩略图的,正好可以用来解决读取大尺寸图片问题 + +BitmapFactory 提供了一个 Options 来配置读取图片的相关选项,其中一个参数就是 inSampleSize,其作用是设置缩放倍数 + +例如原图尺寸是 3264x1840,inSampleSize 是 2,那么最终读到内存的图片的尺寸就是 1632x920,宽高都缩小了 2 倍,其所占内存从 22MB 缩小到 1632x920x4/1024/1024=5.7MB,缩小了 4 倍 + +### MaxSize + +在 Sketch 中 [MaxSize] 用来计算 inSampleSize,好加载适合 ImageView 尺寸的缩略图 #### 缺省值 ->* 在使用load()方法加载图片的时候maxSize的缺省值是当前设备屏幕的宽高 ->* 在使用SketchImageView的时候如果layout_width和layout_height是固定的那么就会用layout_width和layout_height来作为maxSize,否则就使用当前屏幕的宽高来作为maxSize +* 在使用 load() 方法加载图片的时候 [MaxSize] 的缺省值是设备屏幕的宽高 +* 在使用 SketchImageView 的时候如果 layout_width 和 layout_height 是固定的那么就会用 layout_width 和 layout_height 来作为 [MaxSize],否则就使用设备屏幕的宽高来作为 [MaxSize] + +为什么缺省值是设备屏幕的宽高? +* 因为 ImagweView 的尺寸通常不会超过设备屏幕的宽高 + +#### 自定义 MaxSize 计算规则 + +1. 继承 [ImageSizeCalculator] 类重写 calculateImageMaxSize() 方法实现你自己的计算规则 +2. 调用 Sketch.with(context).getConfiguration().setSizeCalculator(ImageSizeCalculator) 方法使用你自定义的 [ImageSizeCalculator] + +### inSampleSize 计算规则 + +默认的实现是 [ImageSizeCalculator].calculateInSampleSize(int, int, int, int) 方法,规则如下(`目标宽高是 MaxSize 的尺寸`): + +1. 先根据 targetSizeScale 缩放目标宽高,默认是1.1f,目的是为了让比目标宽高稍稍大一点的图片能直接显示 +2. 如果目标宽高都小于等于0或都大于原图的宽高,就不计算了直接返回1 +3. 然后根据缩略图的像素数不能超过目标宽高的像素数原则计算 inSampleSize +4. 接下来根据宽高均不能超过 OpenGL 所允许的最大尺寸(不同设备不同版本均不同),原则进一步计算 inSampleSize +5. 最后如果是为超大图功能加载预览图的话,当缩小 2 倍的时为了节省内存考虑还不如缩小 4 倍(缩小1倍时不会启用超大图功能) + +#### 自定义 inSampleSize 计算规则 + +1. 继承 [ImageSizeCalculator] 类重写 calculateInSampleSize(int, int, int, int) 方法实现你自己的计算规则 +2. 调用 Sketch.with(context).getConfiguration().setSizeCalculator(ImageSizeCalculator) 方法使用你自定义的 [ImageSizeCalculator] -因此在大多数情况下你不需要主动设置maxSize,Sketch会自动帮你搞定 -#### 自定义maxSize计算规则 -如果你对现有的maxSize计算规则不满意,那么你可以继承ImageSizeCalculator类重写calculateImageMaxSize()方法实现你自己的计算规则 -然后调用Sketch.with(context).getConfiguration().setSizeCalculator(ImageSizeCalculator)方法来使用你自定义的ImageSizeCalculator +[MaxSize]: ../../sketch/src/main/java/me/xiaopan/sketch/request/[MaxSize].java +[ImageDecoder]: ../../sketch/src/main/java/me/xiaopan/sketch/decode/ImageDecoder.java +[ImageSizeCalculator]: ,,/../sketch/src/main/java/me/xiaopan/sketch/decode/ImageSizeCalculator.java diff --git a/docs/wiki/memory_cache.md b/docs/wiki/memory_cache.md index 8d79e325b8..569f6506e6 100644 --- a/docs/wiki/memory_cache.md +++ b/docs/wiki/memory_cache.md @@ -1,18 +1,70 @@ # 在内存中缓存 Bitmap 提升显示速度 -MemoryCache用来在内存中缓存图片,默认的实现是LruMemoryCache,自动根据最少使用原则释放旧的图片 +[MemoryCache] 用来在内存中缓存图片,默认的实现是 [LruMemoryCache],根据最少使用原则释放旧的图片 -#### 相关方法: ->* getMaxSize():获取最大容量 ->* getSize():获取已用容量 ->* clear():清空内存缓存 ->* remove(String):删除缓存中指定key的图片 ->* get(String):获取缓存中指定的keu的图片 ->* put(String, Drawable):将图片放到缓存中,注意Drawable必须实现RecycleDrawableInterface接口 +#### 最大容量 + +默认最大容量是 3 个屏幕像素数: -#### 配置最大容量 ```java -// 最大容量为APP最大可用内存的十分之一 +final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); +final int screenSize = displayMetrics.widthPixels * displayMetrics.heightPixels * 4; + +final int memoryCacheMaxSize = screenSize * 3; +``` + +由于最大容量一旦创建就不能修改,因此想要修改的话就只能重新创建 [MemoryCache] + +```java +// 最大容量为 APP 最大可用内存的十分之一 int newMemoryCacheMaxSize = (int) (Runtime.getRuntime().maxMemory() / 10); -Sketch.with(context).getConfiguration().setMemoryCache(new LruMemoryCache(newMemoryCacheMaxSize)); +Sketch.with(context).getConfiguration().setMemoryCache(new LruMemoryCache(context, newMemoryCacheMaxSize)); ``` + +#### 开关 MemoryCache + +```java +MemoryCache memoryCache = Sketch.with(context).getConfiguration().getMemoryCache(); + +// 禁用 MemoryCache +memoryCache.setDisabled(true); + +// 恢复 MemoryCache +memoryCache.setDisabled(false); +``` + +### 释放缓存 + +内存缓存的释放是全自动的,使用者无需关心,总结一下会在如下时机自动释放: + +* 达到最大容量时自动释放较旧的缓存 +* 在 SketchImageView 中 Drawable 被替换 +* SketchImageView 执行 onDetachedFromWindow +* 请求取消 +* 设备可用内存较低触发了 Application 的 onLowMemory() 方法 +* 系统整理内存触发了 Application 的 onTrimMemory(int) 方法 + +### 缓存日志 + +内存缓存模块在运行期间会有一些日志输出,但默认是关闭的,开启或关闭的方法如下: + +```java +// 开启 CACHE 类型日志 +SLog.setLoggable(SLog.TYPE_CACHE); + +// 关闭 CACHE 类型日志 +SLog.removeLoggable(SLog.TYPE_CACHE); +``` + +### 其它方法 + +* getMaxSize():获取最大容量 +* getSize():获取已用容量 +* clear():清空内存缓存 +* remove(String):删除缓存中指定 key 的图片 +* get(String):获取缓存中指定的 key 的图片 +* put(String, SketchRefBitmap):将图片放到缓存中 + + +[MemoryCache]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/MemoryCache.java +[LruMemoryCache]: ../../sketch/src/main/java/me/xiaopan/sketch/cache/LruMemoryCache.java diff --git a/docs/wiki/memory_cache_state_image.md b/docs/wiki/memory_cache_state_image.md index d999ad45df..a0e68e3f3b 100644 --- a/docs/wiki/memory_cache_state_image.md +++ b/docs/wiki/memory_cache_state_image.md @@ -1,61 +1,52 @@ # 使用 MemoryCacheStateImage 先显示已缓存的较模糊的图片,然后再显示清晰的图片 -显示一个图片列表的时候通常我们会在列表页显示较小的图片,点击进入图片详情页后再显示原始更清晰的图片 +### 背景 + +显示一个图片列表的时候通常我们会在列表页显示较小的图片,点击进入图片详情页后再显示更清晰原图 在进入图片详情页后通常的做法是先显示一个转转转的进度条,然后下载原始图片并显示,如果这时候网络较慢的话,用户会长时间在看一个空白的页面,体验不太好 要优化这个问题,最容易想到的办法就是先显示在列表页已经显示的较小的图片,等原始大图加载完后再显示原始大图 -现在通过MemoryCacheStateImage就可以轻松实现这样的效果,首先MemoryCacheStateImage需要一个内存缓存key,就能从内存缓存中取出图片,然后用这张图片作为loading占位图显示,所以这里的关键就是怎么才能知道上一个页面中那些图片的内存缓存key呢? - -首先我们需要知道内存缓存key的构成,其实内存缓存key就是请求key -```java -public class DisplayInfo extends LoadInfo { +### MemoryCacheStateImage - public DisplayInfo() { - } +现在通过 [MemoryCacheStateImage] 就可以轻松实现这样的效果,首先 [MemoryCacheStateImage] 需要一个内存缓存 key 才能从内存缓存中取出图片,然后用这张图片作为 loading 占位图显示,所以这里的关键就是怎么才能知道上一个页面中那些图片的内存缓存 key 呢? - public DisplayInfo(DisplayInfo info) { - super(info); - } - - /** - * 获取内存缓存key - */ - public String getMemoryCacheKey() { - return getKey(); - } -} -``` +首先我们需要知道内存缓存 key 的构成,其实内存缓存 key 就是请求 key,请求 key 是由 uri 和选项构成的 -请求key是由uri和显示选项构成的 ``` public class DisplayHelper { protected void preProcess() { .... // 根据URI和显示选项生成请求Key - if (displayInfo.getKey() == null) { - displayInfo.setKey(SketchUtils.makeRequestKey(displayInfo.getUri(), displayOptions)); - } + key = SketchUtils.makeRequestKey(uri, uriModel, displayOptions); } } ``` -可以看到最终是由SketchUtils的makeRequestKey()方法生成的请求KEY + +实际上是由 [SketchUtils].makeRequestKey() 方法生成的请求 key + ```java -public static String makeRequestKey(String imageUri, DownloadOptions options) { +@NonNull +public static String makeRequestKey(@NonNull String imageUri, @NonNull UriModel uriModel, @NonNull String optionsKey) { StringBuilder builder = new StringBuilder(); - builder.append(imageUri); - if (options != null) { - options.makeKey(builder); + if (uriModel.isConvertShortUriForKey()) { + builder.append(SketchMD5Utils.md5(imageUri)); + } else { + builder.append(imageUri); + } + if (!TextUtils.isEmpty(optionsKey)) { + builder.append(optionsKey); } return builder.toString(); } ``` -从上面我们可以看到内存缓存key就是由uri和显示选项构成,因此我们可以拿到上一个页面ImageView的Options Key,然后在图片详情页将uri和Options Key拼接一下就能得到内存缓存key了 +从上面我们可以看到内存缓存 key 就是由 uri 和选项 key 构成,因此我们可以拿到上一个页面 ImageView的选项 key,然后在图片详情页将 uri 和选项 key 拼接一下就能得到内存缓存 key 了 + +第一步,在图片列表页点击图片跳转的时候取出选项 key 并传到图片详情页 -第一步,在图片列表页点击图片跳转的时候取出Options Key并传到图片详情页 ```java public void onClick(View v) { SketchImageView sketchImageView = (SketchImageView) v; @@ -67,9 +58,9 @@ public void onClick(View v) { startActivity(intent); } ``` -`一定要通过SketchImageView的getOptionsKey()方法获取Options Key,因为在commit方法里会对Options进行补充处理,所以通过getOptionsKey()得到的才是才是最终有效的Options Key` +`一定要通过 SketchImageView 的 getOptionsKey() 方法获取选项 key,因为在 commit 方法里会对 Options 进行补充处理,所以通过 getOptionsKey() 得到的才是才是最终有效的选项 key` -第二步,假如ImageDetailActivity里面是一个ViewPager,然后又把Options Key和图片uri,传到了最终的ImageFragment中,那么就在ImageFragment中拼装内存缓存key +第二步,假如 ImageDetailActivity 里面是一个 ViewPager,然后又把选项 key 和图片 uri,传到了最终的 ImageFragment 中,那么就在 ImageFragment 中拼装内存缓存 key ```java public class ImageFragment extends Fragment { String optionsKey; @@ -88,14 +79,18 @@ public class ImageFragment extends Fragment { SketchImageView sketchImageView = view.findViewById(R.id.iamge); DisplayOptions options = sketchImageView.getOptions(); - String loadingImageMemoryCacheKey = SketchUtils.makeRequestKey(imageUri, optionsKey); - options.setLoadingImage(new MemoryCacheStateImage(loadingImageMemoryCacheKey)); + Configuration configuration = Sketch.with(getActivity()).getConfiguration(); + UriModel uriModel = configuration.getUriModelRegistry().match(imageUri); + String loadingImageMemoryCacheKey = SketchUtils.makeRequestKey(imageUri, uriModel, optionsKey); + options.setLoadingImage(new MemoryCacheStateImage(loadingImageMemoryCacheKey, null)); options.set....; sketchImageView.displayImage(imageUri); } } ``` -`SketchUtils还有一个重载的makeRequestKey(String, String)方法可用来生成请求Key` -更详细的实现细节请参考示例app +更详细的实现细节请参考示例 app + +[MemoryCacheStateImage]: ../../sketch/src/main/java/me/xiaopan/sketch/state/MemoryCacheStateImage.java +[SketchUtils]: ../../sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java diff --git a/docs/wiki/options_and_helper.md b/docs/wiki/options_and_helper.md index 2c5b9784c4..025123c8ec 100644 --- a/docs/wiki/options_and_helper.md +++ b/docs/wiki/options_and_helper.md @@ -42,7 +42,7 @@ Sketch 的 display()、load()、download() 方法([参考 load_android_downloa * requestLevel: 指定请求的处理深度-[参考 RequestLevel] * cacheInDiskDisabled: 禁用磁盘缓存-[参考 disk_cache.md] -* maxSize: 限制读取到内存的图片的尺寸-[参考 max_size.md] +* maxSize: 参考 [使用 MaxSize 读取合适尺寸的缩略图,节省内存][max_size] * resize: 修改图片的尺寸-[参考 resize.md] * processor: 处理图片-[参考 image_processor.md] * decodeGifImage: 解码 GIF 图片,开启后可播放 GIF 图片-[参考 play_gif_image.md] @@ -127,7 +127,7 @@ Sketch.with(context).download("http://t.cn/RShdS1f", new DownloadListener(){...} [参考 disk_cache.md]: disk_cache.md [参考 thumbnail_mode.md]: thumbnail_mode.md [ImageType]: ../../sketch/src/main/java/com/xiaopan/sketch/decode/ImageType.java -[参考 max_size.md]: max_size.md +[max_size]: max_size.md [参考 resize.md]: resize.md [参考 image_processor.md]: image_processor.md [参考 play_gif_image.md]: play_gif_image.md diff --git a/docs/wiki/options_filter.md b/docs/wiki/options_filter.md index 71cb1373ee..fb5d58c489 100644 --- a/docs/wiki/options_filter.md +++ b/docs/wiki/options_filter.md @@ -30,10 +30,10 @@ optinsFilterRegistry.add(new OptionsFilterRegistry()); Sketch 内置了四种 [OptionsFilter],如下: * [LowQualityOptionsFilter]:用来控制全局开启低质量模式 * [InPreferQualityOverSpeedOptionsFilter]:用来控制全局开启质量优先模式 -* [PauseDownloadOptionsFilter]:用来控制暂停下载,配合 [MobileNetworkPauseDownloadController] 可实现移动网络下暂停下载,另参见 [移动网络下暂停下载图片,节省流量][pause_download] -* [PauseLoadOptionsFilter]:用来控制暂停加载,配合 [ScrollingPauseLoadManager] 可实现列表滑动中暂停加载,另参见 [列表滑动时暂停加载图片,提升列表滑动流畅度][pause_load] +* [PauseDownloadOptionsFilter]:用来控制暂停下载,配合 [MobileNetworkPauseDownloadController] 可实现移动网络下暂停下载,另参考 [移动网络下暂停下载图片,节省流量][pause_download] +* [PauseLoadOptionsFilter]:用来控制暂停加载,配合 [ScrollingPauseLoadManager] 可实现列表滑动中暂停加载,另参考 [列表滑动时暂停加载图片,提升列表滑动流畅度][pause_load] -上述四个 [OptionsFilter], [Configuration] 和 [OptionsFilterRegistry] 都提供了开关控制,详情参见源码即可 +上述四个 [OptionsFilter], [Configuration] 和 [OptionsFilterRegistry] 都提供了开关控制,详情参考源码即可 [OptionsFilter]: ../../sketch/src/main/java/me/xiaopan/sketch/optionsfilter/OptionsFilter.java [OptionsFilterRegistry]: ../../sketch/src/main/java/me/xiaopan/sketch/optionsfilter/OptionsFilterRegistry.java diff --git a/docs/wiki/other.md b/docs/wiki/other.md deleted file mode 100644 index ea948538c1..0000000000 --- a/docs/wiki/other.md +++ /dev/null @@ -1,18 +0,0 @@ -# 其它知识点 - -### inSampleSize 计算规则 - -inSampleSize用来减小读到内存中的图片的尺寸 - -默认的实现是 [ImageSizeCalculator].calculateInSampleSize(int, int, int, int) 方法,规则如下: -1. 先根据targetSizeScale缩放目标宽高,默认是1.1f,目的是为了让比目标宽高稍稍大一点的图片能直接显示 -2. 如果目标尺寸的宽高都小于等于0或都大于原图的宽高,就不计算了直接返回1 -3. 然后限制像素数不能超过目标宽高的像素数 -4. 接下来限制宽高均不能超过OpenGL所允许的最大尺寸 -5. 最后如果是为大图功能加载预览图的话,当缩小2倍的时为了节省内存考虑还不如缩小4倍(缩小1倍时不会启用大图功能,因此无需处理) - -##### 自定义 inSampleSize 计算规则 -1.继承 ImageSizeCalculator,并重写 calculateInSampleSize(int, int, int, int) 方法, -2.调用 Sketch.with(context).getConfiguration().setSizeCalculator(ImageSizeCalculator) 方法应用即可 - -[ImageSizeCalculator]: ,,/../sketch/src/main/java/me/xiaopan/sketch/decode/ImageSizeCalculator.java diff --git a/docs/wiki/pause_load.md b/docs/wiki/pause_load.md index dc0f35cb69..a0e9c8a181 100644 --- a/docs/wiki/pause_load.md +++ b/docs/wiki/pause_load.md @@ -117,5 +117,5 @@ listView.setOnScrollListener(new ScrollingPauseLoadManager(context)); ``` 注意: ->* 在配置较高的设备上不建议使用此功能,因为实时显示图片的体验要远高于滑动时暂停加载新图片的体验。特别是在列表页点击进入一个新页面又返回的时候,由于新页面加载了新的图片把列表页中图片的缓存挤掉了,回到列表后就会刷新一下重新加载图片 ->* 那么此功能是做着玩的嘛?当然不是,在一些比较老性能很差劲的设备上,开启此功能还是很有必要的。你可以通过 Android 版本号进行判断并开启此功能。 +* 在配置较高的设备上不建议使用此功能,因为实时显示图片的体验要远高于滑动时暂停加载新图片的体验。特别是在列表页点击进入一个新页面又返回的时候,由于新页面加载了新的图片把列表页中图片的缓存挤掉了,回到列表后就会刷新一下重新加载图片 +* 那么此功能是做着玩的嘛?当然不是,在一些比较老性能很差劲的设备上,开启此功能还是很有必要的。你可以通过 Android 版本号进行判断并开启此功能。 diff --git a/docs/wiki/play_gif_image.md b/docs/wiki/play_gif_image.md index bb12e2f1cb..5c67212470 100644 --- a/docs/wiki/play_gif_image.md +++ b/docs/wiki/play_gif_image.md @@ -2,6 +2,10 @@ Sketch集成了android-gif-drawable 1.2.6,可以无障碍的播放gif图片 +### 配置依赖 + +参考 [README] 配置依赖 + #### 配置解码GIF图 Sketch默认不解码gif图,只会通过BitmapFactory读取其第一帧作为一个普通的图片 @@ -13,9 +17,9 @@ sketImageView.getOptions().setDecodeGifImage(true); 属性配置请参考[配置各种属性.md](options_and_helper.md) 注意: ->* Sketch会根据mimeType判断是否是gif图,因此不用担心识别不了伪装成jpg的gif图 ->* GifDrawable不能使用maxSize、resize、TransitionImageDisplayer ->* GifDrawable还不能使用内存缓存,因为GifDrawable需要依赖Callback才能播放, +* Sketch会根据mimeType判断是否是gif图,因此不用担心识别不了伪装成jpg的gif图 +* GifDrawable不能使用maxSize、resize、TransitionImageDisplayer +* GifDrawable还不能使用内存缓存,因为GifDrawable需要依赖Callback才能播放, 如果缓存的话就会出现一个GifDrawable被显示在多个ImageView上的情况,这时候就只有最后一个能正常播放 #### 显示gif图标识 @@ -146,3 +150,5 @@ public void onPause() { } } ``` + +[README]: ../../README.md diff --git a/docs/wiki/sketch_image_view.md b/docs/wiki/sketch_image_view.md index efd166bf6d..b3f057032d 100644 --- a/docs/wiki/sketch_image_view.md +++ b/docs/wiki/sketch_image_view.md @@ -3,14 +3,14 @@ SketchImageView用来代替ImageView,你必须使用SketchImageView才能保证图片会被正常回收 特点: ->* 使用display***Image()系列方法即可方便的显示各种图片 ->* 支持显示下载进度 ->* 支持显示按下状态,长按的时候还会显示类似Android 5.0的涟漪效果 ->* 支持显示图片来源,能方便的看出当前图片来自内存还是本地缓存还是刚从网络下载的 ->* 支持显示gif图标,当显示的是gif图的时候会在右下角显示一个图标,用于提醒用户这是一张gif图片 ->* 支持显示失败的时候点击重新显示图片 ->* 支持暂停下载的时候点击强制显示图片 ->* onDetachedFromWindow的时候主动释放图片以及取消请求 +* 使用display***Image()系列方法即可方便的显示各种图片 +* 支持显示下载进度 +* 支持显示按下状态,长按的时候还会显示类似Android 5.0的涟漪效果 +* 支持显示图片来源,能方便的看出当前图片来自内存还是本地缓存还是刚从网络下载的 +* 支持显示gif图标,当显示的是gif图的时候会在右下角显示一个图标,用于提醒用户这是一张gif图片 +* 支持显示失败的时候点击重新显示图片 +* 支持暂停下载的时候点击强制显示图片 +* onDetachedFromWindow的时候主动释放图片以及取消请求 ### 使用SketchImageView 首先在布局中定义,如下: @@ -80,8 +80,8 @@ sketchImageView.setOptions(displayOptions) // 监听显示过程 sketchImageView.setDisplayListener(new DisplayListener() { @Override - public void onStartLoad() { - // 只有在需要进入异步线程加载数据时才会回调 onStartLoad() 方法 + public void onReadyLoad() { + // 只有在需要进入非主线程加载图片时才会回调 onReadyLoad() 方法 Log.i("displayListener", "开始"); } @@ -164,11 +164,11 @@ sketchImageView.setShowImageFromEnabled(true); ``` 开启此功能后会在SketchImageView的左上角显示一个纯色的三角形,根据三角形的颜色你就可以知道图片是从哪里来的 ->* 紫色表示是从内存中加载的 ->* 绿色表示是从内存缓存中加载的 ->* 蓝色表示是本地图片 ->* 黄色表示是从本地缓存加载的 ->* 红色表示是刚刚从网络下载的 +* 紫色表示是从内存中加载的 +* 绿色表示是从内存缓存中加载的 +* 蓝色表示是本地图片 +* 黄色表示是从本地缓存加载的 +* 红色表示是刚刚从网络下载的 效果如下: diff --git a/docs/wiki/state_image.md b/docs/wiki/state_image.md index 4c067ad07c..760bfac05f 100644 --- a/docs/wiki/state_image.md +++ b/docs/wiki/state_image.md @@ -3,10 +3,10 @@ StateImage用来为loadingImage,errorImage,pauseDownloadImage提供图片 #### 现支持以下几种: ->* DrawableStateImage:给什么图片显示什么图片,支持ShapeSize和ImageShaper ->* OldStateImage:使用当前ImageView正在显示的图片作为状态图片 ->* MemoryCacheStateImage:从内存缓存中获取图片作为状态图片,支持ShapeSize和ImageShaper,[点击查看更详细的介绍](memory_cache_state_image.md) ->* MakerStateImage:可以利用Options中配置的ImageProcessor和resize修改原图片,同样支持ShapeSize和ImageShaper +* DrawableStateImage:给什么图片显示什么图片,支持ShapeSize和ImageShaper +* OldStateImage:使用当前ImageView正在显示的图片作为状态图片 +* MemoryCacheStateImage:从内存缓存中获取图片作为状态图片,支持ShapeSize和ImageShaper,[点击查看更详细的介绍](memory_cache_state_image.md) +* MakerStateImage:可以利用Options中配置的ImageProcessor和resize修改原图片,同样支持ShapeSize和ImageShaper #### 自定义: 直接实现StateImage接口实现即可,但要注意的是你要保证实现ShapeSize和ImageShaper功能,详情可参考DrawableStateImage diff --git a/docs/wiki/thumbnail_mode.md b/docs/wiki/thumbnail_mode.md index bef58a3f70..bdcb231a3e 100644 --- a/docs/wiki/thumbnail_mode.md +++ b/docs/wiki/thumbnail_mode.md @@ -8,11 +8,11 @@ Sketch的缩略图模式专门用于在一个较小的ImageView上清晰的显 最终我们利用BitmapRegionDecoder读取14537,0-15463,926位置的图片并且缩小4倍 ->* 不同的ScaleType计算出的映射位置会有所不同 +* 不同的ScaleType计算出的映射位置会有所不同 #### 支持的图片类型和系统版本 ->* jpeg、png:API 10(2.3.3)及其以上 ->* webp:API 14(4.0)及其以上 +* jpeg、png:API 10(2.3.3)及其以上 +* webp:API 14(4.0)及其以上 #### 使用条件 diff --git a/docs/wiki/transition_image_displayer.md b/docs/wiki/transition_image_displayer.md index 08a9395071..c5c278c80b 100644 --- a/docs/wiki/transition_image_displayer.md +++ b/docs/wiki/transition_image_displayer.md @@ -82,8 +82,8 @@ options.setShapeSize(300, 300); sketchImageView.displayImage(R.drawable.sample); ``` ->* ShapeSize会同时修改loadingImage、errorImage、pauseDownloadImage以及新图片的尺寸 ->* 没有配置loadingImage时无需配置ShapeSize ->* 配置了loadingImage却没有配置ShapeSize时Sketch就会尝试用ImageView的固定宽高(layout_width和layout_height是固定的值)作为ShapeSize,如果宽高不固定就只能抛运行时异常了 +* ShapeSize会同时修改loadingImage、errorImage、pauseDownloadImage以及新图片的尺寸 +* 没有配置loadingImage时无需配置ShapeSize +* 配置了loadingImage却没有配置ShapeSize时Sketch就会尝试用ImageView的固定宽高(layout_width和layout_height是固定的值)作为ShapeSize,如果宽高不固定就只能抛运行时异常了 [详细了解ShapeSize](shape_size.md) diff --git a/docs/wiki/zoom.md b/docs/wiki/zoom.md index afe5584e92..4529a616ed 100644 --- a/docs/wiki/zoom.md +++ b/docs/wiki/zoom.md @@ -12,11 +12,11 @@ sketchImageView.setZoomEnabled(true); #### 对比PhotoView ImageZoomer是直接在PhotoView基础上做的,并且做了以下改进: ->* ImageZoomer的双击缩放只有两级,并且是根据图片的尺寸、ImageView的宽高以及ScaleType动态计算的,而PhotoView则是固定的三级双击缩放比例,体验不好 ->* 手动持续缩放时如果超过了最小比例或最大比例时PhotoView直接就拉不动了,而ImageZoomer依然可以缩放,并且超过后会有种拉橡皮筋的感觉,松手后自动回滚到最小或最大缩放比例,体验更好 ->* ImageZoomer优化了scrollEdge的判断,修复了在不能整除的缩放比例下,无法识别边缘的BUG ->* ImageZoomer增加了滑动条,可以方便的看到当前滑动的位置 ->* ImageZoomer增加了定位功能,可以指定图片上的一个点,然后以动画的方式移动到这个点 +* ImageZoomer的双击缩放只有两级,并且是根据图片的尺寸、ImageView的宽高以及ScaleType动态计算的,而PhotoView则是固定的三级双击缩放比例,体验不好 +* 手动持续缩放时如果超过了最小比例或最大比例时PhotoView直接就拉不动了,而ImageZoomer依然可以缩放,并且超过后会有种拉橡皮筋的感觉,松手后自动回滚到最小或最大缩放比例,体验更好 +* ImageZoomer优化了scrollEdge的判断,修复了在不能整除的缩放比例下,无法识别边缘的BUG +* ImageZoomer增加了滑动条,可以方便的看到当前滑动的位置 +* ImageZoomer增加了定位功能,可以指定图片上的一个点,然后以动画的方式移动到这个点 #### 缩放 @@ -68,21 +68,21 @@ ImageZoomer会根据图片的尺寸、ImageView的宽高以及ScaleType动态计 另外ImageZoomer的双击缩放比例则只有两级,即在最小缩放比例和最大缩放比例之间切换,在保证了合理的最小、最大缩放比例的前提下,这样能简化用户的操作,提升用户体验 先介绍几个概念: ->* fillZoomScale:能够让他图片的宽或高充满view的缩放比例 ->* fullZoomScale:能够看到图片全貌的缩放比例 ->* originZoomScale:如果开启了分块显示超大图功能就是能够让原图一比一显示的缩放比例,否则的话这个固定是1.0f +* fillZoomScale:能够让他图片的宽或高充满view的缩放比例 +* fullZoomScale:能够看到图片全貌的缩放比例 +* originZoomScale:如果开启了分块显示超大图功能就是能够让原图一比一显示的缩放比例,否则的话这个固定是1.0f 最小缩放比例: ->* ScaleType是CENTER或ScaleType是ENTER_INSIDE并且图尺寸片比view小:`1.0f` ->* ScaleType是CENTER_CROP:`fillZoomScale` ->* ScaleType是FIT_START/FIT_CENTE/FIT_END或ScaleType是ENTER_INSIDE并且图尺寸片比view大:`fullZoomScale` ->* ScaleType是FIT_XY:`fullZoomScale` +* ScaleType是CENTER或ScaleType是ENTER_INSIDE并且图尺寸片比view小:`1.0f` +* ScaleType是CENTER_CROP:`fillZoomScale` +* ScaleType是FIT_START/FIT_CENTE/FIT_END或ScaleType是ENTER_INSIDE并且图尺寸片比view大:`fullZoomScale` +* ScaleType是FIT_XY:`fullZoomScale` 最大缩放比例: ->* ScaleType是CENTER或ScaleType是ENTER_INSIDE并且图尺寸片比view小:`Math.max(originZoomScale, fillZoomScale)` ->* ScaleType是CENTER_CROP:`Math.max(originZoomScale, fillZoomScale * 1.5f)` ->* ScaleType是FIT_START/FIT_CENTE/FIT_END或ScaleType是ENTER_INSIDE并且图尺寸片比view大:`Math.max(originZoomScale, fillZoomScale)` ->* ScaleType是FIT_XY:`fullZoomScale` +* ScaleType是CENTER或ScaleType是ENTER_INSIDE并且图尺寸片比view小:`Math.max(originZoomScale, fillZoomScale)` +* ScaleType是CENTER_CROP:`Math.max(originZoomScale, fillZoomScale * 1.5f)` +* ScaleType是FIT_START/FIT_CENTE/FIT_END或ScaleType是ENTER_INSIDE并且图尺寸片比view大:`Math.max(originZoomScale, fillZoomScale)` +* ScaleType是FIT_XY:`fullZoomScale` #### 阅读模式 diff --git a/gradle.properties b/gradle.properties index 6b829c969e..842372c185 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,7 +26,7 @@ MIN_SDK_VERSION=10 TARGET_SDK_VERSION=22 TARGET_SDK_VERSION_LIBRARY=25 -VERSION_CODE=2410 -VERSION_NAME=2.4.1 +VERSION_CODE=2492 +VERSION_NAME=2.5.0-p2 ANDROID_SUPPORT_LIBRARY_VERSION=25.3.0 \ No newline at end of file diff --git a/sample/src/main/java/me/xiaopan/sketchsample/fragment/ImageFragment.java b/sample/src/main/java/me/xiaopan/sketchsample/fragment/ImageFragment.java index d5ff577f72..2f9b9a9405 100644 --- a/sample/src/main/java/me/xiaopan/sketchsample/fragment/ImageFragment.java +++ b/sample/src/main/java/me/xiaopan/sketchsample/fragment/ImageFragment.java @@ -26,8 +26,8 @@ import java.util.List; import butterknife.BindView; +import me.xiaopan.sketch.Configuration; import me.xiaopan.sketch.Sketch; -import me.xiaopan.sketch.cache.MemoryCache; import me.xiaopan.sketch.datasource.DataSource; import me.xiaopan.sketch.display.FadeInImageDisplayer; import me.xiaopan.sketch.drawable.ImageAttrs; @@ -180,9 +180,14 @@ private void initOptions() { // 有占位图选项信息的话就使用内存缓存占位图但不使用任何显示器,否则就是用渐入显示器 if (!TextUtils.isEmpty(loadingImageOptionsKey)) { - String memoryCacheKey = SketchUtils.makeRequestKey(finalShowImageUrl, loadingImageOptionsKey); - MemoryCache memoryCache = Sketch.with(getActivity()).getConfiguration().getMemoryCache(); - SketchRefBitmap cachedRefBitmap = memoryCache.get(memoryCacheKey); + Configuration configuration = Sketch.with(getActivity()).getConfiguration(); + UriModel uriModel = configuration.getUriModelRegistry().match(finalShowImageUrl); + SketchRefBitmap cachedRefBitmap = null; + String memoryCacheKey = null; + if (uriModel != null) { + memoryCacheKey = SketchUtils.makeRequestKey(finalShowImageUrl, uriModel, loadingImageOptionsKey); + cachedRefBitmap = configuration.getMemoryCache().get(memoryCacheKey); + } if (cachedRefBitmap != null) { options.setLoadingImage(new MemoryCacheStateImage(memoryCacheKey, null)); } else { @@ -194,7 +199,7 @@ private void initOptions() { } @Override - public void onStartLoad() { + public void onReadyLoad() { hintView.loading(null); } @@ -207,7 +212,7 @@ public void onCompleted(@NonNull Drawable drawable, @NonNull ImageFrom imageFrom } @Override - public void onError(@NonNull ErrorCause errorCause) { + public void onError(@NonNull ErrorCause cause) { hintView.hint(R.drawable.ic_error, "图片显示失败", "重新显示", new View.OnClickListener() { @Override public void onClick(View v) { @@ -217,8 +222,8 @@ public void onClick(View v) { } @Override - public void onCanceled(@NonNull CancelCause cancelCause) { - switch (cancelCause) { + public void onCanceled(@NonNull CancelCause cause) { + switch (cause) { case BE_CANCELLED: break; case PAUSE_DOWNLOAD: diff --git a/sketch/src/main/java/me/xiaopan/sketch/SLog.java b/sketch/src/main/java/me/xiaopan/sketch/SLog.java index d26b3284c4..cd4e265234 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/SLog.java +++ b/sketch/src/main/java/me/xiaopan/sketch/SLog.java @@ -16,6 +16,7 @@ package me.xiaopan.sketch; +import android.annotation.SuppressLint; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -87,6 +88,13 @@ public class SLog { */ public static final int TYPE_HUGE_IMAGE = 0x01 << 20; + public static final String LEVEL_NAME_VERBOSE = "VERBOSE"; + public static final String LEVEL_NAME_DEBUG = "DEBUG"; + public static final String LEVEL_NAME_INFO = "INFO"; + public static final String LEVEL_NAME_WARNING = "WARNING"; + public static final String LEVEL_NAME_ERROR = "ERROR"; + public static final String LEVEL_NAME_NONE = "NONE"; + private static final String TAG = "Sketch"; private static final String NAME = "SLog"; private static int levelAndTypeFlags; @@ -143,6 +151,7 @@ public static boolean isLoggable(int mask) { * @return 取值范围 {@link #LEVEL_VERBOSE}, {@link #LEVEL_DEBUG}, {@link #LEVEL_INFO}, * {@link #LEVEL_WARNING}, {@link #LEVEL_ERROR}, {@link #LEVEL_NONE} */ + @SuppressLint("WrongConstant") @Level public static int getLevel() { if (isLoggable(LEVEL_VERBOSE)) { @@ -158,7 +167,6 @@ public static int getLevel() { } else if (isLoggable(LEVEL_NONE)) { return LEVEL_NONE; } else { - //noinspection WrongConstant return 0; } } @@ -189,24 +197,27 @@ public static void setLevel(@Level int level) { SLog.levelAndTypeFlags = newFlag; String newLevelName = getLevelName(); - android.util.Log.w(TAG, String.format("%s. setLevel, %s -> %s", NAME, oldLevelName, newLevelName)); + android.util.Log.w(TAG, String.format("%s. setLevel. %s -> %s", NAME, oldLevelName, newLevelName)); } + /** + * 获取日志级别名称 + */ public static String getLevelName() { if (isLoggable(LEVEL_VERBOSE)) { - return "VERBOSE"; + return LEVEL_NAME_VERBOSE; } else if (isLoggable(LEVEL_DEBUG)) { - return "DEBUG"; + return LEVEL_NAME_DEBUG; } else if (isLoggable(LEVEL_INFO)) { - return "INFO"; + return LEVEL_NAME_INFO; } else if (isLoggable(LEVEL_WARNING)) { - return "WARNING"; + return LEVEL_NAME_WARNING; } else if (isLoggable(LEVEL_ERROR)) { - return "ERROR"; + return LEVEL_NAME_ERROR; } else if (isLoggable(LEVEL_NONE)) { - return "NONE"; + return LEVEL_NAME_NONE; } else { - return "UNKNOWN"; + return "UNKNOWN(" + getLevel() + ")"; } } @@ -322,7 +333,7 @@ private static int low16One(int mask) { * @return 整型 * @throws NumberFormatException 字符串异常 */ - public static int parseUnsignedInt(@NonNull String s, int radix) throws NumberFormatException { + public static int parseUnsignedInt(@NonNull String s, @SuppressWarnings("SameParameterValue") int radix) throws NumberFormatException { //noinspection ConstantConditions if (s == null) { throw new NumberFormatException("null"); diff --git a/sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java b/sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java index 5634ecbe55..9c5bd4b464 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java +++ b/sketch/src/main/java/me/xiaopan/sketch/SketchImageView.java @@ -25,6 +25,7 @@ import me.xiaopan.sketch.request.DisplayCache; import me.xiaopan.sketch.request.DisplayRequest; import me.xiaopan.sketch.request.RedisplayListener; +import me.xiaopan.sketch.uri.UriModel; import me.xiaopan.sketch.viewfun.FunctionPropertyView; /** @@ -88,15 +89,15 @@ public boolean redisplay(@Nullable RedisplayListener listener) { /** * 获取选项 KEY,可用于组装缓存 KEY * - * @see me.xiaopan.sketch.util.SketchUtils#makeRequestKey(String, String) + * @see me.xiaopan.sketch.util.SketchUtils#makeRequestKey(String, UriModel, String) */ @NonNull public String getOptionsKey() { DisplayCache displayCache = getDisplayCache(); if (displayCache != null) { - return displayCache.options.makeKey(new StringBuilder()).toString(); + return displayCache.options.makeKey(); } else { - return getOptions().makeKey(new StringBuilder()).toString(); + return getOptions().makeKey(); } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/cache/MemorySizeCalculator.java b/sketch/src/main/java/me/xiaopan/sketch/cache/MemorySizeCalculator.java index 7e5a8684ee..1291512687 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/cache/MemorySizeCalculator.java +++ b/sketch/src/main/java/me/xiaopan/sketch/cache/MemorySizeCalculator.java @@ -14,42 +14,27 @@ * devices screen density, width, and height. */ public class MemorySizeCalculator { - private static final String NAME = "MemorySizeCalculator"; - // Visible for testing. static final int BYTES_PER_ARGB_8888_PIXEL = 4; static final int MEMORY_CACHE_TARGET_SCREENS = 3; static final int BITMAP_POOL_TARGET_SCREENS = 3; static final float MAX_SIZE_MULTIPLIER = 0.4f; static final float LOW_MEMORY_MAX_SIZE_MULTIPLIER = 0.33f; - + private static final String NAME = "MemorySizeCalculator"; private final int bitmapPoolSize; private final int memoryCacheSize; - private final Context context; - - interface ScreenDimensions { - int getWidthPixels(); - - int getHeightPixels(); - } - - public MemorySizeCalculator(Context context) { - this(context, - (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE), - new DisplayMetricsScreenDimensions(context.getResources().getDisplayMetrics())); - } // Visible for testing. - MemorySizeCalculator(Context context, ActivityManager activityManager, ScreenDimensions screenDimensions) { + public MemorySizeCalculator(Context context) { context = context.getApplicationContext(); - this.context = context; + final ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); final int maxSize = getMaxSize(activityManager); - final int screenSize = screenDimensions.getWidthPixels() * screenDimensions.getHeightPixels() - * BYTES_PER_ARGB_8888_PIXEL; + final int screenSize = displayMetrics.widthPixels * displayMetrics.heightPixels * BYTES_PER_ARGB_8888_PIXEL; - int targetPoolSize = screenSize * BITMAP_POOL_TARGET_SCREENS; - int targetMemoryCacheSize = screenSize * MEMORY_CACHE_TARGET_SCREENS; + final int targetPoolSize = screenSize * BITMAP_POOL_TARGET_SCREENS; + final int targetMemoryCacheSize = screenSize * MEMORY_CACHE_TARGET_SCREENS; if (targetMemoryCacheSize + targetPoolSize <= maxSize) { memoryCacheSize = targetMemoryCacheSize; @@ -62,24 +47,11 @@ public MemorySizeCalculator(Context context) { if (SLog.isLoggable(SLog.LEVEL_DEBUG | SLog.TYPE_CACHE)) { SLog.d(NAME, "Calculated memory cache size: %s pool size: %s memory class limited? %s max size: %s memoryClass: %d isLowMemoryDevice: %s", - toMb(memoryCacheSize), toMb(bitmapPoolSize), targetMemoryCacheSize + targetPoolSize > maxSize, toMb(maxSize), activityManager.getMemoryClass(), isLowMemoryDevice(activityManager)); + toMb(context, memoryCacheSize), toMb(context, bitmapPoolSize), targetMemoryCacheSize + targetPoolSize > maxSize, toMb(context, maxSize), + activityManager.getMemoryClass(), isLowMemoryDevice(activityManager)); } } - /** - * Returns the recommended memory cache size for the device it is run on in bytes. - */ - public int getMemoryCacheSize() { - return memoryCacheSize; - } - - /** - * Returns the recommended bitmap pool size for the device it is run on in bytes. - */ - public int getBitmapPoolSize() { - return bitmapPoolSize; - } - private static int getMaxSize(ActivityManager activityManager) { final int memoryClassBytes = activityManager.getMemoryClass() * 1024 * 1024; final boolean isLowMemoryDevice = isLowMemoryDevice(activityManager); @@ -87,7 +59,7 @@ private static int getMaxSize(ActivityManager activityManager) { * (isLowMemoryDevice ? LOW_MEMORY_MAX_SIZE_MULTIPLIER : MAX_SIZE_MULTIPLIER)); } - private String toMb(int bytes) { + private static String toMb(Context context, int bytes) { return Formatter.formatFileSize(context, bytes); } @@ -98,21 +70,17 @@ private static boolean isLowMemoryDevice(ActivityManager activityManager) { || (sdkInt >= Build.VERSION_CODES.KITKAT && activityManager.isLowRamDevice()); } - private static class DisplayMetricsScreenDimensions implements ScreenDimensions { - private final DisplayMetrics displayMetrics; - - public DisplayMetricsScreenDimensions(DisplayMetrics displayMetrics) { - this.displayMetrics = displayMetrics; - } - - @Override - public int getWidthPixels() { - return displayMetrics.widthPixels; - } + /** + * Returns the recommended memory cache size for the device it is run on in bytes. + */ + public int getMemoryCacheSize() { + return memoryCacheSize; + } - @Override - public int getHeightPixels() { - return displayMetrics.heightPixels; - } + /** + * Returns the recommended bitmap pool size for the device it is run on in bytes. + */ + public int getBitmapPoolSize() { + return bitmapPoolSize; } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/CallbackHandler.java b/sketch/src/main/java/me/xiaopan/sketch/request/CallbackHandler.java index bdfaa6a2e9..9e9f7741e4 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/CallbackHandler.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/CallbackHandler.java @@ -59,7 +59,7 @@ public boolean handleMessage(Message msg) { break; case WHAT_CALLBACK_STARTED: - ((Listener) msg.obj).onStartLoad(); + ((Listener) msg.obj).onReadyLoad(); break; case WHAT_CALLBACK_FAILED: ((Listener) msg.obj).onError(ErrorCause.valueOf(msg.getData().getString(PARAM_FAILED_CAUSE))); @@ -124,7 +124,7 @@ static void postRunUpdateProgress(@NonNull AsyncRequest request, int totalLength static void postCallbackStarted(@Nullable Listener listener, boolean sync) { if (listener != null) { if (sync || SketchUtils.isMainThread()) { - listener.onStartLoad(); + listener.onReadyLoad(); } else { handler.obtainMessage(WHAT_CALLBACK_STARTED, listener).sendToTarget(); } diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java b/sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java index 30051e03fc..2fed420712 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/DisplayHelper.java @@ -620,7 +620,7 @@ protected void preProcess() { configuration.getOptionsFilterRegistry().filter(displayOptions); // 根据 URI 和显示选项生成请求 key - key = SketchUtils.makeRequestKey(uri, uriModel, displayOptions); + key = SketchUtils.makeRequestKey(uri, uriModel, displayOptions.makeKey()); } /** diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/DownloadHelper.java b/sketch/src/main/java/me/xiaopan/sketch/request/DownloadHelper.java index 06e5579682..2df566231a 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/DownloadHelper.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/DownloadHelper.java @@ -151,7 +151,7 @@ protected void preProcess() { sketch.getConfiguration().getOptionsFilterRegistry().filter(downloadOptions); // 根据 URI 和下载选项生成请求 key - key = SketchUtils.makeRequestKey(uri, uriModel, downloadOptions); + key = SketchUtils.makeRequestKey(uri, uriModel, downloadOptions.makeKey()); } private boolean checkDiskCache() { diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/DownloadOptions.java b/sketch/src/main/java/me/xiaopan/sketch/request/DownloadOptions.java index 66d7df0b07..3d7fcbb2c5 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/DownloadOptions.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/DownloadOptions.java @@ -36,11 +36,6 @@ public class DownloadOptions { */ private RequestLevel requestLevel; - /** - * 请求 Level 的来源 - */ - private RequestLevelFrom requestLevelFrom; - public DownloadOptions() { reset(); } @@ -87,7 +82,7 @@ public RequestLevel getRequestLevel() { /** * 设置请求Level * - * @param requestLevel {@link RequestLevelFrom} + * @param requestLevel {@link RequestLevel} * @return this * @see RequestLevel */ @@ -103,7 +98,6 @@ public DownloadOptions setRequestLevel(@Nullable RequestLevel requestLevel) { public void reset() { cacheInDiskDisabled = false; requestLevel = null; - requestLevelFrom = null; } /** @@ -116,7 +110,6 @@ public void copy(@Nullable DownloadOptions options) { cacheInDiskDisabled = options.cacheInDiskDisabled; requestLevel = options.requestLevel; - requestLevelFrom = options.requestLevelFrom; } /** @@ -126,8 +119,8 @@ public void copy(@Nullable DownloadOptions options) { * @see me.xiaopan.sketch.util.SketchUtils#makeRequestKey(String, UriModel, DownloadOptions) */ @NonNull - public StringBuilder makeKey(@NonNull StringBuilder builder) { - return builder; + public String makeKey() { + return ""; } /** @@ -136,7 +129,7 @@ public StringBuilder makeKey(@NonNull StringBuilder builder) { * @see me.xiaopan.sketch.util.SketchUtils#makeStateImageMemoryCacheKey(String, DownloadOptions) */ @NonNull - public StringBuilder makeStateImageKey(@NonNull StringBuilder builder) { - return builder; + public String makeStateImageKey() { + return ""; } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/Listener.java b/sketch/src/main/java/me/xiaopan/sketch/request/Listener.java index b207e48587..a4cbc8dc52 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/Listener.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/Listener.java @@ -19,23 +19,25 @@ import android.support.annotation.NonNull; /** - * 请求监听器,可监听开始、失败、取消 + * 请求监听器,可监听准备加载、失败、取消 */ public interface Listener { /** - * 只有进入异步线程才会回调此方法 + * 准备转入异步线程加载图片 */ - void onStartLoad(); + void onReadyLoad(); /** * 失败 * - * @param errorCause 失败原因 + * @param cause 原因 */ - void onError(@NonNull ErrorCause errorCause); + void onError(@NonNull ErrorCause cause); /** * 取消 + * + * @param cause 原因 */ - void onCanceled(@NonNull CancelCause cancelCause); + void onCanceled(@NonNull CancelCause cause); } diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java b/sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java index 0c566608b0..f7bf8f454b 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/LoadHelper.java @@ -316,7 +316,7 @@ protected void preProcess() { configuration.getOptionsFilterRegistry().filter(loadOptions); // 根据 URI 和加载选项生成请求 ID - key = SketchUtils.makeRequestKey(uri, uriModel, loadOptions); + key = SketchUtils.makeRequestKey(uri, uriModel, loadOptions.makeKey()); } private boolean checkRequestLevel() { diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java b/sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java index 79057bb378..4342b7eba2 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java +++ b/sketch/src/main/java/me/xiaopan/sketch/request/LoadOptions.java @@ -427,9 +427,8 @@ public void copy(@Nullable LoadOptions options) { @NonNull @Override - public StringBuilder makeKey(@NonNull StringBuilder builder) { - super.makeKey(builder); - + public String makeKey() { + StringBuilder builder = new StringBuilder(); if (maxSize != null) { builder.append("_").append(maxSize.getKey()); } @@ -461,14 +460,13 @@ public StringBuilder makeKey(@NonNull StringBuilder builder) { builder.append("_").append(processorKey); } } - return builder; + return builder.toString(); } @NonNull @Override - public StringBuilder makeStateImageKey(@NonNull StringBuilder builder) { - super.makeKey(builder); - + public String makeStateImageKey() { + StringBuilder builder = new StringBuilder(); if (resize != null) { builder.append("_").append(resize.getKey()); } @@ -482,6 +480,6 @@ public StringBuilder makeStateImageKey(@NonNull StringBuilder builder) { builder.append("_").append(processorKey); } } - return builder; + return builder.toString(); } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/request/RequestLevelFrom.java b/sketch/src/main/java/me/xiaopan/sketch/request/RequestLevelFrom.java deleted file mode 100644 index 27f17c20f8..0000000000 --- a/sketch/src/main/java/me/xiaopan/sketch/request/RequestLevelFrom.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2013 Peng fei Pan - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package me.xiaopan.sketch.request; - -public enum RequestLevelFrom { - PAUSE_LOAD, - PAUSE_DOWNLOAD, -} diff --git a/sketch/src/main/java/me/xiaopan/sketch/state/MakerStateImage.java b/sketch/src/main/java/me/xiaopan/sketch/state/MakerStateImage.java index 9856ec03eb..32b5ce6a3b 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/state/MakerStateImage.java +++ b/sketch/src/main/java/me/xiaopan/sketch/state/MakerStateImage.java @@ -36,13 +36,13 @@ import me.xiaopan.sketch.drawable.SketchRefBitmap; import me.xiaopan.sketch.drawable.SketchShapeBitmapDrawable; import me.xiaopan.sketch.process.ImageProcessor; -import me.xiaopan.sketch.process.ResizeImageProcessor; import me.xiaopan.sketch.request.DisplayOptions; import me.xiaopan.sketch.request.ImageFrom; import me.xiaopan.sketch.request.Resize; import me.xiaopan.sketch.request.ShapeSize; import me.xiaopan.sketch.shaper.ImageShaper; import me.xiaopan.sketch.uri.DrawableUriModel; +import me.xiaopan.sketch.uri.UriModel; import me.xiaopan.sketch.util.SketchUtils; /** @@ -88,7 +88,10 @@ private Drawable makeDrawable(Sketch sketch, DisplayOptions options) { } // 从内存缓存中取 - String memoryCacheKey = SketchUtils.makeStateImageMemoryCacheKey(String.valueOf(resId), options); + String imageUri = DrawableUriModel.makeUri(resId); + UriModel uriModel = configuration.getUriModelRegistry().match(imageUri); + @SuppressWarnings("ConstantConditions") + String memoryCacheKey = SketchUtils.makeRequestKey(imageUri, uriModel, options.makeStateImageKey()); MemoryCache memoryCache = configuration.getMemoryCache(); SketchRefBitmap cachedRefBitmap = memoryCache.get(memoryCacheKey); if (cachedRefBitmap != null) { @@ -119,9 +122,6 @@ private Drawable makeDrawable(Sketch sketch, DisplayOptions options) { //noinspection ConstantConditions if (processor == null && resize != null) { processor = sketch.getConfiguration().getResizeProcessor(); - if (processor == null) { - processor = new ResizeImageProcessor(); - } } Bitmap newBitmap; try { @@ -143,7 +143,7 @@ private Drawable makeDrawable(Sketch sketch, DisplayOptions options) { } // 新图片不能用说你处理部分出现异常了,直接返回null即可 - if (newBitmap == null || newBitmap.isRecycled()) { + if (newBitmap.isRecycled()) { return null; } diff --git a/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java b/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java index 7802dca3ba..9f0b8141d1 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java +++ b/sketch/src/main/java/me/xiaopan/sketch/util/SketchUtils.java @@ -44,6 +44,7 @@ import android.os.Looper; import android.os.StatFs; import android.os.storage.StorageManager; +import android.support.annotation.NonNull; import android.text.TextUtils; import android.text.format.Formatter; import android.view.ViewGroup; @@ -80,7 +81,6 @@ import me.xiaopan.sketch.drawable.SketchDrawable; import me.xiaopan.sketch.drawable.SketchLoadingDrawable; import me.xiaopan.sketch.request.DisplayRequest; -import me.xiaopan.sketch.request.DownloadOptions; import me.xiaopan.sketch.request.LoadRequest; import me.xiaopan.sketch.uri.UriModel; import me.xiaopan.sketch.viewfun.huge.Tile; @@ -1011,57 +1011,27 @@ public static void rotatePoint(PointF point, int rotateDegrees, Point drawableSi } /** - * 生成请求KEY + * 生成请求 KEY * - * @param imageUri 图片地址 - * @param uriModel {@link UriModel} - * @param options 选项 + * @param imageUri 图片地址 + * @param optionsKey 选项 KEY + * @see me.xiaopan.sketch.SketchImageView#getOptionsKey() */ - public static String makeRequestKey(String imageUri, UriModel uriModel, DownloadOptions options) { + @SuppressWarnings("unused") + @NonNull + public static String makeRequestKey(@NonNull String imageUri, @NonNull UriModel uriModel, @NonNull String optionsKey) { StringBuilder builder = new StringBuilder(); if (uriModel.isConvertShortUriForKey()) { builder.append(SketchMD5Utils.md5(imageUri)); } else { builder.append(imageUri); } - if (options != null) { - options.makeKey(builder); - } - return builder.toString(); - } - - /** - * 生成请求KEY - * - * @param imageUri 图片地址 - * @param optionsKey 选项KEY - * @see me.xiaopan.sketch.SketchImageView#getOptionsKey() - */ - @SuppressWarnings("unused") - public static String makeRequestKey(String imageUri, String optionsKey) { - StringBuilder builder = new StringBuilder(); - builder.append(imageUri); if (!TextUtils.isEmpty(optionsKey)) { builder.append(optionsKey); } return builder.toString(); } - /** - * 生成状态图片用的内存缓存KEY - * - * @param imageUri 图片地址 - * @param options 配置 - */ - public static String makeStateImageMemoryCacheKey(String imageUri, DownloadOptions options) { - StringBuilder builder = new StringBuilder(); - builder.append(imageUri); - if (options != null) { - options.makeStateImageKey(builder); - } - return builder.toString(); - } - /** * 将一个碎片列表转换成字符串 */ diff --git a/sketch/src/main/java/me/xiaopan/sketch/viewfun/DisplayListenerProxy.java b/sketch/src/main/java/me/xiaopan/sketch/viewfun/DisplayListenerProxy.java index 71beca9be2..7f0585992c 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/viewfun/DisplayListenerProxy.java +++ b/sketch/src/main/java/me/xiaopan/sketch/viewfun/DisplayListenerProxy.java @@ -35,19 +35,19 @@ public DisplayListenerProxy(FunctionCallbackView view) { } @Override - public void onStartLoad() { + public void onReadyLoad() { FunctionCallbackView view = viewWeakReference.get(); if (view == null) { return; } - boolean needInvokeInvalidate = view.getFunctions().onDisplayStarted(); + boolean needInvokeInvalidate = view.getFunctions().onDisplayReadyLoad(); if (needInvokeInvalidate) { view.invalidate(); } if (view.wrappedDisplayListener != null) { - view.wrappedDisplayListener.onStartLoad(); + view.wrappedDisplayListener.onReadyLoad(); } } @@ -69,36 +69,36 @@ public void onCompleted(@NonNull Drawable drawable, @NonNull ImageFrom imageFrom } @Override - public void onError(@NonNull ErrorCause errorCause) { + public void onError(@NonNull ErrorCause cause) { FunctionCallbackView view = viewWeakReference.get(); if (view == null) { return; } - boolean needInvokeInvalidate = view.getFunctions().onDisplayError(errorCause); + boolean needInvokeInvalidate = view.getFunctions().onDisplayError(cause); if (needInvokeInvalidate) { view.invalidate(); } if (view.wrappedDisplayListener != null) { - view.wrappedDisplayListener.onError(errorCause); + view.wrappedDisplayListener.onError(cause); } } @Override - public void onCanceled(@NonNull CancelCause cancelCause) { + public void onCanceled(@NonNull CancelCause cause) { FunctionCallbackView view = viewWeakReference.get(); if (view == null) { return; } - boolean needInvokeInvalidate = view.getFunctions().onDisplayCanceled(cancelCause); + boolean needInvokeInvalidate = view.getFunctions().onDisplayCanceled(cause); if (needInvokeInvalidate) { view.invalidate(); } if (view.wrappedDisplayListener != null) { - view.wrappedDisplayListener.onCanceled(cancelCause); + view.wrappedDisplayListener.onCanceled(cause); } } } diff --git a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunction.java b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunction.java index 0e2a4afe51..93650ccc86 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunction.java +++ b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunction.java @@ -79,7 +79,7 @@ public boolean onDetachedFromWindow() { /** * drawable改变 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onDrawableChanged(@NonNull String callPosition, @Nullable Drawable oldDrawable, @Nullable Drawable newDrawable) { return false; @@ -89,18 +89,18 @@ public boolean onDrawableChanged(@NonNull String callPosition, @Nullable Drawabl /** * 准备显示图片 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onReadyDisplay(@Nullable UriModel uriModel) { return false; } /** - * 开始显示图片 + * 准备加载图片 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ - public boolean onDisplayStartLoad() { + public boolean onDisplayReadyLoad() { return false; } @@ -109,7 +109,7 @@ public boolean onDisplayStartLoad() { * * @param totalLength 总长度 * @param completedLength 已完成长度 - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onUpdateDownloadProgress(int totalLength, int completedLength) { return false; @@ -118,7 +118,7 @@ public boolean onUpdateDownloadProgress(int totalLength, int completedLength) { /** * 显示完成 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onDisplayCompleted(@NonNull Drawable drawable, @NonNull ImageFrom imageFrom, @NonNull ImageAttrs imageAttrs) { return false; @@ -127,7 +127,7 @@ public boolean onDisplayCompleted(@NonNull Drawable drawable, @NonNull ImageFrom /** * 显示失败 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onDisplayError(@NonNull ErrorCause errorCause) { return false; @@ -136,7 +136,7 @@ public boolean onDisplayError(@NonNull ErrorCause errorCause) { /** * 显示取消 * - * @return 是否需要调用invalidate()刷新ImageView + * @return 是否需要调用 invalidate() 刷新 ImageView */ public boolean onDisplayCanceled(@NonNull CancelCause cancelCause) { return false; diff --git a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunctions.java b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunctions.java index 75dab0199f..ab56473671 100644 --- a/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunctions.java +++ b/sketch/src/main/java/me/xiaopan/sketch/viewfun/ViewFunctions.java @@ -368,42 +368,42 @@ boolean onReadyDisplay(@Nullable UriModel uriModel) { /** * @return true:需要调用invalidate()刷新view */ - boolean onDisplayStarted() { + boolean onDisplayReadyLoad() { boolean needInvokeInvalidate = false; if (showImageFromFunction != null) { //noinspection ConstantConditions - needInvokeInvalidate |= showImageFromFunction.onDisplayStartLoad(); + needInvokeInvalidate |= showImageFromFunction.onDisplayReadyLoad(); } if (showProgressFunction != null) { - needInvokeInvalidate |= showProgressFunction.onDisplayStartLoad(); + needInvokeInvalidate |= showProgressFunction.onDisplayReadyLoad(); } if (showGifFlagFunction != null) { - needInvokeInvalidate |= showGifFlagFunction.onDisplayStartLoad(); + needInvokeInvalidate |= showGifFlagFunction.onDisplayReadyLoad(); } if (showPressedFunction != null) { - needInvokeInvalidate |= showPressedFunction.onDisplayStartLoad(); + needInvokeInvalidate |= showPressedFunction.onDisplayReadyLoad(); } if (imageShapeFunction != null) { - needInvokeInvalidate |= imageShapeFunction.onDisplayStartLoad(); + needInvokeInvalidate |= imageShapeFunction.onDisplayReadyLoad(); } if (clickRetryFunction != null) { - needInvokeInvalidate |= clickRetryFunction.onDisplayStartLoad(); + needInvokeInvalidate |= clickRetryFunction.onDisplayReadyLoad(); } if (requestFunction != null) { - needInvokeInvalidate |= requestFunction.onDisplayStartLoad(); + needInvokeInvalidate |= requestFunction.onDisplayReadyLoad(); } if (recyclerCompatFunction != null) { - needInvokeInvalidate |= recyclerCompatFunction.onDisplayStartLoad(); + needInvokeInvalidate |= recyclerCompatFunction.onDisplayReadyLoad(); } if (zoomFunction != null) { - needInvokeInvalidate |= zoomFunction.onDisplayStartLoad(); + needInvokeInvalidate |= zoomFunction.onDisplayReadyLoad(); } if (hugeImageFunction != null) { - needInvokeInvalidate |= hugeImageFunction.onDisplayStartLoad(); + needInvokeInvalidate |= hugeImageFunction.onDisplayReadyLoad(); } if (clickPlayGifFunction != null) { - needInvokeInvalidate |= clickPlayGifFunction.onDisplayStartLoad(); + needInvokeInvalidate |= clickPlayGifFunction.onDisplayReadyLoad(); } return needInvokeInvalidate;