1145873331@qq.com 6 ans auparavant
Parent
commit
63487e0a25

+ 46 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/DataImageView.java

@ -0,0 +1,46 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.content.Context;
4
import android.graphics.Bitmap;
5
import android.util.AttributeSet;
6
import android.widget.ImageView;
7
8
/**
9
 * 这只是一个简单的ImageView,可以存放Bitmap和Path等信息
10
 *
11
 * @author xmuSistone
12
 */
13
public class DataImageView extends ImageView {
14
15
    private String absolutePath;
16
    private Bitmap bitmap;
17
18
    public DataImageView(Context context) {
19
        this(context, null);
20
    }
21
22
    public DataImageView(Context context, AttributeSet attrs) {
23
        this(context, attrs, 0);
24
    }
25
26
    public DataImageView(Context context, AttributeSet attrs, int defStyle) {
27
        super(context, attrs, defStyle);
28
    }
29
30
    public String getAbsolutePath() {
31
        return absolutePath;
32
    }
33
34
    public void setAbsolutePath(String absolutePath) {
35
        this.absolutePath = absolutePath;
36
    }
37
38
    public Bitmap getBitmap() {
39
        return bitmap;
40
    }
41
42
    public void setBitmap(Bitmap bitmap) {
43
        this.bitmap = bitmap;
44
    }
45
46
}

+ 60 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/DeletableEditText.java

@ -0,0 +1,60 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.content.Context;
4
import android.util.AttributeSet;
5
import android.view.KeyEvent;
6
import android.view.inputmethod.EditorInfo;
7
import android.view.inputmethod.InputConnection;
8
import android.view.inputmethod.InputConnectionWrapper;
9
import android.widget.EditText;
10
11
/**
12
 * 这个是从stackOverFlow上面找到的解决方案,主要用途是处理软键盘回删按钮backSpace时回调OnKeyListener
13
 *
14
 * @author xmuSistone
15
 */
16
public class DeletableEditText extends EditText {
17
18
    public DeletableEditText(Context context, AttributeSet attrs, int defStyle) {
19
        super(context, attrs, defStyle);
20
    }
21
22
    public DeletableEditText(Context context, AttributeSet attrs) {
23
        super(context, attrs);
24
    }
25
26
    public DeletableEditText(Context context) {
27
        super(context);
28
    }
29
30
    @Override
31
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
32
        return new DeleteInputConnection(super.onCreateInputConnection(outAttrs),
33
                true);
34
    }
35
36
    private class DeleteInputConnection extends InputConnectionWrapper {
37
38
        public DeleteInputConnection(InputConnection target, boolean mutable) {
39
            super(target, mutable);
40
        }
41
42
        @Override
43
        public boolean sendKeyEvent(KeyEvent event) {
44
            return super.sendKeyEvent(event);
45
        }
46
47
        @Override
48
        public boolean deleteSurroundingText(int beforeLength, int afterLength) {
49
            if (beforeLength == 1 && afterLength == 0) {
50
                return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
51
                        KeyEvent.KEYCODE_DEL))
52
                        && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
53
                        KeyEvent.KEYCODE_DEL));
54
            }
55
56
            return super.deleteSurroundingText(beforeLength, afterLength);
57
        }
58
59
    }
60
}

+ 1546 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/ImageUtils.java

@ -0,0 +1,1546 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.graphics.Bitmap;
4
import android.graphics.Bitmap.CompressFormat;
5
import android.graphics.BitmapFactory;
6
import android.graphics.Canvas;
7
import android.graphics.Color;
8
import android.graphics.ColorMatrix;
9
import android.graphics.ColorMatrixColorFilter;
10
import android.graphics.LinearGradient;
11
import android.graphics.Matrix;
12
import android.graphics.Paint;
13
import android.graphics.PixelFormat;
14
import android.graphics.PorterDuff;
15
import android.graphics.PorterDuffXfermode;
16
import android.graphics.Rect;
17
import android.graphics.RectF;
18
import android.graphics.Shader;
19
import android.graphics.drawable.BitmapDrawable;
20
import android.graphics.drawable.Drawable;
21
import android.media.ExifInterface;
22
import android.support.annotation.IntRange;
23
import android.view.View;
24
25
import java.io.ByteArrayOutputStream;
26
import java.io.File;
27
import java.io.FileDescriptor;
28
import java.io.IOException;
29
import java.io.InputStream;
30
31
/**
32
 * Created by demon on 2017/8/30.
33
 */
34
35
public class ImageUtils {
36
    private ImageUtils() {
37
        throw new UnsupportedOperationException("u can't instantiate me...");
38
    }
39
40
    /**
41
     * bitmap转byteArr
42
     *
43
     * @param bitmap bitmap对象
44
     * @param format 格式
45
     * @return 字节数组
46
     */
47
    public static byte[] bitmap2Bytes(final Bitmap bitmap, final CompressFormat format) {
48
        if (bitmap == null) return null;
49
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
50
        bitmap.compress(format, 100, baos);
51
        return baos.toByteArray();
52
    }
53
54
    /**
55
     * byteArr转bitmap
56
     *
57
     * @param bytes 字节数组
58
     * @return bitmap
59
     */
60
    public static Bitmap bytes2Bitmap(final byte[] bytes) {
61
        return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
62
    }
63
64
    /**
65
     * drawable转bitmap
66
     *
67
     * @param drawable drawable对象
68
     * @return bitmap
69
     */
70
    public static Bitmap drawable2Bitmap(final Drawable drawable) {
71
        if (drawable instanceof BitmapDrawable) {
72
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
73
            if (bitmapDrawable.getBitmap() != null) {
74
                return bitmapDrawable.getBitmap();
75
            }
76
        }
77
        Bitmap bitmap;
78
        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
79
            bitmap = Bitmap.createBitmap(1, 1,
80
                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
81
        } else {
82
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
83
                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
84
        }
85
        Canvas canvas = new Canvas(bitmap);
86
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
87
        drawable.draw(canvas);
88
        return bitmap;
89
    }
90
91
    /**
92
     * bitmap转drawable
93
     *
94
     * @param bitmap bitmap对象
95
     * @return drawable
96
     */
97
//    public static Drawable bitmap2Drawable(final Bitmap bitmap) {
98
//        return bitmap == null ? null : new BitmapDrawable(Utils.getApp().getResources(), bitmap);
99
//    }
100
101
    /**
102
     * drawable转byteArr
103
     *
104
     * @param drawable drawable对象
105
     * @param format   格式
106
     * @return 字节数组
107
     */
108
    public static byte[] drawable2Bytes(final Drawable drawable, final CompressFormat format) {
109
        return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format);
110
    }
111
112
    /**
113
     * byteArr转drawable
114
     *
115
     * @param bytes 字节数组
116
     * @return drawable
117
     */
118
//    public static Drawable bytes2Drawable(final byte[] bytes) {
119
//        return bitmap2Drawable(bytes2Bitmap(bytes));
120
//    }
121
122
    /**
123
     * view转Bitmap
124
     *
125
     * @param view 视图
126
     * @return bitmap
127
     */
128
    public static Bitmap view2Bitmap(final View view) {
129
        if (view == null) return null;
130
        Bitmap ret = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
131
        Canvas canvas = new Canvas(ret);
132
        Drawable bgDrawable = view.getBackground();
133
        if (bgDrawable != null) {
134
            bgDrawable.draw(canvas);
135
        } else {
136
            canvas.drawColor(Color.WHITE);
137
        }
138
        view.draw(canvas);
139
        return ret;
140
    }
141
142
    /**
143
     * 计算采样大小
144
     *
145
     * @param options   选项
146
     * @param maxWidth  最大宽度
147
     * @param maxHeight 最大高度
148
     * @return 采样大小
149
     */
150
    private static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth, final int maxHeight) {
151
        if (maxWidth == 0 || maxHeight == 0) return 1;
152
        int height = options.outHeight;
153
        int width = options.outWidth;
154
        int inSampleSize = 1;
155
        while ((height >>= 1) > maxHeight && (width >>= 1) > maxWidth) {
156
            inSampleSize <<= 1;
157
        }
158
        return inSampleSize;
159
    }
160
161
    /**
162
     * 获取bitmap
163
     *
164
     * @param file 文件
165
     * @return bitmap
166
     */
167
//    public static Bitmap getBitmap(final File file) {
168
//        if (file == null) return null;
169
//        InputStream is = null;
170
//        try {
171
//            is = new BufferedInputStream(new FileInputStream(file));
172
//            return BitmapFactory.decodeStream(is);
173
//        } catch (FileNotFoundException e) {
174
//            e.printStackTrace();
175
//            return null;
176
//        } finally {
177
//            CloseUtils.closeIO(is);
178
//        }
179
//    }
180
181
    /**
182
     * 获取bitmap
183
     *
184
     * @param file      文件
185
     * @param maxWidth  最大宽度
186
     * @param maxHeight 最大高度
187
     * @return bitmap
188
     */
189
//    public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) {
190
//        if (file == null) return null;
191
//        InputStream is = null;
192
//        try {
193
//            BitmapFactory.Options options = new BitmapFactory.Options();
194
//            options.inJustDecodeBounds = true;
195
//            is = new BufferedInputStream(new FileInputStream(file));
196
//            BitmapFactory.decodeStream(is, null, options);
197
//            options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
198
//            options.inJustDecodeBounds = false;
199
//            return BitmapFactory.decodeStream(is, null, options);
200
//        } catch (FileNotFoundException e) {
201
//            e.printStackTrace();
202
//            return null;
203
//        } finally {
204
//            CloseUtils.closeIO(is);
205
//        }
206
//    }
207
208
    /**
209
     * 获取bitmap
210
     *
211
     * @param filePath 文件路径
212
     * @return bitmap
213
     */
214
    public static Bitmap getBitmap(final String filePath) {
215
        if (isSpace(filePath)) return null;
216
        return BitmapFactory.decodeFile(filePath);
217
    }
218
219
    /**
220
     * 获取bitmap
221
     *
222
     * @param filePath  文件路径
223
     * @param maxWidth  最大宽度
224
     * @param maxHeight 最大高度
225
     * @return bitmap
226
     */
227
    public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) {
228
        if (isSpace(filePath)) return null;
229
        BitmapFactory.Options options = new BitmapFactory.Options();
230
        options.inJustDecodeBounds = true;
231
        BitmapFactory.decodeFile(filePath, options);
232
        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
233
        options.inJustDecodeBounds = false;
234
        return BitmapFactory.decodeFile(filePath, options);
235
    }
236
237
    /**
238
     * 获取bitmap
239
     *
240
     * @param is 输入流
241
     * @return bitmap
242
     */
243
    public static Bitmap getBitmap(final InputStream is) {
244
        if (is == null) return null;
245
        return BitmapFactory.decodeStream(is);
246
    }
247
248
    /**
249
     * 获取bitmap
250
     *
251
     * @param is        输入流
252
     * @param maxWidth  最大宽度
253
     * @param maxHeight 最大高度
254
     * @return bitmap
255
     */
256
    public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) {
257
        if (is == null) return null;
258
        BitmapFactory.Options options = new BitmapFactory.Options();
259
        options.inJustDecodeBounds = true;
260
        BitmapFactory.decodeStream(is, null, options);
261
        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
262
        options.inJustDecodeBounds = false;
263
        return BitmapFactory.decodeStream(is, null, options);
264
    }
265
266
    /**
267
     * 获取bitmap
268
     *
269
     * @param data   数据
270
     * @param offset 偏移量
271
     * @return bitmap
272
     */
273
    public static Bitmap getBitmap(final byte[] data, final int offset) {
274
        if (data.length == 0) return null;
275
        return BitmapFactory.decodeByteArray(data, offset, data.length);
276
    }
277
278
    /**
279
     * 获取bitmap
280
     *
281
     * @param data      数据
282
     * @param offset    偏移量
283
     * @param maxWidth  最大宽度
284
     * @param maxHeight 最大高度
285
     * @return bitmap
286
     */
287
    public static Bitmap getBitmap(final byte[] data, final int offset, final int maxWidth, final int maxHeight) {
288
        if (data.length == 0) return null;
289
        BitmapFactory.Options options = new BitmapFactory.Options();
290
        options.inJustDecodeBounds = true;
291
        BitmapFactory.decodeByteArray(data, offset, data.length, options);
292
        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
293
        options.inJustDecodeBounds = false;
294
        return BitmapFactory.decodeByteArray(data, offset, data.length, options);
295
    }
