datas) {
156 | this.datas.addAll(datas);
157 | notifyDataSetChanged();
158 | }
159 |
160 | @Override
161 | public int getItemCount() {
162 | return datas.size();
163 | }
164 |
165 | @Override
166 | public PhotoContents.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
167 | return getItemCount() == 1 ?
168 | new SingleViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_img_single, parent, false))
169 | :
170 | new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_img, parent, false));
171 | }
172 |
173 | @Override
174 | public void onBindViewHolder(PhotoContents.ViewHolder viewHolder, int position) {
175 | ImageView iv = null;
176 | if (viewHolder instanceof SingleViewHolder) {
177 | iv = ((SingleViewHolder) viewHolder).iv;
178 | } else if (viewHolder instanceof ViewHolder) {
179 | iv = ((ViewHolder) viewHolder).iv;
180 | }
181 | if (iv == null) return;
182 |
183 | ImageLoadManager.INSTANCE.loadImage(iv, datas.get(position));
184 | }
185 | }
186 |
187 | class SingleViewHolder extends PhotoContents.ViewHolder {
188 | ImageView iv;
189 |
190 | SingleViewHolder(View rootView) {
191 | super(rootView);
192 | iv = findViewById(R.id.iv_img);
193 | }
194 | }
195 |
196 | class ViewHolder extends PhotoContents.ViewHolder {
197 | ImageView iv;
198 |
199 | ViewHolder(View rootView) {
200 | super(rootView);
201 | iv = findViewById(R.id.iv_img);
202 | }
203 | }
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/ForceClickImageView.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Rect;
8 | import android.graphics.drawable.ColorDrawable;
9 | import android.graphics.drawable.Drawable;
10 | import android.graphics.drawable.StateListDrawable;
11 | import android.util.AttributeSet;
12 | import android.widget.ImageView;
13 |
14 | /**
15 | * Created by 大灯泡 on 2016/4/11.
16 | * 朋友圈的imageview,包含点击动作
17 | */
18 | public class ForceClickImageView extends ImageView {
19 | private static final String TAG = "ForceClickImageView";
20 | //前景层
21 | private Drawable mForegroundDrawable;
22 | private Rect mCachedBounds = new Rect();
23 |
24 | public ForceClickImageView(Context context) {
25 | this(context, null);
26 | }
27 |
28 | public ForceClickImageView(Context context, AttributeSet attrs) {
29 | this(context, attrs, 0);
30 | }
31 |
32 | public ForceClickImageView(Context context, AttributeSet attrs, int defStyleAttr) {
33 | super(context, attrs, defStyleAttr);
34 | init(context, attrs, true);
35 | setFocusable(true);
36 | }
37 |
38 | /**
39 | * 初始化
40 | */
41 | private void init(Context context, AttributeSet attrs, boolean needDefaultForceGroundColor) {
42 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForceClickImageView);
43 | mForegroundDrawable = a.getDrawable(R.styleable.ForceClickImageView_foregroundColor);
44 | if (mForegroundDrawable instanceof ColorDrawable || (attrs == null && needDefaultForceGroundColor)) {
45 | int foreGroundColor = a.getColor(R.styleable.ForceClickImageView_foregroundColor, 0x882b2b2b);
46 | mForegroundDrawable = new StateListDrawable();
47 | ColorDrawable forceDrawable = new ColorDrawable(foreGroundColor);
48 | ColorDrawable normalDrawable = new ColorDrawable(Color.TRANSPARENT);
49 | ((StateListDrawable) mForegroundDrawable).addState(new int[]{android.R.attr.state_pressed}, forceDrawable);
50 | ((StateListDrawable) mForegroundDrawable).addState(new int[]{android.R.attr.state_focused}, forceDrawable);
51 | ((StateListDrawable) mForegroundDrawable).addState(new int[]{android.R.attr.state_enabled}, normalDrawable);
52 | ((StateListDrawable) mForegroundDrawable).addState(new int[]{}, normalDrawable);
53 | }
54 | if (mForegroundDrawable != null) {
55 | mForegroundDrawable.setCallback(this);
56 | }
57 | a.recycle();
58 | }
59 |
60 |
61 | @Override
62 | protected void drawableStateChanged() {
63 | super.drawableStateChanged();
64 | if (mForegroundDrawable != null && mForegroundDrawable.isStateful()) {
65 | mForegroundDrawable.setState(getDrawableState());
66 | invalidate();
67 | }
68 | }
69 |
70 |
71 | @Override
72 | protected void onDraw(Canvas canvas) {
73 | super.onDraw(canvas);
74 | if (mForegroundDrawable != null) {
75 | if (getDrawable()!=null) {
76 | mForegroundDrawable.setBounds(getDrawable().getBounds());
77 | }else {
78 | mForegroundDrawable.setBounds(mCachedBounds);
79 | }
80 | mForegroundDrawable.draw(canvas);
81 | }
82 | }
83 |
84 | @Override
85 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
86 | super.onSizeChanged(w, h, oldw, oldh);
87 | if (mForegroundDrawable != null) mCachedBounds.set(0, 0, w, h);
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/ImageLoadManager.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.graphics.Bitmap;
7 | import android.graphics.drawable.Drawable;
8 | import android.media.MediaMetadataRetriever;
9 | import android.net.Uri;
10 | import android.support.annotation.DrawableRes;
11 | import android.support.annotation.NonNull;
12 | import android.support.annotation.Nullable;
13 | import android.support.annotation.Px;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.widget.ImageView;
17 |
18 | import com.bumptech.glide.Glide;
19 | import com.bumptech.glide.RequestBuilder;
20 | import com.bumptech.glide.RequestManager;
21 | import com.bumptech.glide.load.engine.DiskCacheStrategy;
22 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
23 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
24 | import com.bumptech.glide.load.resource.bitmap.CenterCrop;
25 | import com.bumptech.glide.load.resource.bitmap.CenterInside;
26 | import com.bumptech.glide.load.resource.bitmap.FitCenter;
27 | import com.bumptech.glide.request.RequestOptions;
28 | import com.bumptech.glide.request.target.CustomTarget;
29 | import com.bumptech.glide.request.transition.Transition;
30 |
31 | import java.io.File;
32 | import java.security.MessageDigest;
33 | import java.util.concurrent.ExecutionException;
34 |
35 | import razerdp.github.com.demo.utils.rx.RxCall;
36 | import razerdp.github.com.demo.utils.rx.RxHelper;
37 | import razerdp.github.com.demo.utils.rx.RxTaskCall;
38 |
39 | import static com.bumptech.glide.load.resource.bitmap.VideoDecoder.FRAME_OPTION;
40 |
41 | /**
42 | * Created by 大灯泡 on 2019/4/10.
43 | *
44 | * 图片加载单例 啊啊啊啊。。。好多重载,这时候有个kotlin多好啊噗
45 | */
46 |
47 | public enum ImageLoadManager {
48 | INSTANCE;
49 | private final GlideDispatcher DISPATCHER = new GlideDispatcher();
50 |
51 | private static RequestOptions DEFAULT_REQUEST_OPTIONS = new RequestOptions()
52 | .placeholder(R.drawable.ic_loading)
53 | .error(R.drawable.ic_error);
54 |
55 | public void clearMemory(Context context) {
56 | Glide.get(context).clearMemory();
57 | }
58 |
59 | public static Glide getGlide(Context context) {
60 | return Glide.get(context);
61 | }
62 |
63 | public void loadImage(View target, Object from) {
64 | loadImage(target, from, R.drawable.ic_loading, R.drawable.ic_error);
65 | }
66 |
67 | public void loadImage(View target, Object from, @DrawableRes int errorImage) {
68 | loadImage(target, from, R.drawable.ic_loading, errorImage);
69 | }
70 |
71 | public void loadImage(View target, Object from, @DrawableRes int loadingImg, @DrawableRes int errorImage) {
72 | loadImage(target, from, 0, 0, loadingImg, errorImage);
73 | }
74 |
75 | public void loadImage(View target, Object from, int width, int hegith, @DrawableRes int loadingImg, @DrawableRes int errorImage) {
76 | RequestOptions options = getOption().placeholder(loadingImg).error(errorImage);
77 | if (width != 0 && hegith != 0) {
78 | options = options.override(width, hegith);
79 | }
80 | loadImage(target, from, options);
81 | }
82 |
83 | public void loadImage(View target, String from, @DrawableRes int loadingImg, @DrawableRes int errorImage, boolean cache) {
84 | RequestOptions options = getOption()
85 | .placeholder(loadingImg)
86 | .error(errorImage)
87 | .skipMemoryCache(!cache)
88 | .diskCacheStrategy(cache ? DiskCacheStrategy.AUTOMATIC : DiskCacheStrategy.NONE);
89 | loadImage(target, from, options);
90 | }
91 |
92 | public RequestOptions getOption() {
93 | try {
94 | return DEFAULT_REQUEST_OPTIONS.clone();
95 | } catch (Exception e) {
96 | return new RequestOptions()
97 | .placeholder(R.drawable.ic_loading)
98 | .error(R.drawable.ic_error);
99 | }
100 | }
101 |
102 | @SuppressLint("CheckResult")
103 | public void loadImage(View target, Object from, RequestOptions options) {
104 | DISPATCHER.getGlide(target, from).apply(options).into((ImageView) target);
105 | }
106 |
107 | public void loadRoundImage(ImageView target, Object from, @Px int radius) {
108 | loadRoundImage(target, from, RoundedCornersTransformation.CornerType.ALL, radius);
109 | }
110 |
111 | public void loadRoundImage(ImageView target, Object from, RoundedCornersTransformation.CornerType type, @Px int radius) {
112 | loadRoundImage(target, from, R.drawable.ic_error, R.drawable.ic_error, type, radius);
113 | }
114 |
115 |
116 | public void loadRoundImage(ImageView target, Object from, @DrawableRes int loadingImg, @DrawableRes int errorImage, RoundedCornersTransformation.CornerType type, @Px int radius) {
117 | RequestOptions options = getOption()
118 | .placeholder(loadingImg)
119 | .error(errorImage);
120 | if (target.getScaleType() == ImageView.ScaleType.CENTER_CROP) {
121 | options.transform(new CenterCrop(), new RoundedCornersTransformation(radius, 0, type));
122 | } else if (target.getScaleType() == ImageView.ScaleType.CENTER_INSIDE) {
123 | options.transform(new CenterInside(), new RoundedCornersTransformation(radius, 0, type));
124 | } else if (target.getScaleType() == ImageView.ScaleType.FIT_CENTER) {
125 | options.transform(new FitCenter(), new RoundedCornersTransformation(radius, 0, type));
126 | } else {
127 | options.transform(new RoundedCornersTransformation(radius, 0, type));
128 | }
129 | loadImage(target, from, options);
130 | }
131 |
132 | public void loadBitmap(Context context, String imageUrl, final OnLoadBitmapListener listener) {
133 | Log.i("下载图片", "url=" + imageUrl);
134 | RequestOptions options = DEFAULT_REQUEST_OPTIONS.diskCacheStrategy(DiskCacheStrategy.ALL).skipMemoryCache(false);
135 | Glide.with(context)
136 | .asBitmap()
137 | .load(imageUrl)
138 | .apply(options)
139 | .into(new CustomTarget() {
140 | @Override
141 | public void onLoadFailed(@Nullable Drawable errorDrawable) {
142 | super.onLoadFailed(errorDrawable);
143 | if (listener != null) {
144 | listener.onFailed(new IllegalArgumentException("download failed"));
145 | }
146 | }
147 |
148 | @Override
149 | public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition super Bitmap> transition) {
150 | if (listener != null) {
151 | listener.onSuccess(resource);
152 | }
153 | }
154 |
155 | @Override
156 | public void onLoadCleared(@Nullable Drawable placeholder) {
157 |
158 | }
159 | });
160 | }
161 |
162 | public void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) {
163 | RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros)
164 | .set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST)
165 | .transform(new BitmapTransformation() {
166 | @Override
167 | protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
168 | return toTransform;
169 | }
170 |
171 | @Override
172 | public void updateDiskCacheKey(MessageDigest messageDigest) {
173 | try {
174 | messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));
175 | } catch (Exception e) {
176 | e.printStackTrace();
177 | }
178 | }
179 | });
180 | Glide.with(context).load(uri).apply(requestOptions).into(imageView);
181 | }
182 |
183 | public interface OnLoadBitmapListener {
184 | void onSuccess(Bitmap bitmap);
185 |
186 | void onFailed(Exception e);
187 | }
188 |
189 | public void getCache(final Object o, final RxCall cb) {
190 | if (o == null || cb == null) return;
191 | if (o instanceof File) {
192 | cb.onCall(((File) o).getAbsolutePath());
193 | return;
194 | }
195 | RxHelper.runOnBackground(new RxTaskCall() {
196 | @Override
197 | public String doInBackground() {
198 | File file = null;
199 | try {
200 | file = Glide.with(AppContext.getAppContext()).asFile().onlyRetrieveFromCache(true).load(o).submit().get();
201 | } catch (ExecutionException e) {
202 | e.printStackTrace();
203 | } catch (InterruptedException e) {
204 | e.printStackTrace();
205 | }
206 | return (file != null && file.exists()) ? file.getAbsolutePath() : null;
207 | }
208 |
209 | @Override
210 | public void onResult(String result) {
211 | cb.onCall(result);
212 | }
213 | });
214 |
215 | }
216 |
217 | private class GlideDispatcher {
218 |
219 | RequestManager getRequestManager(View v, Object o) {
220 | RequestManager manager;
221 | if (v.getContext() instanceof Activity) {
222 | manager = Glide.with((Activity) v.getContext());
223 | } else {
224 | manager = Glide.with(v.getContext() == null ? AppContext.getAppContext() : v.getContext());
225 | }
226 | return manager;
227 | }
228 |
229 | RequestBuilder getGlide(View v, Object o) {
230 | RequestManager manager = getRequestManager(v, o);
231 | if (o instanceof String) {
232 | return getGlideString(manager, (String) o);
233 | } else if (o instanceof Integer) {
234 | return getGlideInteger(manager, (Integer) o);
235 | } else if (o instanceof Uri) {
236 | return getGlideUri(manager, (Uri) o);
237 | } else if (o instanceof File) {
238 | return getGlideFile(manager, (File) o);
239 | }
240 | return getGlideString(manager, "");
241 | }
242 |
243 | private RequestBuilder getGlideString(RequestManager manager, String str) {
244 | return manager.load(str);
245 | }
246 |
247 | private RequestBuilder getGlideInteger(RequestManager manager, int source) {
248 | return manager.load(source);
249 | }
250 |
251 | private RequestBuilder getGlideUri(RequestManager manager, Uri uri) {
252 | return manager.load(uri);
253 | }
254 |
255 | private RequestBuilder getGlideFile(RequestManager manager, File file) {
256 | return manager.load(file);
257 | }
258 | }
259 |
260 | public static abstract class OnLoadBitmapListenerAdapter implements OnLoadBitmapListener {
261 |
262 | @Override
263 | public void onSuccess(Bitmap bitmap) {
264 |
265 | }
266 |
267 | @Override
268 | public void onFailed(Exception e) {
269 |
270 | }
271 | }
272 |
273 | }
274 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/PhotoContentsDemoApp.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 |
7 | /**
8 | * Created by 大灯泡 on 2016/11/28.
9 | */
10 |
11 | public class PhotoContentsDemoApp extends Application {
12 | private static Context CONTEXT;
13 |
14 | @Override
15 | public void onCreate() {
16 | super.onCreate();
17 | CONTEXT = PhotoContentsDemoApp.this.getApplicationContext();
18 | }
19 |
20 | public static Context getAppContext(){
21 | return CONTEXT;
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/RoundedCornersTransformation.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 | /**
4 | * https://github.com/wasabeef/glide-transformations/blob/master/transformations/src/main/java/jp/wasabeef/glide/transformations/RoundedCornersTransformation.java
5 | */
6 |
7 | import android.graphics.Bitmap;
8 | import android.graphics.BitmapShader;
9 | import android.graphics.Canvas;
10 | import android.graphics.Paint;
11 | import android.graphics.RectF;
12 | import android.graphics.Shader;
13 | import android.support.annotation.NonNull;
14 |
15 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
16 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
17 |
18 | import java.security.MessageDigest;
19 |
20 | public class RoundedCornersTransformation extends BitmapTransformation {
21 |
22 | private static final int VERSION = 1;
23 | private static final String ID = "jp.wasabeef.glide.transformations.RoundedCornersTransformation." + VERSION;
24 |
25 | @Override
26 | protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
27 | int width = toTransform.getWidth();
28 | int height = toTransform.getHeight();
29 |
30 | Bitmap bitmap = pool.get(width, height, Bitmap.Config.ARGB_8888);
31 | bitmap.setHasAlpha(true);
32 |
33 | Canvas canvas = new Canvas(bitmap);
34 | Paint paint = new Paint();
35 | paint.setAntiAlias(true);
36 | paint.setShader(new BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
37 | drawRoundRect(canvas, paint, width, height);
38 | return bitmap;
39 | }
40 |
41 | public enum CornerType {
42 | ALL,
43 | TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
44 | TOP, BOTTOM, LEFT, RIGHT,
45 | OTHER_TOP_LEFT, OTHER_TOP_RIGHT, OTHER_BOTTOM_LEFT, OTHER_BOTTOM_RIGHT,
46 | DIAGONAL_FROM_TOP_LEFT, DIAGONAL_FROM_TOP_RIGHT
47 | }
48 |
49 | private int radius;
50 | private int diameter;
51 | private int margin;
52 | private CornerType cornerType;
53 |
54 | public RoundedCornersTransformation(int radius, int margin) {
55 | this(radius, margin, CornerType.ALL);
56 | }
57 |
58 | public RoundedCornersTransformation(int radius, int margin, CornerType cornerType) {
59 | this.radius = radius;
60 | this.diameter = this.radius * 2;
61 | this.margin = margin;
62 | this.cornerType = cornerType;
63 | }
64 |
65 | private void drawRoundRect(Canvas canvas, Paint paint, float width, float height) {
66 | float right = width - margin;
67 | float bottom = height - margin;
68 |
69 | switch (cornerType) {
70 | case ALL:
71 | canvas.drawRoundRect(new RectF(margin, margin, right, bottom), radius, radius, paint);
72 | break;
73 | case TOP_LEFT:
74 | drawTopLeftRoundRect(canvas, paint, right, bottom);
75 | break;
76 | case TOP_RIGHT:
77 | drawTopRightRoundRect(canvas, paint, right, bottom);
78 | break;
79 | case BOTTOM_LEFT:
80 | drawBottomLeftRoundRect(canvas, paint, right, bottom);
81 | break;
82 | case BOTTOM_RIGHT:
83 | drawBottomRightRoundRect(canvas, paint, right, bottom);
84 | break;
85 | case TOP:
86 | drawTopRoundRect(canvas, paint, right, bottom);
87 | break;
88 | case BOTTOM:
89 | drawBottomRoundRect(canvas, paint, right, bottom);
90 | break;
91 | case LEFT:
92 | drawLeftRoundRect(canvas, paint, right, bottom);
93 | break;
94 | case RIGHT:
95 | drawRightRoundRect(canvas, paint, right, bottom);
96 | break;
97 | case OTHER_TOP_LEFT:
98 | drawOtherTopLeftRoundRect(canvas, paint, right, bottom);
99 | break;
100 | case OTHER_TOP_RIGHT:
101 | drawOtherTopRightRoundRect(canvas, paint, right, bottom);
102 | break;
103 | case OTHER_BOTTOM_LEFT:
104 | drawOtherBottomLeftRoundRect(canvas, paint, right, bottom);
105 | break;
106 | case OTHER_BOTTOM_RIGHT:
107 | drawOtherBottomRightRoundRect(canvas, paint, right, bottom);
108 | break;
109 | case DIAGONAL_FROM_TOP_LEFT:
110 | drawDiagonalFromTopLeftRoundRect(canvas, paint, right, bottom);
111 | break;
112 | case DIAGONAL_FROM_TOP_RIGHT:
113 | drawDiagonalFromTopRightRoundRect(canvas, paint, right, bottom);
114 | break;
115 | default:
116 | canvas.drawRoundRect(new RectF(margin, margin, right, bottom), radius, radius, paint);
117 | break;
118 | }
119 | }
120 |
121 | private void drawTopLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
122 | canvas.drawRoundRect(new RectF(margin, margin, margin + diameter, margin + diameter), radius,
123 | radius, paint);
124 | canvas.drawRect(new RectF(margin, margin + radius, margin + radius, bottom), paint);
125 | canvas.drawRect(new RectF(margin + radius, margin, right, bottom), paint);
126 | }
127 |
128 | private void drawTopRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
129 | canvas.drawRoundRect(new RectF(right - diameter, margin, right, margin + diameter), radius,
130 | radius, paint);
131 | canvas.drawRect(new RectF(margin, margin, right - radius, bottom), paint);
132 | canvas.drawRect(new RectF(right - radius, margin + radius, right, bottom), paint);
133 | }
134 |
135 | private void drawBottomLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
136 | canvas.drawRoundRect(new RectF(margin, bottom - diameter, margin + diameter, bottom), radius,
137 | radius, paint);
138 | canvas.drawRect(new RectF(margin, margin, margin + diameter, bottom - radius), paint);
139 | canvas.drawRect(new RectF(margin + radius, margin, right, bottom), paint);
140 | }
141 |
142 | private void drawBottomRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
143 | canvas.drawRoundRect(new RectF(right - diameter, bottom - diameter, right, bottom), radius,
144 | radius, paint);
145 | canvas.drawRect(new RectF(margin, margin, right - radius, bottom), paint);
146 | canvas.drawRect(new RectF(right - radius, margin, right, bottom - radius), paint);
147 | }
148 |
149 | private void drawTopRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
150 | canvas.drawRoundRect(new RectF(margin, margin, right, margin + diameter), radius, radius,
151 | paint);
152 | canvas.drawRect(new RectF(margin, margin + radius, right, bottom), paint);
153 | }
154 |
155 | private void drawBottomRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
156 | canvas.drawRoundRect(new RectF(margin, bottom - diameter, right, bottom), radius, radius,
157 | paint);
158 | canvas.drawRect(new RectF(margin, margin, right, bottom - radius), paint);
159 | }
160 |
161 | private void drawLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
162 | canvas.drawRoundRect(new RectF(margin, margin, margin + diameter, bottom), radius, radius,
163 | paint);
164 | canvas.drawRect(new RectF(margin + radius, margin, right, bottom), paint);
165 | }
166 |
167 | private void drawRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
168 | canvas.drawRoundRect(new RectF(right - diameter, margin, right, bottom), radius, radius, paint);
169 | canvas.drawRect(new RectF(margin, margin, right - radius, bottom), paint);
170 | }
171 |
172 | private void drawOtherTopLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
173 | canvas.drawRoundRect(new RectF(margin, bottom - diameter, right, bottom), radius, radius,
174 | paint);
175 | canvas.drawRoundRect(new RectF(right - diameter, margin, right, bottom), radius, radius, paint);
176 | canvas.drawRect(new RectF(margin, margin, right - radius, bottom - radius), paint);
177 | }
178 |
179 | private void drawOtherTopRightRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
180 | canvas.drawRoundRect(new RectF(margin, margin, margin + diameter, bottom), radius, radius,
181 | paint);
182 | canvas.drawRoundRect(new RectF(margin, bottom - diameter, right, bottom), radius, radius,
183 | paint);
184 | canvas.drawRect(new RectF(margin + radius, margin, right, bottom - radius), paint);
185 | }
186 |
187 | private void drawOtherBottomLeftRoundRect(Canvas canvas, Paint paint, float right, float bottom) {
188 | canvas.drawRoundRect(new RectF(margin, margin, right, margin + diameter), radius, radius,
189 | paint);
190 | canvas.drawRoundRect(new RectF(right - diameter, margin, right, bottom), radius, radius, paint);
191 | canvas.drawRect(new RectF(margin, margin + radius, right - radius, bottom), paint);
192 | }
193 |
194 | private void drawOtherBottomRightRoundRect(Canvas canvas, Paint paint, float right,
195 | float bottom) {
196 | canvas.drawRoundRect(new RectF(margin, margin, right, margin + diameter), radius, radius,
197 | paint);
198 | canvas.drawRoundRect(new RectF(margin, margin, margin + diameter, bottom), radius, radius,
199 | paint);
200 | canvas.drawRect(new RectF(margin + radius, margin + radius, right, bottom), paint);
201 | }
202 |
203 | private void drawDiagonalFromTopLeftRoundRect(Canvas canvas, Paint paint, float right,
204 | float bottom) {
205 | canvas.drawRoundRect(new RectF(margin, margin, margin + diameter, margin + diameter), radius,
206 | radius, paint);
207 | canvas.drawRoundRect(new RectF(right - diameter, bottom - diameter, right, bottom), radius,
208 | radius, paint);
209 | canvas.drawRect(new RectF(margin, margin + radius, right - diameter, bottom), paint);
210 | canvas.drawRect(new RectF(margin + diameter, margin, right, bottom - radius), paint);
211 | }
212 |
213 | private void drawDiagonalFromTopRightRoundRect(Canvas canvas, Paint paint, float right,
214 | float bottom) {
215 | canvas.drawRoundRect(new RectF(right - diameter, margin, right, margin + diameter), radius,
216 | radius, paint);
217 | canvas.drawRoundRect(new RectF(margin, bottom - diameter, margin + diameter, bottom), radius,
218 | radius, paint);
219 | canvas.drawRect(new RectF(margin, margin, right - radius, bottom - radius), paint);
220 | canvas.drawRect(new RectF(margin + radius, margin + radius, right, bottom), paint);
221 | }
222 |
223 | @Override
224 | public String toString() {
225 | return "RoundedTransformation(radius=" + radius + ", margin=" + margin + ", diameter="
226 | + diameter + ", cornerType=" + cornerType.name() + ")";
227 | }
228 |
229 | @Override
230 | public boolean equals(Object o) {
231 | return o instanceof RoundedCornersTransformation &&
232 | ((RoundedCornersTransformation) o).radius == radius &&
233 | ((RoundedCornersTransformation) o).diameter == diameter &&
234 | ((RoundedCornersTransformation) o).margin == margin &&
235 | ((RoundedCornersTransformation) o).cornerType == cornerType;
236 | }
237 |
238 | @Override
239 | public int hashCode() {
240 | return ID.hashCode() + radius * 10000 + diameter * 1000 + margin * 100 + cornerType.ordinal() * 10;
241 | }
242 |
243 | @Override
244 | public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
245 | messageDigest.update((ID + radius + diameter + margin + cornerType).getBytes(CHARSET));
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/TestServerData.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 |
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Random;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import razerdp.github.com.demo.utils.FileUtil;
12 | import razerdp.github.com.demo.utils.ToolUtil;
13 |
14 | /**
15 | * Created by 大灯泡 on 2019/4/10
16 | *
17 | * Description:内测版测试数据
18 | */
19 | public class TestServerData {
20 |
21 | private static List pics;
22 | private static List avatars;
23 |
24 |
25 | public static String getPicUrl() {
26 | if (ToolUtil.isListEmpty(pics)) {
27 | Pattern pattern = Pattern.compile("(https?|ftp|file)://(?!(\\.jpg|\\.png|\\.gif)).+?(\\.jpg|\\.png|\\.gif)");
28 | pics = new ArrayList<>();
29 | String result = FileUtil.getFromAssets("pics");
30 | Matcher matcher = pattern.matcher(result);
31 | while (matcher.find()) {
32 | pics.add(matcher.group(0));
33 | }
34 | }
35 | Random random = new Random();
36 | return pics.get(random.nextInt(pics.size()));
37 | }
38 |
39 | public static String getAvatar() {
40 | if (ToolUtil.isListEmpty(avatars)) {
41 | Pattern pattern = Pattern.compile("(https?|ftp|file)://(?!(\\.jpg|\\.png|\\.gif)).+?(\\.jpg|\\.png|\\.gif)");
42 | avatars = new ArrayList<>();
43 | String result = FileUtil.getFromAssets("avatars");
44 | Matcher matcher = pattern.matcher(result);
45 | while (matcher.find()) {
46 | avatars.add(matcher.group(0));
47 | }
48 | }
49 | Random random = new Random();
50 | return avatars.get(random.nextInt(avatars.size()));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/BaseAbsViewAdapter.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by 大灯泡 on 2016/11/9.
14 | *
15 | * absview的adapter
16 | */
17 |
18 | public abstract class BaseAbsViewAdapter extends BaseAdapter {
19 | protected List datas;
20 | protected LayoutInflater mInflater;
21 | protected Context mContext;
22 |
23 | public BaseAbsViewAdapter(Context context, List datas) {
24 | mContext = context;
25 | this.datas=new ArrayList<>();
26 | this.datas.addAll(datas);
27 | mInflater= LayoutInflater.from(context);
28 | }
29 |
30 |
31 | @Override
32 | public int getCount() {
33 | return datas == null ? 0 : datas.size();
34 | }
35 |
36 | @Override
37 | public T getItem(int position) {
38 | return datas.get(position);
39 | }
40 |
41 | @Override
42 | public long getItemId(int position) {
43 | return position;
44 | }
45 |
46 | @Override
47 | public View getView(int position, View convertView, ViewGroup parent) {
48 | V holder = null;
49 | if (convertView == null) {
50 | convertView = mInflater.inflate(getLayoutRes(),parent,false);
51 | holder = initViewHolder();
52 | holder.onInFlate(convertView);
53 | convertView.setTag(holder);
54 | }
55 | else {
56 | holder = (V) convertView.getTag();
57 | }
58 | onBindData(position, getItem(position), holder);
59 | return convertView;
60 | }
61 |
62 | public void updateData(List datas){
63 | this.datas.clear();
64 | this.datas.addAll(datas);
65 | notifyDataSetChanged();
66 | }
67 |
68 | public abstract int getLayoutRes();
69 |
70 | public abstract V initViewHolder();
71 |
72 | public abstract void onBindData(int position, T data, V holder);
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/BaseAbsViewHolder.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by 大灯泡 on 2016/11/9.
7 | */
8 |
9 | public interface BaseAbsViewHolder {
10 | void onInFlate(View v);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/BaseRecyclerViewAdapter.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | import razerdp.github.com.demo.R;
14 | import razerdp.github.com.demo.utils.ToolUtil;
15 |
16 |
17 | /**
18 | * Created by 大灯泡 on 2016/11/1.
19 | *
20 | * 抽象的adapter
21 | */
22 |
23 | public abstract class BaseRecyclerViewAdapter extends RecyclerView.Adapter> {
24 |
25 | protected Context context;
26 | protected List datas;
27 | protected LayoutInflater mInflater;
28 |
29 | private OnRecyclerViewItemClickListener onRecyclerViewItemClickListener;
30 | private OnRecyclerViewLongItemClickListener onRecyclerViewLongItemClickListener;
31 |
32 | public BaseRecyclerViewAdapter(@NonNull Context context, @NonNull List datas) {
33 | this.context = context;
34 | this.datas = new ArrayList<>();
35 | if (!ToolUtil.isListEmpty(datas))
36 | this.datas.addAll(datas);
37 | mInflater = LayoutInflater.from(context);
38 | }
39 |
40 | @Override
41 | public int getItemViewType(int position) {
42 | return getViewType(position, datas.get(position));
43 | }
44 |
45 | @Override
46 | public BaseRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
47 | BaseRecyclerViewHolder holder = null;
48 | if (getLayoutResId(viewType) != 0) {
49 | View rootView = mInflater.inflate(getLayoutResId(viewType), parent, false);
50 | holder = getViewHolder(parent, rootView, viewType);
51 | } else {
52 | holder = getViewHolder(parent, null, viewType);
53 | }
54 | setUpItemEvent(holder);
55 | return holder;
56 | }
57 |
58 | @Override
59 | public void onBindViewHolder(BaseRecyclerViewHolder holder, int position) {
60 | T data = datas.get(position);
61 | holder.itemView.setTag(R.id.recycler_view_tag, data);
62 | holder.onBindData(data, position);
63 | onBindData(holder, data, position);
64 | }
65 |
66 | private void setUpItemEvent(final BaseRecyclerViewHolder holder) {
67 | if (onRecyclerViewItemClickListener != null) {
68 | holder.itemView.setOnClickListener(new View.OnClickListener() {
69 | @Override
70 | public void onClick(View v) {
71 | //这个获取位置的方法,防止添加删除导致位置不变
72 | int layoutPosition = holder.getAdapterPosition();
73 | onRecyclerViewItemClickListener.onItemClick(holder.itemView,
74 | layoutPosition,
75 | datas.get(layoutPosition));
76 | }
77 | });
78 | }
79 | if (onRecyclerViewLongItemClickListener != null) {
80 | //longclick
81 | holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
82 | @Override
83 | public boolean onLongClick(View v) {
84 | int layoutPosition = holder.getAdapterPosition();
85 | onRecyclerViewLongItemClickListener.onItemLongClick(holder.itemView,
86 | layoutPosition,
87 | datas.get(layoutPosition));
88 | return false;
89 | }
90 | });
91 | }
92 | }
93 |
94 | @Override
95 | public int getItemCount() {
96 | return datas == null ? 0 : datas.size();
97 | }
98 |
99 | public void updateData(List datas) {
100 | this.datas.clear();
101 | this.datas.addAll(datas);
102 | notifyDataSetChanged();
103 | }
104 |
105 | public void addMore(List datas) {
106 | if (!ToolUtil.isListEmpty(datas)) {
107 | int lastDataSize = this.datas.size();
108 | this.datas.addAll(datas);
109 | notifyItemRangeChanged(lastDataSize, datas.size());
110 | }
111 | }
112 |
113 | public void addData(int pos, @NonNull T data) {
114 | if (datas != null) {
115 | datas.add(pos, data);
116 | notifyItemInserted(pos);
117 | }
118 | }
119 |
120 | public void addData(@NonNull T data) {
121 | if (datas != null) {
122 | datas.add(data);
123 | notifyItemInserted(datas.size() - 1);
124 | }
125 | }
126 |
127 | public void deleteData(int pos) {
128 | if (datas != null && datas.size() > pos) {
129 | datas.remove(pos);
130 | notifyItemRemoved(pos);
131 | }
132 | }
133 |
134 | protected abstract int getViewType(int position, @NonNull T data);
135 |
136 | protected abstract int getLayoutResId(int viewType);
137 |
138 | protected abstract BaseRecyclerViewHolder getViewHolder(ViewGroup parent, View inflatedView, int viewType);
139 |
140 | protected void onBindData(BaseRecyclerViewHolder holder, T data, int position) {
141 |
142 | }
143 |
144 | public OnRecyclerViewItemClickListener getOnRecyclerViewItemClickListener() {
145 | return onRecyclerViewItemClickListener;
146 | }
147 |
148 | public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener onRecyclerViewItemClickListener) {
149 | this.onRecyclerViewItemClickListener = onRecyclerViewItemClickListener;
150 | }
151 |
152 | public OnRecyclerViewLongItemClickListener getOnRecyclerViewLongItemClickListener() {
153 | return onRecyclerViewLongItemClickListener;
154 | }
155 |
156 | public void setOnRecyclerViewLongItemClickListener(OnRecyclerViewLongItemClickListener onRecyclerViewLongItemClickListener) {
157 | this.onRecyclerViewLongItemClickListener = onRecyclerViewLongItemClickListener;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/BaseRecyclerViewHolder.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | /**
10 | * Created by 大灯泡 on 2016/11/1.
11 | *
12 | * 对viewholder的封装
13 | */
14 |
15 | public abstract class BaseRecyclerViewHolder extends RecyclerView.ViewHolder {
16 |
17 | private int viewType;
18 |
19 | public BaseRecyclerViewHolder(Context context, ViewGroup viewGroup, int layoutResId) {
20 | super(LayoutInflater.from(context).inflate(layoutResId, viewGroup, false));
21 | }
22 |
23 | public BaseRecyclerViewHolder(View itemView, int viewType) {
24 | super(itemView);
25 | this.viewType = viewType;
26 | }
27 |
28 | public abstract void onBindData(T data, int position);
29 |
30 | protected int getViewType() {
31 | return viewType;
32 | }
33 |
34 | protected Context getContext() {
35 | return itemView.getContext();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/OnRecyclerViewItemClickListener.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by 大灯泡 on 2016/11/1.
7 | */
8 |
9 | public interface OnRecyclerViewItemClickListener {
10 | void onItemClick(View v, int position, T data);
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/baseadapter/OnRecyclerViewLongItemClickListener.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.baseadapter;
2 |
3 | import android.view.View;
4 |
5 | /**
6 | * Created by 大灯泡 on 2016/11/1.
7 | *
8 | */
9 |
10 | public interface OnRecyclerViewLongItemClickListener {
11 | boolean onItemLongClick(View v, int position, T data);
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/FileUtil.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils;
2 |
3 | import java.io.InputStream;
4 | import java.nio.charset.StandardCharsets;
5 |
6 | import razerdp.github.com.demo.AppContext;
7 |
8 | public class FileUtil {
9 | public static String getFromAssets(String fileName) {
10 | String result = "";
11 | try {
12 | InputStream in = AppContext.getResources().getAssets().open(fileName);
13 | byte[] buffered = new byte[in.available()];
14 | in.read(buffered);
15 | result = new String(buffered, StandardCharsets.UTF_8);
16 | in.close();
17 | } catch (Exception e) {
18 | e.printStackTrace();
19 | }
20 | return result;
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/RandomUtil.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils;
2 |
3 | import java.util.Random;
4 |
5 | /**
6 | * Created by 大灯泡 on 2019/6/28
7 | *
8 | * Description:
9 | */
10 | public class RandomUtil {
11 | private static final String CHAR_LETTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
12 | private static final String CHAR_NUMBER = "0123456789";
13 | private static final String CHAR_ALL = CHAR_NUMBER + CHAR_LETTERS;
14 |
15 | /**
16 | * 产生长度为length的随机字符串(包括字母和数字)
17 | *
18 | * @param length
19 | * @return
20 | */
21 | public static String randomString(int length) {
22 | StringBuilder sb = new StringBuilder();
23 | Random random = new Random();
24 | for (int i = 0; i < length; i++) {
25 | sb.append(CHAR_ALL.charAt(random.nextInt(CHAR_ALL.length())));
26 | }
27 | return sb.toString();
28 | }
29 |
30 | public static String randomNonNumberString(int length) {
31 | StringBuilder sb = new StringBuilder();
32 | Random random = new Random();
33 | for (int i = 0; i < length; i++) {
34 | sb.append(CHAR_LETTERS.charAt(random.nextInt(CHAR_LETTERS.length())));
35 | }
36 | return sb.toString();
37 | }
38 |
39 | public static String randomLowerNonNumberString(int length) {
40 | return randomNonNumberString(length).toLowerCase();
41 | }
42 |
43 | public static String randomUpperNonNumberString(int length) {
44 | return randomNonNumberString(length).toUpperCase();
45 | }
46 |
47 | public static int randomInt(int min, int max) {
48 | return new Random().nextInt(max - min + 1) + min;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/ToolUtil.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by 大灯泡 on 2016/10/27.
7 | */
8 |
9 | public class ToolUtil {
10 |
11 | public static boolean isListEmpty(List> datas) {
12 | return datas == null || datas.size() <= 0;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/UIHelper.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.os.Handler;
6 | import android.support.annotation.Nullable;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.view.inputmethod.InputMethodManager;
10 | import android.widget.EditText;
11 | import android.widget.Toast;
12 |
13 | import razerdp.github.com.demo.PhotoContentsDemoApp;
14 |
15 | /**
16 | * Created by 大灯泡 on 2016/10/26.
17 | *
18 | * ui工具类
19 | */
20 | public class UIHelper {
21 |
22 | // =============================================================tools
23 | // methods
24 |
25 | /**
26 | * dip转px
27 | */
28 | public static int dipToPx(float dip) {
29 | return (int) (dip * PhotoContentsDemoApp.getAppContext().getResources()
30 | .getDisplayMetrics().density + 0.5f);
31 | }
32 |
33 | /**
34 | * px转dip
35 | */
36 | public static int pxToDip(float pxValue) {
37 | final float scale = PhotoContentsDemoApp.getAppContext().getResources()
38 | .getDisplayMetrics().density;
39 | return (int) (pxValue / scale + 0.5f);
40 | }
41 |
42 | /**
43 | * 将sp值转换为px值
44 | */
45 | public static int sp2px(float spValue) {
46 | final float fontScale = PhotoContentsDemoApp.getAppContext().getResources()
47 | .getDisplayMetrics().scaledDensity;
48 | return (int) (spValue * fontScale + 0.5f);
49 | }
50 |
51 | /**
52 | * 获取屏幕分辨率:宽
53 | */
54 | public static int getScreenWidthPix(@Nullable Context context) {
55 | if (context == null) context = PhotoContentsDemoApp.getAppContext();
56 | int width = context.getResources().getDisplayMetrics().widthPixels;
57 | return width;
58 | }
59 |
60 | /**
61 | * 获取屏幕分辨率:高
62 | */
63 | public static int getScreenHeightPix(@Nullable Context context) {
64 | if (context == null) context = PhotoContentsDemoApp.getAppContext();
65 | int height = context.getResources().getDisplayMetrics().heightPixels;
66 | return height;
67 | }
68 |
69 | /**
70 | * 获取状态栏的高度
71 | */
72 | public static int getStatusBarHeight(Context context) {
73 | int result = 0;
74 | int resourceId = context.getResources()
75 | .getIdentifier("status_bar_height", "dimen", "android");
76 | if (resourceId > 0) {
77 | result = context.getResources()
78 | .getDimensionPixelSize(resourceId);
79 | }
80 | return result;
81 | }
82 |
83 | /**
84 | * 隐藏软键盘
85 | */
86 | public static void hideInputMethod(View view) {
87 | try {
88 | InputMethodManager imm = (InputMethodManager) view.getContext()
89 | .getSystemService(Context.INPUT_METHOD_SERVICE);
90 | if (imm != null) {
91 | imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
92 | }
93 | } catch (Exception e) {
94 | e.printStackTrace();
95 | Log.e("error", e.toString());
96 | }
97 | }
98 |
99 | public static void hideInputMethod(final View view, long delayMillis) {
100 | new Handler().postDelayed(new Runnable() {
101 | @Override
102 | public void run() {
103 | hideInputMethod(view);
104 | }
105 | }, delayMillis);
106 | }
107 |
108 | /**
109 | * 显示软键盘
110 | */
111 | public static void showInputMethod(View view) {
112 | if (view != null && view instanceof EditText) view.requestFocus();
113 | InputMethodManager imm = (InputMethodManager) view.getContext()
114 | .getSystemService(Context.INPUT_METHOD_SERVICE);
115 | if (imm != null) {
116 | imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
117 | }
118 | }
119 |
120 | /**
121 | * 显示软键盘
122 | */
123 | public static void showInputMethod(Context context) {
124 | InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
125 | imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
126 | }
127 |
128 | /**
129 | * 多少时间后显示软键盘
130 | */
131 | public static void showInputMethod(final View view, long delayMillis) {
132 | if (view != null)
133 | // 显示输入法
134 | {
135 | view.postDelayed(new Runnable() {
136 |
137 | @Override
138 | public void run() {
139 | UIHelper.showInputMethod(view);
140 | }
141 | }, delayMillis);
142 | }
143 | }
144 |
145 |
146 | /**
147 | * Toast封装
148 | */
149 | public static void ToastMessage(String msg) {
150 | Toast.makeText(PhotoContentsDemoApp.getAppContext(), msg, Toast.LENGTH_LONG).show();
151 | }
152 |
153 | /**
154 | * =============================================================
155 | * 资源工具
156 | */
157 |
158 | public static int getResourceColor(int colorResId) {
159 | if (colorResId > 0) {
160 | return PhotoContentsDemoApp.getAppContext()
161 | .getResources()
162 | .getColor(colorResId);
163 | } else {
164 | return Color.TRANSPARENT;
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/rx/DefaultLogThrowableConsumer.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils.rx;
2 |
3 | import android.util.Log;
4 |
5 |
6 | import io.reactivex.functions.Consumer;
7 |
8 | /**
9 | * Created by 大灯泡 on 2019/4/9.
10 | * 默认打印日志的错误consumer
11 | */
12 | public final class DefaultLogThrowableConsumer implements Consumer {
13 | private String TAG = this.getClass().getSimpleName();
14 |
15 | public DefaultLogThrowableConsumer() {
16 | }
17 |
18 | public DefaultLogThrowableConsumer(String TAG) {
19 | this.TAG = TAG;
20 | }
21 |
22 | @Override
23 | public void accept(Throwable throwable) throws Exception {
24 | Log.e(TAG, throwable.getMessage());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/rx/RxCall.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils.rx;
2 |
3 | /**
4 | * Created by 大灯泡 on 2018/5/24.
5 | */
6 | public interface RxCall {
7 |
8 | void onCall(T data);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/rx/RxCallImpl.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils.rx;
2 |
3 | /**
4 | * Created by 大灯泡 on 2018/5/24.
5 | */
6 | public abstract class RxCallImpl implements RxCall {
7 | T data;
8 |
9 | public RxCallImpl() {
10 | }
11 |
12 | public RxCallImpl(T data) {
13 | this.data = data;
14 | }
15 |
16 | public T getData() {
17 | return data;
18 | }
19 |
20 | public RxCallImpl setData(T data) {
21 | this.data = data;
22 | return this;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/rx/RxHelper.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils.rx;
2 |
3 |
4 | import android.support.annotation.NonNull;
5 | import android.widget.EditText;
6 |
7 |
8 | import java.util.concurrent.TimeUnit;
9 |
10 | import io.reactivex.Flowable;
11 | import io.reactivex.FlowableTransformer;
12 | import io.reactivex.Observable;
13 | import io.reactivex.ObservableEmitter;
14 | import io.reactivex.ObservableOnSubscribe;
15 | import io.reactivex.ObservableSource;
16 | import io.reactivex.ObservableTransformer;
17 | import io.reactivex.Single;
18 | import io.reactivex.SingleSource;
19 | import io.reactivex.SingleTransformer;
20 | import io.reactivex.android.schedulers.AndroidSchedulers;
21 | import io.reactivex.disposables.Disposable;
22 | import io.reactivex.functions.Consumer;
23 | import io.reactivex.schedulers.Schedulers;
24 |
25 | /**
26 | * Created by 大灯泡 on 2019/4/9.
27 | */
28 | public class RxHelper {
29 |
30 | //-----------------------------------------transformer-----------------------------------------
31 | public static ObservableTransformer io_main() {
32 |
33 | return new ObservableTransformer() {
34 |
35 | @Override
36 | public ObservableSource apply(Observable upstream) {
37 | return upstream.subscribeOn(Schedulers.io())
38 | .observeOn(AndroidSchedulers.mainThread());
39 | }
40 | };
41 | }
42 |
43 | public static FlowableTransformer io_main_flowable() {
44 | return new FlowableTransformer() {
45 | @Override
46 | public Flowable apply(Flowable observable) {
47 | return observable.subscribeOn(Schedulers.io())
48 | .observeOn(AndroidSchedulers.mainThread());
49 | }
50 | };
51 | }
52 |
53 | public static SingleTransformer single_io_main() {
54 |
55 | return new SingleTransformer() {
56 |
57 | @Override
58 | public SingleSource apply(Single upstream) {
59 | return upstream.subscribeOn(Schedulers.io())
60 | .observeOn(AndroidSchedulers.mainThread());
61 | }
62 | };
63 | }
64 |
65 | public static ObservableTransformer computation_main() {
66 |
67 | return new ObservableTransformer() {
68 |
69 | @Override
70 | public ObservableSource apply(Observable upstream) {
71 | return upstream.subscribeOn(Schedulers.computation())
72 | .observeOn(AndroidSchedulers.mainThread());
73 | }
74 | };
75 | }
76 |
77 | public static SingleTransformer single_computation_main() {
78 |
79 | return new SingleTransformer() {
80 |
81 | @Override
82 | public SingleSource apply(Single upstream) {
83 | return upstream.subscribeOn(Schedulers.computation())
84 | .observeOn(AndroidSchedulers.mainThread());
85 | }
86 | };
87 | }
88 |
89 |
90 | //-----------------------------------------method-----------------------------------------
91 |
92 | public static Disposable runOnBackground(@NonNull final RxCall call) {
93 | return Observable.create(new ObservableOnSubscribe() {
94 | @Override
95 | public void subscribe(ObservableEmitter emitter) throws Exception {
96 | if (call != null) {
97 | call.onCall(null);
98 | }
99 | emitter.onComplete();
100 | }
101 | })
102 | .subscribeOn(Schedulers.io())
103 | .subscribe();
104 | }
105 |
106 | public static Disposable runOnBackground(@NonNull final RxTaskCall call) {
107 | return Observable.create(new ObservableOnSubscribe() {
108 | @Override
109 | public void subscribe(ObservableEmitter emitter) throws Exception {
110 | T result = call.doInBackground();
111 | if (result == null) {
112 | call.onError(new NullPointerException());
113 | emitter.onComplete();
114 | } else {
115 | emitter.onNext(result);
116 | }
117 |
118 | }
119 | }).subscribeOn(Schedulers.io())
120 | .observeOn(AndroidSchedulers.mainThread())
121 | .subscribe(new Consumer() {
122 | @Override
123 | public void accept(T t) throws Exception {
124 | call.onResult(t);
125 | }
126 | });
127 |
128 | }
129 |
130 | public static Disposable runOnUiThread(@NonNull RxCall call) {
131 | return runOnUiThread(call, new DefaultLogThrowableConsumer("RxRunOnUiThread"));
132 | }
133 |
134 | public static Disposable runOnUiThread(@NonNull RxCall call, @NonNull Consumer errorConsumer) {
135 | return Flowable.just(call)
136 | .observeOn(AndroidSchedulers.mainThread())
137 | .subscribe(new Consumer>() {
138 | @Override
139 | public void accept(RxCall tiRxCall) throws Exception {
140 | if (tiRxCall instanceof RxCallImpl) {
141 | RxCallImpl uiCall = ((RxCallImpl) tiRxCall);
142 | uiCall.onCall(uiCall.getData());
143 | } else {
144 | tiRxCall.onCall(null);
145 | }
146 | }
147 | }, errorConsumer);
148 | }
149 |
150 |
151 | public static Disposable delay(long delayTime, @NonNull RxCall call) {
152 | return delay(delayTime, TimeUnit.MILLISECONDS, call);
153 |
154 | }
155 |
156 | public static Disposable delay(long delayTime, TimeUnit unit, @NonNull RxCall call) {
157 | return delay(delayTime, unit, call, new DefaultLogThrowableConsumer());
158 | }
159 |
160 | public static Disposable delay(long delayTime, TimeUnit unit, @NonNull final RxCall call, @NonNull Consumer errorConsumer) {
161 | return Flowable.timer(delayTime, unit)
162 | .observeOn(AndroidSchedulers.mainThread())
163 | .subscribe(new Consumer() {
164 | @Override
165 | public void accept(Long aLong) throws Exception {
166 | call.onCall(aLong);
167 | }
168 | }, errorConsumer);
169 | }
170 |
171 | }
172 |
--------------------------------------------------------------------------------
/app/src/main/java/razerdp/github/com/demo/utils/rx/RxTaskCall.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo.utils.rx;
2 |
3 | /**
4 | * Created by 大灯泡 on 2019/4/10
5 | *
6 | * Description:
7 | */
8 | public abstract class RxTaskCall {
9 |
10 | public abstract T doInBackground();
11 |
12 | public abstract void onResult(T result);
13 |
14 | public void onError(Throwable e){
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_error.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/drawable-xhdpi/ic_error.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/drawable-xhdpi/ic_loading.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_img.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_img_single.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_multi_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
25 |
26 |
27 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #c6c6c6
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PhotoContents
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/razerdp/github/com/demo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.demo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/art/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/art/preview.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | maven { url "https://maven.google.com" }
6 | jcenter()
7 | google()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.3'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
12 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | maven { url "https://maven.google.com" }
21 | jcenter()
22 | google()
23 | }
24 |
25 | configurations.all {
26 | resolutionStrategy.eachDependency { DependencyResolveDetails details ->
27 | def requested = details.requested
28 | if (requested.group == 'com.android.support') {
29 | if (!requested.name.startsWith("multidex")) {
30 | details.useVersion '28.0.0'
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
37 | task clean(type: Delete) {
38 | delete rootProject.buildDir
39 | }
40 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razerdp/PhotoContents/208a46ae1d137511c5decad7906a668acbab14ce/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Dec 10 09:36:48 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'com.jfrog.bintray'
4 |
5 | android {
6 | compileSdkVersion 28
7 |
8 | defaultConfig {
9 | minSdkVersion 16
10 | targetSdkVersion 28
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
15 |
16 | }
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | lintOptions {
25 | abortOnError false
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
32 | exclude group: 'com.android.support', module: 'support-annotations'
33 | })
34 | testImplementation 'junit:junit:4.12'
35 | compileOnly 'com.android.support:appcompat-v7:28.0.0'
36 | }
37 |
38 | def siteUrl = 'https://github.com/razerdp/PhotoContents'
39 | def gitUrl = 'git@github.com:razerdp/PhotoContents.git'
40 | def moduleName = 'PhotoContents'
41 |
42 |
43 | group = "com.github.razerdp"
44 | version = "2.0.0"
45 |
46 | install {
47 | repositories.mavenInstaller {
48 | pom.artifactId = moduleName
49 | pom {
50 | project {
51 | packaging 'aar'
52 | name moduleName
53 | description '朋友圈九宫格'
54 | url siteUrl
55 |
56 | licenses {
57 | license {
58 | name 'The Apache Software License, Version 2.0'
59 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
60 | }
61 | }
62 |
63 | developers {
64 | developer {
65 | id 'razerdp'
66 | name 'razerdp'
67 | email 'razerdp123@gmail.com'
68 | }
69 | }
70 |
71 | scm {
72 | connection gitUrl
73 | developerConnection gitUrl
74 | url siteUrl
75 | }
76 | }
77 | }
78 | }
79 | }
80 |
81 | task sourcesJar(type: Jar) {
82 | from android.sourceSets.main.java.srcDirs
83 | classifier = 'sources'
84 | }
85 | task javadoc(type: Javadoc) {
86 | source = android.sourceSets.main.java.srcDirs
87 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
88 | failOnError false
89 | }
90 |
91 | task javadocJar(type: Jar, dependsOn: javadoc) {
92 | classifier = 'javadoc'
93 | from javadoc.destinationDir
94 | }
95 |
96 | javadoc {
97 | options {
98 | encoding "UTF-8"
99 | charSet 'UTF-8'
100 | author true
101 | version true
102 | links "https://developer.android.google.cn/reference/"
103 | addStringOption('Xdoclint:none', '-quiet')
104 | }
105 | }
106 |
107 | artifacts {
108 | archives javadocJar
109 | archives sourcesJar
110 | }
111 |
112 | Properties properties = new Properties()
113 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
114 | bintray {
115 | user = properties.getProperty("bintray.user")
116 | key = properties.getProperty("bintray.apikey")
117 |
118 | configurations = ['archives']
119 | pkg {
120 | repo = "maven"
121 | name = moduleName
122 | userOrg = 'razerdp'
123 | websiteUrl = siteUrl
124 | vcsUrl = gitUrl
125 | licenses = ["Apache-2.0"]
126 | publish = true
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\AndroidSDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/lib/src/androidTest/java/razerdp/github/com/widget/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.widget;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("razerdp.github.com.widget.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/src/main/java/razerdp/github/com/widget/PhotoContents.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.widget;
2 |
3 | import android.content.Context;
4 | import android.database.Observable;
5 | import android.graphics.Rect;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | import android.support.v4.view.ViewCompat;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 |
13 | import razerdp.github.com.widget.util.SimplePool;
14 |
15 |
16 | /**
17 | * Created by 大灯泡 on 2019/7/1
18 | *
19 | * Description:
20 | */
21 | public class PhotoContents extends ViewGroup {
22 | private static final String TAG = "PhotoContents";
23 |
24 | PhotoContents.LayoutManager mLayout;
25 | SimplePool mPools;
26 | State mState;
27 | Adapter mAdapter;
28 | AdapterDataObserver mObserver;
29 |
30 |
31 | public PhotoContents(Context context) {
32 | this(context, null);
33 | }
34 |
35 | public PhotoContents(Context context, AttributeSet attrs) {
36 | this(context, attrs, 0);
37 | }
38 |
39 | public PhotoContents(Context context, AttributeSet attrs, int defStyleAttr) {
40 | super(context, attrs, defStyleAttr);
41 | init();
42 | }
43 |
44 | private void init() {
45 | mObserver = new PhotoContentsObserver();
46 | mPools = new SimplePool<>(9);
47 | mState = new State();
48 | }
49 |
50 | @Override
51 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
52 | if (mLayout == null) {
53 | defaultOnMeasure(widthMeasureSpec, heightMeasureSpec);
54 | } else {
55 | preventRequestLayout();
56 | mState.itemCount = mAdapter.getItemCount();
57 |
58 | if (mState.itemCount == 0 || mAdapter == null) {
59 | doRecycler();
60 | defaultOnMeasure(widthMeasureSpec, heightMeasureSpec);
61 | resumeRequestLayout();
62 | return;
63 | }
64 | mLayout.onMeasure(mPools, mState, widthMeasureSpec, heightMeasureSpec);
65 | resumeRequestLayout();
66 | }
67 | }
68 |
69 | void defaultOnMeasure(int widthSpec, int heightSpec) {
70 | int width = LayoutManager.chooseSize(widthSpec, this.getPaddingLeft() + this.getPaddingRight(), ViewCompat.getMinimumWidth(this));
71 | int height = LayoutManager.chooseSize(heightSpec, this.getPaddingTop() + this.getPaddingBottom(), ViewCompat.getMinimumHeight(this));
72 | setMeasuredDimension(width, height);
73 | }
74 |
75 | @Override
76 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
77 | preventRequestLayout();
78 | if (mAdapter == null) {
79 | doRecycler();
80 | resumeRequestLayout();
81 | return;
82 | }
83 | if (mLayout == null) {
84 | resumeRequestLayout();
85 | return;
86 | }
87 | mState.setBounds(getPaddingLeft(), getPaddingTop(), r - getPaddingRight(), b - getPaddingBottom());
88 | mLayout.onLayoutChildren(mPools, mState);
89 | mState.dataChanged = false;
90 | resumeRequestLayout();
91 | }
92 |
93 | private void doRecycler() {
94 | final int childCount = getChildCount();
95 | if (childCount <= 0) return;
96 | for (int i = 0; i < childCount; i++) {
97 | View v = getChildAt(i);
98 | ViewGroup.LayoutParams p = v.getLayoutParams();
99 | if (!checkLayoutParams(p)) continue;
100 | ViewHolder holder = ((LayoutParams) p).mViewHolder;
101 | if (holder == null) continue;
102 | boolean intercept = mLayout != null && mLayout.onInterceptRecycle(childCount, i, holder);
103 | if (!intercept) {
104 | mPools.release(holder);
105 | }
106 | holder.onRecycled();
107 | }
108 | detachAllViewsFromParent();
109 | }
110 |
111 |
112 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
113 | return p instanceof LayoutParams;
114 | }
115 |
116 | public Adapter getAdapter() {
117 | return mAdapter;
118 | }
119 |
120 | public PhotoContents setAdapter(Adapter adapter) {
121 | if (this.mAdapter == adapter) return this;
122 | if (this.mAdapter != null) {
123 | this.mAdapter.unregisterAdapterDataObserver(mObserver);
124 | this.mAdapter.onDetachedFromPhotoContents(this);
125 | }
126 | doRecycler();
127 | mPools.clearPool(new SimplePool.OnClearListener() {
128 | @Override
129 | public void onClear(ViewHolder cached) {
130 | cached.onDead();
131 | }
132 | });
133 |
134 | this.mAdapter = adapter;
135 | if (adapter != null) {
136 | adapter.registerAdapterDataObserver(mObserver);
137 | adapter.onAttachedToPhotoContents(this);
138 | }
139 | mState.dataChanged = true;
140 | return this;
141 | }
142 |
143 | public PhotoContents setLayoutManager(LayoutManager manager) {
144 | if (this.mLayout != null) {
145 | doRecycler();
146 | this.mLayout = null;
147 | }
148 | this.mLayout = manager;
149 | mLayout.bindPhotoContents(this);
150 | requestLayout();
151 |
152 | return this;
153 | }
154 |
155 | protected LayoutParams generateDefaultLayoutParams() {
156 | return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
157 | }
158 |
159 | public SimplePool getRecyclerPools() {
160 | return mPools;
161 | }
162 |
163 | private void preventRequestLayout() {
164 | mState.preventRequestLayout++;
165 | }
166 |
167 | private void resumeRequestLayout() {
168 | mState.preventRequestLayout--;
169 | if (mState.preventRequestLayout < 0) {
170 | mState.preventRequestLayout = 0;
171 | }
172 | }
173 |
174 | @Override
175 | public void requestLayout() {
176 | // if (mState == null || mState.preventRequestLayout != 0) {
177 | // NELog.i(TAG,"preventRequest");
178 | // return;
179 | // }
180 | super.requestLayout();
181 | }
182 |
183 | public State getState() {
184 | return mState;
185 | }
186 |
187 | @Nullable
188 | public ViewHolder findViewHolderForPosition(int position) {
189 | View v = getChildAt(position);
190 | if (v == null) return null;
191 | if (!checkLayoutParams(v.getLayoutParams())) return null;
192 | return ((LayoutParams) v.getLayoutParams()).mViewHolder;
193 |
194 | }
195 |
196 | public abstract static class LayoutManager {
197 |
198 | PhotoContents mPhotoContents;
199 |
200 | public abstract void onMeasure(@NonNull SimplePool pool, @NonNull State state, int widthSpec, int heightSpec);
201 |
202 | public abstract void onLayoutChildren(@NonNull SimplePool pool, @NonNull State state);
203 |
204 | public static int chooseSize(int spec, int desired, int min) {
205 | int mode = MeasureSpec.getMode(spec);
206 | int size = MeasureSpec.getSize(spec);
207 | switch (mode) {
208 | case MeasureSpec.AT_MOST:
209 | return Math.min(size, Math.max(desired, min));
210 | case MeasureSpec.EXACTLY:
211 | return size;
212 | case MeasureSpec.UNSPECIFIED:
213 | default:
214 | return Math.max(desired, min);
215 | }
216 | }
217 |
218 | protected void setMeasuredDimension(int widthSize, int heightSize) {
219 | this.mPhotoContents.setMeasuredDimension(widthSize, heightSize);
220 | }
221 |
222 | private void bindPhotoContents(PhotoContents mPhotoContents) {
223 | boolean call = this.mPhotoContents != mPhotoContents;
224 | this.mPhotoContents = mPhotoContents;
225 | if (call) {
226 | onAttachedPhotoContents(mPhotoContents);
227 | }
228 | }
229 |
230 | public void requestLayout() {
231 | this.mPhotoContents.requestLayout();
232 | }
233 |
234 | public PhotoContents getParent() {
235 | return mPhotoContents;
236 | }
237 |
238 | public int getChildCount() {
239 | return mPhotoContents.getChildCount();
240 | }
241 |
242 | public void addView(View child) {
243 | this.addView(child, -1);
244 | }
245 |
246 | public void addView(View child, int index) {
247 | this.addView(child, index, null);
248 | }
249 |
250 |
251 | public void addView(View child, int index, LayoutParams p) {
252 | this.addView(child, index, p, false);
253 | }
254 |
255 | public void addView(View child, int index, LayoutParams p, boolean preventRequestLayout) {
256 | if (preventRequestLayout) {
257 | mPhotoContents.addViewInLayout(child, index, p, true);
258 | } else {
259 | mPhotoContents.addView(child, index, p);
260 | }
261 | }
262 |
263 |
264 | public void removeViewAt(int index) {
265 | View child = this.getChildAt(index);
266 | if (child != null) {
267 | mPhotoContents.removeViewAt(index);
268 | }
269 |
270 | }
271 |
272 | public void removeAllViews() {
273 | int childCount = mPhotoContents.getChildCount();
274 | for (int i = childCount - 1; i >= 0; --i) {
275 | mPhotoContents.removeViewAt(i);
276 | }
277 | }
278 |
279 | public void detachView(@NonNull View child) {
280 | mPhotoContents.detachViewFromParent(child);
281 | }
282 |
283 | public void detachViewAt(int index) {
284 | mPhotoContents.detachViewsFromParent(index, 1);
285 | }
286 |
287 | public LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
288 | if (lp instanceof LayoutParams) {
289 | return new LayoutParams((LayoutParams) lp);
290 | } else {
291 | return lp instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) lp) : new LayoutParams(lp);
292 | }
293 | }
294 |
295 | public View getChildAt(int index) {
296 | return mPhotoContents.getChildAt(index);
297 | }
298 |
299 | public void onAttachedPhotoContents(PhotoContents PhotoContents) {
300 |
301 | }
302 |
303 | public boolean onInterceptRecycle(int childCount, int position, @NonNull ViewHolder holder) {
304 | return false;
305 | }
306 |
307 | protected ViewHolder obtainViewHolder(int position) {
308 | ViewHolder result = mPhotoContents.mPools.acquire();
309 | if (result == null) {
310 | result = createFromAdapter(position);
311 | }
312 | onPrepareViewHolder(result, position);
313 | return result;
314 | }
315 |
316 | protected ViewHolder createFromAdapter(int position) {
317 | return mPhotoContents.getAdapter().createViewHolder(mPhotoContents, position);
318 | }
319 |
320 | protected void onPrepareViewHolder(ViewHolder holder, int position) {
321 | ViewGroup.LayoutParams lp = holder.rootView.getLayoutParams();
322 | LayoutParams p;
323 |
324 | if (lp == null) {
325 | p = generateDefaultLayoutParams();
326 | holder.rootView.setLayoutParams(p);
327 | } else if (!checkLayoutParams(lp)) {
328 | p = generateLayoutParams(lp);
329 | holder.rootView.setLayoutParams(p);
330 | } else {
331 | p = (LayoutParams) lp;
332 | }
333 |
334 | holder.position = position;
335 | p.mViewHolder = holder;
336 | }
337 |
338 | public LayoutParams generateDefaultLayoutParams() {
339 | return mPhotoContents.generateDefaultLayoutParams();
340 | }
341 |
342 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
343 | return mPhotoContents.checkLayoutParams(p);
344 | }
345 |
346 | protected void doRecycler() {
347 | mPhotoContents.doRecycler();
348 | }
349 |
350 | protected ViewHolder findViewHolderForView(View v) {
351 | if (!checkLayoutParams(v.getLayoutParams())) {
352 | throw new IllegalArgumentException("View的layoutparams不正确");
353 | }
354 |
355 | return ((LayoutParams) v.getLayoutParams()).mViewHolder;
356 |
357 | }
358 | }
359 |
360 |
361 | public abstract static class AdapterDataObserver {
362 | public void onChanged() {
363 |
364 | }
365 | }
366 |
367 | private class PhotoContentsObserver extends AdapterDataObserver {
368 | @Override
369 | public void onChanged() {
370 | mState.reset();
371 | doRecycler();
372 | requestLayout();
373 | }
374 | }
375 |
376 | static class AdapterDataObservable extends Observable {
377 | AdapterDataObservable() {
378 | }
379 |
380 | public boolean hasObservers() {
381 | return !this.mObservers.isEmpty();
382 | }
383 |
384 | public void notifyChanged() {
385 | for (int i = this.mObservers.size() - 1; i >= 0; --i) {
386 | this.mObservers.get(i).onChanged();
387 | }
388 | }
389 | }
390 |
391 | public abstract static class Adapter {
392 | private final AdapterDataObservable mObservable = new AdapterDataObservable();
393 |
394 | public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
395 | this.mObservable.registerObserver(observer);
396 | }
397 |
398 | public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
399 | this.mObservable.unregisterObserver(observer);
400 | }
401 |
402 | public void onViewRecycled(@NonNull VH holder) {
403 | }
404 |
405 |
406 | public void onAttachedToPhotoContents(@NonNull PhotoContents PhotoContents) {
407 | }
408 |
409 | public void onDetachedFromPhotoContents(@NonNull PhotoContents PhotoContents) {
410 | }
411 |
412 | public abstract int getItemCount();
413 |
414 | public abstract VH onCreateViewHolder(ViewGroup parent, int position);
415 |
416 | public abstract void onBindViewHolder(VH viewHolder, int position);
417 |
418 | public final VH createViewHolder(ViewGroup parent, int position) {
419 | VH holder = onCreateViewHolder(parent, position);
420 | if (holder.rootView == null) {
421 | throw new NullPointerException("ViewHolder的rootView不能为空");
422 | }
423 | if (holder.rootView.getParent() != null) {
424 | throw new IllegalArgumentException("ViewHolder的rootView不能有Parent");
425 | }
426 | return holder;
427 | }
428 |
429 | public final void bindViewHolder(@NonNull VH viewHolder, int position) {
430 | viewHolder.position = position;
431 | onBindViewHolder(viewHolder, position);
432 | }
433 |
434 |
435 | public final void notifyDataSetChanged() {
436 | this.mObservable.notifyChanged();
437 | }
438 | }
439 |
440 | public abstract static class ViewHolder {
441 | public final View rootView;
442 | int position;
443 |
444 | public ViewHolder(View rootView) {
445 | this.rootView = rootView;
446 | }
447 |
448 | public int getPosition() {
449 | return position;
450 | }
451 |
452 | public void onRecycled() {
453 |
454 | }
455 |
456 | public void onDead() {
457 |
458 | }
459 |
460 | public final V findViewById(int resid) {
461 | if (resid > 0 && rootView != null) {
462 | return rootView.findViewById(resid);
463 | }
464 | return null;
465 | }
466 | }
467 |
468 | public static class LayoutParams extends MarginLayoutParams {
469 | ViewHolder mViewHolder;
470 |
471 | public LayoutParams(Context c, AttributeSet attrs) {
472 | super(c, attrs);
473 | }
474 |
475 | public LayoutParams(int width, int height) {
476 | super(width, height);
477 | }
478 |
479 | public LayoutParams(MarginLayoutParams source) {
480 | super(source);
481 | }
482 |
483 | public LayoutParams(ViewGroup.LayoutParams source) {
484 | super(source);
485 | }
486 | }
487 |
488 | public static class State {
489 |
490 | int preventRequestLayout;
491 | boolean dataChanged;
492 | int itemCount;
493 | Rect rect = new Rect();
494 | Rect tempRect = new Rect();
495 |
496 | void setBounds(int l, int t, int r, int b) {
497 | rect.set(l, t, r, b);
498 | }
499 |
500 | public boolean isDataChanged() {
501 | return dataChanged;
502 | }
503 |
504 | public int getItemCount() {
505 | return itemCount;
506 | }
507 |
508 | public Rect getBounds() {
509 | tempRect.set(rect);
510 | return tempRect;
511 | }
512 |
513 | void reset() {
514 | dataChanged = true;
515 | itemCount = 0;
516 | rect.setEmpty();
517 | tempRect.setEmpty();
518 | }
519 |
520 | @Override
521 | public String toString() {
522 | return "State{" +
523 | "preventRequestLayout=" + preventRequestLayout +
524 | ", dataChanged=" + dataChanged +
525 | ", itemCount=" + itemCount +
526 | ", rect=" + rect +
527 | ", tempRect=" + tempRect +
528 | '}';
529 | }
530 | }
531 | }
532 |
--------------------------------------------------------------------------------
/lib/src/main/java/razerdp/github/com/widget/layoutmanager/NineGridLayoutManager.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.widget.layoutmanager;
2 |
3 | import android.graphics.Rect;
4 | import android.support.annotation.NonNull;
5 | import android.view.View;
6 |
7 | import razerdp.github.com.widget.PhotoContents;
8 | import razerdp.github.com.widget.util.SimplePool;
9 |
10 | /**
11 | * Created by 大灯泡 on 2019/7/2
12 | *
13 | * Description:
14 | */
15 | public class NineGridLayoutManager extends PhotoContents.LayoutManager {
16 | private static final String TAG = "NineGridLayoutManager";
17 | private int itemSpace = 0;
18 | private LayoutState mLayoutState;
19 | SimplePool mSinglePool;
20 |
21 | public NineGridLayoutManager(int itemSpace) {
22 | this.itemSpace = itemSpace;
23 | mSinglePool = new SimplePool<>(9);
24 | mLayoutState = new LayoutState();
25 | }
26 |
27 | @Override
28 | public void onMeasure(@NonNull SimplePool pool, @NonNull PhotoContents.State state, int widthSpec, int heightSpec) {
29 | mLayoutState.widthMeasureSpec = widthSpec;
30 | mLayoutState.heightMeasureSpec = heightSpec;
31 | mLayoutState.itemCount = state.getItemCount();
32 | if (state.isDataChanged()) {
33 | doRecycler();
34 | }
35 |
36 | if (state.getItemCount() == 1) {
37 | int parentHeight = View.MeasureSpec.getSize(heightSpec) - getParent().getPaddingTop() - getParent().getPaddingBottom();
38 | int parentWidth = View.MeasureSpec.getSize(widthSpec) - getParent().getPaddingLeft() - getParent().getPaddingRight();
39 | View v = measureChild(getChildAt(0), 0, widthSpec, View.MeasureSpec.makeMeasureSpec(parentHeight <= 0 ? parentWidth : parentHeight, View.MeasureSpec.AT_MOST));
40 | mLayoutState.widthMeasureSpec = widthSpec;
41 | mLayoutState.heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(v.getMeasuredHeight(), View.MeasureSpec.AT_MOST);
42 | } else {
43 | if (View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY && View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) {
44 | measureWithExactly(state, widthSpec, heightSpec);
45 | } else {
46 | measureWithAtMost(state, widthSpec, heightSpec);
47 | }
48 | }
49 |
50 | setMeasuredDimension(mLayoutState.widthMeasureSpec, mLayoutState.heightMeasureSpec);
51 | }
52 |
53 | private void measureWithExactly(PhotoContents.State state, int widthSpec, int heightSpec) {
54 | int parentHeight = View.MeasureSpec.getSize(heightSpec) - getParent().getPaddingTop() - getParent().getPaddingBottom();
55 | int parentWidth = View.MeasureSpec.getSize(widthSpec) - getParent().getPaddingLeft() - getParent().getPaddingRight();
56 |
57 | final int itemCount = state.getItemCount();
58 |
59 | int childWidth = (parentWidth - (itemSpace << 1)) / 3;
60 | int childHeight = (parentHeight - (itemSpace << 1)) / 3;
61 | int size = Math.min(childWidth, childHeight);
62 |
63 | for (int i = 0; i < itemCount; i++) {
64 | measureChild(getChildAt(i), i,
65 | View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY),
66 | View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY));
67 | }
68 |
69 | mLayoutState.widthMeasureSpec = widthSpec;
70 | mLayoutState.heightMeasureSpec = heightSpec;
71 | }
72 |
73 | private void measureWithAtMost(PhotoContents.State state, int widthSpec, int heightSpec) {
74 | int widthMode = View.MeasureSpec.getMode(widthSpec);
75 | int heightMode = View.MeasureSpec.getMode(heightSpec);
76 |
77 | int parentHeight = View.MeasureSpec.getSize(heightSpec) - getParent().getPaddingTop() - getParent().getPaddingBottom();
78 | int parentWidth = View.MeasureSpec.getSize(widthSpec) - getParent().getPaddingLeft() - getParent().getPaddingRight();
79 |
80 | final int itemCount = state.getItemCount();
81 |
82 | int rowCount = itemCount % 3 == 0 ? itemCount / 3 : itemCount / 3 + 1;
83 | int columnCount = itemCount >= 3 ? 3 : itemCount;
84 |
85 | if (itemCount == 4) {
86 | rowCount = 2;
87 | columnCount = 2;
88 | }
89 |
90 | if (widthMode == View.MeasureSpec.EXACTLY) {
91 | int size = (parentWidth - (itemSpace << 1)) / 3;
92 |
93 | for (int i = 0; i < itemCount; i++) {
94 | measureChild(getChildAt(i), i, View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY),
95 | View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY));
96 | }
97 | parentHeight = size * rowCount + Math.max(0, rowCount - 1) * itemSpace;
98 | heightSpec = View.MeasureSpec.makeMeasureSpec(parentHeight, heightMode);
99 | } else if (heightMode == View.MeasureSpec.EXACTLY) {
100 | int size = (parentHeight - (itemSpace << 1)) / 3;
101 | for (int i = 0; i < itemCount; i++) {
102 | measureChild(getChildAt(i), i, View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY),
103 | View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY));
104 | }
105 | parentWidth = size * columnCount + Math.max(0, columnCount - 1) * itemSpace;
106 | widthSpec = View.MeasureSpec.makeMeasureSpec(parentWidth, widthMode);
107 | }
108 | mLayoutState.widthMeasureSpec = widthSpec;
109 | mLayoutState.heightMeasureSpec = heightSpec;
110 | }
111 |
112 |
113 | private View measureChild(View v, int position, int widthSpec, int heightSpec) {
114 | if (v == null) {
115 | v = obtainViewHolder(position).rootView;
116 | }
117 | if (v.getVisibility() == View.GONE) return v;
118 | v.measure(widthSpec, heightSpec);
119 | if (v.getParent() == null) {
120 | addViewInternal(v, position);
121 | }
122 | return v;
123 | }
124 |
125 | private void addViewInternal(View v, int pos) {
126 | if (v == null) return;
127 | PhotoContents.ViewHolder holder = findViewHolderForView(v);
128 | if (holder == null) {
129 | throw new NullPointerException("ViewHolder为空");
130 | }
131 |
132 | getParent().getAdapter().bindViewHolder(holder, pos);
133 | addView(v, -1, (PhotoContents.LayoutParams) v.getLayoutParams(), true);
134 | }
135 |
136 | @Override
137 | public void onLayoutChildren(@NonNull SimplePool pool, @NonNull PhotoContents.State state) {
138 | Rect bounds = state.getBounds();
139 |
140 | final int childCount = getChildCount();
141 | if (childCount == 1) {
142 | View child = getChildAt(0);
143 | child.layout(bounds.left, bounds.top, bounds.left + child.getMeasuredWidth(), bounds.top + child.getMeasuredHeight());
144 | } else {
145 | int left = bounds.left;
146 | int top = bounds.top;
147 | for (int i = 0; i < childCount; i++) {
148 | View child = getChildAt(i);
149 | if (child.getVisibility() == View.GONE) continue;
150 |
151 | int right = left + child.getMeasuredWidth();
152 | int bottom = top + child.getMeasuredHeight();
153 |
154 | child.layout(left, top, right, bottom);
155 |
156 | left += child.getMeasuredWidth() + itemSpace;
157 |
158 | if (childCount == 4) {
159 | if (i == 1) {
160 | //换行
161 | left = bounds.left;
162 | top = bottom + itemSpace;
163 | }
164 | } else {
165 | if ((i + 1) % 3 == 0) {
166 | left = bounds.left;
167 | top = bottom + itemSpace;
168 | }
169 | }
170 | }
171 | }
172 | }
173 |
174 | @Override
175 | protected PhotoContents.ViewHolder obtainViewHolder(int position) {
176 | if (mLayoutState.itemCount == 1) {
177 | PhotoContents.ViewHolder result = mSinglePool.acquire();
178 | if (result == null) {
179 | result = createFromAdapter(position);
180 | }
181 | onPrepareViewHolder(result, position);
182 | return result;
183 | }
184 | return super.obtainViewHolder(position);
185 | }
186 |
187 | @Override
188 | public boolean onInterceptRecycle(int childCount, int position, @NonNull PhotoContents.ViewHolder holder) {
189 | if (childCount == 1) {
190 | mSinglePool.release(holder);
191 | return true;
192 | }
193 | return super.onInterceptRecycle(childCount, position, holder);
194 | }
195 |
196 | private static class LayoutState {
197 | int widthMeasureSpec;
198 | int heightMeasureSpec;
199 | int itemCount;
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/lib/src/main/java/razerdp/github/com/widget/util/SimplePool.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.widget.util;
2 |
3 |
4 | import android.util.Log;
5 |
6 | /**
7 | * Created by 大灯泡 on 2019/4/25
8 | *
9 | * Description:
10 | */
11 | public class SimplePool {
12 | private static final String TAG = "SimplePool";
13 | private final Object[] mPool;
14 | private int mPoolSize;
15 |
16 | public SimplePool(int maxPoolSize) {
17 | if (maxPoolSize <= 0) {
18 | throw new IllegalArgumentException("The max pool size must be > 0");
19 | } else {
20 | this.mPool = new Object[maxPoolSize];
21 | }
22 | }
23 |
24 | public T acquire() {
25 | if (this.mPoolSize > 0) {
26 | int lastPooledIndex = this.mPoolSize - 1;
27 | T instance = (T) this.mPool[lastPooledIndex];
28 | this.mPool[lastPooledIndex] = null;
29 | --this.mPoolSize;
30 | return instance;
31 | } else {
32 | return null;
33 | }
34 | }
35 |
36 | public boolean release(T instance) {
37 | if (this.isInPool(instance)) {
38 | Log.e(TAG, "Already in the pool!");
39 | return false;
40 | } else if (this.mPoolSize < this.mPool.length) {
41 | this.mPool[this.mPoolSize] = instance;
42 | ++this.mPoolSize;
43 | return true;
44 | } else {
45 | return false;
46 | }
47 | }
48 |
49 | public boolean isFull() {
50 | return this.mPoolSize == mPool.length;
51 | }
52 |
53 | public boolean isInPool(T instance) {
54 | for (int i = 0; i < this.mPoolSize; ++i) {
55 | if (this.mPool[i] == instance) {
56 | return true;
57 | }
58 | }
59 |
60 | return false;
61 | }
62 |
63 | public void clearPool() {
64 | clearPool(null);
65 | }
66 |
67 | public void clearPool(OnClearListener l) {
68 | for (int i = 0; i < mPool.length; i++) {
69 | if (l != null && mPool[i] != null) {
70 | l.onClear((T) mPool[i]);
71 | }
72 | mPool[i] = null;
73 | }
74 | mPoolSize = 0;
75 | }
76 |
77 | public int size() {
78 | return mPoolSize;
79 | }
80 |
81 | public interface OnClearListener {
82 | void onClear(T cached);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/lib/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Widget
3 |
4 |
--------------------------------------------------------------------------------
/lib/src/test/java/razerdp/github/com/widget/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package razerdp.github.com.widget;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':lib'
2 |
--------------------------------------------------------------------------------