(3),
156 | new ThreadPoolExecutor.DiscardOldestPolicy()));
157 | }
158 |
159 | /**
160 | * 清除内存缓存数据
161 | */
162 | public static void clearMemoryCache() {
163 |
164 | try {
165 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
166 | imagePipeline.clearMemoryCaches();
167 | } catch (Throwable t) {
168 | }
169 | }
170 |
171 | /**
172 | * 清除磁盘缓存
173 | */
174 | public static void clearDiskCache() {
175 |
176 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
177 | imagePipeline.clearDiskCaches();
178 | }
179 |
180 | /**
181 | * 清除内存及磁盘缓存
182 | */
183 | public static void clearMemoryAndDiskCache() {
184 |
185 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
186 | imagePipeline.clearCaches();
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/fresco/FrescoImageView.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.fresco;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.net.Uri;
6 | import android.util.AttributeSet;
7 | import android.view.View;
8 |
9 | import com.facebook.common.internal.Supplier;
10 | import com.facebook.drawee.backends.pipeline.PipelineDraweeController;
11 | import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
12 | import com.facebook.drawee.controller.BaseControllerListener;
13 | import com.facebook.drawee.drawable.ProgressBarDrawable;
14 | import com.facebook.drawee.drawable.ScalingUtils;
15 | import com.facebook.drawee.generic.RoundingParams;
16 | import com.facebook.drawee.view.GenericDraweeView;
17 | import com.facebook.imagepipeline.common.ResizeOptions;
18 | import com.facebook.imagepipeline.request.ImageRequest;
19 | import com.facebook.imagepipeline.request.ImageRequestBuilder;
20 | import com.facebook.imagepipeline.request.Postprocessor;
21 | import com.zkteam.discover.util.TextUtil;
22 |
23 |
24 | /**
25 | * 自定义fresco图片加载框架视图
26 | *
27 | * Scaling 是一种画布操作,通常是由硬件加速的。图片实际大小保持不变,它只不过在绘制时被放大或缩小。
28 | * Resizing 是一种软件执行的管道操作。它返回一张新的,尺寸不同的图片。
29 | * Downsampling 同样是软件实现的管道操作。它不是创建一张新的图片,而是在解码时改变图片的大小。
30 | */
31 | public class FrescoImageView extends GenericDraweeView {
32 |
33 | private static Supplier extends PipelineDraweeControllerBuilder> sDraweeControllerBuilderSupplier;
34 | private PipelineDraweeControllerBuilder mDraweeControllerBuilder;
35 |
36 | private BaseControllerListener mBaseControllerListener;//临听下载
37 | private Postprocessor mPostprocessor;//后置处理图片
38 | private boolean mSmallCache;//是否为小图,缓存至小图目录
39 | private static boolean mGlobalWindowVisibilityChangedAutoDetachEnable;
40 | private boolean mWindowVisibilityChangedAutoDetachEnable;
41 | private boolean mAutoDetached;
42 |
43 | public FrescoImageView(Context context) {
44 |
45 | super(context);
46 | init();
47 | }
48 |
49 | public FrescoImageView(Context context, AttributeSet attrs) {
50 |
51 | super(context, attrs);
52 | init();
53 | }
54 |
55 | public FrescoImageView(Context context, AttributeSet attrs, int defStyle) {
56 |
57 | super(context, attrs, defStyle);
58 | init();
59 | }
60 |
61 | private void init() {
62 |
63 | mDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
64 | }
65 |
66 | public static void initialize(Supplier extends PipelineDraweeControllerBuilder> draweeControllerBuilderSupplier) {
67 |
68 | sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier;
69 | }
70 |
71 | public static void shutDown() {
72 |
73 | sDraweeControllerBuilderSupplier = null;
74 | }
75 |
76 | public static void setGlobalOnWindowVisibilityChangedAutoDetachEnable(boolean enable){
77 |
78 | mGlobalWindowVisibilityChangedAutoDetachEnable = enable;
79 | }
80 |
81 | //###################### 初始化配置项 #########################
82 |
83 |
84 | /**
85 | * 默认占位图
86 | *
87 | * @param resourceId
88 | */
89 | public void setDefPlaceholderImage(int resourceId) {
90 |
91 | getHierarchy().setPlaceholderImage(resourceId);
92 | }
93 |
94 | /**
95 | * 失败默认占位图
96 | *
97 | * @param resourceId
98 | */
99 | public void setDefFailureImage(int resourceId) {
100 |
101 | getHierarchy().setFailureImage(resourceId);
102 | }
103 |
104 | /**
105 | * 设置圆角
106 | *
107 | * @param roundingParams
108 | */
109 | public void setRoundingParams(RoundingParams roundingParams) {
110 |
111 | getHierarchy().setRoundingParams(roundingParams);
112 | }
113 |
114 | /**
115 | * 设置fadeIn 效果
116 | *
117 | * @param fadeIn
118 | */
119 | public void setFadeIn(boolean fadeIn) {
120 |
121 | if (!fadeIn)
122 | getHierarchy().setFadeDuration(0);
123 | }
124 |
125 | /**
126 | * fresco 支持的缩放模式不支持ImageView ScaleType。
127 | *
128 | * @param scaleType
129 | */
130 | public void setFrescoScaleType(ScalingUtils.ScaleType scaleType) {
131 |
132 | getHierarchy().setActualImageScaleType(scaleType);
133 | }
134 |
135 | public ScalingUtils.ScaleType getFrescoScaleType() {
136 |
137 | return getHierarchy().getActualImageScaleType();
138 | }
139 |
140 | /**
141 | * 监听下载
142 | *
143 | * @param baseControllerListener 下载成功回调 onIntermediateImageSet,失败回调 onFailure
144 | */
145 | public void setBaseControllerListener(BaseControllerListener baseControllerListener) {
146 |
147 | mBaseControllerListener = baseControllerListener;
148 | }
149 |
150 | /**
151 | * 图片下载完成后图片处理
152 | *
153 | * @param postprocessor 下载加载完成后图片处理:process方法处理bitmap
154 | */
155 | public void setPostprocessor(Postprocessor postprocessor) {
156 |
157 | mPostprocessor = postprocessor;
158 | }
159 |
160 | /**
161 | * 设置加载图片进度显示效果
162 | *
163 | * @param progressBarDrawable
164 | */
165 | public void setProgressBarDrawable(ProgressBarDrawable progressBarDrawable) {
166 |
167 | getHierarchy().setProgressBarImage(progressBarDrawable);
168 | }
169 |
170 | public void setSmallCache() {
171 |
172 | mSmallCache = true;
173 | }
174 |
175 | public void setWindowVisibilityChangedAutoDetach(boolean autoDetach){
176 |
177 | mWindowVisibilityChangedAutoDetachEnable = autoDetach;
178 | }
179 |
180 | //###################### 图片加载项 #########################
181 |
182 | /* 加载res本地图片 */
183 | public void setImageResId(int resourceId) {
184 |
185 | setImageResIdResize(resourceId, null);
186 | }
187 |
188 | public void setImageResIdByLp(int resourceId) {
189 |
190 | if (getLayoutParams().width > 0 && getLayoutParams().height > 0)
191 | setImageResIdResize(resourceId, new ResizeOptions(getLayoutParams().width, getLayoutParams().height));
192 | else
193 | setImageResIdResize(resourceId, null);
194 | }
195 |
196 | public void setImageResIdResize(int resourceId, ResizeOptions resizeOptions) {
197 |
198 | setImageUriResize(Uri.parse("res:///" + resourceId), resizeOptions);
199 | }
200 |
201 | /* 加载图片url or sdcard图片路径 */
202 | public void setImageUri(String imageUri) {
203 |
204 | setImageUriResize(imageUri, null);
205 | }
206 |
207 | public void setImageUriByLp(String imageUri) {
208 |
209 | if (getLayoutParams().width > 0 && getLayoutParams().height > 0)
210 | setImageUriResize(imageUri, new ResizeOptions(getLayoutParams().width, getLayoutParams().height));
211 | else
212 | setImageUriResize(imageUri, null);
213 | }
214 |
215 | public void setImageUriResize(String imageUri, ResizeOptions resizeOptions) {
216 |
217 | imageUri = TextUtil.filterNull(imageUri);
218 |
219 | if (imageUri.startsWith("/"))
220 | imageUri = "file://" + imageUri;
221 |
222 | setImageUriResize(Uri.parse(imageUri), resizeOptions);
223 | }
224 |
225 | /* 加载uri图片 */
226 | public void setImageUri(Uri uri) {
227 |
228 | if (uri == null)
229 | uri = Uri.parse(TextUtil.TEXT_EMPTY);
230 |
231 | setImageUriResize(uri, null);
232 | }
233 |
234 | public void setImageUriByLp(Uri uri) {
235 |
236 | if (uri == null)
237 | uri = Uri.parse(TextUtil.TEXT_EMPTY);
238 |
239 | setImageUriResize(uri, new ResizeOptions(getLayoutParams().width, getLayoutParams().height));
240 | }
241 |
242 | public void setImageUriResize(Uri uri, ResizeOptions resizeOptions) {
243 |
244 | if (uri == null)
245 | uri = Uri.parse(TextUtil.TEXT_EMPTY);
246 |
247 | loadImageUri(uri, resizeOptions);
248 | }
249 |
250 | /**
251 | * 通过uri加载图片
252 | *
253 | * @param uri
254 | * @param resizeOptions
255 | */
256 | private void loadImageUri(final Uri uri, ResizeOptions resizeOptions) {
257 |
258 | ImageRequest request;
259 | if (mSmallCache) {
260 |
261 | request = ImageRequestBuilder.newBuilderWithSource(uri)
262 | .setResizeOptions(resizeOptions)//Resize 并不改变原始图片,它只在解码前修改内存中的图片大小
263 | .setPostprocessor(mPostprocessor)
264 | .setCacheChoice(ImageRequest.CacheChoice.SMALL)
265 | .build();
266 | } else {
267 |
268 | request = ImageRequestBuilder.newBuilderWithSource(uri)
269 | .setResizeOptions(resizeOptions)
270 | .setPostprocessor(mPostprocessor)
271 | .build();
272 | }
273 |
274 | PipelineDraweeController controller = (PipelineDraweeController) mDraweeControllerBuilder.setControllerListener(mBaseControllerListener)
275 | .setUri(uri)
276 | .setImageRequest(request)
277 | .setTapToRetryEnabled(false)
278 | .setOldController(getController()) // 列表滚动 优化
279 | .setAutoPlayAnimations(true) //自动播放gif动画
280 | .build();
281 |
282 | setController(controller);
283 | }
284 |
285 | @Override
286 | protected void onWindowVisibilityChanged(int visibility) {
287 |
288 | super.onWindowVisibilityChanged(visibility);
289 |
290 | if (View.VISIBLE == visibility) {
291 |
292 | if(mAutoDetached){
293 |
294 | doAttach();
295 | mAutoDetached = false;
296 | }
297 |
298 | } else {
299 |
300 | if (mWindowVisibilityChangedAutoDetachEnable ||
301 | mGlobalWindowVisibilityChangedAutoDetachEnable) {
302 |
303 | doDetach();
304 | mAutoDetached = true;
305 | }
306 | }
307 | }
308 |
309 | public void attachImage(){
310 |
311 | doAttach();
312 | }
313 |
314 | public void detachImage(){
315 |
316 | doDetach();
317 | }
318 |
319 | @Override
320 | protected void onDraw(Canvas canvas) {
321 |
322 | try {
323 | super.onDraw(canvas);
324 | } catch (Exception e) {
325 | }
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/fresco/FrescoInitUtil.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.fresco;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.os.Build;
6 |
7 | import com.facebook.cache.disk.DiskCacheConfig;
8 | import com.facebook.common.disk.NoOpDiskTrimmableRegistry;
9 | import com.facebook.common.internal.Supplier;
10 | import com.facebook.common.memory.NoOpMemoryTrimmableRegistry;
11 | import com.facebook.common.util.ByteConstants;
12 | import com.facebook.imagepipeline.cache.DefaultCacheKeyFactory;
13 | import com.facebook.imagepipeline.cache.MemoryCacheParams;
14 | import com.facebook.imagepipeline.core.ImagePipelineConfig;
15 | import com.zkteam.discover.app.BaseApp;
16 | import com.zkteam.discover.util.StorageUtil;
17 |
18 | public class FrescoInitUtil {
19 |
20 | //分配的可用内存
21 | private static final int MAX_HEAP_SIZE = (int) Runtime.getRuntime().maxMemory();
22 |
23 | //使用的缓存数量
24 | private static final int MAX_MEMORY_CACHE_SIZE = MAX_HEAP_SIZE / 4;
25 |
26 | //默认图所放路径的文件夹名
27 | private static final String IMAGE_CACHE_DEFAULT_DIR = "frescoCacheDefault";
28 |
29 | //默认图磁盘缓存的最大值
30 | private static final int MAX_DISK_CACHE_SIZE = 80 * ByteConstants.MB;
31 |
32 | //默认图低磁盘空间缓存的最大值
33 | private static final int MAX_DISK_CACHE_LOW_SIZE = 50 * ByteConstants.MB;
34 |
35 | //默认图极低磁盘空间缓存的最大值
36 | private static final int MAX_DISK_CACHE_VERYLOW_SIZE = 30 * ByteConstants.MB;
37 |
38 | //小图所放路径的文件夹名
39 | private static final String IMAGE_CACHE_SMALL_DIR = "frescoCacheSmall";
40 |
41 | //小图低磁盘空间缓存的最大值
42 | //如何区分小图片需要在ImageRequestBuilder手动调用setCacheChoice(ImageRequest.CacheChoice.SMALL)指定
43 | private static final int MAX_SMALL_DISK_LOW_CACHE_SIZE = 20 * ByteConstants.MB;
44 |
45 | //小图极低磁盘空间缓存的最大值(特性:可将大量的小图放到额外放在另一个磁盘空间防止大图占用磁盘空间而删除了大量的小图)
46 | private static final int MAX_SMALL_DISK_VERYLOW_CACHE_SIZE = 10 * ByteConstants.MB;
47 |
48 | private static final int MAX_CACHE_ENTRIES = Integer.MAX_VALUE;
49 | // private static final int MAX_EVICTION_QUEUE_SIZE = 6*ByteConstants.MB;//Integer.MAX_VALUE;
50 | private static final int MAX_EVICTION_QUEUE_ENTRIES = 128;//Integer.MAX_VALUE;
51 | private static final int MAX_CACHE_ENTRY_SIZE = Integer.MAX_VALUE;
52 |
53 | private static ActivityManager mActivityManager;
54 |
55 | public static void initFrescoConfig() {
56 |
57 | ImagePipelineConfig pipeConfig = ImagePipelineConfig.newBuilder(BaseApp.getContext())
58 | .setBitmapMemoryCacheParamsSupplier(new BitmapCacheParamsSupplier())//bitmap内存缓存数据的策略
59 | .setCacheKeyFactory(DefaultCacheKeyFactory.getInstance())//缓存键值对的获取 参考DefaultCacheKeyFactory进行修改缓存key
60 | .setDownsampleEnabled(true)//缩放图片 同样是软件实现的管道操作。它不是创建一张新的图片,而是在解码时改变图片的大小。
61 | .setEncodedMemoryCacheParamsSupplier(new EncodedMemoryCacheParamsSupplier())//未解码(原始压缩格式的图片)内存设置
62 | //设置本地读写线程池,网络数据线程池 解码线程池,以及后台线程池. DefaultExecutorSupplier默认设置
63 | //.setExecutorSupplier(executorSupplier)
64 | //NoOpImageCacheStatsTracker 追踪图片缓存相关状态
65 | //.setImageCacheStatsTracker(imageCacheStatsTracker)
66 | //设置磁盘缓存项配置
67 | .setMainDiskCacheConfig(getDisCacheConfigDefault(BaseApp.getContext()))
68 | .setSmallImageDiskCacheConfig(getDisCacheConfigSmall(BaseApp.getContext()))
69 | //设置管理池,默认为PoolFactory 存放了相应的bitmap,nativememory,shareByte数据池管理
70 | //.setPoolFactory(poolFactory)
71 | //渐进式JPEG配置 SimpleProgressiveJpegConfig
72 | //.setProgressiveJpegConfig(progressiveJpegConfig)
73 | //设置请求监听集合
74 | //.setRequestListeners(requestListeners)
75 | .setMemoryTrimmableRegistry(NoOpMemoryTrimmableRegistry.getInstance())
76 | .build();
77 |
78 | FrescoIniter.initialize(BaseApp.getContext(), pipeConfig);
79 |
80 | }
81 |
82 | public static DiskCacheConfig getDisCacheConfigDefault(final Context context) {
83 |
84 | return DiskCacheConfig.newBuilder(context).setBaseDirectoryPath(StorageUtil.getAppPicDir())
85 | .setBaseDirectoryName(IMAGE_CACHE_DEFAULT_DIR)//文件夹名
86 | .setMaxCacheSize(MAX_DISK_CACHE_SIZE)//默认缓存的最大大小。
87 | .setMaxCacheSizeOnLowDiskSpace(MAX_DISK_CACHE_LOW_SIZE)//缓存的最大大小,使用设备时低磁盘空间。
88 | .setMaxCacheSizeOnVeryLowDiskSpace(MAX_DISK_CACHE_VERYLOW_SIZE)//缓存的最大大小,当设备极低磁盘空间
89 | .setDiskTrimmableRegistry(NoOpDiskTrimmableRegistry.getInstance())
90 | .build();
91 | }
92 |
93 | public static DiskCacheConfig getDisCacheConfigSmall(final Context context) {
94 |
95 | return DiskCacheConfig.newBuilder(context).setBaseDirectoryPath(StorageUtil.getAppPicDir())
96 | .setBaseDirectoryName(IMAGE_CACHE_SMALL_DIR)//文件夹名
97 | .setMaxCacheSize(MAX_DISK_CACHE_SIZE)//默认缓存的最大大小。
98 | .setMaxCacheSizeOnLowDiskSpace(MAX_SMALL_DISK_LOW_CACHE_SIZE)//缓存的最大大小,使用设备时低磁盘空间。
99 | .setMaxCacheSizeOnVeryLowDiskSpace(MAX_SMALL_DISK_VERYLOW_CACHE_SIZE)//缓存的最大大小,当设备极低磁盘空间
100 | .setDiskTrimmableRegistry(NoOpDiskTrimmableRegistry.getInstance())
101 | .build();
102 | }
103 |
104 | public static class BitmapCacheParamsSupplier implements Supplier {
105 |
106 | @Override
107 | public MemoryCacheParams get() {
108 |
109 | mActivityManager = (ActivityManager) BaseApp.getContext().getSystemService(Context.ACTIVITY_SERVICE);
110 |
111 | final int maxCacheSize = getMaxCacheSize();
112 | final int maxCacheEntrySize = maxCacheSize / 2;
113 |
114 | return new MemoryCacheParams(
115 | maxCacheSize, // 内存缓存中总图片的最大大小,以字节为单位。
116 | MAX_CACHE_ENTRIES, // 内存缓存中图片的最大数量
117 | maxCacheSize / 2, // 内存缓存待回收 但尚未被回收的最大大小,以字节为单位。
118 | MAX_EVICTION_QUEUE_ENTRIES,//// 内存缓存待回收图片的最大数量
119 | maxCacheEntrySize);// 内存缓存中单个图片的最大大小
120 | }
121 |
122 | private int getMaxCacheSize() {
123 |
124 | try {
125 |
126 | final int maxMemory =
127 | Math.min(mActivityManager.getMemoryClass() * ByteConstants.MB, Integer.MAX_VALUE);
128 | if (maxMemory < 32 * ByteConstants.MB) {
129 | return 4 * ByteConstants.MB;
130 | } else if (maxMemory < 64 * ByteConstants.MB) {
131 | return 6 * ByteConstants.MB;
132 | } else {
133 | // We don't want to use more ashmem on Gingerbread for now, since it doesn't respond well to
134 | // native memory pressure (doesn't throw exceptions, crashes app, crashes phone)
135 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
136 | return 8 * ByteConstants.MB;
137 | } else {
138 | return maxMemory / 2;
139 | }
140 | }
141 | } catch (Exception e) {
142 |
143 | e.printStackTrace();
144 | }
145 | return 8 * ByteConstants.MB;
146 | }
147 | }
148 |
149 | public static class EncodedMemoryCacheParamsSupplier implements Supplier {
150 |
151 | private static final int MAX_CACHE_ENTRIES = Integer.MAX_VALUE;
152 | private static final int MAX_EVICTION_QUEUE_ENTRIES = MAX_CACHE_ENTRIES;
153 |
154 | @Override
155 | public MemoryCacheParams get() {
156 |
157 | final int maxCacheSize = getMaxCacheSize();
158 | final int maxCacheEntrySize = maxCacheSize / 4;
159 |
160 | return new MemoryCacheParams(
161 | maxCacheSize,
162 | MAX_CACHE_ENTRIES,
163 | maxCacheSize,
164 | MAX_EVICTION_QUEUE_ENTRIES,
165 | maxCacheEntrySize);
166 | }
167 |
168 | private int getMaxCacheSize() {
169 |
170 | final int maxMemory = (int) Math.min(Runtime.getRuntime().maxMemory(), Integer.MAX_VALUE);
171 |
172 | if (maxMemory < 16 * ByteConstants.MB) {
173 | return 1 * ByteConstants.MB;
174 | } else if (maxMemory < 32 * ByteConstants.MB) {
175 | return 2 * ByteConstants.MB;
176 | } else {
177 | return 4 * ByteConstants.MB;
178 | }
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/fresco/FrescoIniter.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.fresco;
2 |
3 | import android.content.Context;
4 |
5 | import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilder;
6 | import com.facebook.drawee.backends.pipeline.PipelineDraweeControllerBuilderSupplier;
7 | import com.facebook.imagepipeline.core.ImagePipeline;
8 | import com.facebook.imagepipeline.core.ImagePipelineConfig;
9 | import com.facebook.imagepipeline.core.ImagePipelineFactory;
10 |
11 | /**
12 | * Fresco 初始化类
13 | */
14 | public class FrescoIniter {
15 |
16 | private static PipelineDraweeControllerBuilderSupplier sDraweeControllerBuilderSupplier;
17 |
18 | public static void initialize(Context context) {
19 |
20 | ImagePipelineFactory.initialize(context);
21 | initializeDrawee(context);
22 | }
23 |
24 | public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) {
25 |
26 | ImagePipelineFactory.initialize(imagePipelineConfig);
27 | initializeDrawee(context);
28 | }
29 |
30 | private static void initializeDrawee(Context context) {
31 |
32 | sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
33 | FrescoImageView.initialize(sDraweeControllerBuilderSupplier);
34 | }
35 |
36 | public static PipelineDraweeControllerBuilderSupplier getDraweeControllerBuilderSupplier() {
37 |
38 | return sDraweeControllerBuilderSupplier;
39 | }
40 |
41 | public static PipelineDraweeControllerBuilder newDraweeControllerBuilder() {
42 |
43 | return sDraweeControllerBuilderSupplier.get();
44 | }
45 |
46 | public static ImagePipelineFactory getImagePipelineFactory() {
47 |
48 | return ImagePipelineFactory.getInstance();
49 | }
50 |
51 | public static ImagePipeline getImagePipeline() {
52 |
53 | return getImagePipelineFactory().getImagePipeline();
54 | }
55 |
56 | /**
57 | * 此方法不需要调用,因在应用当中只初始化一次,如调用之后必须得重调用initialize 进行初始化
58 | */
59 | public static void shutDown() {
60 |
61 | sDraweeControllerBuilderSupplier = null;
62 | FrescoImageView.shutDown();
63 | ImagePipelineFactory.shutDown();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/manager/LinearSmoothScroller.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.manager;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.RecyclerView.LayoutManager;
7 | import android.support.v7.widget.RecyclerView.LayoutParams;
8 | import android.support.v7.widget.RecyclerView.SmoothScroller;
9 | import android.support.v7.widget.RecyclerView.State;
10 | import android.util.DisplayMetrics;
11 | import android.view.View;
12 | import android.view.animation.DecelerateInterpolator;
13 | import android.view.animation.LinearInterpolator;
14 |
15 | /**
16 | * ===========================================================
17 | * 作 者:大印(高印) Github地址:https://github.com/GaoYin2016
18 | * 邮 箱:18810474975@163.com
19 | * 版 本:
20 | * 创建日期:2018/3/12 下午8:36
21 | * 描 述: 自定义SmoothScroller 控制RecyclerView 滚动速度,滚动时间
22 | * 修订历史:
23 | * ===========================================================
24 | */
25 | public class LinearSmoothScroller extends SmoothScroller {
26 |
27 | private static final float MILLISECONDS_PER_INCH = 25.0f;
28 | public static final int SNAP_TO_ANY = 0;
29 | public static final int SNAP_TO_END = 1;
30 | public static final int SNAP_TO_START = -1;
31 | private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f;
32 | private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
33 | private final float MILLISECONDS_PER_PX;
34 | protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
35 | protected int mInterimTargetDx = SNAP_TO_ANY;
36 | protected int mInterimTargetDy = SNAP_TO_ANY;
37 | protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
38 | protected PointF mTargetVector;
39 |
40 | public LinearSmoothScroller(Context context) {
41 | this.MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
42 | }
43 |
44 | protected void onStart() {
45 | }
46 |
47 | protected void onTargetFound(View targetView, State state, Action action) {
48 | int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
49 | int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
50 | int time = calculateTimeForDeceleration((int) Math.sqrt((double) ((dx * dx) + (dy * dy))));
51 | if (time > 0) {
52 | action.update(-dx, -dy, time, this.mDecelerateInterpolator);
53 | }
54 | }
55 |
56 | protected void onSeekTargetStep(int dx, int dy, State state, Action action) {
57 | if (getChildCount() == 0) {
58 | stop();
59 | return;
60 | }
61 | this.mInterimTargetDx = clampApplyScroll(this.mInterimTargetDx, dx);
62 | this.mInterimTargetDy = clampApplyScroll(this.mInterimTargetDy, dy);
63 | if (this.mInterimTargetDx == 0 && this.mInterimTargetDy == 0) {
64 | updateActionForInterimTarget(action);
65 | }
66 | }
67 |
68 | protected void onStop() {
69 | this.mInterimTargetDy = SNAP_TO_ANY;
70 | this.mInterimTargetDx = SNAP_TO_ANY;
71 | this.mTargetVector = null;
72 | }
73 |
74 | /**
75 | * 计算滚动时长
76 | *
77 | * @param displayMetrics
78 | * @return
79 | */
80 | protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
81 | return MILLISECONDS_PER_INCH / ((float) displayMetrics.densityDpi);
82 | }
83 |
84 | protected int calculateTimeForDeceleration(int dx) {
85 | return (int) Math.ceil(((double) calculateTimeForScrolling(dx)) / 0.3356d);
86 | }
87 |
88 | protected int calculateTimeForScrolling(int dx) {
89 | return (int) Math.ceil((double) (((float) Math.abs(dx)) * this.MILLISECONDS_PER_PX));
90 | }
91 |
92 | protected int getHorizontalSnapPreference() {
93 | if (this.mTargetVector == null || this.mTargetVector.x == 0.0f) {
94 | return SNAP_TO_ANY;
95 | }
96 | return this.mTargetVector.x > 0.0f ? SNAP_TO_END : SNAP_TO_START;
97 | }
98 |
99 | protected int getVerticalSnapPreference() {
100 | if (this.mTargetVector == null || this.mTargetVector.y == 0.0f) {
101 | return SNAP_TO_ANY;
102 | }
103 | return this.mTargetVector.y > 0.0f ? SNAP_TO_END : SNAP_TO_START;
104 | }
105 |
106 | protected void updateActionForInterimTarget(Action action) {
107 | PointF scrollVector = computeScrollVectorForPosition(getTargetPosition());
108 | if (scrollVector == null || (scrollVector.x == 0.0f && scrollVector.y == 0.0f)) {
109 | action.jumpTo(getTargetPosition());
110 | stop();
111 | return;
112 | }
113 | normalize(scrollVector);
114 | this.mTargetVector = scrollVector;
115 | this.mInterimTargetDx = (int) (scrollVector.x * 10000.0f);
116 | this.mInterimTargetDy = (int) (scrollVector.y * 10000.0f);
117 | action.update((int) (((float) this.mInterimTargetDx) * TARGET_SEEK_EXTRA_SCROLL_RATIO), (int) (((float) this.mInterimTargetDy) * TARGET_SEEK_EXTRA_SCROLL_RATIO), (int) (((float) calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX)) * TARGET_SEEK_EXTRA_SCROLL_RATIO), this.mLinearInterpolator);
118 | }
119 |
120 | private int clampApplyScroll(int tmpDt, int dt) {
121 | int before = tmpDt;
122 | tmpDt -= dt;
123 | if (before * tmpDt <= 0) {
124 | return SNAP_TO_ANY;
125 | }
126 | return tmpDt;
127 | }
128 |
129 | public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
130 | switch (snapPreference) {
131 | case SNAP_TO_START /*-1*/:
132 | return boxStart - viewStart;
133 | case SNAP_TO_ANY /*0*/:
134 | int dtStart = boxStart - viewStart;
135 | if (dtStart > 0) {
136 | return dtStart;
137 | }
138 | int dtEnd = boxEnd - viewEnd;
139 | if (dtEnd < 0) {
140 | return dtEnd;
141 | }
142 | return SNAP_TO_ANY;
143 | case SNAP_TO_END /*1*/:
144 | return boxEnd - viewEnd;
145 | default:
146 | throw new IllegalArgumentException("snap preference should be one of the constants defined in SmoothScroller, starting with SNAP_");
147 | }
148 | }
149 |
150 | public int calculateDyToMakeVisible(View view, int snapPreference) {
151 | LayoutManager layoutManager = getLayoutManager();
152 | if (layoutManager == null || !layoutManager.canScrollVertically()) {
153 | return SNAP_TO_ANY;
154 | }
155 | LayoutParams params = (LayoutParams) view.getLayoutParams();
156 | return calculateDtToFit(layoutManager.getDecoratedTop(view) - params.topMargin, layoutManager.getDecoratedBottom(view) + params.bottomMargin, layoutManager.getPaddingTop(), layoutManager.getHeight() - layoutManager.getPaddingBottom(), snapPreference);
157 | }
158 |
159 | public int calculateDxToMakeVisible(View view, int snapPreference) {
160 | LayoutManager layoutManager = getLayoutManager();
161 | if (layoutManager == null || !layoutManager.canScrollHorizontally()) {
162 | return SNAP_TO_ANY;
163 | }
164 | LayoutParams params = (LayoutParams) view.getLayoutParams();
165 | return calculateDtToFit(layoutManager.getDecoratedLeft(view) - params.leftMargin, layoutManager.getDecoratedRight(view) + params.rightMargin, layoutManager.getPaddingLeft(), layoutManager.getWidth() - layoutManager.getPaddingRight(), snapPreference);
166 | }
167 |
168 | @Nullable
169 | public PointF computeScrollVectorForPosition(int targetPosition) {
170 | LayoutManager layoutManager = getLayoutManager();
171 | if (layoutManager instanceof ScrollVectorProvider) {
172 | return ((ScrollVectorProvider) layoutManager).computeScrollVectorForPosition(targetPosition);
173 | }
174 | return null;
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/manager/TopSnappedLayoutManager.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.manager;
2 |
3 | import android.content.Context;
4 | import android.graphics.PointF;
5 | import android.support.v7.widget.GridLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.support.v7.widget.RecyclerView.SmoothScroller;
8 | import android.support.v7.widget.RecyclerView.State;
9 | import android.util.AttributeSet;
10 |
11 | /**
12 | * ===========================================================
13 | * 作 者:大印(高印) Github地址:https://github.com/GaoYin2016
14 | * 邮 箱:18810474975@163.com
15 | * 版 本:
16 | * 创建日期:2018/3/12 下午12:37
17 | * 描 述: 重写 smoothScrollToPosition 滚动置顶
18 | * 修订历史:
19 | * ===========================================================
20 | */
21 | public class TopSnappedLayoutManager extends GridLayoutManager {
22 |
23 | private class TopSnappedSmoothScroller extends LinearSmoothScroller {
24 | public TopSnappedSmoothScroller(Context context) {
25 | super(context);
26 | }
27 |
28 | public PointF computeScrollVectorForPosition(int targetPosition) {
29 |
30 | return TopSnappedLayoutManager.this.computeScrollVectorForPosition(targetPosition);
31 | }
32 |
33 | protected int getVerticalSnapPreference() {
34 |
35 | return -1;
36 | }
37 | }
38 |
39 | public TopSnappedLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
40 | super(context, attrs, defStyleAttr, defStyleRes);
41 | }
42 |
43 | public TopSnappedLayoutManager(Context context, int spanCount) {
44 | super(context, spanCount);
45 | }
46 |
47 | public TopSnappedLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
48 | super(context, spanCount, orientation, reverseLayout);
49 | }
50 |
51 | public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) {
52 | SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
53 | smoothScroller.setTargetPosition(position);
54 | startSmoothScroll(smoothScroller);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zkteam/discover/util/CollectionUtil.java:
--------------------------------------------------------------------------------
1 | package com.zkteam.discover.util;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * 集合工具类
10 | */
11 | public class CollectionUtil {
12 |
13 | /**
14 | * 判断集合是否为空
15 | *
16 | * @param collection
17 | * @return
18 | */
19 | public static boolean isEmpty(Collection> collection) {
20 |
21 | return collection == null || collection.isEmpty();
22 | }
23 |
24 | /**
25 | * 返回集合的大小,null集合返回0
26 | *
27 | * @param collection
28 | * @return
29 | */
30 | public static int size(Collection> collection) {
31 |
32 | return collection == null ? 0 : collection.size();
33 | }
34 |
35 | /**
36 | * 返回map大小
37 | * @param map
38 | * @return
39 | */
40 | public static int size(Map map){
41 |
42 | return map == null ? 0 : map.size();
43 | }
44 |
45 | /**
46 | * 判断map是否为空
47 | * @param map
48 | * @return
49 | */
50 | public static boolean isEmpty(Map map){
51 |
52 | return map == null ? true : map.isEmpty();
53 | }
54 |
55 | /**
56 | * 判断position是否在集合长度范围内
57 | *
58 | * @param collection
59 | * @param position
60 | * @return
61 | */
62 | public static boolean checkPosition(Collection> collection, int position) {
63 |
64 | return position >= 0 && position < size(collection);
65 | }
66 |
67 | /**
68 | * 获取过滤后的position
69 | *
70 | * @param collection
71 | * @param position
72 | * @return
73 | */
74 | public static int filterPosition(Collection> collection, int position) {
75 |
76 | if (isEmpty(collection))
77 | return 0;
78 |
79 | if (position < 0) {
80 | position = 0;
81 | } else if (position >= collection.size()) {
82 | position = collection.size() - 1;
83 | }
84 |
85 | return position;
86 | }
87 |
88 | /**
89 | * 做下标越界判断,如果越界返回null
90 | *
91 | * @param list
92 | * @param position
93 | * @param
94 | * @return
95 | */
96 | public static T getItem(List list, int position) {
97 |
98 | return checkPosition(list, position) ? list.get(position) : null;
99 | }
100 |
101 | /**
102 | * 获取集合最后一个元素
103 | *
104 | * @param list
105 | * @param
106 | * @return
107 | */
108 | public static T getLastItem(List list) {
109 |
110 | return getItem(list, size(list) - 1);
111 | }
112 |
113 | /**
114 | * 清空集合
115 | *
116 | * @param list
117 | * @param
118 | */
119 | public static void clear(List list) {
120 |
121 | if (!isEmpty(list))
122 | list.clear();
123 | }
124 |
125 | /**
126 | * 移除item
127 | *
128 | * @param list
129 | * @param item
130 | * @param
131 | * @return
132 | */
133 | public static boolean remove(List list, T item) {
134 |
135 | if (!isEmpty(list) && item != null)
136 | return list.remove(item);
137 | else
138 | return false;
139 | }
140 |
141 | /**
142 | * 移除item
143 | *
144 | * @param list
145 | * @param position
146 | * @param
147 | */
148 | public static boolean remove(List list, int position) {
149 |
150 | if (list != null && checkPosition(list, position)) {
151 |
152 | list.remove(position);
153 | return true;
154 | } else {
155 |
156 | return false;
157 | }
158 | }
159 |
160 | /**
161 | * 移除指定子集
162 | *
163 | * @param srcList
164 | * @param removeList
165 | * @param
166 | */
167 | public static void removeAll(List srcList, List removeList) {
168 |
169 | if (CollectionUtil.isEmpty(srcList) || CollectionUtil.isEmpty(removeList))
170 | return;
171 |
172 | srcList.removeAll(removeList);
173 | }
174 |
175 | /**
176 | * 将文本根据指定的字符串分割成字符串集合
177 | *
178 | * @param text
179 | * @param split
180 | * @return
181 | */
182 | public static ArrayList splitText(String text, String split) {
183 |
184 | return splitText(text, split, false);
185 | }
186 |
187 | /**
188 | * 将文本根据指定的字符串分割成字符串集合
189 | *
190 | * @param text
191 | * @param split
192 | * @return
193 | */
194 | public static ArrayList splitText(String text, String split, boolean filterEmpty) {
195 |
196 | ArrayList list = new ArrayList();
197 |
198 | if (TextUtil.isEmpty(text))
199 | return list;
200 |
201 | String[] array = text.split(TextUtil.filterNull(split));
202 | if (array == null || array.length == 0)
203 | return list;
204 |
205 | for (int i = 0; i < array.length; i++) {
206 |
207 | if (filterEmpty) {
208 |
209 | if (!TextUtil.isEmptyTrim(array[i]))
210 | list.add(array[i]);
211 | } else {
212 |
213 | list.add(array[i]);
214 | }
215 | }
216 |
217 | return list;
218 | }
219 |
220 | /**
221 | * 根据集合拼成指定分隔符分隔的字符串
222 | *
223 | * @param texts
224 | * @param split
225 | * @return
226 | */
227 | public static String getText(List texts, String split) {
228 |
229 | if (isEmpty(texts))
230 | return TextUtil.TEXT_EMPTY;
231 |
232 | StringBuilder sb = new StringBuilder();
233 |
234 | for (int i = 0; i < texts.size(); i++) {
235 |
236 | if (i > 0)
237 | sb.append(split);
238 |
239 | sb.append(texts.get(i));
240 | }
241 |
242 | return sb.toString();
243 | }
244 |
245 | /**
246 | * 判断数组是否为空
247 | *
248 | * @param array
249 | * @return
250 | */
251 | public static boolean isEmpty(T[] array) {
252 |
253 | return array == null || array.length == 0;
254 | }
255 |
256 | /**
257 | * 返回数组的大小,null数组返回0
258 | *
259 | * @param array
260 | * @return
261 | */
262 | public static int size(T[] array) {
263 |
264 | return array == null ? 0 : array.length;
265 | }
266 |
267 | /**
268 | * 判断position是否在数组长度范围内
269 | *
270 | * @param array
271 | * @param position
272 | * @return
273 | */
274 | public static boolean checkPosition(T[] array, int position) {
275 |
276 | return position >= 0 && position < size(array);
277 | }
278 |
279 | /**
280 | * 做下标越界判断,如果越界返回null
281 | *
282 | * @param array
283 | * @param position
284 | * @param
285 | * @return
286 | */
287 | public static T getItem(T[] array, int position) {
288 |
289 | return checkPosition(array, position) ? array[position] : null;
290 | }
291 |
292 | /**
293 | * 截取子集,该函数会对集合和索引做边界判断
294 | * 如果endIndex超出长度,则会截取到末尾
295 | *
296 | * @param list
297 | * @param startIndex
298 | * @param endIndex
299 | * @param
300 | * @return
301 | */
302 | public static List subList(List list, int startIndex, int endIndex) {
303 |
304 | if (CollectionUtil.isEmpty(list))
305 | return list;
306 |
307 | if (startIndex < 0)
308 | startIndex = 0;
309 |
310 | if (startIndex > list.size())
311 | startIndex = list.size();
312 |
313 | if (endIndex < 0)
314 | endIndex = 0;
315 |
316 | if (endIndex > list.size())
317 | endIndex = list.size();
318 |
319 | if (startIndex > endIndex)
320 | startIndex = endIndex;
321 |
322 | return list.subList(startIndex, endIndex);
323 | }
324 |
325 | /**
326 | * 比较两个集合是否相同
327 | *
328 | * @param c1
329 | * @param c2
330 | * @return
331 | */
332 | public static boolean equals(Collection c1, Collection c2) {
333 |
334 | if (c1 == null || c2 == null)
335 | return false;
336 |
337 | return c1.equals(c2);
338 | }
339 |
340 | /**
341 | * 循环
342 | *
343 | * @param data
344 | * @param target
345 | * @param handleAbort
346 | * @param lisn
347 | * @param
348 | * @param
349 | * @return
350 | */
351 | public static boolean forLoop(List extends T> data, K target, boolean handleAbort, ForLoopListener lisn) {
352 |
353 | if (isEmpty(data) || lisn == null)
354 | return false;
355 |
356 | boolean result = false;
357 | for (int i = 0; i < data.size(); i++) {
358 |
359 | if (lisn.onItem(i, data.get(i), target)) {
360 |
361 | result = true;
362 | if (handleAbort)
363 | break;
364 | }
365 | }
366 |
367 | return result;
368 | }
369 |
370 | public static void addAll(List