296
297
    /**
298
     * 获取bitmap
299
     *
300
     * @param resId 资源id
301
     * @return bitmap
302
     */
303
//    public static Bitmap getBitmap(@DrawableRes final int resId) {
304
//        return BitmapFactory.decodeResource(Utils.getApp().getResources(), resId);
305
//    }
306
307
    /**
308
     * 获取bitmap
309
     *
310
     * @param resId     资源id
311
     * @param maxWidth  最大宽度
312
     * @param maxHeight 最大高度
313
     * @return bitmap
314
     */
315
//    public static Bitmap getBitmap(@DrawableRes final int resId, final int maxWidth, final int maxHeight) {
316
//        BitmapFactory.Options options = new BitmapFactory.Options();
317
//        options.inJustDecodeBounds = true;
318
//        BitmapFactory.decodeResource(Utils.getApp().getResources(), resId, options);
319
//        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
320
//        options.inJustDecodeBounds = false;
321
//        return BitmapFactory.decodeResource(Utils.getApp().getResources(), resId, options);
322
//    }
323
324
    /**
325
     * 获取bitmap
326
     *
327
     * @param fd 文件描述
328
     * @return bitmap
329
     */
330
    public static Bitmap getBitmap(final FileDescriptor fd) {
331
        if (fd == null) return null;
332
        return BitmapFactory.decodeFileDescriptor(fd);
333
    }
334
335
    /**
336
     * 获取bitmap
337
     *
338
     * @param fd        文件描述
339
     * @param maxWidth  最大宽度
340
     * @param maxHeight 最大高度
341
     * @return bitmap
342
     */
343
    public static Bitmap getBitmap(final FileDescriptor fd, final int maxWidth, final int maxHeight) {
344
        if (fd == null) return null;
345
        BitmapFactory.Options options = new BitmapFactory.Options();
346
        options.inJustDecodeBounds = true;
347
        BitmapFactory.decodeFileDescriptor(fd, null, options);
348
        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
349
        options.inJustDecodeBounds = false;
350
        return BitmapFactory.decodeFileDescriptor(fd, null, options);
351
    }
352
353
    /**
354
     * 缩放图片
355
     *
356
     * @param src       源图片
357
     * @param newWidth  新宽度
358
     * @param newHeight 新高度
359
     * @return 缩放后的图片
360
     */
361
    public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight) {
362
        return scale(src, newWidth, newHeight, false);
363
    }
364
365
    /**
366
     * 缩放图片
367
     *
368
     * @param src       源图片
369
     * @param newWidth  新宽度
370
     * @param newHeight 新高度
371
     * @param recycle   是否回收
372
     * @return 缩放后的图片
373
     */
374
    public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
375
        if (isEmptyBitmap(src)) return null;
376
        Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true);
377
        if (recycle && !src.isRecycled()) src.recycle();
378
        return ret;
379
    }
380
381
    /**
382
     * 缩放图片
383
     *
384
     * @param src         源图片
385
     * @param scaleWidth  缩放宽度倍数
386
     * @param scaleHeight 缩放高度倍数
387
     * @return 缩放后的图片
388
     */
389
    public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight) {
390
        return scale(src, scaleWidth, scaleHeight, false);
391
    }
392
393
    /**
394
     * 缩放图片
395
     *
396
     * @param src         源图片
397
     * @param scaleWidth  缩放宽度倍数
398
     * @param scaleHeight 缩放高度倍数
399
     * @param recycle     是否回收
400
     * @return 缩放后的图片
401
     */
402
    public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
403
        if (isEmptyBitmap(src)) return null;
404
        Matrix matrix = new Matrix();
405
        matrix.setScale(scaleWidth, scaleHeight);
406
        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
407
        if (recycle && !src.isRecycled()) src.recycle();
408
        return ret;
409
    }
410
411
    /**
412
     * 裁剪图片
413
     *
414
     * @param src    源图片
415
     * @param x      开始坐标x
416
     * @param y      开始坐标y
417
     * @param width  裁剪宽度
418
     * @param height 裁剪高度
419
     * @return 裁剪后的图片
420
     */
421
    public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height) {
422
        return clip(src, x, y, width, height, false);
423
    }
424
425
    /**
426
     * 裁剪图片
427
     *
428
     * @param src     源图片
429
     * @param x       开始坐标x
430
     * @param y       开始坐标y
431
     * @param width   裁剪宽度
432
     * @param height  裁剪高度
433
     * @param recycle 是否回收
434
     * @return 裁剪后的图片
435
     */
436
    public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height, final boolean recycle) {
437
        if (isEmptyBitmap(src)) return null;
438
        Bitmap ret = Bitmap.createBitmap(src, x, y, width, height);
439
        if (recycle && !src.isRecycled()) src.recycle();
440
        return ret;
441
    }
442
443
    /**
444
     * 倾斜图片
445
     *
446
     * @param src 源图片
447
     * @param kx  倾斜因子x
448
     * @param ky  倾斜因子y
449
     * @return 倾斜后的图片
450
     */
451
    public static Bitmap skew(final Bitmap src, final float kx, final float ky) {
452
        return skew(src, kx, ky, 0, 0, false);
453
    }
454
455
    /**
456
     * 倾斜图片
457
     *
458
     * @param src     源图片
459
     * @param kx      倾斜因子x
460
     * @param ky      倾斜因子y
461
     * @param recycle 是否回收
462
     * @return 倾斜后的图片
463
     */
464
    public static Bitmap skew(final Bitmap src, final float kx, final float ky, final boolean recycle) {
465
        return skew(src, kx, ky, 0, 0, recycle);
466
    }
467
468
    /**
469
     * 倾斜图片
470
     *
471
     * @param src 源图片
472
     * @param kx  倾斜因子x
473
     * @param ky  倾斜因子y
474
     * @param px  平移因子x
475
     * @param py  平移因子y
476
     * @return 倾斜后的图片
477
     */
478
    public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py) {
479
        return skew(src, kx, ky, px, py, false);
480
    }
481
482
    /**
483
     * 倾斜图片
484
     *
485
     * @param src     源图片
486
     * @param kx      倾斜因子x
487
     * @param ky      倾斜因子y
488
     * @param px      平移因子x
489
     * @param py      平移因子y
490
     * @param recycle 是否回收
491
     * @return 倾斜后的图片
492
     */
493
    public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py, final boolean recycle) {
494
        if (isEmptyBitmap(src)) return null;
495
        Matrix matrix = new Matrix();
496
        matrix.setSkew(kx, ky, px, py);
497
        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
498
        if (recycle && !src.isRecycled()) src.recycle();
499
        return ret;
500
    }
501
502
    /**
503
     * 旋转图片
504
     *
505
     * @param src     源图片
506
     * @param degrees 旋转角度
507
     * @param px      旋转点横坐标
508
     * @param py      旋转点纵坐标
509
     * @return 旋转后的图片
510
     */
511
    public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py) {
512
        return rotate(src, degrees, px, py, false);
513
    }
514
515
    /**
516
     * 旋转图片
517
     *
518
     * @param src     源图片
519
     * @param degrees 旋转角度
520
     * @param px      旋转点横坐标
521
     * @param py      旋转点纵坐标
522
     * @param recycle 是否回收
523
     * @return 旋转后的图片
524
     */
525
    public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py, final boolean recycle) {
526
        if (isEmptyBitmap(src)) return null;
527
        if (degrees == 0) return src;
528
        Matrix matrix = new Matrix();
529
        matrix.setRotate(degrees, px, py);
530
        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
531
        if (recycle && !src.isRecycled()) src.recycle();
532
        return ret;
533
    }
534
535
    /**
536
     * 获取图片旋转角度
537
     *
538
     * @param filePath 文件路径
539
     * @return 旋转角度
540
     */
541
    public static int getRotateDegree(final String filePath) {
542
        int degree = 0;
543
        try {
544
            ExifInterface exifInterface = new ExifInterface(filePath);
545
            int orientation = exifInterface.getAttributeInt(
546
                    ExifInterface.TAG_ORIENTATION,
547
                    ExifInterface.ORIENTATION_NORMAL);
548
            switch (orientation) {
549
                default:
550
                case ExifInterface.ORIENTATION_ROTATE_90:
551
                    degree = 90;
552
                    break;
553
                case ExifInterface.ORIENTATION_ROTATE_180:
554
                    degree = 180;
555
                    break;
556
                case ExifInterface.ORIENTATION_ROTATE_270:
557
                    degree = 270;
558
                    break;
559
            }
560
        } catch (IOException e) {
561
            e.printStackTrace();
562
        }
563
        return degree;
564
    }
565
566
    /**
567
     * 转为圆形图片
568
     *
569
     * @param src 源图片
570
     * @return 圆形图片
571
     */
572
    public static Bitmap toRound(final Bitmap src) {
573
        return toRound(src, false);
574
    }
575
576
    /**
577
     * 转为圆形图片
578
     *
579
     * @param src     源图片
580
     * @param recycle 是否回收
581
     * @return 圆形图片
582
     */
583
    public static Bitmap toRound(final Bitmap src, final boolean recycle) {
584
        if (isEmptyBitmap(src)) return null;
585
        int width = src.getWidth();
586
        int height = src.getHeight();
587
        int radius = Math.min(width, height) >> 1;
588
        Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
589
        Paint paint = new Paint();
590
        Canvas canvas = new Canvas(ret);
591
        Rect rect = new Rect(0, 0, width, height);
592
        paint.setAntiAlias(true);
593
        canvas.drawARGB(0, 0, 0, 0);
594
        canvas.drawCircle(width >> 1, height >> 1, radius, paint);
595
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
596
        canvas.drawBitmap(src, rect, rect, paint);
597
        if (recycle && !src.isRecycled()) src.recycle();
598
        return ret;
599
    }
600
601
    /**
602
     * 转为圆角图片
603
     *
604
     * @param src    源图片
605
     * @param radius 圆角的度数
606
     * @return 圆角图片
607
     */
608
    public static Bitmap toRoundCorner(final Bitmap src, final float radius) {
609
        return toRoundCorner(src, radius, false);
610
    }
611
612
    /**
613
     * 转为圆角图片
614
     *
615
     * @param src     源图片
616
     * @param radius  圆角的度数
617
     * @param recycle 是否回收
618
     * @return 圆角图片
619
     */
620
    public static Bitmap toRoundCorner(final Bitmap src, final float radius, final boolean recycle) {
621
        if (null == src) return null;
622
        int width = src.getWidth();
623
        int height = src.getHeight();
624
        Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
625
        Paint paint = new Paint();
626
        Canvas canvas = new Canvas(ret);
627
        Rect rect = new Rect(0, 0, width, height);
628
        paint.setAntiAlias(true);
629
        canvas.drawRoundRect(new RectF(rect), radius, radius, paint);
630
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
631
        canvas.drawBitmap(src, rect, rect, paint);
632
        if (recycle && !src.isRecycled()) src.recycle();
633
        return ret;
634
    }
635
636
    /**
637
     * 快速模糊
638
     * <p>先缩小原图,对小图进行模糊,再放大回原先尺寸</p>
639
     *
640
     * @param src    源图片
641
     * @param scale  缩放比例(0...1)
642
     * @param radius 模糊半径
643
     * @return 模糊后的图片
644
     */
645
//    public static Bitmap fastBlur(final Bitmap src,
646
//                                  @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale,
647
//                                  @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) {
648
//        return fastBlur(src, scale, radius, false);
649
//    }
650
651
    /**
652
     * 快速模糊图片
653
     * <p>先缩小原图,对小图进行模糊,再放大回原先尺寸</p>
654
     *
655
     * @param src     源图片
656
     * @param scale   缩放比例(0...1)
657
     * @param radius  模糊半径(0...25)
658
     * @param recycle 是否回收
659
     * @return 模糊后的图片
660
     */
661
//    public static Bitmap fastBlur(final Bitmap src,
662
//                                  @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale,
663
//                                  @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius,
664
//                                  boolean recycle) {
665
//        if (isEmptyBitmap(src)) return null;
666
//        int width = src.getWidth();
667
//        int height = src.getHeight();
668
//        int scaleWidth = (int) (width * scale + 0.5f);
669
//        int scaleHeight = (int) (height * scale + 0.5f);
670
//        if (scaleWidth == 0 || scaleHeight == 0) return null;
671
//        Bitmap scaleBitmap = Bitmap.createScaledBitmap(src, scaleWidth, scaleHeight, true);
672
//        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
673
//        Canvas canvas = new Canvas();
674
//        PorterDuffColorFilter filter = new PorterDuffColorFilter(
675
//                Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
676
//        paint.setColorFilter(filter);
677
//        canvas.scale(scale, scale);
678
//        canvas.drawBitmap(scaleBitmap, 0, 0, paint);
679
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
680
//            scaleBitmap = renderScriptBlur(scaleBitmap, radius);
681
//        } else {
682
//            scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle);
683
//        }
684
//        if (scale == 1) return scaleBitmap;
685
//        Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true);
686
//        if (scaleBitmap != null && !scaleBitmap.isRecycled()) scaleBitmap.recycle();
687
//        if (recycle && !src.isRecycled()) src.recycle();
688
//        return ret;
689
//    }
690
691
    /**
692
     * renderScript模糊图片
693
     * <p>API大于17</p>
694
     *
695
     * @param src    源图片
696
     * @param radius 模糊半径(0...25)
697
     * @return 模糊后的图片
698
     */
699
//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
700
//    public static Bitmap renderScriptBlur(final Bitmap src,
701
//                                          @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) {
702
//        if (isEmptyBitmap(src)) return null;
703
//        RenderScript rs = null;
704
//        try {
705
//            rs = RenderScript.create(Utils.getApp());
706
//            rs.setMessageHandler(new RenderScript.RSMessageHandler());
707
//            Allocation input = Allocation.createFromBitmap(rs, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation
708
//                    .USAGE_SCRIPT);
709
//            Allocation output = Allocation.createTyped(rs, input.getType());
710
//            ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
711
//            blurScript.setInput(input);
712
//            blurScript.setRadius(radius);
713
//            blurScript.forEach(output);
714
//            output.copyTo(src);
715
//        } finally {
716
//            if (rs != null) {
717
//                rs.destroy();
718
//            }
719
//        }
720
//        return src;
721
//    }
722
723
    /**
724
     * stack模糊图片
725
     *
726
     * @param src     源图片
727
     * @param radius  模糊半径
728
     * @param recycle 是否回收
729
     * @return stack模糊后的图片
730
     */
731
    public static Bitmap stackBlur(final Bitmap src, final int radius, final boolean recycle) {
732
        Bitmap ret;
733
        if (recycle) {
734
            ret = src;
735
        } else {
736
            ret = src.copy(src.getConfig(), true);
737
        }
738
739
        if (radius < 1) {
740
            return null;
741
        }
742
743
        int w = ret.getWidth();
744
        int h = ret.getHeight();
745
746
        int[] pix = new int[w * h];
747
        ret.getPixels(pix, 0, w, 0, 0, w, h);
748
749
        int wm = w - 1;
750
        int hm = h - 1;
751
        int wh = w * h;
752
        int div = radius + radius + 1;
753
754
        int r[] = new int[wh];
755
        int g[] = new int[wh];
756
        int b[] = new int[wh];
757
        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
758
        int vmin[] = new int[Math.max(w, h)];
759
760
        int divsum = (div + 1) >> 1;
761
        divsum *= divsum;
762
        int dv[] = new int[256 * divsum];
763
        for (i = 0; i < 256 * divsum; i++) {
764
            dv[i] = (i / divsum);
765
        }
766
767
        yw = yi = 0;
768
769
        int[][] stack = new int[div][3];
770
        int stackpointer;
771
        int stackstart;
772
        int[] sir;
773
        int rbs;
774
        int r1 = radius + 1;
775
        int routsum, goutsum, boutsum;
776
        int rinsum, ginsum, binsum;
777
778
        for (y = 0; y < h; y++) {
779
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
780
            for (i = -radius; i <= radius; i++) {
781
                p = pix[yi + Math.min(wm, Math.max(i, 0))];
782
                sir = stack[i + radius];
783
                sir[0] = (p & 0xff0000) >> 16;
784
                sir[1] = (p & 0x00ff00) >> 8;
785
                sir[2] = (p & 0x0000ff);
786
                rbs = r1 - Math.abs(i);
787
                rsum += sir[0] * rbs;
788
                gsum += sir[1] * rbs;
789
                bsum += sir[2] * rbs;
790
                if (i > 0) {
791
                    rinsum += sir[0];
792
                    ginsum += sir[1];
793
                    binsum += sir[2];
794
                } else {
795
                    routsum += sir[0];
796
                    goutsum += sir[1];
797
                    boutsum += sir[2];
798
                }
799
            }
800
            stackpointer = radius;
801
802
            for (x = 0; x < w; x++) {
803
804
                r[yi] = dv[rsum];
805
                g[yi] = dv[gsum];
806
                b[yi] = dv[bsum];
807
808
                rsum -= routsum;
809
                gsum -= goutsum;
810
                bsum -= boutsum;
811
812
                stackstart = stackpointer - radius + div;
813
                sir = stack[stackstart % div];
814
815
                routsum -= sir[0];
816
                goutsum -= sir[1];
817
                boutsum -= sir[2];
818
819
                if (y == 0) {
820
                    vmin[x] = Math.min(x + radius + 1, wm);
821
                }
822
                p = pix[yw + vmin[x]];
823
824
                sir[0] = (p & 0xff0000) >> 16;
825
                sir[1] = (p & 0x00ff00) >> 8;
826
                sir[2] = (p & 0x0000ff);
827
828
                rinsum += sir[0];
829
                ginsum += sir[1];
830
                binsum += sir[2];
831
832
                rsum += rinsum;
833
                gsum += ginsum;
834
                bsum += binsum;
835
836
                stackpointer = (stackpointer + 1) % div;
837
                sir = stack[(stackpointer) % div];
838
839
                routsum += sir[0];
840
                goutsum += sir[1];
841
                boutsum += sir[2];
842
843
                rinsum -= sir[0];
844
                ginsum -= sir[1];
845
                binsum -= sir[2];
846
847
                yi++;
848
            }
849
            yw += w;
850
        }
851
        for (x = 0; x < w; x++) {
852
            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
853
            yp = -radius * w;
854
            for (i = -radius; i <= radius; i++) {
855
                yi = Math.max(0, yp) + x;
856
857
                sir = stack[i + radius];
858
859
                sir[0] = r[yi];
860
                sir[1] = g[yi];
861
                sir[2] = b[yi];
862
863
                rbs = r1 - Math.abs(i);
864
865
                rsum += r[yi] * rbs;
866
                gsum += g[yi] * rbs;
867
                bsum += b[yi] * rbs;
868
869
                if (i > 0) {
870
                    rinsum += sir[0];
871
                    ginsum += sir[1];
872
                    binsum += sir[2];
873
                } else {
874
                    routsum += sir[0];
875
                    goutsum += sir[1];
876
                    boutsum += sir[2];
877
                }
878
879
                if (i < hm) {
880
                    yp += w;
881
                }
882
            }
883
            yi = x;
884
            stackpointer = radius;
885
            for (y = 0; y < h; y++) {
886
                // Preserve alpha channel: ( 0xff000000 & pix[yi] )
887
                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
888
889
                rsum -= routsum;
890
                gsum -= goutsum;
891
                bsum -= boutsum;
892
893
                stackstart = stackpointer - radius + div;
894
                sir = stack[stackstart % div];
895
896
                routsum -= sir[0];
897
                goutsum -= sir[1];
898
                boutsum -= sir[2];
899
900
                if (x == 0) {
901
                    vmin[y] = Math.min(y + r1, hm) * w;
902
                }
903
                p = x + vmin[y];
904
905
                sir[0] = r[p];
906
                sir[1] = g[p];
907
                sir[2] = b[p];
908
909
                rinsum += sir[0];
910
                ginsum += sir[1];
911
                binsum += sir[2];
912
913
                rsum += rinsum;
914
                gsum += ginsum;
915
                bsum += binsum;
916
917
                stackpointer = (stackpointer + 1) % div;
918
                sir = stack[stackpointer];
919
920
                routsum += sir[0];
921
                goutsum += sir[1];
922
                boutsum += sir[2];
923
924
                rinsum -= sir[0];
925
                ginsum -= sir[1];
926
                binsum -= sir[2];
927
928
                yi += w;
929
            }
930
        }
931
        ret.setPixels(pix, 0, w, 0, 0, w, h);
932
        return ret;
933
    }
934
935
    /**
936
     * 添加颜色边框
937
     *
938
     * @param src         源图片
939
     * @param borderWidth 边框宽度
940
     * @param color       边框的颜色值
941
     * @return 带颜色边框图
942
     */
943
    public static Bitmap addFrame(final Bitmap src, final int borderWidth, final int color) {
944
        return addFrame(src, borderWidth, color, false);
945
    }
946
947
    /**
948
     * 添加颜色边框
949
     *
950
     * @param src         源图片
951
     * @param borderWidth 边框宽度
952
     * @param color       边框的颜色值
953
     * @param recycle     是否回收
954
     * @return 带颜色边框图
955
     */
956
    public static Bitmap addFrame(final Bitmap src, final int borderWidth, final int color, final boolean recycle) {
957
        if (isEmptyBitmap(src)) return null;
958
        int doubleBorder = borderWidth << 1;
959
        int newWidth = src.getWidth() + doubleBorder;
960
        int newHeight = src.getHeight() + doubleBorder;
961
        Bitmap ret = Bitmap.createBitmap(newWidth, newHeight, src.getConfig());
962
        Canvas canvas = new Canvas(ret);
963
        //noinspection SuspiciousNameCombination
964
        canvas.drawBitmap(src, borderWidth, borderWidth, null);
965
        Paint paint = new Paint();
966
        paint.setColor(color);
967
        paint.setStyle(Paint.Style.STROKE);
968
        // setStrokeWidth是居中画的,所以要两倍的宽度才能画,否则有一半的宽度是空的
969
        paint.setStrokeWidth(doubleBorder);
970
        Rect rect = new Rect(0, 0, newWidth, newHeight);
971
        canvas.drawRect(rect, paint);
972
        if (recycle && !src.isRecycled()) src.recycle();
973
        return ret;
974
    }
975
976
    /**
977
     * 添加倒影
978
     *
979
     * @param src              源图片的
980
     * @param reflectionHeight 倒影高度
981
     * @return 带倒影图片
982
     */
983
    public static Bitmap addReflection(final Bitmap src, final int reflectionHeight) {
984
        return addReflection(src, reflectionHeight, false);
985
    }
986
987
    /**
988
     * 添加倒影
989
     *
990
     * @param src              源图片的
991
     * @param reflectionHeight 倒影高度
992
     * @param recycle          是否回收
993
     * @return 带倒影图片
994
     */
995
    public static Bitmap addReflection(final Bitmap src, final int reflectionHeight, final boolean recycle) {
996
        if (isEmptyBitmap(src)) return null;
997
        // 原图与倒影之间的间距
998
        final int REFLECTION_GAP = 0;
999
        int srcWidth = src.getWidth();
1000
        int srcHeight = src.getHeight();
1001
        Matrix matrix = new Matrix();
1002
        matrix.preScale(1, -1);
1003
        Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight,
1004
                srcWidth, reflectionHeight, matrix, false);
1005
        Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig());
1006
        Canvas canvas = new Canvas(ret);
1007
        canvas.drawBitmap(src, 0, 0, null);
1008
        canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
1009
        Paint paint = new Paint();
1010
        paint.setAntiAlias(true);
1011
        LinearGradient shader = new LinearGradient(0, srcHeight,
1012
                0, ret.getHeight() + REFLECTION_GAP,
1013
                0x70FFFFFF, 0x00FFFFFF, Shader.TileMode.MIRROR);
1014
        paint.setShader(shader);
1015
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
1016
        canvas.drawRect(0, srcHeight + REFLECTION_GAP,
1017
                srcWidth, ret.getHeight(), paint);
1018
        if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle();
1019
        if (recycle && !src.isRecycled()) src.recycle();
1020
        return ret;
1021
    }
1022
1023
    /**
1024
     * 添加文字水印
1025
     *
1026
     * @param src      源图片
1027
     * @param content  水印文本
1028
     * @param textSize 水印字体大小
1029
     * @param color    水印字体颜色
1030
     * @param x        起始坐标x
1031
     * @param y        起始坐标y
1032
     * @return 带有文字水印的图片
1033
     */
1034
    public static Bitmap addTextWatermark(final Bitmap src,
1035
                                          final String content,
1036
                                          final int textSize,
1037
                                          final int color,
1038
                                          final float x,
1039
                                          final float y) {
1040
        return addTextWatermark(src, content, textSize, color, x, y, false);
1041
    }
1042
1043
    /**
1044
     * 添加文字水印
1045
     *
1046
     * @param src      源图片
1047
     * @param content  水印文本
1048
     * @param textSize 水印字体大小
1049
     * @param color    水印字体颜色
1050
     * @param x        起始坐标x
1051
     * @param y        起始坐标y
1052
     * @param recycle  是否回收
1053
     * @return 带有文字水印的图片
1054
     */
1055
    public static Bitmap addTextWatermark(final Bitmap src,
1056
                                          final String content,
1057
                                          final float textSize,
1058
                                          final int color,
1059
                                          final float x,
1060
                                          final float y,
1061
                                          final boolean recycle) {
1062
        if (isEmptyBitmap(src) || content == null) return null;
1063
        Bitmap ret = src.copy(src.getConfig(), true);
1064
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
1065
        Canvas canvas = new Canvas(ret);
1066
        paint.setColor(color);
1067
        paint.setTextSize(textSize);
1068
        Rect bounds = new Rect();
1069
        paint.getTextBounds(content, 0, content.length(), bounds);
1070
        canvas.drawText(content, x, y + textSize, paint);
1071
        if (recycle && !src.isRecycled()) src.recycle();
1072
        return ret;
1073
    }
1074
1075
    /**
1076
     * 添加图片水印
1077
     *
1078
     * @param src       源图片
1079
     * @param watermark 图片水印
1080
     * @param x         起始坐标x
1081
     * @param y         起始坐标y
1082
     * @param alpha     透明度
1083
     * @return 带有图片水印的图片
1084
     */
1085
    public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha) {
1086
        return addImageWatermark(src, watermark, x, y, alpha, false);
1087
    }
1088
1089
    /**
1090
     * 添加图片水印
1091
     *
1092
     * @param src       源图片
1093
     * @param watermark 图片水印
1094
     * @param x         起始坐标x
1095
     * @param y         起始坐标y
1096
     * @param alpha     透明度
1097
     * @param recycle   是否回收
1098
     * @return 带有图片水印的图片
1099
     */
1100
    public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha, final boolean recycle) {
1101
        if (isEmptyBitmap(src)) return null;
1102
        Bitmap ret = src.copy(src.getConfig(), true);
1103
        if (!isEmptyBitmap(watermark)) {
1104
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
1105
            Canvas canvas = new Canvas(ret);
1106
            paint.setAlpha(alpha);
1107
            canvas.drawBitmap(watermark, x, y, paint);
1108
        }
1109
        if (recycle && !src.isRecycled()) src.recycle();
1110
        return ret;
1111
    }
1112
1113
    /**
1114
     * 转为alpha位图
1115
     *
1116
     * @param src 源图片
1117
     * @return alpha位图
1118
     */
1119
    public static Bitmap toAlpha(final Bitmap src) {
1120
        return toAlpha(src, false);
1121
    }
1122
1123
    /**
1124
     * 转为alpha位图
1125
     *
1126
     * @param src     源图片
1127
     * @param recycle 是否回收
1128
     * @return alpha位图
1129
     */
1130
    public static Bitmap toAlpha(final Bitmap src, final Boolean recycle) {
1131
        if (isEmptyBitmap(src)) return null;
1132
        Bitmap ret = src.extractAlpha();
1133
        if (recycle && !src.isRecycled()) src.recycle();
1134
        return ret;
1135
    }
1136
1137
    /**
1138
     * 转为灰度图片
1139
     *
1140
     * @param src 源图片
1141
     * @return 灰度图
1142
     */
1143
    public static Bitmap toGray(final Bitmap src) {
1144
        return toGray(src, false);
1145
    }
1146
1147
    /**
1148
     * 转为灰度图片
1149
     *
1150
     * @param src     源图片
1151
     * @param recycle 是否回收
1152
     * @return 灰度图
1153
     */
1154
    public static Bitmap toGray(final Bitmap src, final boolean recycle) {
1155
        if (isEmptyBitmap(src)) return null;
1156
        Bitmap grayBitmap = Bitmap.createBitmap(src.getWidth(),
1157
                src.getHeight(), Bitmap.Config.ARGB_8888);
1158
        Canvas canvas = new Canvas(grayBitmap);
1159
        Paint paint = new Paint();
1160
        ColorMatrix colorMatrix = new ColorMatrix();
1161
        colorMatrix.setSaturation(0);
1162
        ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
1163
        paint.setColorFilter(colorMatrixColorFilter);
1164
        canvas.drawBitmap(src, 0, 0, paint);
1165
        if (recycle && !src.isRecycled()) src.recycle();
1166
        return grayBitmap;
1167
    }
1168
1169
    /**
1170
     * 保存图片
1171
     *
1172
     * @param src      源图片
1173
     * @param filePath 要保存到的文件路径
1174
     * @param format   格式
1175
     * @return {@code true}: 成功<br>{@code false}: 失败
1176
     */
1177
//    public static boolean save(final Bitmap src, final String filePath, final CompressFormat format) {
1178
//        return save(src, FileUtils.getFileByPath(filePath), format, false);
1179
//    }
1180
1181
    /**
1182
     * 保存图片
1183
     *
1184
     * @param src    源图片
1185
     * @param file   要保存到的文件
1186
     * @param format 格式
1187
     * @return {@code true}: 成功<br>{@code false}: 失败
1188
     */
1189
//    public static boolean save(final Bitmap src, final File file, final CompressFormat format) {
1190
//        return save(src, file, format, false);
1191
//    }
1192
1193
    /**
1194
     * 保存图片
1195
     *
1196
     * @param src      源图片
1197
     * @param filePath 要保存到的文件路径
1198
     * @param format   格式
1199
     * @param recycle  是否回收
1200
     * @return {@code true}: 成功<br>{@code false}: 失败
1201
     */
1202
//    public static boolean save(final Bitmap src, final String filePath, final CompressFormat format, final boolean recycle) {
1203
//        return save(src, FileUtils.getFileByPath(filePath), format, recycle);
1204
//    }
1205
1206
    /**
1207
     * 保存图片
1208
     *
1209
     * @param src     源图片
1210
     * @param file    要保存到的文件
1211
     * @param format  格式
1212
     * @param recycle 是否回收
1213
     * @return {@code true}: 成功<br>{@code false}: 失败
1214
     */
1215
//    public static boolean save(final Bitmap src, final File file, final CompressFormat format, final boolean recycle) {
1216
//        if (isEmptyBitmap(src) || !FileUtils.createOrExistsFile(file)) return false;
1217
//        System.out.println(src.getWidth() + ", " + src.getHeight());
1218
//        OutputStream os = null;
1219
//        boolean ret = false;
1220
//        try {
1221
//            os = new BufferedOutputStream(new FileOutputStream(file));
1222
//            ret = src.compress(format, 100, os);
1223
//            if (recycle && !src.isRecycled()) src.recycle();
1224
//        } catch (IOException e) {
1225
//            e.printStackTrace();
1226
//        } finally {
1227
//            CloseUtils.closeIO(os);
1228
//        }
1229
//        return ret;
1230
//    }
1231
1232
    /**
1233
     * 根据文件名判断文件是否为图片
1234
     *
1235
     * @param file  文件
1236
     * @return {@code true}: 是<br>{@code false}: 否
1237
     */
1238
    public static boolean isImage(final File file) {
1239
        return file != null && isImage(file.getPath());
1240
    }
1241
1242
    /**
1243
     * 根据文件名判断文件是否为图片
1244
     *
1245
     * @param filePath  文件路径
1246
     * @return {@code true}: 是<br>{@code false}: 否
1247
     */
1248
    public static boolean isImage(final String filePath) {
1249
        String path = filePath.toUpperCase();
1250
        return path.endsWith(".PNG") || path.endsWith(".JPG")
1251
                || path.endsWith(".JPEG") || path.endsWith(".BMP")
1252
                || path.endsWith(".GIF");
1253
    }
1254
1255
    /**
1256
     * 获取图片类型
1257
     *
1258
     * @param filePath 文件路径
1259
     * @return 图片类型
1260
     */
1261
//    public static String getImageType(final String filePath) {
1262
//        return getImageType(FileUtils.getFileByPath(filePath));
1263
//    }
1264
1265
    /**
1266
     * 获取图片类型
1267
     *
1268
     * @param file 文件
1269
     * @return 图片类型
1270
     */
1271
//    public static String getImageType(final File file) {
1272
//        if (file == null) return null;
1273
//        InputStream is = null;
1274
//        try {
1275
//            is = new FileInputStream(file);
1276
//            return getImageType(is);
1277
//        } catch (IOException e) {
1278
//            e.printStackTrace();
1279
//            return null;
1280
//        } finally {
1281
//            CloseUtils.closeIO(is);
1282
//        }
1283
//    }
1284
1285
    /**
1286
     * 流获取图片类型
1287
     *
1288
     * @param is 图片输入流
1289
     * @return 图片类型
1290
     */
1291
    public static String getImageType(final InputStream is) {
1292
        if (is == null) return null;
1293
        try {
1294
            byte[] bytes = new byte[8];
1295
            return is.read(bytes, 0, 8) != -1 ? getImageType(bytes) : null;
1296
        } catch (IOException e) {
1297
            e.printStackTrace();
1298
            return null;
1299
        }
1300
    }
1301
1302
    /**
1303
     * 获取图片类型
1304
     *
1305
     * @param bytes bitmap的前8字节
1306
     * @return 图片类型
1307
     */
1308
    public static String getImageType(final byte[] bytes) {
1309
        if (isJPEG(bytes)) return "JPEG";
1310
        if (isGIF(bytes)) return "GIF";
1311
        if (isPNG(bytes)) return "PNG";
1312
        if (isBMP(bytes)) return "BMP";
1313
        return null;
1314
    }
1315
1316
    private static boolean isJPEG(final byte[] b) {
1317
        return b.length >= 2
1318
                && (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8);
1319
    }
1320
1321
    private static boolean isGIF(final byte[] b) {
1322
        return b.length >= 6
1323
                && b[0] == 'G' && b[1] == 'I'
1324
                && b[2] == 'F' && b[3] == '8'
1325
                && (b[4] == '7' || b[4] == '9') && b[5] == 'a';
1326
    }
1327
1328
    private static boolean isPNG(final byte[] b) {
1329
        return b.length >= 8
1330
                && (b[0] == (byte) 137 && b[1] == (byte) 80
1331
                && b[2] == (byte) 78 && b[3] == (byte) 71
1332
                && b[4] == (byte) 13 && b[5] == (byte) 10
1333
                && b[6] == (byte) 26 && b[7] == (byte) 10);
1334
    }
1335
1336
    private static boolean isBMP(final byte[] b) {
1337
        return b.length >= 2
1338
                && (b[0] == 0x42) && (b[1] == 0x4d);
1339
    }
1340
1341
    /**
1342
     * 判断bitmap对象是否为空
1343
     *
1344
     * @param src 源图片
1345
     * @return {@code true}: 是<br>{@code false}: 否
1346
     */
1347
    private static boolean isEmptyBitmap(final Bitmap src) {
1348
        return src == null || src.getWidth() == 0 || src.getHeight() == 0;
1349
    }
1350
1351
    ///////////////////////////////////////////////////////////////////////////
1352
    // 下方和压缩有关
1353
    ///////////////////////////////////////////////////////////////////////////
1354
1355
    /**
1356
     * 按缩放压缩
1357
     *
1358
     * @param src       源图片
1359
     * @param newWidth  新宽度
1360
     * @param newHeight 新高度
1361
     * @return 缩放压缩后的图片
1362
     */
1363
    public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight) {
1364
        return scale(src, newWidth, newHeight, false);
1365
    }
1366
1367
    /**
1368
     * 按缩放压缩
1369
     *
1370
     * @param src       源图片
1371
     * @param newWidth  新宽度
1372
     * @param newHeight 新高度
1373
     * @param recycle   是否回收
1374
     * @return 缩放压缩后的图片
1375
     */
1376
    public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
1377
        return scale(src, newWidth, newHeight, recycle);
1378
    }
1379
1380
    /**
1381
     * 按缩放压缩
1382
     *
1383
     * @param src         源图片
1384
     * @param scaleWidth  缩放宽度倍数
1385
     * @param scaleHeight 缩放高度倍数
1386
     * @return 缩放压缩后的图片
1387
     */
1388
    public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight) {
1389
        return scale(src, scaleWidth, scaleHeight, false);
1390
    }
1391
1392
    /**
1393
     * 按缩放压缩
1394
     *
1395
     * @param src         源图片
1396
     * @param scaleWidth  缩放宽度倍数
1397
     * @param scaleHeight 缩放高度倍数
1398
     * @param recycle     是否回收
1399
     * @return 缩放压缩后的图片
1400
     */
1401
    public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
1402
        return scale(src, scaleWidth, scaleHeight, recycle);
1403
    }
1404
1405
    /**
1406
     * 按质量压缩
1407
     *
1408
     * @param src     源图片
1409
     * @param quality 质量
1410
     * @return 质量压缩后的图片
1411
     */
1412
    public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality) {
1413
        return compressByQuality(src, quality, false);
1414
    }
1415
1416
    /**
1417
     * 按质量压缩
1418
     *
1419
     * @param src     源图片
1420
     * @param quality 质量
1421
     * @param recycle 是否回收
1422
     * @return 质量压缩后的图片
1423
     */
1424
    public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) {
1425
        if (isEmptyBitmap(src)) return null;
1426
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1427
        src.compress(CompressFormat.JPEG, quality, baos);
1428
        byte[] bytes = baos.toByteArray();
1429
        if (recycle && !src.isRecycled()) src.recycle();
1430
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
1431
    }
1432
1433
    /**
1434
     * 按质量压缩
1435
     *
1436
     * @param src         源图片
1437
     * @param maxByteSize 允许最大值字节数
1438
     * @return 质量压缩压缩过的图片
1439
     */
1440
    public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize) {
1441
        return compressByQuality(src, maxByteSize, false);
1442
    }
1443
1444
    /**
1445
     * 按质量压缩
1446
     *
1447
     * @param src         源图片
1448
     * @param maxByteSize 允许最大值字节数
1449
     * @param recycle     是否回收
1450
     * @return 质量压缩压缩过的图片
1451
     */
1452
    public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) {
1453
        if (isEmptyBitmap(src) || maxByteSize <= 0) return null;
1454
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1455
        int quality = 100;
1456
        src.compress(CompressFormat.JPEG, quality, baos);
1457
        while (baos.toByteArray().length > maxByteSize && quality > 0) {
1458
            baos.reset();
1459
            src.compress(CompressFormat.JPEG, quality -= 5, baos);
1460
        }
1461
        if (quality < 0) return null;
1462
        byte[] bytes = baos.toByteArray();
1463
        if (recycle && !src.isRecycled()) src.recycle();
1464
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
1465
    }
1466
1467
    /**
1468
     * 按采样大小压缩
1469
     *
1470
     * @param src        源图片
1471
     * @param sampleSize 采样率大小
1472
     * @return 按采样率压缩后的图片
1473
     */
1474
    public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize) {
1475
        return compressBySampleSize(src, sampleSize, false);
1476
    }
1477
1478
    /**
1479
     * 按采样大小压缩
1480
     *
1481
     * @param src        源图片
1482
     * @param sampleSize 采样率大小
1483
     * @param recycle    是否回收
1484
     * @return 按采样率压缩后的图片
1485
     */
1486
    public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) {
1487
        if (isEmptyBitmap(src)) return null;
1488
        BitmapFactory.Options options = new BitmapFactory.Options();
1489
        options.inSampleSize = sampleSize;
1490
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1491
        src.compress(CompressFormat.JPEG, 100, baos);
1492
        byte[] bytes = baos.toByteArray();
1493
        if (recycle && !src.isRecycled()) src.recycle();
1494
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
1495
    }
1496
1497
    private static boolean isSpace(final String s) {
1498
        if (s == null) return true;
1499
        for (int i = 0, len = s.length(); i < len; ++i) {
1500
            if (!Character.isWhitespace(s.charAt(i))) {
1501
                return false;
1502
            }
1503
        }
1504
        return true;
1505
    }
1506
1507
    /**
1508
     * 读取照片exif信息中的旋转角度
1509
     *
1510
     * @param path 照片路径
1511
     * @return角度
1512
     */
1513
    public static int readPictureDegree(String path) {
1514
        int degree = 0;
1515
        try {
1516
            ExifInterface exifInterface = new ExifInterface(path);
1517
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
1518
            switch (orientation) {
1519
                case ExifInterface.ORIENTATION_ROTATE_90:
1520
                    degree = 90;
1521
                    break;
1522
                case ExifInterface.ORIENTATION_ROTATE_180:
1523
                    degree = 180;
1524
                    break;
1525
                case ExifInterface.ORIENTATION_ROTATE_270:
1526
                    degree = 270;
1527
                    break;
1528
            }
1529
        } catch (IOException e) {
1530
            e.printStackTrace();
1531
        }
1532
        return degree;
1533
    }
1534
1535
1536
    public static Bitmap toturn(Bitmap img) {
1537
        Matrix matrix = new Matrix();
1538
        matrix.postRotate(+90); /*翻转90度*/
1539
        int width = img.getWidth();
1540
        int height = img.getHeight();
1541
        img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
1542
        return img;
1543
    }
1544
1545
1546
}

+ 415 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/RichTextEditor.java

@ -0,0 +1,415 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.animation.LayoutTransition;
4
import android.content.Context;
5
import android.graphics.Bitmap;
6
import android.graphics.BitmapFactory;
7
import android.graphics.Matrix;
8
import android.media.ExifInterface;
9
import android.util.AttributeSet;
10
import android.util.Log;
11
import android.view.KeyEvent;
12
import android.view.LayoutInflater;
13
import android.view.View;
14
import android.view.ViewGroup;
15
import android.view.inputmethod.InputMethodManager;
16
import android.widget.EditText;
17
import android.widget.ImageView;
18
import android.widget.LinearLayout;
19
import android.widget.RelativeLayout;
20
import android.widget.ScrollView;
21
22
import com.bumptech.glide.Glide;
23
import com.bumptech.glide.request.RequestOptions;
24
import com.electric.chargingpile.R;
25
26
import java.io.IOException;
27
import java.util.ArrayList;
28
import java.util.List;
29
30
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
31
32
/**
33
 * 可编辑富文本
34
 */
35
public class RichTextEditor extends ScrollView {
36
    private static final String TAG = "RichTextEditor";
37
    private static final int EDIT_PADDING = 10; // edittext常规padding是10dp
38
39
    private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
40
    private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
41
    private LayoutInflater inflater;
42
    private OnKeyListener keyListener; // 所有EditText的软键盘监听器
43
    private OnClickListener btnListener; // 图片右上角红叉按钮监听器
44
    private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener
45
    private EditText lastFocusEdit; // 最近被聚焦的EditText
46
    private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
47
    private int editNormalPadding = 0; //
48
    private int disappearingImageIndex = 0;
49
50
    public RichTextEditor(Context context) {
51
        this(context, null);
52
    }
53
54
    public RichTextEditor(Context context, AttributeSet attrs) {
55
        this(context, attrs, 0);
56
    }
57
58
    public RichTextEditor(Context context, AttributeSet attrs, int defStyleAttr) {
59
        super(context, attrs, defStyleAttr);
60
        inflater = LayoutInflater.from(context);
61
62
        // 1. 初始化allLayout
63
        allLayout = new LinearLayout(context);
64
        allLayout.setOrientation(LinearLayout.VERTICAL);
65
        //allLayout.setBackgroundColor(Color.WHITE);
66
        setupLayoutTransitions();
67
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
68
                LayoutParams.WRAP_CONTENT);
69
        allLayout.setPadding(50, 15, 50, 15);//设置间距,防止生成图片时文字太靠边,不能用margin,否则有黑边
70
        addView(allLayout, layoutParams);
71
72
        // 2. 初始化键盘退格监听
73
        // 主要用来处理点击回删按钮时,view的一些列合并操作
74
        keyListener = new OnKeyListener() {
75
76
            @Override
77
            public boolean onKey(View v, int keyCode, KeyEvent event) {
78
                if (event.getAction() == KeyEvent.ACTION_DOWN
79
                        && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
80
                    EditText edit = (EditText) v;
81
                    onBackspacePress(edit);
82
                }
83
                return false;
84
            }
85
        };
86
87
        // 3. 图片叉掉处理
88
        btnListener = new OnClickListener() {
89
90
            @Override
91
            public void onClick(View v) {
92
                RelativeLayout parentView = (RelativeLayout) v.getParent();
93
                onImageCloseClick(parentView);
94
            }
95
        };
96
97
        focusListener = new OnFocusChangeListener() {
98
99
            @Override
100
            public void onFocusChange(View v, boolean hasFocus) {
101
                if (hasFocus) {
102
                    lastFocusEdit = (EditText) v;
103
                }
104
            }
105
        };
106
107
        LinearLayout.LayoutParams firstEditParam = new LinearLayout.LayoutParams(
108
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
109
        //editNormalPadding = dip2px(EDIT_PADDING);
110
        EditText firstEdit = createEditText("请填写问题相关描述信息(选填)", dip2px(context, EDIT_PADDING));
111
        allLayout.addView(firstEdit, firstEditParam);
112
        lastFocusEdit = firstEdit;
113
    }
114
115
    /**
116
     * 初始化transition动画
117
     */
118
    private void setupLayoutTransitions() {
119
        mTransitioner = new LayoutTransition();
120
        allLayout.setLayoutTransition(mTransitioner);
121
        mTransitioner.addTransitionListener(new LayoutTransition.TransitionListener() {
122
123
            @Override
124
            public void startTransition(LayoutTransition transition,
125
                                        ViewGroup container, View view, int transitionType) {
126
127
            }
128
129
            @Override
130
            public void endTransition(LayoutTransition transition,
131
                                      ViewGroup container, View view, int transitionType) {
132
                if (!transition.isRunning()
133
                        && transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
134
                    // transition动画结束,合并EditText
135
                    // mergeEditText();
136
                }
137
            }
138
        });
139
        mTransitioner.setDuration(300);
140
    }
141
142
    public int dip2px(Context context, float dipValue) {
143
        float m = context.getResources().getDisplayMetrics().density;
144
        return (int) (dipValue * m + 0.5f);
145
    }
146
147
    /**
148
     * 处理软键盘backSpace回退事件
149
     *
150
     * @param editTxt 光标所在的文本输入框
151
     */
152
    private void onBackspacePress(EditText editTxt) {
153
        int startSelection = editTxt.getSelectionStart();
154
        // 只有在光标已经顶到文本输入框的最前方,在判定是否删除之前的图片,或两个View合并
155
        if (startSelection == 0) {
156
            int editIndex = allLayout.indexOfChild(editTxt);
157
            View preView = allLayout.getChildAt(editIndex - 1); // 如果editIndex-1<0,
158
            // 则返回的是null
159
            if (null != preView) {
160
                if (preView instanceof RelativeLayout) {
161
                    // 光标EditText的上一个view对应的是图片
162
                    onImageCloseClick(preView);
163
                } else if (preView instanceof EditText) {
164
                    // 光标EditText的上一个view对应的还是文本框EditText
165
                    String str1 = editTxt.getText().toString();
166
                    EditText preEdit = (EditText) preView;
167
                    String str2 = preEdit.getText().toString();
168
169
                    allLayout.removeView(editTxt);
170
171
                    // 文本合并
172
                    preEdit.setText(str2 + str1);
173
                    preEdit.requestFocus();
174
                    preEdit.setSelection(str2.length(), str2.length());
175
                    lastFocusEdit = preEdit;
176
                }
177
            }
178
        }
179
    }
180
181
    /**
182
     * 处理图片叉掉的点击事件
183
     *
184
     * @param view 整个image对应的relativeLayout view
185
     * @type 删除类型 0代表backspace删除 1代表按红叉按钮删除
186
     */
187
    private void onImageCloseClick(View view) {
188
        disappearingImageIndex = allLayout.indexOfChild(view);
189
        Log.e(TAG, "onImageCloseClick: " + disappearingImageIndex);
190
        //删除文件夹里的图片
191
        List<EditData> dataList = buildEditData();
192
        EditData editData = dataList.get(disappearingImageIndex);
193
        //Log.i("", "editData: "+editData);
194
        if (editData.imagePath != null) {
195
            SDCardUtil.deleteFile(editData.imagePath);
196
        }
197
198
        allLayout.removeView(view);
199
    }
200
201
    public void clearAllLayout() {
202
        allLayout.removeAllViews();
203
    }
204
205
    public int getLastIndex() {
206
        int lastEditIndex = allLayout.getChildCount();
207
        return lastEditIndex;
208
    }
209
210
    /**
211
     * 生成文本输入框
212
     */
213
    public EditText createEditText(String hint, int paddingTop) {
214
        EditText editText = (EditText) inflater.inflate(R.layout.rich_edittext, null);
215
        editText.setOnKeyListener(keyListener);
216
        editText.setTag(viewTagIndex++);
217
        editText.setPadding(editNormalPadding, paddingTop, editNormalPadding, paddingTop);
218
        editText.setHint(hint);
219
        editText.setOnFocusChangeListener(focusListener);
220
        return editText;
221
    }
222
223
    /**
224
     * 生成图片View
225
     */
226
    private RelativeLayout createImageLayout() {
227
        RelativeLayout layout = (RelativeLayout) inflater.inflate(
228
                R.layout.edit_imageview, null);
229
        layout.setTag(viewTagIndex++);
230
        View closeView = layout.findViewById(R.id.image_close);
231
        //closeView.setVisibility(GONE);
232
        closeView.setTag(layout.getTag());
233
        closeView.setOnClickListener(btnListener);
234
        return layout;
235
    }
236
237
    /**
238
     * 根据绝对路径添加view
239
     *
240
     * @param imagePath
241
     */
242
    public void insertImage(String imagePath, int width) {
243
        Bitmap bmp = getScaledBitmap(imagePath, width);
244
        insertImage(bmp, imagePath);
245
    }
246
247
    /**
248
     * 插入一张图片
249
     */
250
    public void insertImage(Bitmap bitmap, String imagePath) {
251
        String lastEditStr = lastFocusEdit.getText().toString();
252
        int cursorIndex = lastFocusEdit.getSelectionStart();
253
        String editStr1 = lastEditStr.substring(0, cursorIndex).trim();
254
        int lastEditIndex = allLayout.indexOfChild(lastFocusEdit);
255
256
        if (lastEditStr.length() == 0 || editStr1.length() == 0) {
257
            // 如果EditText为空,或者光标已经顶在了editText的最前面,则直接插入图片,并且EditText下移即可
258
            addImageViewAtIndex(lastEditIndex, imagePath);
259
        } else {
260
            // 如果EditText非空且光标不在最顶端,则需要添加新的imageView和EditText
261
            lastFocusEdit.setText(editStr1);
262
            String editStr2 = lastEditStr.substring(cursorIndex).trim();
263
            if (editStr2.length() == 0) {
264
                editStr2 = " ";
265
            }
266
            if (allLayout.getChildCount() - 1 == lastEditIndex) {
267
                addEditTextAtIndex(lastEditIndex + 1, editStr2);
268
            }
269
270
            addImageViewAtIndex(lastEditIndex + 1, imagePath);
271
            lastFocusEdit.requestFocus();
272
            lastFocusEdit.setSelection(editStr1.length(), editStr1.length());//TODO
273
        }
274
        hideKeyBoard();
275
    }
276
277
    /**
278
     * 隐藏小键盘
279
     */
280
    public void hideKeyBoard() {
281
        InputMethodManager imm = (InputMethodManager) getContext()
282
                .getSystemService(Context.INPUT_METHOD_SERVICE);
283
        imm.hideSoftInputFromWindow(lastFocusEdit.getWindowToken(), 0);
284
    }
285
286
    /**
287
     * 在特定位置插入EditText
288
     *
289
     * @param index   位置
290
     * @param editStr EditText显示的文字
291
     */
292
    public void addEditTextAtIndex(final int index, CharSequence editStr) {
293
        EditText editText2 = createEditText("", EDIT_PADDING);
294
        editText2.setText(editStr);
295
        editText2.setOnFocusChangeListener(focusListener);
296
297
        allLayout.addView(editText2, index);
298
    }
299
300
    /**
301
     * 在特定位置添加ImageView
302
     */
303
    public void addImageViewAtIndex(final int index, String imagePath) {
304
        final RelativeLayout imageLayout = createImageLayout();
305
        DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
306
        Log.e(TAG, "addImageViewAtIndex: " + imagePath);
307
308
309
        if (readPictureDegree(imagePath) != 0) {
310
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
311
            Log.e(TAG, "addImageViewAtIndex: " + "!!!!!!!!!!");
312
            bitmap = toturn(bitmap);
313
            Log.e(TAG, "addImageViewAtIndex: " + "--------");
314
            imageView.setImageBitmap(bitmap);
315
        } else {
316
            RequestOptions options = new RequestOptions().centerCrop();
317
            Glide.with(getContext()).load(imagePath).apply(options).transition(withCrossFade()).into(imageView);
318
        }
319
320
321
        imageView.setAbsolutePath(imagePath);//保留这句,后面保存数据会用
322
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中
323
324
        // 调整imageView的高度,根据宽度来调整高度
325
        Bitmap bmp = BitmapFactory.decodeFile(imagePath);
326
        int imageHeight = 500;
327
        if (bmp != null) {
328
            imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth();
329
            bmp.recycle();
330
        }
331
        // TODO: 17/3/1 调整图片高度,这里是否有必要,如果出现微博长图,可能会很难看
332
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
333
                LayoutParams.MATCH_PARENT, imageHeight);//设置图片固定高度
334
        lp.bottomMargin = 10;
335
        imageView.setLayoutParams(lp);
336
337
        allLayout.addView(imageLayout, index);
338
    }
339
340
    /**
341
     * 根据view的宽度,动态缩放bitmap尺寸
342
     *
343
     * @param width view的宽度
344
     */
345
    public Bitmap getScaledBitmap(String filePath, int width) {
346
        BitmapFactory.Options options = new BitmapFactory.Options();
347
        options.inJustDecodeBounds = true;
348
        BitmapFactory.decodeFile(filePath, options);
349
        int sampleSize = options.outWidth > width ? options.outWidth / width
350
                + 1 : 1;
351
        options.inJustDecodeBounds = false;
352
        options.inSampleSize = sampleSize;
353
        return BitmapFactory.decodeFile(filePath, options);
354
    }
355
356
    /**
357
     * 对外提供的接口, 生成编辑数据上传
358
     */
359
    public List<EditData> buildEditData() {
360
        List<EditData> dataList = new ArrayList<EditData>();
361
        int num = allLayout.getChildCount();
362
        for (int index = 0; index < num; index++) {
363
            View itemView = allLayout.getChildAt(index);
364
            EditData itemData = new EditData();
365
            if (itemView instanceof EditText) {
366
                EditText item = (EditText) itemView;
367
                itemData.inputStr = item.getText().toString();
368
            } else if (itemView instanceof RelativeLayout) {
369
                DataImageView item = (DataImageView) itemView.findViewById(R.id.edit_imageView);
370
                itemData.imagePath = item.getAbsolutePath();
371
            }
372
            dataList.add(itemData);
373
        }
374
375
        return dataList;
376
    }
377
378
    public class EditData {
379
        public String inputStr;
380
        public String imagePath;
381
    }
382
383
    public static int readPictureDegree(String path) {
384
        int degree = 0;
385
        try {
386
            ExifInterface exifInterface = new ExifInterface(path);
387
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
388
            switch (orientation) {
389
                case ExifInterface.ORIENTATION_ROTATE_90:
390
                    degree = 90;
391
                    break;
392
                case ExifInterface.ORIENTATION_ROTATE_180:
393
                    degree = 180;
394
                    break;
395
                case ExifInterface.ORIENTATION_ROTATE_270:
396
                    degree = 270;
397
                    break;
398
            }
399
        } catch (IOException e) {
400
            e.printStackTrace();
401
        }
402
        Log.e(TAG, "readPictureDegree: degree=" + degree);
403
        return degree;
404
    }
405
406
407
    public static Bitmap toturn(Bitmap img) {
408
        Matrix matrix = new Matrix();
409
        matrix.postRotate(+90); /*翻转90度*/
410
        int width = img.getWidth();
411
        int height = img.getHeight();
412
        img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
413
        return img;
414
    }
415
}

+ 412 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/RichTextEditorForA.java

@ -0,0 +1,412 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.animation.LayoutTransition;
4
import android.content.Context;
5
import android.graphics.Bitmap;
6
import android.graphics.BitmapFactory;
7
import android.graphics.Matrix;
8
import android.media.ExifInterface;
9
import android.util.AttributeSet;
10
import android.util.Log;
11
import android.view.KeyEvent;
12
import android.view.LayoutInflater;
13
import android.view.View;
14
import android.view.ViewGroup;
15
import android.view.inputmethod.InputMethodManager;
16
import android.widget.EditText;
17
import android.widget.ImageView;
18
import android.widget.LinearLayout;
19
import android.widget.RelativeLayout;
20
import android.widget.ScrollView;
21
22
import com.bumptech.glide.Glide;
23
import com.bumptech.glide.request.RequestOptions;
24
import com.electric.chargingpile.R;
25
26
import java.io.IOException;
27
import java.util.ArrayList;
28
import java.util.List;
29
30
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
31
32
/**
33
 * 可编辑富文本
34
 */
35
public class RichTextEditorForA extends ScrollView {
36
    private static final String TAG = "RichTextEditorForA";
37
    private static final int EDIT_PADDING = 10; // edittext常规padding是10dp
38
39
    private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
40
    private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
41
    private LayoutInflater inflater;
42
    private OnKeyListener keyListener; // 所有EditText的软键盘监听器
43
    private OnClickListener btnListener; // 图片右上角红叉按钮监听器
44
    private OnFocusChangeListener focusListener; // 所有EditText的焦点监听listener
45
    private EditText lastFocusEdit; // 最近被聚焦的EditText
46
    private LayoutTransition mTransitioner; // 只在图片View添加或remove时,触发transition动画
47
    private int editNormalPadding = 0; //
48
    private int disappearingImageIndex = 0;
49
50
    public RichTextEditorForA(Context context) {
51
        this(context, null);
52
    }
53
54
    public RichTextEditorForA(Context context, AttributeSet attrs) {
55
        this(context, attrs, 0);
56
    }
57
58
    public RichTextEditorForA(Context context, AttributeSet attrs, int defStyleAttr) {
59
        super(context, attrs, defStyleAttr);
60
        inflater = LayoutInflater.from(context);
61
62
        // 1. 初始化allLayout
63
        allLayout = new LinearLayout(context);
64
        allLayout.setOrientation(LinearLayout.VERTICAL);
65
        //allLayout.setBackgroundColor(Color.WHITE);
66
        setupLayoutTransitions();
67
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
68
                LayoutParams.WRAP_CONTENT);
69
        allLayout.setPadding(50, 15, 50, 15);//设置间距,防止生成图片时文字太靠边,不能用margin,否则有黑边
70
        addView(allLayout, layoutParams);
71
72
        // 2. 初始化键盘退格监听
73
        // 主要用来处理点击回删按钮时,view的一些列合并操作
74
        keyListener = new OnKeyListener() {
75
76
            @Override
77
            public boolean onKey(View v, int keyCode, KeyEvent event) {
78
                if (event.getAction() == KeyEvent.ACTION_DOWN
79
                        && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
80
                    EditText edit = (EditText) v;
81
                    onBackspacePress(edit);
82
                }
83
                return false;
84
            }
85
        };
86
87
        // 3. 图片叉掉处理
88
        btnListener = new OnClickListener() {
89
90
            @Override
91
            public void onClick(View v) {
92
                RelativeLayout parentView = (RelativeLayout) v.getParent();
93
                onImageCloseClick(parentView);
94
            }
95
        };
96
97
        focusListener = new OnFocusChangeListener() {
98
99
            @Override
100
            public void onFocusChange(View v, boolean hasFocus) {
101
                if (hasFocus) {
102
                    lastFocusEdit = (EditText) v;
103
                }
104
            }
105
        };
106
107
        LinearLayout.LayoutParams firstEditParam = new LinearLayout.LayoutParams(
108
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
109
        //editNormalPadding = dip2px(EDIT_PADDING);
110
        EditText firstEdit = createEditText("请填写问题相关的回答", dip2px(context, EDIT_PADDING));
111
        allLayout.addView(firstEdit, firstEditParam);
112
        lastFocusEdit = firstEdit;
113
    }
114
115
    /**
116
     * 初始化transition动画
117
     */
118
    private void setupLayoutTransitions() {
119
        mTransitioner = new LayoutTransition();
120
        allLayout.setLayoutTransition(mTransitioner);
121
        mTransitioner.addTransitionListener(new LayoutTransition.TransitionListener() {
122
123
            @Override
124
            public void startTransition(LayoutTransition transition,
125
                                        ViewGroup container, View view, int transitionType) {
126
127
            }
128
129
            @Override
130
            public void endTransition(LayoutTransition transition,
131
                                      ViewGroup container, View view, int transitionType) {
132
                if (!transition.isRunning()
133
                        && transitionType == LayoutTransition.CHANGE_DISAPPEARING) {
134
                    // transition动画结束,合并EditText
135
                    // mergeEditText();
136
                }
137
            }
138
        });
139
        mTransitioner.setDuration(300);
140
    }
141
142
    public int dip2px(Context context, float dipValue) {
143
        float m = context.getResources().getDisplayMetrics().density;
144
        return (int) (dipValue * m + 0.5f);
145
    }
146
147
    /**
148
     * 处理软键盘backSpace回退事件
149
     *
150
     * @param editTxt 光标所在的文本输入框
151
     */
152
    private void onBackspacePress(EditText editTxt) {
153
        int startSelection = editTxt.getSelectionStart();
154
        // 只有在光标已经顶到文本输入框的最前方,在判定是否删除之前的图片,或两个View合并
155
        if (startSelection == 0) {
156
            int editIndex = allLayout.indexOfChild(editTxt);
157
            View preView = allLayout.getChildAt(editIndex - 1); // 如果editIndex-1<0,
158
            // 则返回的是null
159
            if (null != preView) {
160
                if (preView instanceof RelativeLayout) {
161
                    // 光标EditText的上一个view对应的是图片
162
                    onImageCloseClick(preView);
163
                } else if (preView instanceof EditText) {
164
                    // 光标EditText的上一个view对应的还是文本框EditText
165
                    String str1 = editTxt.getText().toString();
166
                    EditText preEdit = (EditText) preView;
167
                    String str2 = preEdit.getText().toString();
168
169
                    allLayout.removeView(editTxt);
170
171
                    // 文本合并
172
                    preEdit.setText(str2 + str1);
173
                    preEdit.requestFocus();
174
                    preEdit.setSelection(str2.length(), str2.length());
175
                    lastFocusEdit = preEdit;
176
                }
177
            }
178
        }
179
    }
180
181
    /**
182
     * 处理图片叉掉的点击事件
183
     *
184
     * @param view 整个image对应的relativeLayout view
185
     * @type 删除类型 0代表backspace删除 1代表按红叉按钮删除
186
     */
187
    private void onImageCloseClick(View view) {
188
        disappearingImageIndex = allLayout.indexOfChild(view);
189
        Log.e(TAG, "onImageCloseClick: " + disappearingImageIndex);
190
        //删除文件夹里的图片
191
        List<EditData> dataList = buildEditData();
192
        EditData editData = dataList.get(disappearingImageIndex);
193
        //Log.i("", "editData: "+editData);
194
        if (editData.imagePath != null) {
195
            SDCardUtil.deleteFile(editData.imagePath);
196
        }
197
198
        allLayout.removeView(view);
199
    }
200
201
    public void clearAllLayout() {
202
        allLayout.removeAllViews();
203
    }
204
205
    public int getLastIndex() {
206
        int lastEditIndex = allLayout.getChildCount();
207
        return lastEditIndex;
208
    }
209
210
    /**
211
     * 生成文本输入框
212
     */
213
    public EditText createEditText(String hint, int paddingTop) {
214
        EditText editText = (EditText) inflater.inflate(R.layout.rich_edittext, null);
215
        editText.setOnKeyListener(keyListener);
216
        editText.setTag(viewTagIndex++);
217
        editText.setPadding(editNormalPadding, paddingTop, editNormalPadding, paddingTop);
218
        editText.setHint(hint);
219
        editText.setOnFocusChangeListener(focusListener);
220
        return editText;
221
    }
222
223
    /**
224
     * 生成图片View
225
     */
226
    private RelativeLayout createImageLayout() {
227
        RelativeLayout layout = (RelativeLayout) inflater.inflate(
228
                R.layout.edit_imageview, null);
229
        layout.setTag(viewTagIndex++);
230
        View closeView = layout.findViewById(R.id.image_close);
231
        //closeView.setVisibility(GONE);
232
        closeView.setTag(layout.getTag());
233
        closeView.setOnClickListener(btnListener);
234
        return layout;
235
    }
236
237
    /**
238
     * 根据绝对路径添加view
239
     *
240
     * @param imagePath
241
     */
242
    public void insertImage(String imagePath, int width) {
243
        Bitmap bmp = getScaledBitmap(imagePath, width);
244
        insertImage(bmp, imagePath);
245
    }
246
247
    /**
248
     * 插入一张图片
249
     */
250
    public void insertImage(Bitmap bitmap, String imagePath) {
251
        String lastEditStr = lastFocusEdit.getText().toString();
252
        int cursorIndex = lastFocusEdit.getSelectionStart();
253
        String editStr1 = lastEditStr.substring(0, cursorIndex).trim();
254
        int lastEditIndex = allLayout.indexOfChild(lastFocusEdit);
255
256
        if (lastEditStr.length() == 0 || editStr1.length() == 0) {
257
            // 如果EditText为空,或者光标已经顶在了editText的最前面,则直接插入图片,并且EditText下移即可
258
            addImageViewAtIndex(lastEditIndex, imagePath);
259
        } else {
260
            // 如果EditText非空且光标不在最顶端,则需要添加新的imageView和EditText
261
            lastFocusEdit.setText(editStr1);
262
            String editStr2 = lastEditStr.substring(cursorIndex).trim();
263
            if (editStr2.length() == 0) {
264
                editStr2 = " ";
265
            }
266
            if (allLayout.getChildCount() - 1 == lastEditIndex) {
267
                addEditTextAtIndex(lastEditIndex + 1, editStr2);
268
            }
269
270
            addImageViewAtIndex(lastEditIndex + 1, imagePath);
271
            lastFocusEdit.requestFocus();
272
            lastFocusEdit.setSelection(editStr1.length(), editStr1.length());//TODO
273
        }
274
        hideKeyBoard();
275
    }
276
277
    /**
278
     * 隐藏小键盘
279
     */
280
    public void hideKeyBoard() {
281
        InputMethodManager imm = (InputMethodManager) getContext()
282
                .getSystemService(Context.INPUT_METHOD_SERVICE);
283
        imm.hideSoftInputFromWindow(lastFocusEdit.getWindowToken(), 0);
284
    }
285
286
    /**
287
     * 在特定位置插入EditText
288
     *
289
     * @param index   位置
290
     * @param editStr EditText显示的文字
291
     */
292
    public void addEditTextAtIndex(final int index, CharSequence editStr) {
293
        EditText editText2 = createEditText("", EDIT_PADDING);
294
        editText2.setText(editStr);
295
        editText2.setOnFocusChangeListener(focusListener);
296
297
        allLayout.addView(editText2, index);
298
    }
299
300
    /**
301
     * 在特定位置添加ImageView
302
     */
303
    public void addImageViewAtIndex(final int index, String imagePath) {
304
        final RelativeLayout imageLayout = createImageLayout();
305
        DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
306
307
308
        if (readPictureDegree(imagePath) != 0) {
309
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
310
            Log.e(TAG, "addImageViewAtIndex: " + "!!!!!!!!!!");
311
            bitmap = toturn(bitmap);
312
            Log.e(TAG, "addImageViewAtIndex: " + "--------");
313
            imageView.setImageBitmap(bitmap);
314
        } else {
315
            RequestOptions options = new RequestOptions().centerCrop();
316
            Glide.with(getContext()).load(imagePath).apply(options).transition(withCrossFade()).into(imageView);
317
        }
318
        imageView.setAbsolutePath(imagePath);//保留这句,后面保存数据会用
319
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中
320
321
        // 调整imageView的高度,根据宽度来调整高度
322
        Bitmap bmp = BitmapFactory.decodeFile(imagePath);
323
        int imageHeight = 500;
324
        if (bmp != null) {
325
            imageHeight = allLayout.getWidth() * bmp.getHeight() / bmp.getWidth();
326
            bmp.recycle();
327
        }
328
        // TODO: 17/3/1 调整图片高度,这里是否有必要,如果出现微博长图,可能会很难看
329
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
330
                LayoutParams.MATCH_PARENT, imageHeight);//设置图片固定高度
331
        lp.bottomMargin = 10;
332
        imageView.setLayoutParams(lp);
333
334
        allLayout.addView(imageLayout, index);
335
    }
336
337
    /**
338
     * 根据view的宽度,动态缩放bitmap尺寸
339
     *
340
     * @param width view的宽度
341
     */
342
    public Bitmap getScaledBitmap(String filePath, int width) {
343
        BitmapFactory.Options options = new BitmapFactory.Options();
344
        options.inJustDecodeBounds = true;
345
        BitmapFactory.decodeFile(filePath, options);
346
        int sampleSize = options.outWidth > width ? options.outWidth / width
347
                + 1 : 1;
348
        options.inJustDecodeBounds = false;
349
        options.inSampleSize = sampleSize;
350
        return BitmapFactory.decodeFile(filePath, options);
351
    }
352
353
    /**
354
     * 对外提供的接口, 生成编辑数据上传
355
     */
356
    public List<EditData> buildEditData() {
357
        List<EditData> dataList = new ArrayList<EditData>();
358
        int num = allLayout.getChildCount();
359
        for (int index = 0; index < num; index++) {
360
            View itemView = allLayout.getChildAt(index);
361
            EditData itemData = new EditData();
362
            if (itemView instanceof EditText) {
363
                EditText item = (EditText) itemView;
364
                itemData.inputStr = item.getText().toString();
365
            } else if (itemView instanceof RelativeLayout) {
366
                DataImageView item = (DataImageView) itemView.findViewById(R.id.edit_imageView);
367
                itemData.imagePath = item.getAbsolutePath();
368
            }
369
            dataList.add(itemData);
370
        }
371
372
        return dataList;
373
    }
374
375
    public class EditData {
376
        public String inputStr;
377
        public String imagePath;
378
    }
379
380
    public static int readPictureDegree(String path) {
381
        int degree = 0;
382
        try {
383
            ExifInterface exifInterface = new ExifInterface(path);
384
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
385
            switch (orientation) {
386
                case ExifInterface.ORIENTATION_ROTATE_90:
387
                    degree = 90;
388
                    break;
389
                case ExifInterface.ORIENTATION_ROTATE_180:
390
                    degree = 180;
391
                    break;
392
                case ExifInterface.ORIENTATION_ROTATE_270:
393
                    degree = 270;
394
                    break;
395
            }
396
        } catch (IOException e) {
397
            e.printStackTrace();
398
        }
399
        Log.e(TAG, "readPictureDegree: degree=" + degree);
400
        return degree;
401
    }
402
403
404
    public static Bitmap toturn(Bitmap img) {
405
        Matrix matrix = new Matrix();
406
        matrix.postRotate(+90); /*翻转90度*/
407
        int width = img.getWidth();
408
        int height = img.getHeight();
409
        img = Bitmap.createBitmap(img, 0, 0, width, height, matrix, true);
410
        return img;
411
    }
412
}

+ 179 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/RichTextView.java

@ -0,0 +1,179 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.content.Context;
4
import android.graphics.Bitmap;
5
import android.graphics.BitmapFactory;
6
import android.util.AttributeSet;
7
import android.view.LayoutInflater;
8
import android.view.View;
9
import android.widget.ImageView;
10
import android.widget.LinearLayout;
11
import android.widget.RelativeLayout;
12
import android.widget.ScrollView;
13
import android.widget.TextView;
14
15
import com.bumptech.glide.Glide;
16
import com.bumptech.glide.request.RequestOptions;
17
import com.electric.chargingpile.R;
18
19
/**
20
 * Created by sendtion on 2016/6/24.
21
 * 显示富文本
22
 */
23
public class RichTextView extends ScrollView {
24
    private static final String TAG = "RichTextView";
25
    private static final int EDIT_PADDING = 3; // edittext常规padding是10dp
26
27
    private int viewTagIndex = 1; // 新生的view都会打一个tag,对每个view来说,这个tag是唯一的。
28
    private LinearLayout allLayout; // 这个是所有子view的容器,scrollView内部的唯一一个ViewGroup
29
    private LayoutInflater inflater;
30
    private int editNormalPadding = 0; //
31
32
    public RichTextView(Context context) {
33
        this(context, null);
34
    }
35
36
    public RichTextView(Context context, AttributeSet attrs) {
37
        this(context, attrs, 0);
38
    }
39
40
    public RichTextView(Context context, AttributeSet attrs, int defStyleAttr) {
41
        super(context, attrs, defStyleAttr);
42
        inflater = LayoutInflater.from(context);
43
44
        // 1. 初始化allLayout
45
        allLayout = new LinearLayout(context);
46
        allLayout.setOrientation(LinearLayout.VERTICAL);
47
        //allLayout.setBackgroundColor(Color.WHITE);//去掉背景
48
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
49
                LayoutParams.WRAP_CONTENT);
50
        allLayout.setPadding(50, 15, 50, 15);//设置间距,防止生成图片时文字太靠边
51
        addView(allLayout, layoutParams);
52
53
        LinearLayout.LayoutParams firstEditParam = new LinearLayout.LayoutParams(
54
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
55
        //editNormalPadding = dip2px(EDIT_PADDING);
56
        TextView firstText = createTextView("没有内容", dip2px(context, EDIT_PADDING));
57
        allLayout.addView(firstText, firstEditParam);
58
    }
59
60
    public int dip2px(Context context, float dipValue) {
61
        float m = context.getResources().getDisplayMetrics().density;
62
        return (int) (dipValue * m + 0.5f);
63
    }
64
65
    /**
66
     * 清除所有的view
67
     */
68
    public void clearAllLayout() {
69
        allLayout.removeAllViews();
70
    }
71
72
    /**
73
     * 获得最后一个子view的位置
74
     *
75
     * @return
76
     */
77
    public int getLastIndex() {
78
        int lastEditIndex = allLayout.getChildCount();
79
        return lastEditIndex;
80
    }
81
82
    /**
83
     * 生成文本输入框
84
     */
85
    public TextView createTextView(String hint, int paddingTop) {
86
        TextView textView = (TextView) inflater.inflate(R.layout.rich_textview, null);
87
        textView.setTag(viewTagIndex++);
88
        textView.setPadding(editNormalPadding, paddingTop, editNormalPadding, paddingTop);
89
        textView.setHint(hint);
90
        return textView;
91
    }
92
93
    /**
94
     * 生成图片View
95
     */
96
    private RelativeLayout createImageLayout() {
97
        RelativeLayout layout = (RelativeLayout) inflater.inflate(
98
                R.layout.edit_imageview, null);
99
        layout.setTag(viewTagIndex++);
100
        View closeView = layout.findViewById(R.id.image_close);
101
        closeView.setVisibility(GONE);
102
        return layout;
103
    }
104
105
    /**
106
     * 在特定位置插入EditText
107
     *
108
     * @param index   位置
109
     * @param editStr EditText显示的文字
110
     */
111
    public void addTextViewAtIndex(final int index, CharSequence editStr) {
112
        TextView textView = createTextView("", EDIT_PADDING);
113
        textView.setText(editStr);
114
115
        allLayout.addView(textView, index);
116
    }
117
118
    /**
119
     * 在特定位置添加ImageView
120
     */
121
    public void addImageViewAtIndex(final int index, String imagePath) {
122
        String width, height;
123
        String[] strarray = imagePath.split("\\&");
124
        final String path = strarray[0];
125
        width = strarray[1];
126
        if (width.contains(".")) {
127
            width = width.substring(0, width.indexOf("."));
128
        }
129
        height = strarray[2];
130
        if (height.contains(".")) {
131
            height = height.substring(0, height.indexOf("."));
132
        }
133
134
//        Bitmap bmp = BitmapFactory.decodeFile(imagePath);
135
136
        final RelativeLayout imageLayout = createImageLayout();
137
        DataImageView imageView = (DataImageView) imageLayout.findViewById(R.id.edit_imageView);
138
        RequestOptions options = new RequestOptions().centerCrop();
139
        Glide.with(getContext()).load(imagePath).apply(options).into(imageView);
140
        //imageView.setImageBitmap(bmp);//这里改用Glide加载图片
141
        //imageView.setBitmap(bmp);//这句去掉,保留下面的图片地址即可,优化图片占用
142
//        imageView.setAbsolutePath(imagePath);//保留这句,后面保存数据会用
143
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);//裁剪剧中
144
145
        // 调整imageView的高度
146
        int imageHeight = 500;
147
        int img_width = Integer.parseInt(width);
148
        int img_height = Integer.parseInt(height);
149
        if (width != null) {
150
            imageHeight = allLayout.getWidth() * img_height / img_width;
151
            // 使用之后,还是回收掉吧
152
//            bmp.recycle();
153
        }
154
//        Log.e(TAG, "addImageViewAtIndex_imageHeight: "+imageHeight );
155
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
156
                LayoutParams.MATCH_PARENT, imageHeight);
157
        lp.bottomMargin = 6;
158
        imageView.setLayoutParams(lp);
159
160
        allLayout.addView(imageLayout, index);
161
    }
162
163
    /**
164
     * 根据view的宽度,动态缩放bitmap尺寸
165
     *
166
     * @param width view的宽度
167
     */
168
    public Bitmap getScaledBitmap(String filePath, int width) {
169
        BitmapFactory.Options options = new BitmapFactory.Options();
170
        options.inJustDecodeBounds = true;
171
        BitmapFactory.decodeFile(filePath, options);
172
        int sampleSize = options.outWidth > width ? options.outWidth / width
173
                + 1 : 1;
174
        options.inJustDecodeBounds = false;
175
        options.inSampleSize = sampleSize;
176
        return BitmapFactory.decodeFile(filePath, options);
177
    }
178
179
}

+ 131 - 0
app/src/main/java/com/electric/chargingpile/view/xrichtext/SDCardUtil.java

@ -0,0 +1,131 @@
1
package com.electric.chargingpile.view.xrichtext;
2
3
import android.content.ContentResolver;
4
import android.content.Context;
5
import android.database.Cursor;
6
import android.graphics.Bitmap;
7
import android.net.Uri;
8
import android.os.Environment;
9
import android.provider.MediaStore;
10
11
import java.io.File;
12
import java.io.FileNotFoundException;
13
import java.io.FileOutputStream;
14
import java.io.IOException;
15
16
public class SDCardUtil {
17
    public static String SDCardRoot = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
18
19
    /**
20
     * 检查是否存在SDCard
21
     *
22
     * @return
23
     */
24
    public static boolean hasSdcard() {
25
        String state = Environment.getExternalStorageState();
26
        if (state.equals(Environment.MEDIA_MOUNTED)) {
27
            return true;
28
        } else {
29
            return false;
30
        }
31
    }
32
33
    /**
34
     * 获得文章图片保存路径
35
     *
36
     * @return
37
     */
38
    public static String getPictureDir() {
39
        String imageCacheUrl = SDCardRoot + "XRichText" + File.separator;
40
        File file = new File(imageCacheUrl);
41
        if (!file.exists())
42
            file.mkdir();  //如果不存在则创建
43
        return imageCacheUrl;
44
    }
45
46
    /**
47
     * 图片保存到SD卡
48
     *
49
     * @param bitmap
50
     * @return
51
     */
52
    public static String saveToSdCard(Bitmap bitmap) {
53
        String imageUrl = getPictureDir() + System.currentTimeMillis() + "-.jpeg";
54
        File file = new File(imageUrl);
55
        try {
56
            FileOutputStream out = new FileOutputStream(file);
57
            if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)) {
58
                out.flush();
59
                out.close();
60
            }
61
        } catch (FileNotFoundException e) {
62
            e.printStackTrace();
63
        } catch (IOException e) {
64
            e.printStackTrace();
65
        }
66
        return file.getAbsolutePath();
67
    }
68
69
    /**
70
     * 保存到指定路径,笔记中插入图片
71
     *
72
     * @param bitmap
73
     * @param path
74
     * @return
75
     */
76
    public static String saveToSdCard(Bitmap bitmap, String path) {
77
        File file = new File(path);
78
        try {
79
            FileOutputStream out = new FileOutputStream(file);
80
            if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)) {
81
                out.flush();
82
                out.close();
83
            }
84
        } catch (FileNotFoundException e) {
85
            e.printStackTrace();
86
        } catch (IOException e) {
87
            e.printStackTrace();
88
        }
89
        //System.out.println("文件保存路径:"+ file.getAbsolutePath());
90
        return file.getAbsolutePath();
91
    }
92
93
    /**
94
     * 删除文件
95
     **/
96
    public static void deleteFile(String filePath) {
97
        File file = new File(filePath);
98
        if (file.isFile() && file.exists())
99
            file.delete(); // 删除文件
100
    }
101
102
    /**
103
     * 根据Uri获取图片文件的绝对路径
104
     */
105
    public static String getFilePathByUri(Context context, final Uri uri) {
106
        if (null == uri) {
107
            return null;
108
        }
109
        final String scheme = uri.getScheme();
110
        String data = null;
111
        if (scheme == null) {
112
            data = uri.getPath();
113
        } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
114
            data = uri.getPath();
115
        } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
116
            Cursor cursor = context.getContentResolver().query(uri,
117
                    new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
118
            if (null != cursor) {
119
                if (cursor.moveToFirst()) {
120
                    int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
121
                    if (index > -1) {
122
                        data = cursor.getString(index);
123
                    }
124
                }
125
                cursor.close();
126
            }
127
        }
128
        return data;
129
    }
130
131
}

+ 24 - 0
app/src/main/res/layout/edit_imageview.xml

@ -0,0 +1,24 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content">
5
6
    <com.electric.chargingpile.view.xrichtext.DataImageView
7
        android:id="@+id/edit_imageView"
8
        android:layout_width="match_parent"
9
        android:layout_height="wrap_content"
10
        android:scaleType="fitXY" />
11
12
    <ImageView
13
        android:id="@+id/image_close"
14
        android:layout_width="50dp"
15
        android:layout_height="50dp"
16
        android:layout_alignParentRight="true"
17
        android:paddingLeft="20dp"
18
        android:paddingTop="5dp"
19
        android:paddingRight="5dp"
20
        android:paddingBottom="20dp"
21
        android:scaleType="fitXY"
22
        android:src="@drawable/icon_close" />
23
24
</RelativeLayout>

+ 9 - 0
app/src/main/res/layout/rich_edittext.xml

@ -0,0 +1,9 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<com.electric.chargingpile.view.xrichtext.DeletableEditText xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content"
5
    android:background="@null"
6
    android:cursorVisible="true"
7
    android:lineSpacingExtra="8dp"
8
    android:textColor="#616161"
9
    android:textSize="16sp" />

+ 10 - 0
app/src/main/res/layout/rich_textview.xml

@ -0,0 +1,10 @@
1
<?xml version="1.0" encoding="utf-8"?>
2
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="wrap_content"
5
    android:background="@null"
6
    android:cursorVisible="true"
7
    android:lineSpacingExtra="8dp"
8
    android:textIsSelectable="true"
9
    android:textSize="16sp"
10
    android:textColor="#616161" />