response) {
+ observer.onNext(response);
+ }
+
+ @Override
+ public void onError(@NonNull Throwable e) {
+ e.printStackTrace();
+
+ }
+
+ @Override
+ public void onComplete() {
+
+ }
+ });
+ }
+ };
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/fenghoo/seven/utils/AbImageUtil.java b/app/src/main/java/com/fenghoo/seven/utils/AbImageUtil.java
new file mode 100644
index 0000000..211f758
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/AbImageUtil.java
@@ -0,0 +1,750 @@
+/*
+ *
+ */
+package com.fenghoo.seven.utils;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Base64;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.ImageView;
+
+
+import com.fenghoo.seven.R;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+// TODO: Auto-generated Javadoc
+
+/**
+ * 描述:图片处理类.
+ *
+ */
+public class AbImageUtil {
+
+ /**
+ * The tag.
+ */
+ private static String TAG = "AbImageUtil";
+
+
+
+ private static OnImageUtilsListener onImageUtilsListener;
+
+ public void setOnImageUtilsListener(OnImageUtilsListener listener) {
+ onImageUtilsListener = listener;
+ }
+
+ public interface OnImageUtilsListener {
+ void backBitmap(Bitmap bt);
+ }
+
+ /**
+ * 描述:缩放图片.压缩
+ *
+ * @param file File对象
+ * @param newWidth 新图片的宽
+ * @param newHeight 新图片的高
+ * @return Bitmap 新图片
+ */
+ @SuppressWarnings("resource")
+ public static Bitmap scaleImg(File file, int newWidth, int newHeight) {
+ Bitmap resizeBmp = null;
+ try {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ // 设置为true,decodeFile先不创建内存 只获取一些解码边界信息即图片大小信息
+ opts.inJustDecodeBounds = true;
+ // BitmapFactory.decodeFile(file.getPath(), opts);
+ BitmapFactory.decodeFileDescriptor(new FileInputStream(file.getPath()).getFD(), null, opts);
+ if (newWidth != -1 && newHeight != -1) {
+ // inSampleSize=2表示图片宽高都为原来的二分之一,即图片为原来的四分之一
+ // 缩放可以将像素点打薄
+ int srcWidth = opts.outWidth; // 获取图片的原始宽度
+ int srcHeight = opts.outHeight;// 获取图片原始高度
+ int destWidth = 0;
+ int destHeight = 0;
+ // 缩放的比例
+ double ratio = 0.0;
+ if (srcWidth < newWidth || srcHeight < newHeight) {
+ ratio = 0.0;
+ destWidth = srcWidth;
+ destHeight = srcHeight;
+ // 按比例计算缩放后的图片大小
+ } else if (srcWidth > srcHeight) {
+ ratio = (double) srcWidth / newWidth;
+ destWidth = newWidth;
+ destHeight = (int) (srcHeight / ratio);
+ } else {
+ ratio = (double) srcHeight / newHeight;
+ destHeight = newHeight;
+ destWidth = (int) (srcWidth / ratio);
+ }
+ // 缩放的比例,缩放是很难按准备的比例进行缩放的,目前我只发现只能通过inSampleSize来进行缩放,其值表明缩放的倍数,SDK中建议其值是2的指数值
+ opts.inSampleSize = (int) ratio + 1;
+ // 设置大小
+ opts.outHeight = destHeight;
+ opts.outWidth = destWidth;
+ } else {
+ opts.inSampleSize = 1;
+ }
+ // 创建内存
+ opts.inJustDecodeBounds = false;
+ // 使图片不抖动
+ opts.inDither = false;
+ resizeBmp = BitmapFactory.decodeFileDescriptor(new FileInputStream(file.getPath()).getFD(), null, opts);
+ // resizeBmp = BitmapFactory.decodeFile(file.getPath(), opts);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return resizeBmp;
+ }
+
+ /**
+ * 描述:缩放图片,不压缩的缩放.
+ *
+ * @param bitmap the bitmap
+ * @param newWidth 新图片的宽
+ * @param newHeight 新图片的高
+ * @return Bitmap 新图片
+ */
+ public static Bitmap scaleImg(Bitmap bitmap, int newWidth, int newHeight) {
+ if (bitmap == null) {
+ return null;
+ }
+ if (newHeight <= 0 || newWidth <= 0) {
+ return bitmap;
+ }
+ // 获得图片的宽高
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+ // 计算缩放比例
+ float scaleWidth = ((float) newWidth) / width;
+ float scaleHeight = ((float) newHeight) / height;
+ // 取得想要缩放的matrix参数
+ Matrix matrix = new Matrix();
+ matrix.postScale(scaleWidth, scaleHeight);
+ // 得到新的图片
+ Bitmap newBm = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
+ return newBm;
+ }
+
+ /**
+ * 描述:缩放图片.
+ *
+ * @param bitmap the bitmap
+ * @param scale 比例
+ * @return Bitmap 新图片
+ */
+ public static Bitmap scaleImg(Bitmap bitmap, float scale) {
+ Bitmap resizeBmp = null;
+ try {
+ // 获取Bitmap资源的宽和高
+ int bmpW = bitmap.getWidth();
+ int bmpH = bitmap.getHeight();
+ // 注意这个Matirx是android.graphics底下的那个
+ Matrix mt = new Matrix();
+ // 设置缩放系数,分别为原来的0.8和0.8
+ mt.postScale(scale, scale);
+ resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bmpW, bmpH, mt, true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return resizeBmp;
+ }
+
+ /**
+ * 描述:裁剪图片.
+ *
+ * @param file File对象
+ * @param newWidth 新图片的宽
+ * @param newHeight 新图片的高
+ * @return Bitmap 新图片
+ */
+ public static Bitmap cutImg(File file, int newWidth, int newHeight) {
+ Bitmap newBitmap = null;
+ try {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ // 设置为true,decodeFile先不创建内存 只获取一些解码边界信息即图片大小信息
+ opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeFileDescriptor(new FileInputStream(file.getPath()).getFD(), null, opts);
+ // BitmapFactory.decodeFile(file.getPath(), opts);
+ if (newWidth != -1 && newHeight != -1) {
+ // inSampleSize=2表示图片宽高都为原来的二分之一,即图片为原来的四分之一
+ // 缩放可以将像素点打薄,裁剪前将图片缩放一些
+ int srcWidth = opts.outWidth; // 获取图片的原始宽度
+ int srcHeight = opts.outHeight;// 获取图片原始高度
+ int destWidth = 0;
+ int destHeight = 0;
+ int cutSrcWidth = newWidth * 2;
+ int cutSrcHeight = newHeight * 2;
+
+ // 缩放的比例
+ double ratio = 0.0;
+ if (srcWidth < cutSrcWidth || srcHeight < cutSrcHeight) {
+ ratio = 0.0;
+ destWidth = srcWidth;
+ destHeight = srcHeight;
+ // 按比例计算缩放后的图片大小
+ } else if (srcWidth > srcHeight) {
+ ratio = (double) srcWidth / cutSrcWidth;
+ destWidth = cutSrcWidth;
+ destHeight = (int) (srcHeight / ratio);
+ } else {
+ ratio = (double) srcHeight / cutSrcHeight;
+ destHeight = cutSrcHeight;
+ destWidth = (int) (srcWidth / ratio);
+ }
+ // 缩放的比例,缩放是很难按准备的比例进行缩放的,目前我只发现只能通过inSampleSize来进行缩放,其值表明缩放的倍数,SDK中建议其值是2的指数值
+ opts.inSampleSize = (int) ratio + 1;
+ // 设置大小
+ opts.outHeight = destHeight;
+ opts.outWidth = destWidth;
+ } else {
+ opts.inSampleSize = 1;
+ }
+ // 创建内存
+ opts.inJustDecodeBounds = false;
+ // 使图片不抖动
+ opts.inDither = false;
+ // Bitmap resizeBmp = BitmapFactory.decodeFile(file.getPath(), opts);
+ Bitmap resizeBmp = BitmapFactory.decodeFileDescriptor(new FileInputStream(file.getPath()).getFD(), null, opts);
+ if (resizeBmp != null) {
+ newBitmap = cutImg(resizeBmp, newWidth, newHeight);
+ }
+ if (newBitmap != null) {
+ return newBitmap;
+ } else {
+ return resizeBmp;
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return newBitmap;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return newBitmap;
+ }
+ }
+
+ /**
+ * 描述:裁剪图片.
+ *
+ * @param bitmap the bitmap
+ * @param newWidth 新图片的宽
+ * @param newHeight 新图片的高
+ * @return Bitmap 新图片
+ */
+ public static Bitmap cutImg(Bitmap bitmap, int newWidth, int newHeight) {
+ if (bitmap == null) {
+ return null;
+ }
+ Bitmap newBitmap = null;
+ if (newHeight <= 0 || newWidth <= 0) {
+ return bitmap;
+ }
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+ int offsetX = 0;
+ int offsetY = 0;
+
+ if (width > newWidth) {
+ offsetX = (width - newWidth) / 2;
+ }
+ if (height > newHeight) {
+ offsetY = (height - newHeight) / 2;
+ }
+
+ newBitmap = Bitmap.createBitmap(bitmap, offsetX, offsetY, newWidth, newHeight);
+ return newBitmap;
+ }
+
+ /**
+ * Drawable转Bitmap.
+ *
+ * @param drawable 要转化的Drawable
+ * @return Bitmap
+ */
+ public static Bitmap drawableToBitmap(Drawable drawable) {
+ Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888 : Config.RGB_565);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ /**
+ * Drawable对象转换Bitmap对象.
+ *
+ * @param bitmap 要转化的Bitmap对象
+ * @return Drawable 转化完成的Drawable对象
+ */
+ @SuppressWarnings("deprecation")
+ public static Drawable bitmapToDrawable(Bitmap bitmap) {
+ BitmapDrawable mBitmapDrawable = null;
+ try {
+ if (bitmap == null) {
+ return null;
+ }
+ mBitmapDrawable = new BitmapDrawable(bitmap);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return mBitmapDrawable;
+ }
+
+ /**
+ * 将Bitmap转换为byte[].
+ *
+ * @param bitmap the bitmap
+ * @param mCompressFormat 图片格式 Bitmap.CompressFormat.JPEG,CompressFormat.PNG
+ * @param needRecycle 是否需要回收
+ * @return byte[] 图片的byte[]
+ */
+ public static byte[] bitmap2Bytes(Bitmap bitmap, Bitmap.CompressFormat mCompressFormat, final boolean needRecycle) {
+ byte[] result = null;
+ ByteArrayOutputStream output = null;
+ try {
+ output = new ByteArrayOutputStream();
+ bitmap.compress(mCompressFormat, 100, output);
+ result = output.toByteArray();
+ if (needRecycle) {
+ bitmap.recycle();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (output != null) {
+ try {
+ output.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 描述:将byte[]转换为Bitmap.
+ *
+ * @param b 图片格式的byte[]数组
+ * @return bitmap 得到的Bitmap
+ */
+ public static Bitmap bytes2Bimap(byte[] b) {
+ Bitmap bitmap = null;
+ try {
+ if (b.length != 0) {
+ bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ /**
+ * 将ImageView转换为Bitmap.
+ *
+ * @param view 要转换为bitmap的View
+ * @return byte[] 图片的byte[]
+ */
+ public static Bitmap imageView2Bitmap(ImageView view) {
+ Bitmap bitmap = null;
+ try {
+ bitmap = Bitmap.createBitmap(view.getDrawingCache());
+ view.setDrawingCacheEnabled(false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ /**
+ * 将View转换为Drawable.需要最上层布局为Linearlayout
+ *
+ * @param view 要转换为Drawable的View
+ * @return BitmapDrawable Drawable
+ */
+ @SuppressWarnings("deprecation")
+ public static Drawable view2Drawable(View view) {
+ BitmapDrawable mBitmapDrawable = null;
+ try {
+ Bitmap newbmp = view2Bitmap(view);
+ if (newbmp != null) {
+ mBitmapDrawable = new BitmapDrawable(newbmp);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return mBitmapDrawable;
+ }
+
+ /**
+ * 将View转换为Bitmap.需要最上层布局为Linearlayout
+ *
+ * @param view 要转换为bitmap的View
+ * @return byte[] 图片的byte[]
+ */
+ public static Bitmap view2Bitmap(View view) {
+ Bitmap bitmap = null;
+ try {
+ if (view != null) {
+ view.setDrawingCacheEnabled(true);
+ view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+ view.buildDrawingCache();
+ bitmap = view.getDrawingCache();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bitmap;
+ }
+
+ /**
+ * 将View转换为byte[].
+ *
+ * @param view 要转换为byte[]的View
+ * @param compressFormat the compress format
+ * @return byte[] View图片的byte[]
+ */
+ public static byte[] view2Bytes(View view, Bitmap.CompressFormat compressFormat) {
+ byte[] b = null;
+ try {
+ Bitmap bitmap = AbImageUtil.view2Bitmap(view);
+ b = AbImageUtil.bitmap2Bytes(bitmap, compressFormat, true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return b;
+ }
+
+ /**
+ * 描述:旋转Bitmap为一定的角度.
+ *
+ * @param bitmap the bitmap
+ * @param degrees the degrees
+ * @return the bitmap
+ */
+ public static Bitmap rotateBitmap(Bitmap bitmap, float degrees) {
+ Bitmap mBitmap = null;
+ try {
+ Matrix m = new Matrix();
+ m.setRotate(degrees % 360);
+ mBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return mBitmap;
+ }
+
+ /**
+ * 描述:旋转Bitmap为一定的角度并四周暗化处理.
+ *
+ * @param bitmap the bitmap
+ * @param degrees the degrees
+ * @return the bitmap
+ */
+ public static Bitmap rotateBitmapTranslate(Bitmap bitmap, float degrees) {
+ Bitmap mBitmap = null;
+ int width;
+ int height;
+ try {
+ Matrix matrix = new Matrix();
+ if ((degrees / 90) % 2 != 0) {
+ width = bitmap.getWidth();
+ height = bitmap.getHeight();
+ } else {
+ width = bitmap.getHeight();
+ height = bitmap.getWidth();
+ }
+ int cx = width / 2;
+ int cy = height / 2;
+ matrix.preTranslate(-cx, -cy);
+ matrix.postRotate(degrees);
+ matrix.postTranslate(cx, cy);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return mBitmap;
+ }
+
+ /**
+ * 转换图片转换成圆形.
+ *
+ * @param bitmap 传入Bitmap对象
+ * @return the bitmap
+ */
+ public static Bitmap toRoundBitmap(Bitmap bitmap) {
+ if (bitmap == null) {
+ return null;
+ }
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ float roundPx;
+ float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
+ if (width <= height) {
+ roundPx = width / 2;
+ top = 0;
+ bottom = width;
+ left = 0;
+ right = width;
+ height = width;
+ dst_left = 0;
+ dst_top = 0;
+ dst_right = width;
+ dst_bottom = width;
+ } else {
+ roundPx = height / 2;
+ float clip = (width - height) / 2;
+ left = clip;
+ right = width - clip;
+ top = 0;
+ bottom = height;
+ width = height;
+ dst_left = 0;
+ dst_top = 0;
+ dst_right = height;
+ dst_bottom = height;
+ }
+
+ Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ final int color = 0xff424242;
+ final Paint paint = new Paint();
+ final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom);
+ final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom);
+ final RectF rectF = new RectF(dst);
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ paint.setColor(color);
+ canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, src, dst, paint);
+ return output;
+ }
+
+
+
+ public static byte[] imagePathToByte(String path) {
+ byte[] data = null;
+ FileInputStream input = null;
+ try {
+ input = new FileInputStream(new File(path));
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024];
+ int numBytesRead = 0;
+ while ((numBytesRead = input.read(buf)) != -1) {
+ output.write(buf, 0, numBytesRead);
+ }
+ data = output.toByteArray();
+ output.close();
+ input.close();
+ } catch (FileNotFoundException ex1) {
+ ex1.printStackTrace();
+ } catch (IOException ex1) {
+ ex1.printStackTrace();
+ }
+ return data;
+ }
+
+
+
+ /**
+ * @param bitmap 原图
+ * @param edgeLength 希望得到的正方形部分的边长
+ * @return 缩放截取正中部分后的位图。
+ */
+ public static Bitmap centerSquareScaleBitmap(Bitmap bitmap, int edgeLength) {
+ if (null == bitmap || edgeLength <= 0) {
+ return null;
+ }
+
+ Bitmap result = bitmap;
+ int widthOrg = bitmap.getWidth();
+ int heightOrg = bitmap.getHeight();
+
+ if (widthOrg > edgeLength && heightOrg > edgeLength) {
+ // 压缩到一个最小长度是edgeLength的bitmap
+ int longerEdge = edgeLength * Math.max(widthOrg, heightOrg) / Math.min(widthOrg, heightOrg);
+ int scaledWidth = widthOrg > heightOrg ? longerEdge : edgeLength;
+ int scaledHeight = widthOrg > heightOrg ? edgeLength : longerEdge;
+ Bitmap scaledBitmap;
+
+ try {
+ scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
+ } catch (Exception e) {
+ return null;
+ }
+
+ // 从图中截取正中间的正方形部分。
+ int xTopLeft = (scaledWidth - edgeLength) / 2;
+ int yTopLeft = (scaledHeight - edgeLength) / 2;
+
+ try {
+ result = Bitmap.createBitmap(scaledBitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
+ scaledBitmap.recycle();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param bitmap 原图
+ * @param edgeLength 希望得到的正方形部分的边长
+ * @return 缩放截取上面部分后的位图。
+ */
+ public static Bitmap topSquareScaleBitmap(Bitmap bitmap, int edgeLength) {
+ if (null == bitmap || edgeLength <= 0) {
+ return null;
+ }
+
+ Bitmap result = bitmap;
+ int widthOrg = bitmap.getWidth();
+ int heightOrg = bitmap.getHeight();
+
+ if (widthOrg > edgeLength && heightOrg > edgeLength) {
+ // 压缩到一个最小长度是edgeLength的bitmap
+ int longerEdge = edgeLength * Math.max(widthOrg, heightOrg) / Math.min(widthOrg, heightOrg);
+ int scaledWidth = widthOrg > heightOrg ? longerEdge : edgeLength;
+ int scaledHeight = widthOrg > heightOrg ? edgeLength : longerEdge;
+ Bitmap scaledBitmap;
+
+ try {
+ scaledBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
+ } catch (Exception e) {
+ return null;
+ }
+
+ // 从图中截取正中间的正方形部分。
+ int xTopLeft = (scaledWidth - edgeLength) / 2;
+ // int yTopLeft = (scaledHeight - edgeLength) / 2;
+ int yTopLeft = 0;
+
+ try {
+ result = Bitmap.createBitmap(scaledBitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
+ scaledBitmap.recycle();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * 转换Resources图片转换成Bitmap.
+ */
+ public static Bitmap ResourcesToBitmap(Resources resId) {
+ Bitmap bmp = BitmapFactory.decodeResource(resId, R.mipmap.ic_launcher);
+ return bmp;
+ }
+ /**
+ * bitmap转为base64
+ * @param bitmap
+ * @return
+ */
+ public static String bitmapToBase64(Bitmap bitmap) {
+
+ String result = null;
+ ByteArrayOutputStream baos = null;
+ try {
+ if (bitmap != null) {
+ baos = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
+
+ baos.flush();
+ baos.close();
+
+ byte[] bitmapBytes = baos.toByteArray();
+ result ="data:image/jpg;base64," + Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (baos != null) {
+ baos.flush();
+ baos.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
+
+ /**
+ * base64转为bitmap
+ * @param base64Data
+ * @return
+ */
+ public static Bitmap base64ToBitmap(String base64Data) {
+ byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);
+ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ }
+ /**
+ * bitmap转成string
+ *
+ * @param bitmap
+ * @return
+ */
+ public static String bitmapToString(Bitmap bitmap)
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();// outputstream
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
+ byte[] appicon = baos.toByteArray();// 转为byte数组
+ return Base64.encodeToString(appicon, Base64.DEFAULT);
+
+ }
+
+ /**
+ * string转成bitmap
+ *
+ * @param st
+ */
+ public static Bitmap stringToBitmap(String st)
+ {
+ Bitmap bitmap = null;
+ try
+ {
+ byte[] bitmapArray;
+ bitmapArray = Base64.decode(st, Base64.DEFAULT);
+ bitmap =
+ BitmapFactory.decodeByteArray(bitmapArray, 0,
+ bitmapArray.length);
+ return bitmap;
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/com/fenghoo/seven/utils/CloseUtils.java b/app/src/main/java/com/fenghoo/seven/utils/CloseUtils.java
new file mode 100644
index 0000000..f6c62d9
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/CloseUtils.java
@@ -0,0 +1,54 @@
+package com.fenghoo.seven.utils;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2016/10/09
+ * desc : 关闭相关工具类
+ *
+ */
+public final class CloseUtils {
+
+ private CloseUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * 关闭IO
+ *
+ * @param closeables closeables
+ */
+ public static void closeIO(final Closeable... closeables) {
+ if (closeables == null) return;
+ for (Closeable closeable : closeables) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * 安静关闭IO
+ *
+ * @param closeables closeables
+ */
+ public static void closeIOQuietly(final Closeable... closeables) {
+ if (closeables == null) return;
+ for (Closeable closeable : closeables) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/fenghoo/seven/utils/FileIOUtils.java b/app/src/main/java/com/fenghoo/seven/utils/FileIOUtils.java
new file mode 100644
index 0000000..e689ede
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/FileIOUtils.java
@@ -0,0 +1,674 @@
+package com.fenghoo.seven.utils;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2017/06/22
+ * desc : 文件读写相关工具类
+ *
+ */
+public final class FileIOUtils {
+
+ private FileIOUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ private static final String LINE_SEP = System.getProperty("line.separator");
+
+ private static int sBufferSize = 8192;
+
+ /**
+ * 将输入流写入文件
+ *
+ * @param filePath 路径
+ * @param is 输入流
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromIS(final String filePath, final InputStream is) {
+ return writeFileFromIS(getFileByPath(filePath), is, false);
+ }
+
+ /**
+ * 将输入流写入文件
+ *
+ * @param filePath 路径
+ * @param is 输入流
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromIS(final String filePath, final InputStream is, final boolean append) {
+ return writeFileFromIS(getFileByPath(filePath), is, append);
+ }
+
+ /**
+ * 将输入流写入文件
+ *
+ * @param file 文件
+ * @param is 输入流
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromIS(final File file, final InputStream is) {
+ return writeFileFromIS(file, is, false);
+ }
+
+ /**
+ * 将输入流写入文件
+ *
+ * @param file 文件
+ * @param is 输入流
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromIS(final File file, final InputStream is, final boolean append) {
+ if (!createOrExistsFile(file) || is == null) return false;
+ OutputStream os = null;
+ try {
+ os = new BufferedOutputStream(new FileOutputStream(file, append));
+ byte data[] = new byte[sBufferSize];
+ int len;
+ while ((len = is.read(data, 0, sBufferSize)) != -1) {
+ os.write(data, 0, len);
+ }
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ CloseUtils.closeIO(is, os);
+ }
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) {
+ return writeFileFromBytesByStream(getFileByPath(filePath), bytes, false);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes, final boolean append) {
+ return writeFileFromBytesByStream(getFileByPath(filePath), bytes, append);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) {
+ return writeFileFromBytesByStream(file, bytes, false);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes, final boolean append) {
+ if (bytes == null || !createOrExistsFile(file)) return false;
+ BufferedOutputStream bos = null;
+ try {
+ bos = new BufferedOutputStream(new FileOutputStream(file, append));
+ bos.write(bytes);
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ CloseUtils.closeIO(bos);
+ }
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean isForce) {
+ return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, false, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByChannel(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) {
+ return writeFileFromBytesByChannel(getFileByPath(filePath), bytes, append, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean isForce) {
+ return writeFileFromBytesByChannel(file, bytes, false, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByChannel(final File file, final byte[] bytes, final boolean append, final boolean isForce) {
+ if (bytes == null) return false;
+ FileChannel fc = null;
+ try {
+ fc = new FileOutputStream(file, append).getChannel();
+ fc.position(fc.size());
+ fc.write(ByteBuffer.wrap(bytes));
+ if (isForce) fc.force(true);
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ CloseUtils.closeIO(fc);
+ }
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean isForce) {
+ return writeFileFromBytesByMap(filePath, bytes, false, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param filePath 文件路径
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByMap(final String filePath, final byte[] bytes, final boolean append, final boolean isForce) {
+ return writeFileFromBytesByMap(getFileByPath(filePath), bytes, append, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean isForce) {
+ return writeFileFromBytesByMap(file, bytes, false, isForce);
+ }
+
+ /**
+ * 将字节数组写入文件
+ *
+ * @param file 文件
+ * @param bytes 字节数组
+ * @param append 是否追加在文件末
+ * @param isForce 是否写入文件
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromBytesByMap(final File file, final byte[] bytes, final boolean append, final boolean isForce) {
+ if (bytes == null || !createOrExistsFile(file)) return false;
+ FileChannel fc = null;
+ try {
+ fc = new FileOutputStream(file, append).getChannel();
+ MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length);
+ mbb.put(bytes);
+ if (isForce) mbb.force();
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ CloseUtils.closeIO(fc);
+ }
+ }
+
+ /**
+ * 将字符串写入文件
+ *
+ * @param filePath 文件路径
+ * @param content 写入内容
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromString(final String filePath, final String content) {
+ return writeFileFromString(getFileByPath(filePath), content, false);
+ }
+
+ /**
+ * 将字符串写入文件
+ *
+ * @param filePath 文件路径
+ * @param content 写入内容
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromString(final String filePath, final String content, final boolean append) {
+ return writeFileFromString(getFileByPath(filePath), content, append);
+ }
+
+ /**
+ * 将字符串写入文件
+ *
+ * @param file 文件
+ * @param content 写入内容
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromString(final File file, final String content) {
+ return writeFileFromString(file, content, false);
+ }
+
+ /**
+ * 将字符串写入文件
+ *
+ * @param file 文件
+ * @param content 写入内容
+ * @param append 是否追加在文件末
+ * @return {@code true}: 写入成功
{@code false}: 写入失败
+ */
+ public static boolean writeFileFromString(final File file, final String content, final boolean append) {
+ if (file == null || content == null) return false;
+ if (!createOrExistsFile(file)) return false;
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter(file, append));
+ bw.write(content);
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ CloseUtils.closeIO(bw);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // the divide line of write and read
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param filePath 文件路径
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final String filePath) {
+ return readFile2List(getFileByPath(filePath), null);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param filePath 文件路径
+ * @param charsetName 编码格式
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final String filePath, final String charsetName) {
+ return readFile2List(getFileByPath(filePath), charsetName);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param file 文件
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final File file) {
+ return readFile2List(file, 0, 0x7FFFFFFF, null);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param file 文件
+ * @param charsetName 编码格式
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final File file, final String charsetName) {
+ return readFile2List(file, 0, 0x7FFFFFFF, charsetName);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param filePath 文件路径
+ * @param st 需要读取的开始行数
+ * @param end 需要读取的结束行数
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final String filePath, final int st, final int end) {
+ return readFile2List(getFileByPath(filePath), st, end, null);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param filePath 文件路径
+ * @param st 需要读取的开始行数
+ * @param end 需要读取的结束行数
+ * @param charsetName 编码格式
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final String filePath, final int st, final int end, final String charsetName) {
+ return readFile2List(getFileByPath(filePath), st, end, charsetName);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param file 文件
+ * @param st 需要读取的开始行数
+ * @param end 需要读取的结束行数
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final File file, final int st, final int end) {
+ return readFile2List(file, st, end, null);
+ }
+
+ /**
+ * 读取文件到字符串链表中
+ *
+ * @param file 文件
+ * @param st 需要读取的开始行数
+ * @param end 需要读取的结束行数
+ * @param charsetName 编码格式
+ * @return 字符串链表中
+ */
+ public static List readFile2List(final File file, final int st, final int end, final String charsetName) {
+ if (!isFileExists(file)) return null;
+ if (st > end) return null;
+ BufferedReader reader = null;
+ try {
+ String line;
+ int curLine = 1;
+ List list = new ArrayList<>();
+ if (isSpace(charsetName)) {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+ } else {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
+ }
+ while ((line = reader.readLine()) != null) {
+ if (curLine > end) break;
+ if (st <= curLine && curLine <= end) list.add(line);
+ ++curLine;
+ }
+ return list;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(reader);
+ }
+ }
+
+ /**
+ * 读取文件到字符串中
+ *
+ * @param filePath 文件路径
+ * @return 字符串
+ */
+ public static String readFile2String(final String filePath) {
+ return readFile2String(getFileByPath(filePath), null);
+ }
+
+ /**
+ * 读取文件到字符串中
+ *
+ * @param filePath 文件路径
+ * @param charsetName 编码格式
+ * @return 字符串
+ */
+ public static String readFile2String(final String filePath, final String charsetName) {
+ return readFile2String(getFileByPath(filePath), charsetName);
+ }
+
+ /**
+ * 读取文件到字符串中
+ *
+ * @param file 文件
+ * @return 字符串
+ */
+ public static String readFile2String(final File file) {
+ return readFile2String(file, null);
+ }
+
+ /**
+ * 读取文件到字符串中
+ *
+ * @param file 文件
+ * @param charsetName 编码格式
+ * @return 字符串
+ */
+ public static String readFile2String(final File file, final String charsetName) {
+ if (!isFileExists(file)) return null;
+ BufferedReader reader = null;
+ try {
+ StringBuilder sb = new StringBuilder();
+ if (isSpace(charsetName)) {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+ } else {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
+ }
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line).append(LINE_SEP);
+ }
+ // delete the last line separator
+ return sb.delete(sb.length() - LINE_SEP.length(), sb.length()).toString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(reader);
+ }
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param filePath 文件路径
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByStream(final String filePath) {
+ return readFile2BytesByStream(getFileByPath(filePath));
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param file 文件
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByStream(final File file) {
+ if (!isFileExists(file)) return null;
+ FileInputStream fis = null;
+ ByteArrayOutputStream os = null;
+ try {
+ fis = new FileInputStream(file);
+ os = new ByteArrayOutputStream();
+ byte[] b = new byte[sBufferSize];
+ int len;
+ while ((len = fis.read(b, 0, sBufferSize)) != -1) {
+ os.write(b, 0, len);
+ }
+ return os.toByteArray();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(fis, os);
+ }
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param filePath 文件路径
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByChannel(final String filePath) {
+ return readFile2BytesByChannel(getFileByPath(filePath));
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param file 文件
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByChannel(final File file) {
+ if (!isFileExists(file)) return null;
+ FileChannel fc = null;
+ try {
+ fc = new RandomAccessFile(file, "r").getChannel();
+ ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size());
+ while (true) {
+ if (!((fc.read(byteBuffer)) > 0)) break;
+ }
+ return byteBuffer.array();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(fc);
+ }
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param filePath 文件路径
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByMap(final String filePath) {
+ return readFile2BytesByMap(getFileByPath(filePath));
+ }
+
+ /**
+ * 读取文件到字节数组中
+ *
+ * @param file 文件
+ * @return 字符数组
+ */
+ public static byte[] readFile2BytesByMap(final File file) {
+ if (!isFileExists(file)) return null;
+ FileChannel fc = null;
+ try {
+ fc = new RandomAccessFile(file, "r").getChannel();
+ int size = (int) fc.size();
+ MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
+ byte[] result = new byte[size];
+ mbb.get(result, 0, size);
+ return result;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(fc);
+ }
+ }
+
+ /**
+ * 设置缓冲区尺寸
+ *
+ * @param bufferSize 缓冲区大小
+ */
+ public static void setBufferSize(final int bufferSize) {
+ sBufferSize = bufferSize;
+ }
+
+ private static File getFileByPath(final String filePath) {
+ return isSpace(filePath) ? null : new File(filePath);
+ }
+
+ private static boolean createOrExistsFile(final String filePath) {
+ return createOrExistsFile(getFileByPath(filePath));
+ }
+
+ private static boolean createOrExistsFile(final File file) {
+ if (file == null) return false;
+ if (file.exists()) return file.isFile();
+ if (!createOrExistsDir(file.getParentFile())) return false;
+ try {
+ return file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private static boolean createOrExistsDir(final File file) {
+ return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
+ }
+
+ private static boolean isFileExists(final File file) {
+ return file != null && file.exists();
+ }
+
+ private static boolean isSpace(final String s) {
+ if (s == null) return true;
+ for (int i = 0, len = s.length(); i < len; ++i) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/fenghoo/seven/utils/FileUtils.java b/app/src/main/java/com/fenghoo/seven/utils/FileUtils.java
new file mode 100644
index 0000000..8ff7ee0
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/FileUtils.java
@@ -0,0 +1,1133 @@
+package com.fenghoo.seven.utils;
+
+import android.annotation.SuppressLint;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2016/05/03
+ * desc : 文件相关工具类
+ *
+ */
+public final class FileUtils {
+
+ private FileUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ private static final String LINE_SEP = System.getProperty("line.separator");
+
+ /**
+ * 根据文件路径获取文件
+ *
+ * @param filePath 文件路径
+ * @return 文件
+ */
+ public static File getFileByPath(final String filePath) {
+ return isSpace(filePath) ? null : new File(filePath);
+ }
+
+ /**
+ * 判断文件是否存在
+ *
+ * @param filePath 文件路径
+ * @return {@code true}: 存在
{@code false}: 不存在
+ */
+ public static boolean isFileExists(final String filePath) {
+ return isFileExists(getFileByPath(filePath));
+ }
+
+ /**
+ * 判断文件是否存在
+ *
+ * @param file 文件
+ * @return {@code true}: 存在
{@code false}: 不存在
+ */
+ public static boolean isFileExists(final File file) {
+ return file != null && file.exists();
+ }
+
+ /**
+ * 重命名文件
+ *
+ * @param filePath 文件路径
+ * @param newName 新名称
+ * @return {@code true}: 重命名成功
{@code false}: 重命名失败
+ */
+ public static boolean rename(final String filePath, final String newName) {
+ return rename(getFileByPath(filePath), newName);
+ }
+
+ /**
+ * 重命名文件
+ *
+ * @param file 文件
+ * @param newName 新名称
+ * @return {@code true}: 重命名成功
{@code false}: 重命名失败
+ */
+ public static boolean rename(final File file, final String newName) {
+ // 文件为空返回false
+ if (file == null) return false;
+ // 文件不存在返回false
+ if (!file.exists()) return false;
+ // 新的文件名为空返回false
+ if (isSpace(newName)) return false;
+ // 如果文件名没有改变返回true
+ if (newName.equals(file.getName())) return true;
+ File newFile = new File(file.getParent() + File.separator + newName);
+ // 如果重命名的文件已存在返回false
+ return !newFile.exists()
+ && file.renameTo(newFile);
+ }
+
+ /**
+ * 判断是否是目录
+ *
+ * @param dirPath 目录路径
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isDir(final String dirPath) {
+ return isDir(getFileByPath(dirPath));
+ }
+
+ /**
+ * 判断是否是目录
+ *
+ * @param file 文件
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isDir(final File file) {
+ return isFileExists(file) && file.isDirectory();
+ }
+
+ /**
+ * 判断是否是文件
+ *
+ * @param filePath 文件路径
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isFile(final String filePath) {
+ return isFile(getFileByPath(filePath));
+ }
+
+ /**
+ * 判断是否是文件
+ *
+ * @param file 文件
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isFile(final File file) {
+ return isFileExists(file) && file.isFile();
+ }
+
+ /**
+ * 判断目录是否存在,不存在则判断是否创建成功
+ *
+ * @param dirPath 目录路径
+ * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败
+ */
+ public static boolean createOrExistsDir(final String dirPath) {
+ return createOrExistsDir(getFileByPath(dirPath));
+ }
+
+ /**
+ * 判断目录是否存在,不存在则判断是否创建成功
+ *
+ * @param file 文件
+ * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败
+ */
+ public static boolean createOrExistsDir(final File file) {
+ // 如果存在,是目录则返回true,是文件则返回false,不存在则返回是否创建成功
+ return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
+ }
+
+ /**
+ * 判断文件是否存在,不存在则判断是否创建成功
+ *
+ * @param filePath 文件路径
+ * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败
+ */
+ public static boolean createOrExistsFile(final String filePath) {
+ return createOrExistsFile(getFileByPath(filePath));
+ }
+
+ /**
+ * 判断文件是否存在,不存在则判断是否创建成功
+ *
+ * @param file 文件
+ * @return {@code true}: 存在或创建成功
{@code false}: 不存在或创建失败
+ */
+ public static boolean createOrExistsFile(final File file) {
+ if (file == null) return false;
+ // 如果存在,是文件则返回true,是目录则返回false
+ if (file.exists()) return file.isFile();
+ if (!createOrExistsDir(file.getParentFile())) return false;
+ try {
+ return file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 判断文件是否存在,存在则在创建之前删除
+ *
+ * @param file 文件
+ * @return {@code true}: 创建成功
{@code false}: 创建失败
+ */
+ public static boolean createFileByDeleteOldFile(final File file) {
+ if (file == null) return false;
+ // 文件存在并且删除失败返回false
+ if (file.exists() && !file.delete()) return false;
+ // 创建目录失败返回false
+ if (!createOrExistsDir(file.getParentFile())) return false;
+ try {
+ return file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 复制或移动目录
+ *
+ * @param srcDirPath 源目录路径
+ * @param destDirPath 目标目录路径
+ * @param isMove 是否移动
+ * @return {@code true}: 复制或移动成功
{@code false}: 复制或移动失败
+ */
+ private static boolean copyOrMoveDir(final String srcDirPath, final String destDirPath, final boolean isMove) {
+ return copyOrMoveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath), isMove);
+ }
+
+ /**
+ * 复制或移动目录
+ *
+ * @param srcDir 源目录
+ * @param destDir 目标目录
+ * @param isMove 是否移动
+ * @return {@code true}: 复制或移动成功
{@code false}: 复制或移动失败
+ */
+ private static boolean copyOrMoveDir(final File srcDir, final File destDir, final boolean isMove) {
+ if (srcDir == null || destDir == null) return false;
+ // 如果目标目录在源目录中则返回false,看不懂的话好好想想递归怎么结束
+ // srcPath : F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res
+ // destPath: F:\\MyGithub\\AndroidUtilCode\\utilcode\\src\\test\\res1
+ // 为防止以上这种情况出现出现误判,须分别在后面加个路径分隔符
+ String srcPath = srcDir.getPath() + File.separator;
+ String destPath = destDir.getPath() + File.separator;
+ if (destPath.contains(srcPath)) return false;
+ // 源文件不存在或者不是目录则返回false
+ if (!srcDir.exists() || !srcDir.isDirectory()) return false;
+ // 目标目录不存在返回false
+ if (!createOrExistsDir(destDir)) return false;
+ File[] files = srcDir.listFiles();
+ for (File file : files) {
+ File oneDestFile = new File(destPath + file.getName());
+ if (file.isFile()) {
+ // 如果操作失败返回false
+ if (!copyOrMoveFile(file, oneDestFile, isMove)) return false;
+ } else if (file.isDirectory()) {
+ // 如果操作失败返回false
+ if (!copyOrMoveDir(file, oneDestFile, isMove)) return false;
+ }
+ }
+ return !isMove || deleteDir(srcDir);
+ }
+
+ /**
+ * 复制或移动文件
+ *
+ * @param srcFilePath 源文件路径
+ * @param destFilePath 目标文件路径
+ * @param isMove 是否移动
+ * @return {@code true}: 复制或移动成功
{@code false}: 复制或移动失败
+ */
+ private static boolean copyOrMoveFile(final String srcFilePath, final String destFilePath, final boolean isMove) {
+ return copyOrMoveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath), isMove);
+ }
+
+ /**
+ * 复制或移动文件
+ *
+ * @param srcFile 源文件
+ * @param destFile 目标文件
+ * @param isMove 是否移动
+ * @return {@code true}: 复制或移动成功
{@code false}: 复制或移动失败
+ */
+ private static boolean copyOrMoveFile(final File srcFile, final File destFile, final boolean isMove) {
+ if (srcFile == null || destFile == null) return false;
+ // 源文件不存在或者不是文件则返回false
+ if (!srcFile.exists() || !srcFile.isFile()) return false;
+ // 目标文件存在且是文件则返回false
+ if (destFile.exists() && destFile.isFile()) return false;
+ // 目标目录不存在返回false
+ if (!createOrExistsDir(destFile.getParentFile())) return false;
+ try {
+ return FileIOUtils.writeFileFromIS(destFile, new FileInputStream(srcFile), false)
+ && !(isMove && !deleteFile(srcFile));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 复制目录
+ *
+ * @param srcDirPath 源目录路径
+ * @param destDirPath 目标目录路径
+ * @return {@code true}: 复制成功
{@code false}: 复制失败
+ */
+ public static boolean copyDir(final String srcDirPath, final String destDirPath) {
+ return copyDir(getFileByPath(srcDirPath), getFileByPath(destDirPath));
+ }
+
+ /**
+ * 复制目录
+ *
+ * @param srcDir 源目录
+ * @param destDir 目标目录
+ * @return {@code true}: 复制成功
{@code false}: 复制失败
+ */
+ public static boolean copyDir(final File srcDir, final File destDir) {
+ return copyOrMoveDir(srcDir, destDir, false);
+ }
+
+ /**
+ * 复制文件
+ *
+ * @param srcFilePath 源文件路径
+ * @param destFilePath 目标文件路径
+ * @return {@code true}: 复制成功
{@code false}: 复制失败
+ */
+ public static boolean copyFile(final String srcFilePath, final String destFilePath) {
+ return copyFile(getFileByPath(srcFilePath), getFileByPath(destFilePath));
+ }
+
+ /**
+ * 复制文件
+ *
+ * @param srcFile 源文件
+ * @param destFile 目标文件
+ * @return {@code true}: 复制成功
{@code false}: 复制失败
+ */
+ public static boolean copyFile(final File srcFile, final File destFile) {
+ return copyOrMoveFile(srcFile, destFile, false);
+ }
+
+ /**
+ * 移动目录
+ *
+ * @param srcDirPath 源目录路径
+ * @param destDirPath 目标目录路径
+ * @return {@code true}: 移动成功
{@code false}: 移动失败
+ */
+ public static boolean moveDir(final String srcDirPath, final String destDirPath) {
+ return moveDir(getFileByPath(srcDirPath), getFileByPath(destDirPath));
+ }
+
+ /**
+ * 移动目录
+ *
+ * @param srcDir 源目录
+ * @param destDir 目标目录
+ * @return {@code true}: 移动成功
{@code false}: 移动失败
+ */
+ public static boolean moveDir(final File srcDir, final File destDir) {
+ return copyOrMoveDir(srcDir, destDir, true);
+ }
+
+ /**
+ * 移动文件
+ *
+ * @param srcFilePath 源文件路径
+ * @param destFilePath 目标文件路径
+ * @return {@code true}: 移动成功
{@code false}: 移动失败
+ */
+ public static boolean moveFile(final String srcFilePath, final String destFilePath) {
+ return moveFile(getFileByPath(srcFilePath), getFileByPath(destFilePath));
+ }
+
+ /**
+ * 移动文件
+ *
+ * @param srcFile 源文件
+ * @param destFile 目标文件
+ * @return {@code true}: 移动成功
{@code false}: 移动失败
+ */
+ public static boolean moveFile(final File srcFile, final File destFile) {
+ return copyOrMoveFile(srcFile, destFile, true);
+ }
+
+ /**
+ * 删除目录
+ *
+ * @param dirPath 目录路径
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteDir(final String dirPath) {
+ return deleteDir(getFileByPath(dirPath));
+ }
+
+ /**
+ * 删除目录
+ *
+ * @param dir 目录
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteDir(final File dir) {
+ if (dir == null) return false;
+ // 目录不存在返回true
+ if (!dir.exists()) return true;
+ // 不是目录返回false
+ if (!dir.isDirectory()) return false;
+ // 现在文件存在且是文件夹
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.isFile()) {
+ if (!file.delete()) return false;
+ } else if (file.isDirectory()) {
+ if (!deleteDir(file)) return false;
+ }
+ }
+ }
+ return dir.delete();
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param srcFilePath 文件路径
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteFile(final String srcFilePath) {
+ return deleteFile(getFileByPath(srcFilePath));
+ }
+
+ /**
+ * 删除文件
+ *
+ * @param file 文件
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteFile(final File file) {
+ return file != null && (!file.exists() || file.isFile() && file.delete());
+ }
+
+ /**
+ * 删除目录下的所有文件
+ *
+ * @param dirPath 目录路径
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteFilesInDir(final String dirPath) {
+ return deleteFilesInDir(getFileByPath(dirPath));
+ }
+
+ /**
+ * 删除目录下的所有文件
+ *
+ * @param dir 目录
+ * @return {@code true}: 删除成功
{@code false}: 删除失败
+ */
+ public static boolean deleteFilesInDir(final File dir) {
+ if (dir == null) return false;
+ // 目录不存在返回true
+ if (!dir.exists()) return true;
+ // 不是目录返回false
+ if (!dir.isDirectory()) return false;
+ // 现在文件存在且是文件夹
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.isFile()) {
+ if (!file.delete()) return false;
+ } else if (file.isDirectory()) {
+ if (!deleteDir(file)) return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 获取目录下所有文件
+ *
+ * @param dirPath 目录路径
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDir(final String dirPath, final boolean isRecursive) {
+ return listFilesInDir(getFileByPath(dirPath), isRecursive);
+ }
+
+ /**
+ * 获取目录下所有文件
+ *
+ * @param dir 目录
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDir(final File dir, final boolean isRecursive) {
+ if (!isDir(dir)) return null;
+ if (isRecursive) return listFilesInDir(dir);
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ Collections.addAll(list, files);
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下所有文件包括子目录
+ *
+ * @param dirPath 目录路径
+ * @return 文件链表
+ */
+ public static List listFilesInDir(final String dirPath) {
+ return listFilesInDir(getFileByPath(dirPath));
+ }
+
+ /**
+ * 获取目录下所有文件包括子目录
+ *
+ * @param dir 目录
+ * @return 文件链表
+ */
+ public static List listFilesInDir(final File dir) {
+ if (!isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ list.add(file);
+ if (file.isDirectory()) {
+ List fileList = listFilesInDir(file);
+ if (fileList != null) {
+ list.addAll(fileList);
+ }
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下所有后缀名为suffix的文件
+ * 大小写忽略
+ *
+ * @param dirPath 目录路径
+ * @param suffix 后缀名
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final String dirPath, final String suffix, final boolean isRecursive) {
+ return listFilesInDirWithFilter(getFileByPath(dirPath), suffix, isRecursive);
+ }
+
+ /**
+ * 获取目录下所有后缀名为suffix的文件
+ * 大小写忽略
+ *
+ * @param dir 目录
+ * @param suffix 后缀名
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final File dir, final String suffix, final boolean isRecursive) {
+ if (isRecursive) return listFilesInDirWithFilter(dir, suffix);
+ if (dir == null || !isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.getName().toUpperCase().endsWith(suffix.toUpperCase())) {
+ list.add(file);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下所有后缀名为suffix的文件包括子目录
+ * 大小写忽略
+ *
+ * @param dirPath 目录路径
+ * @param suffix 后缀名
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final String dirPath, final String suffix) {
+ return listFilesInDirWithFilter(getFileByPath(dirPath), suffix);
+ }
+
+ /**
+ * 获取目录下所有后缀名为suffix的文件包括子目录
+ * 大小写忽略
+ *
+ * @param dir 目录
+ * @param suffix 后缀名
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final File dir, final String suffix) {
+ if (dir == null || !isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.getName().toUpperCase().endsWith(suffix.toUpperCase())) {
+ list.add(file);
+ }
+ if (file.isDirectory()) {
+ list.addAll(listFilesInDirWithFilter(file, suffix));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下所有符合filter的文件
+ *
+ * @param dirPath 目录路径
+ * @param filter 过滤器
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final String dirPath, final FilenameFilter filter, final boolean isRecursive) {
+ return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive);
+ }
+
+ /**
+ * 获取目录下所有符合filter的文件
+ *
+ * @param dir 目录
+ * @param filter 过滤器
+ * @param isRecursive 是否递归进子目录
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final File dir, final FilenameFilter filter, final boolean isRecursive) {
+ if (isRecursive) return listFilesInDirWithFilter(dir, filter);
+ if (dir == null || !isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (filter.accept(file.getParentFile(), file.getName())) {
+ list.add(file);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下所有符合filter的文件包括子目录
+ *
+ * @param dirPath 目录路径
+ * @param filter 过滤器
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final String dirPath, final FilenameFilter filter) {
+ return listFilesInDirWithFilter(getFileByPath(dirPath), filter);
+ }
+
+ /**
+ * 获取目录下所有符合filter的文件包括子目录
+ *
+ * @param dir 目录
+ * @param filter 过滤器
+ * @return 文件链表
+ */
+ public static List listFilesInDirWithFilter(final File dir, final FilenameFilter filter) {
+ if (dir == null || !isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (filter.accept(file.getParentFile(), file.getName())) {
+ list.add(file);
+ }
+ if (file.isDirectory()) {
+ list.addAll(listFilesInDirWithFilter(file, filter));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取目录下指定文件名的文件包括子目录
+ * 大小写忽略
+ *
+ * @param dirPath 目录路径
+ * @param fileName 文件名
+ * @return 文件链表
+ */
+ public static List searchFileInDir(final String dirPath, final String fileName) {
+ return searchFileInDir(getFileByPath(dirPath), fileName);
+ }
+
+ /**
+ * 获取目录下指定文件名的文件包括子目录
+ * 大小写忽略
+ *
+ * @param dir 目录
+ * @param fileName 文件名
+ * @return 文件链表
+ */
+ public static List searchFileInDir(final File dir, final String fileName) {
+ if (dir == null || !isDir(dir)) return null;
+ List list = new ArrayList<>();
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.getName().toUpperCase().equals(fileName.toUpperCase())) {
+ list.add(file);
+ }
+ if (file.isDirectory()) {
+ list.addAll(searchFileInDir(file, fileName));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取文件最后修改的毫秒时间戳
+ *
+ * @param filePath 文件路径
+ * @return 文件最后修改的毫秒时间戳
+ */
+ public static long getFileLastModified(final String filePath) {
+ return getFileLastModified(getFileByPath(filePath));
+ }
+
+ /**
+ * 获取文件最后修改的毫秒时间戳
+ *
+ * @param file 文件
+ * @return 文件最后修改的毫秒时间戳
+ */
+ public static long getFileLastModified(final File file) {
+ if (file == null) return -1;
+ return file.lastModified();
+ }
+
+ /**
+ * 简单获取文件编码格式
+ *
+ * @param filePath 文件路径
+ * @return 文件编码
+ */
+ public static String getFileCharsetSimple(final String filePath) {
+ return getFileCharsetSimple(getFileByPath(filePath));
+ }
+
+ /**
+ * 简单获取文件编码格式
+ *
+ * @param file 文件
+ * @return 文件编码
+ */
+ public static String getFileCharsetSimple(final File file) {
+ int p = 0;
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(file));
+ p = (is.read() << 8) + is.read();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ CloseUtils.closeIO(is);
+ }
+ switch (p) {
+ case 0xefbb:
+ return "UTF-8";
+ case 0xfffe:
+ return "Unicode";
+ case 0xfeff:
+ return "UTF-16BE";
+ default:
+ return "GBK";
+ }
+ }
+
+ /**
+ * 获取文件行数
+ *
+ * @param filePath 文件路径
+ * @return 文件行数
+ */
+ public static int getFileLines(final String filePath) {
+ return getFileLines(getFileByPath(filePath));
+ }
+
+ /**
+ * 获取文件行数
+ * 比readLine要快很多
+ *
+ * @param file 文件
+ * @return 文件行数
+ */
+ public static int getFileLines(final File file) {
+ int count = 1;
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(file));
+ byte[] buffer = new byte[1024];
+ int readChars;
+ if (LINE_SEP.endsWith("\n")) {
+ while ((readChars = is.read(buffer, 0, 1024)) != -1) {
+ for (int i = 0; i < readChars; ++i) {
+ if (buffer[i] == '\n') ++count;
+ }
+ }
+ } else {
+ while ((readChars = is.read(buffer, 0, 1024)) != -1) {
+ for (int i = 0; i < readChars; ++i) {
+ if (buffer[i] == '\r') ++count;
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ CloseUtils.closeIO(is);
+ }
+ return count;
+ }
+
+ /**
+ * 获取目录大小
+ *
+ * @param dirPath 目录路径
+ * @return 文件大小
+ */
+ public static String getDirSize(final String dirPath) {
+ return getDirSize(getFileByPath(dirPath));
+ }
+
+ /**
+ * 获取目录大小
+ *
+ * @param dir 目录
+ * @return 文件大小
+ */
+ public static String getDirSize(final File dir) {
+ long len = getDirLength(dir);
+ return len == -1 ? "" : byte2FitMemorySize(len);
+ }
+
+ /**
+ * 获取文件大小
+ *
+ * @param filePath 文件路径
+ * @return 文件大小
+ */
+ public static String getFileSize(final String filePath) {
+ return getFileSize(getFileByPath(filePath));
+ }
+
+ /**
+ * 获取文件大小
+ *
+ * @param file 文件
+ * @return 文件大小
+ */
+ public static String getFileSize(final File file) {
+ long len = getFileLength(file);
+ return len == -1 ? "" : byte2FitMemorySize(len);
+ }
+
+ /**
+ * 获取目录长度
+ *
+ * @param dirPath 目录路径
+ * @return 目录长度
+ */
+ public static long getDirLength(final String dirPath) {
+ return getDirLength(getFileByPath(dirPath));
+ }
+
+ /**
+ * 获取目录长度
+ *
+ * @param dir 目录
+ * @return 目录长度
+ */
+ public static long getDirLength(final File dir) {
+ if (!isDir(dir)) return -1;
+ long len = 0;
+ File[] files = dir.listFiles();
+ if (files != null && files.length != 0) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ len += getDirLength(file);
+ } else {
+ len += file.length();
+ }
+ }
+ }
+ return len;
+ }
+
+ /**
+ * 获取文件长度
+ *
+ * @param filePath 文件路径
+ * @return 文件长度
+ */
+ public static long getFileLength(final String filePath) {
+ return getFileLength(getFileByPath(filePath));
+ }
+
+ /**
+ * 获取文件长度
+ *
+ * @param file 文件
+ * @return 文件长度
+ */
+ public static long getFileLength(final File file) {
+ if (!isFile(file)) return -1;
+ return file.length();
+ }
+
+ /**
+ * 获取文件的MD5校验码
+ *
+ * @param filePath 文件路径
+ * @return 文件的MD5校验码
+ */
+ public static String getFileMD5ToString(final String filePath) {
+ File file = isSpace(filePath) ? null : new File(filePath);
+ return getFileMD5ToString(file);
+ }
+
+ /**
+ * 获取文件的MD5校验码
+ *
+ * @param filePath 文件路径
+ * @return 文件的MD5校验码
+ */
+ public static byte[] getFileMD5(final String filePath) {
+ File file = isSpace(filePath) ? null : new File(filePath);
+ return getFileMD5(file);
+ }
+
+ /**
+ * 获取文件的MD5校验码
+ *
+ * @param file 文件
+ * @return 文件的MD5校验码
+ */
+ public static String getFileMD5ToString(final File file) {
+ return bytes2HexString(getFileMD5(file));
+ }
+
+ /**
+ * 获取文件的MD5校验码
+ *
+ * @param file 文件
+ * @return 文件的MD5校验码
+ */
+ public static byte[] getFileMD5(final File file) {
+ if (file == null) return null;
+ DigestInputStream dis = null;
+ try {
+ FileInputStream fis = new FileInputStream(file);
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ dis = new DigestInputStream(fis, md);
+ byte[] buffer = new byte[1024 * 256];
+ while (true) {
+ if (!(dis.read(buffer) > 0)) break;
+ }
+ md = dis.getMessageDigest();
+ return md.digest();
+ } catch (NoSuchAlgorithmException | IOException e) {
+ e.printStackTrace();
+ } finally {
+ CloseUtils.closeIO(dis);
+ }
+ return null;
+ }
+
+ /**
+ * 获取全路径中的最长目录
+ *
+ * @param file 文件
+ * @return filePath最长目录
+ */
+ public static String getDirName(final File file) {
+ if (file == null) return null;
+ return getDirName(file.getPath());
+ }
+
+ /**
+ * 获取全路径中的最长目录
+ *
+ * @param filePath 文件路径
+ * @return filePath最长目录
+ */
+ public static String getDirName(final String filePath) {
+ if (isSpace(filePath)) return filePath;
+ int lastSep = filePath.lastIndexOf(File.separator);
+ return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1);
+ }
+
+ /**
+ * 获取全路径中的文件名
+ *
+ * @param file 文件
+ * @return 文件名
+ */
+ public static String getFileName(final File file) {
+ if (file == null) return null;
+ return getFileName(file.getPath());
+ }
+
+ /**
+ * 获取全路径中的文件名
+ *
+ * @param filePath 文件路径
+ * @return 文件名
+ */
+ public static String getFileName(final String filePath) {
+ if (isSpace(filePath)) return filePath;
+ int lastSep = filePath.lastIndexOf(File.separator);
+ return lastSep == -1 ? filePath : filePath.substring(lastSep + 1);
+ }
+
+ /**
+ * 获取全路径中的不带拓展名的文件名
+ *
+ * @param file 文件
+ * @return 不带拓展名的文件名
+ */
+ public static String getFileNameNoExtension(final File file) {
+ if (file == null) return null;
+ return getFileNameNoExtension(file.getPath());
+ }
+
+ /**
+ * 获取全路径中的不带拓展名的文件名
+ *
+ * @param filePath 文件路径
+ * @return 不带拓展名的文件名
+ */
+ public static String getFileNameNoExtension(final String filePath) {
+ if (isSpace(filePath)) return filePath;
+ int lastPoi = filePath.lastIndexOf('.');
+ int lastSep = filePath.lastIndexOf(File.separator);
+ if (lastSep == -1) {
+ return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi));
+ }
+ if (lastPoi == -1 || lastSep > lastPoi) {
+ return filePath.substring(lastSep + 1);
+ }
+ return filePath.substring(lastSep + 1, lastPoi);
+ }
+
+ /**
+ * 获取全路径中的文件拓展名
+ *
+ * @param file 文件
+ * @return 文件拓展名
+ */
+ public static String getFileExtension(final File file) {
+ if (file == null) return null;
+ return getFileExtension(file.getPath());
+ }
+
+ /**
+ * 获取全路径中的文件拓展名
+ *
+ * @param filePath 文件路径
+ * @return 文件拓展名
+ */
+ public static String getFileExtension(final String filePath) {
+ if (isSpace(filePath)) return filePath;
+ int lastPoi = filePath.lastIndexOf('.');
+ int lastSep = filePath.lastIndexOf(File.separator);
+ if (lastPoi == -1 || lastSep >= lastPoi) return "";
+ return filePath.substring(lastPoi + 1);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // copy from ConvertUtils
+ ///////////////////////////////////////////////////////////////////////////
+
+ private static final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /**
+ * byteArr转hexString
+ * 例如:
+ * bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns 00A8
+ *
+ * @param bytes 字节数组
+ * @return 16进制大写字符串
+ */
+ private static String bytes2HexString(final byte[] bytes) {
+ if (bytes == null) return null;
+ int len = bytes.length;
+ if (len <= 0) return null;
+ char[] ret = new char[len << 1];
+ for (int i = 0, j = 0; i < len; i++) {
+ ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];
+ ret[j++] = hexDigits[bytes[i] & 0x0f];
+ }
+ return new String(ret);
+ }
+
+ /**
+ * 字节数转合适内存大小
+ * 保留3位小数
+ *
+ * @param byteNum 字节数
+ * @return 合适内存大小
+ */
+ @SuppressLint("DefaultLocale")
+ private static String byte2FitMemorySize(final long byteNum) {
+ if (byteNum < 0) {
+ return "shouldn't be less than zero!";
+ } else if (byteNum < 1024) {
+ return String.format("%.3fB", (double) byteNum + 0.0005);
+ } else if (byteNum < 1048576) {
+ return String.format("%.3fKB", (double) byteNum / 1024 + 0.0005);
+ } else if (byteNum < 1073741824) {
+ return String.format("%.3fMB", (double) byteNum / 1048576 + 0.0005);
+ } else {
+ return String.format("%.3fGB", (double) byteNum / 1073741824 + 0.0005);
+ }
+ }
+
+ private static boolean isSpace(final String s) {
+ if (s == null) return true;
+ for (int i = 0, len = s.length(); i < len; ++i) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/fenghoo/seven/utils/ImageUtils.java b/app/src/main/java/com/fenghoo/seven/utils/ImageUtils.java
new file mode 100644
index 0000000..fa16f1d
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/ImageUtils.java
@@ -0,0 +1,1526 @@
+package com.fenghoo.seven.utils;
+
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.ExifInterface;
+import android.os.Build;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicBlur;
+import android.view.View;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.IntRange;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 2016/08/12
+ * desc : 图片相关工具类
+ *
+ */
+public final class ImageUtils {
+
+ private ImageUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * bitmap转byteArr
+ *
+ * @param bitmap bitmap对象
+ * @param format 格式
+ * @return 字节数组
+ */
+ public static byte[] bitmap2Bytes(final Bitmap bitmap, final CompressFormat format) {
+ if (bitmap == null) return null;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ bitmap.compress(format, 100, baos);
+ return baos.toByteArray();
+ }
+
+ /**
+ * byteArr转bitmap
+ *
+ * @param bytes 字节数组
+ * @return bitmap
+ */
+ public static Bitmap bytes2Bitmap(final byte[] bytes) {
+ return (bytes == null || bytes.length == 0) ? null : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ }
+
+ /**
+ * drawable转bitmap
+ *
+ * @param drawable drawable对象
+ * @return bitmap
+ */
+ public static Bitmap drawable2Bitmap(final Drawable drawable) {
+ if (drawable instanceof BitmapDrawable) {
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ if (bitmapDrawable.getBitmap() != null) {
+ return bitmapDrawable.getBitmap();
+ }
+ }
+ Bitmap bitmap;
+ if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+ bitmap = Bitmap.createBitmap(1, 1,
+ drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
+ } else {
+ bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
+ drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
+ }
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+
+ /**
+ * bitmap转drawable
+ *
+ * @param bitmap bitmap对象
+ * @return drawable-xxhdpi
+ */
+ public static Drawable bitmap2Drawable(final Bitmap bitmap) {
+ return bitmap == null ? null : new BitmapDrawable(Utils.getContext().getResources(), bitmap);
+ }
+
+ /**
+ * drawable转byteArr
+ *
+ * @param drawable drawable对象
+ * @param format 格式
+ * @return 字节数组
+ */
+ public static byte[] drawable2Bytes(final Drawable drawable, final CompressFormat format) {
+ return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format);
+ }
+
+ /**
+ * byteArr转drawable
+ *
+ * @param bytes 字节数组
+ * @return drawable-xxhdpi
+ */
+ public static Drawable bytes2Drawable(final byte[] bytes) {
+ return bitmap2Drawable(bytes2Bitmap(bytes));
+ }
+
+ /**
+ * view转Bitmap
+ *
+ * @param view 视图
+ * @return bitmap
+ */
+ public static Bitmap view2Bitmap(final View view) {
+ if (view == null) return null;
+ Bitmap ret = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(ret);
+ Drawable bgDrawable = view.getBackground();
+ if (bgDrawable != null) {
+ bgDrawable.draw(canvas);
+ } else {
+ canvas.drawColor(Color.WHITE);
+ }
+ view.draw(canvas);
+ return ret;
+ }
+
+ /**
+ * 计算采样大小
+ *
+ * @param options 选项
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return 采样大小
+ */
+ private static int calculateInSampleSize(final BitmapFactory.Options options, final int maxWidth, final int maxHeight) {
+ if (maxWidth == 0 || maxHeight == 0) return 1;
+ int height = options.outHeight;
+ int width = options.outWidth;
+ int inSampleSize = 1;
+ while ((height >>= 1) > maxHeight && (width >>= 1) > maxWidth) {
+ inSampleSize <<= 1;
+ }
+ return inSampleSize;
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param file 文件
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final File file) {
+ if (file == null) return null;
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new FileInputStream(file));
+ return BitmapFactory.decodeStream(is);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(is);
+ }
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param file 文件
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) {
+ if (file == null) return null;
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ is = new BufferedInputStream(new FileInputStream(file));
+ BitmapFactory.decodeStream(is, null, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeStream(is, null, options);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(is);
+ }
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param filePath 文件路径
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final String filePath) {
+ if (isSpace(filePath)) return null;
+ return BitmapFactory.decodeFile(filePath);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param filePath 文件路径
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) {
+ if (isSpace(filePath)) return null;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(filePath, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeFile(filePath, options);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param is 输入流
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final InputStream is) {
+ if (is == null) return null;
+ return BitmapFactory.decodeStream(is);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param is 输入流
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) {
+ if (is == null) return null;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeStream(is, null, options);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param data 数据
+ * @param offset 偏移量
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final byte[] data, final int offset) {
+ if (data.length == 0) return null;
+ return BitmapFactory.decodeByteArray(data, offset, data.length);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param data 数据
+ * @param offset 偏移量
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final byte[] data, final int offset, final int maxWidth, final int maxHeight) {
+ if (data.length == 0) return null;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(data, offset, data.length, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeByteArray(data, offset, data.length, options);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param id 资源id
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final int id) {
+ return BitmapFactory.decodeResource(Utils.getContext().getResources(), id);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param id 资源id
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final int id, final int maxWidth, final int maxHeight) {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeResource(Utils.getContext().getResources(), id, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeResource(Utils.getContext().getResources(), id, options);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param fd 文件描述
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final FileDescriptor fd) {
+ if (fd == null) return null;
+ return BitmapFactory.decodeFileDescriptor(fd);
+ }
+
+ /**
+ * 获取bitmap
+ *
+ * @param fd 文件描述
+ * @param maxWidth 最大宽度
+ * @param maxHeight 最大高度
+ * @return bitmap
+ */
+ public static Bitmap getBitmap(final FileDescriptor fd, final int maxWidth, final int maxHeight) {
+ if (fd == null) return null;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFileDescriptor(fd, null, options);
+ options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
+ options.inJustDecodeBounds = false;
+ return BitmapFactory.decodeFileDescriptor(fd, null, options);
+ }
+
+ /**
+ * 缩放图片
+ *
+ * @param src 源图片
+ * @param newWidth 新宽度
+ * @param newHeight 新高度
+ * @return 缩放后的图片
+ */
+ public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight) {
+ return scale(src, newWidth, newHeight, false);
+ }
+
+ /**
+ * 缩放图片
+ *
+ * @param src 源图片
+ * @param newWidth 新宽度
+ * @param newHeight 新高度
+ * @param recycle 是否回收
+ * @return 缩放后的图片
+ */
+ public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 缩放图片
+ *
+ * @param src 源图片
+ * @param scaleWidth 缩放宽度倍数
+ * @param scaleHeight 缩放高度倍数
+ * @return 缩放后的图片
+ */
+ public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight) {
+ return scale(src, scaleWidth, scaleHeight, false);
+ }
+
+ /**
+ * 缩放图片
+ *
+ * @param src 源图片
+ * @param scaleWidth 缩放宽度倍数
+ * @param scaleHeight 缩放高度倍数
+ * @param recycle 是否回收
+ * @return 缩放后的图片
+ */
+ public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Matrix matrix = new Matrix();
+ matrix.setScale(scaleWidth, scaleHeight);
+ Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 裁剪图片
+ *
+ * @param src 源图片
+ * @param x 开始坐标x
+ * @param y 开始坐标y
+ * @param width 裁剪宽度
+ * @param height 裁剪高度
+ * @return 裁剪后的图片
+ */
+ public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height) {
+ return clip(src, x, y, width, height, false);
+ }
+
+ /**
+ * 裁剪图片
+ *
+ * @param src 源图片
+ * @param x 开始坐标x
+ * @param y 开始坐标y
+ * @param width 裁剪宽度
+ * @param height 裁剪高度
+ * @param recycle 是否回收
+ * @return 裁剪后的图片
+ */
+ public static Bitmap clip(final Bitmap src, final int x, final int y, final int width, final int height, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Bitmap ret = Bitmap.createBitmap(src, x, y, width, height);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 倾斜图片
+ *
+ * @param src 源图片
+ * @param kx 倾斜因子x
+ * @param ky 倾斜因子y
+ * @return 倾斜后的图片
+ */
+ public static Bitmap skew(final Bitmap src, final float kx, final float ky) {
+ return skew(src, kx, ky, 0, 0, false);
+ }
+
+ /**
+ * 倾斜图片
+ *
+ * @param src 源图片
+ * @param kx 倾斜因子x
+ * @param ky 倾斜因子y
+ * @param recycle 是否回收
+ * @return 倾斜后的图片
+ */
+ public static Bitmap skew(final Bitmap src, final float kx, final float ky, final boolean recycle) {
+ return skew(src, kx, ky, 0, 0, recycle);
+ }
+
+ /**
+ * 倾斜图片
+ *
+ * @param src 源图片
+ * @param kx 倾斜因子x
+ * @param ky 倾斜因子y
+ * @param px 平移因子x
+ * @param py 平移因子y
+ * @return 倾斜后的图片
+ */
+ public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py) {
+ return skew(src, kx, ky, px, py, false);
+ }
+
+ /**
+ * 倾斜图片
+ *
+ * @param src 源图片
+ * @param kx 倾斜因子x
+ * @param ky 倾斜因子y
+ * @param px 平移因子x
+ * @param py 平移因子y
+ * @param recycle 是否回收
+ * @return 倾斜后的图片
+ */
+ public static Bitmap skew(final Bitmap src, final float kx, final float ky, final float px, final float py, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Matrix matrix = new Matrix();
+ matrix.setSkew(kx, ky, px, py);
+ Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 旋转图片
+ *
+ * @param src 源图片
+ * @param degrees 旋转角度
+ * @param px 旋转点横坐标
+ * @param py 旋转点纵坐标
+ * @return 旋转后的图片
+ */
+ public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py) {
+ return rotate(src, degrees, px, py, false);
+ }
+
+ /**
+ * 旋转图片
+ *
+ * @param src 源图片
+ * @param degrees 旋转角度
+ * @param px 旋转点横坐标
+ * @param py 旋转点纵坐标
+ * @param recycle 是否回收
+ * @return 旋转后的图片
+ */
+ public static Bitmap rotate(final Bitmap src, final int degrees, final float px, final float py, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ if (degrees == 0) return src;
+ Matrix matrix = new Matrix();
+ matrix.setRotate(degrees, px, py);
+ Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 获取图片旋转角度
+ *
+ * @param filePath 文件路径
+ * @return 旋转角度
+ */
+ public static int getRotateDegree(final String filePath) {
+ int degree = 0;
+ try {
+ ExifInterface exifInterface = new ExifInterface(filePath);
+ int orientation = exifInterface.getAttributeInt(
+ ExifInterface.TAG_ORIENTATION,
+ ExifInterface.ORIENTATION_NORMAL);
+ switch (orientation) {
+ default:
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ degree = 90;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ degree = 180;
+ break;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ degree = 270;
+ break;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return degree;
+ }
+
+ /**
+ * 转为圆形图片
+ *
+ * @param src 源图片
+ * @return 圆形图片
+ */
+ public static Bitmap toRound(final Bitmap src) {
+ return toRound(src, false);
+ }
+
+ /**
+ * 转为圆形图片
+ *
+ * @param src 源图片
+ * @param recycle 是否回收
+ * @return 圆形图片
+ */
+ public static Bitmap toRound(final Bitmap src, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ int width = src.getWidth();
+ int height = src.getHeight();
+ int radius = Math.min(width, height) >> 1;
+ Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
+ Paint paint = new Paint();
+ Canvas canvas = new Canvas(ret);
+ Rect rect = new Rect(0, 0, width, height);
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ canvas.drawCircle(width >> 1, height >> 1, radius, paint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(src, rect, rect, paint);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 转为圆角图片
+ *
+ * @param src 源图片
+ * @param radius 圆角的度数
+ * @return 圆角图片
+ */
+ public static Bitmap toRoundCorner(final Bitmap src, final float radius) {
+ return toRoundCorner(src, radius, false);
+ }
+
+ /**
+ * 转为圆角图片
+ *
+ * @param src 源图片
+ * @param radius 圆角的度数
+ * @param recycle 是否回收
+ * @return 圆角图片
+ */
+ public static Bitmap toRoundCorner(final Bitmap src, final float radius, final boolean recycle) {
+ if (null == src) return null;
+ int width = src.getWidth();
+ int height = src.getHeight();
+ Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());
+ Paint paint = new Paint();
+ Canvas canvas = new Canvas(ret);
+ Rect rect = new Rect(0, 0, width, height);
+ paint.setAntiAlias(true);
+ canvas.drawRoundRect(new RectF(rect), radius, radius, paint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(src, rect, rect, paint);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 快速模糊
+ * 先缩小原图,对小图进行模糊,再放大回原先尺寸
+ *
+ * @param src 源图片
+ * @param scale 缩放比例(0...1)
+ * @param radius 模糊半径
+ * @return 模糊后的图片
+ */
+ public static Bitmap fastBlur(final Bitmap src,
+ @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale,
+ @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) {
+ return fastBlur(src, scale, radius, false);
+ }
+
+ /**
+ * 快速模糊图片
+ * 先缩小原图,对小图进行模糊,再放大回原先尺寸
+ *
+ * @param src 源图片
+ * @param scale 缩放比例(0...1)
+ * @param radius 模糊半径(0...25)
+ * @param recycle 是否回收
+ * @return 模糊后的图片
+ */
+ public static Bitmap fastBlur(final Bitmap src,
+ @FloatRange(from = 0, to = 1, fromInclusive = false) final float scale,
+ @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius,
+ boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ int width = src.getWidth();
+ int height = src.getHeight();
+ int scaleWidth = (int) (width * scale + 0.5f);
+ int scaleHeight = (int) (height * scale + 0.5f);
+ if (scaleWidth == 0 || scaleHeight == 0) return null;
+ Bitmap scaleBitmap = Bitmap.createScaledBitmap(src, scaleWidth, scaleHeight, true);
+ Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
+ Canvas canvas = new Canvas();
+ PorterDuffColorFilter filter = new PorterDuffColorFilter(
+ Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
+ paint.setColorFilter(filter);
+ canvas.scale(scale, scale);
+ canvas.drawBitmap(scaleBitmap, 0, 0, paint);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ scaleBitmap = renderScriptBlur(scaleBitmap, radius);
+ } else {
+ scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle);
+ }
+ if (scale == 1) return scaleBitmap;
+ Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true);
+ if (scaleBitmap != null && !scaleBitmap.isRecycled()) scaleBitmap.recycle();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * renderScript模糊图片
+ * API大于17
+ *
+ * @param src 源图片
+ * @param radius 模糊半径(0...25)
+ * @return 模糊后的图片
+ */
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ public static Bitmap renderScriptBlur(final Bitmap src,
+ @FloatRange(from = 0, to = 25, fromInclusive = false) final float radius) {
+ if (isEmptyBitmap(src)) return null;
+ RenderScript rs = null;
+ try {
+ rs = RenderScript.create(Utils.getContext());
+ rs.setMessageHandler(new RenderScript.RSMessageHandler());
+ Allocation input = Allocation.createFromBitmap(rs, src, Allocation.MipmapControl.MIPMAP_NONE, Allocation
+ .USAGE_SCRIPT);
+ Allocation output = Allocation.createTyped(rs, input.getType());
+ ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
+ blurScript.setInput(input);
+ blurScript.setRadius(radius);
+ blurScript.forEach(output);
+ output.copyTo(src);
+ } finally {
+ if (rs != null) {
+ rs.destroy();
+ }
+ }
+ return src;
+ }
+
+ /**
+ * stack模糊图片
+ *
+ * @param src 源图片
+ * @param radius 模糊半径
+ * @param recycle 是否回收
+ * @return stack模糊后的图片
+ */
+ public static Bitmap stackBlur(final Bitmap src, final int radius, final boolean recycle) {
+ Bitmap ret;
+ if (recycle) {
+ ret = src;
+ } else {
+ ret = src.copy(src.getConfig(), true);
+ }
+
+ if (radius < 1) {
+ return null;
+ }
+
+ int w = ret.getWidth();
+ int h = ret.getHeight();
+
+ int[] pix = new int[w * h];
+ ret.getPixels(pix, 0, w, 0, 0, w, h);
+
+ int wm = w - 1;
+ int hm = h - 1;
+ int wh = w * h;
+ int div = radius + radius + 1;
+
+ int r[] = new int[wh];
+ int g[] = new int[wh];
+ int b[] = new int[wh];
+ int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+ int vmin[] = new int[Math.max(w, h)];
+
+ int divsum = (div + 1) >> 1;
+ divsum *= divsum;
+ int dv[] = new int[256 * divsum];
+ for (i = 0; i < 256 * divsum; i++) {
+ dv[i] = (i / divsum);
+ }
+
+ yw = yi = 0;
+
+ int[][] stack = new int[div][3];
+ int stackpointer;
+ int stackstart;
+ int[] sir;
+ int rbs;
+ int r1 = radius + 1;
+ int routsum, goutsum, boutsum;
+ int rinsum, ginsum, binsum;
+
+ for (y = 0; y < h; y++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ for (i = -radius; i <= radius; i++) {
+ p = pix[yi + Math.min(wm, Math.max(i, 0))];
+ sir = stack[i + radius];
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+ rbs = r1 - Math.abs(i);
+ rsum += sir[0] * rbs;
+ gsum += sir[1] * rbs;
+ bsum += sir[2] * rbs;
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+ }
+ stackpointer = radius;
+
+ for (x = 0; x < w; x++) {
+
+ r[yi] = dv[rsum];
+ g[yi] = dv[gsum];
+ b[yi] = dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (y == 0) {
+ vmin[x] = Math.min(x + radius + 1, wm);
+ }
+ p = pix[yw + vmin[x]];
+
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[(stackpointer) % div];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi++;
+ }
+ yw += w;
+ }
+ for (x = 0; x < w; x++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ yp = -radius * w;
+ for (i = -radius; i <= radius; i++) {
+ yi = Math.max(0, yp) + x;
+
+ sir = stack[i + radius];
+
+ sir[0] = r[yi];
+ sir[1] = g[yi];
+ sir[2] = b[yi];
+
+ rbs = r1 - Math.abs(i);
+
+ rsum += r[yi] * rbs;
+ gsum += g[yi] * rbs;
+ bsum += b[yi] * rbs;
+
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+
+ if (i < hm) {
+ yp += w;
+ }
+ }
+ yi = x;
+ stackpointer = radius;
+ for (y = 0; y < h; y++) {
+ // Preserve alpha channel: ( 0xff000000 & pix[yi] )
+ pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (x == 0) {
+ vmin[y] = Math.min(y + r1, hm) * w;
+ }
+ p = x + vmin[y];
+
+ sir[0] = r[p];
+ sir[1] = g[p];
+ sir[2] = b[p];
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[stackpointer];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi += w;
+ }
+ }
+ ret.setPixels(pix, 0, w, 0, 0, w, h);
+ return ret;
+ }
+
+ /**
+ * 添加颜色边框
+ *
+ * @param src 源图片
+ * @param borderWidth 边框宽度
+ * @param color 边框的颜色值
+ * @return 带颜色边框图
+ */
+ public static Bitmap addFrame(final Bitmap src, final int borderWidth, final int color) {
+ return addFrame(src, borderWidth, color, false);
+ }
+
+ /**
+ * 添加颜色边框
+ *
+ * @param src 源图片
+ * @param borderWidth 边框宽度
+ * @param color 边框的颜色值
+ * @param recycle 是否回收
+ * @return 带颜色边框图
+ */
+ public static Bitmap addFrame(final Bitmap src, final int borderWidth, final int color, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ int doubleBorder = borderWidth << 1;
+ int newWidth = src.getWidth() + doubleBorder;
+ int newHeight = src.getHeight() + doubleBorder;
+ Bitmap ret = Bitmap.createBitmap(newWidth, newHeight, src.getConfig());
+ Canvas canvas = new Canvas(ret);
+ //noinspection SuspiciousNameCombination
+ canvas.drawBitmap(src, borderWidth, borderWidth, null);
+ Paint paint = new Paint();
+ paint.setColor(color);
+ paint.setStyle(Paint.Style.STROKE);
+ // setStrokeWidth是居中画的,所以要两倍的宽度才能画,否则有一半的宽度是空的
+ paint.setStrokeWidth(doubleBorder);
+ Rect rect = new Rect(0, 0, newWidth, newHeight);
+ canvas.drawRect(rect, paint);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 添加倒影
+ *
+ * @param src 源图片的
+ * @param reflectionHeight 倒影高度
+ * @return 带倒影图片
+ */
+ public static Bitmap addReflection(final Bitmap src, final int reflectionHeight) {
+ return addReflection(src, reflectionHeight, false);
+ }
+
+ /**
+ * 添加倒影
+ *
+ * @param src 源图片的
+ * @param reflectionHeight 倒影高度
+ * @param recycle 是否回收
+ * @return 带倒影图片
+ */
+ public static Bitmap addReflection(final Bitmap src, final int reflectionHeight, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ // 原图与倒影之间的间距
+ final int REFLECTION_GAP = 0;
+ int srcWidth = src.getWidth();
+ int srcHeight = src.getHeight();
+ Matrix matrix = new Matrix();
+ matrix.preScale(1, -1);
+ Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight,
+ srcWidth, reflectionHeight, matrix, false);
+ Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig());
+ Canvas canvas = new Canvas(ret);
+ canvas.drawBitmap(src, 0, 0, null);
+ canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ LinearGradient shader = new LinearGradient(0, srcHeight,
+ 0, ret.getHeight() + REFLECTION_GAP,
+ 0x70FFFFFF, 0x00FFFFFF, Shader.TileMode.MIRROR);
+ paint.setShader(shader);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+ canvas.drawRect(0, srcHeight + REFLECTION_GAP,
+ srcWidth, ret.getHeight(), paint);
+ if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 添加文字水印
+ *
+ * @param src 源图片
+ * @param content 水印文本
+ * @param textSize 水印字体大小
+ * @param color 水印字体颜色
+ * @param x 起始坐标x
+ * @param y 起始坐标y
+ * @return 带有文字水印的图片
+ */
+ public static Bitmap addTextWatermark(final Bitmap src,
+ final String content,
+ final int textSize,
+ final int color,
+ final float x,
+ final float y) {
+ return addTextWatermark(src, content, textSize, color, x, y, false);
+ }
+
+ /**
+ * 添加文字水印
+ *
+ * @param src 源图片
+ * @param content 水印文本
+ * @param textSize 水印字体大小
+ * @param color 水印字体颜色
+ * @param x 起始坐标x
+ * @param y 起始坐标y
+ * @param recycle 是否回收
+ * @return 带有文字水印的图片
+ */
+ public static Bitmap addTextWatermark(final Bitmap src,
+ final String content,
+ final float textSize,
+ final int color,
+ final float x,
+ final float y,
+ final boolean recycle) {
+ if (isEmptyBitmap(src) || content == null) return null;
+ Bitmap ret = src.copy(src.getConfig(), true);
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ Canvas canvas = new Canvas(ret);
+ paint.setColor(color);
+ paint.setTextSize(textSize);
+ Rect bounds = new Rect();
+ paint.getTextBounds(content, 0, content.length(), bounds);
+ canvas.drawText(content, x, y + textSize, paint);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 添加图片水印
+ *
+ * @param src 源图片
+ * @param watermark 图片水印
+ * @param x 起始坐标x
+ * @param y 起始坐标y
+ * @param alpha 透明度
+ * @return 带有图片水印的图片
+ */
+ public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha) {
+ return addImageWatermark(src, watermark, x, y, alpha, false);
+ }
+
+ /**
+ * 添加图片水印
+ *
+ * @param src 源图片
+ * @param watermark 图片水印
+ * @param x 起始坐标x
+ * @param y 起始坐标y
+ * @param alpha 透明度
+ * @param recycle 是否回收
+ * @return 带有图片水印的图片
+ */
+ public static Bitmap addImageWatermark(final Bitmap src, final Bitmap watermark, final int x, final int y, final int alpha, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Bitmap ret = src.copy(src.getConfig(), true);
+ if (!isEmptyBitmap(watermark)) {
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ Canvas canvas = new Canvas(ret);
+ paint.setAlpha(alpha);
+ canvas.drawBitmap(watermark, x, y, paint);
+ }
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 转为alpha位图
+ *
+ * @param src 源图片
+ * @return alpha位图
+ */
+ public static Bitmap toAlpha(final Bitmap src) {
+ return toAlpha(src, false);
+ }
+
+ /**
+ * 转为alpha位图
+ *
+ * @param src 源图片
+ * @param recycle 是否回收
+ * @return alpha位图
+ */
+ public static Bitmap toAlpha(final Bitmap src, final Boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Bitmap ret = src.extractAlpha();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return ret;
+ }
+
+ /**
+ * 转为灰度图片
+ *
+ * @param src 源图片
+ * @return 灰度图
+ */
+ public static Bitmap toGray(final Bitmap src) {
+ return toGray(src, false);
+ }
+
+ /**
+ * 转为灰度图片
+ *
+ * @param src 源图片
+ * @param recycle 是否回收
+ * @return 灰度图
+ */
+ public static Bitmap toGray(final Bitmap src, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ Bitmap grayBitmap = Bitmap.createBitmap(src.getWidth(),
+ src.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(grayBitmap);
+ Paint paint = new Paint();
+ ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(0);
+ ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
+ paint.setColorFilter(colorMatrixColorFilter);
+ canvas.drawBitmap(src, 0, 0, paint);
+ if (recycle && !src.isRecycled()) src.recycle();
+ return grayBitmap;
+ }
+
+ /**
+ * 保存图片
+ *
+ * @param src 源图片
+ * @param filePath 要保存到的文件路径
+ * @param format 格式
+ * @return {@code true}: 成功
{@code false}: 失败
+ */
+ public static boolean save(final Bitmap src, final String filePath, final CompressFormat format) {
+ return save(src, FileUtils.getFileByPath(filePath), format, false);
+ }
+
+ /**
+ * 保存图片
+ *
+ * @param src 源图片
+ * @param file 要保存到的文件
+ * @param format 格式
+ * @return {@code true}: 成功
{@code false}: 失败
+ */
+ public static boolean save(final Bitmap src, final File file, final CompressFormat format) {
+ return save(src, file, format, false);
+ }
+
+ /**
+ * 保存图片
+ *
+ * @param src 源图片
+ * @param filePath 要保存到的文件路径
+ * @param format 格式
+ * @param recycle 是否回收
+ * @return {@code true}: 成功
{@code false}: 失败
+ */
+ public static boolean save(final Bitmap src, final String filePath, final CompressFormat format, final boolean recycle) {
+ return save(src, FileUtils.getFileByPath(filePath), format, recycle);
+ }
+
+ /**
+ * 保存图片
+ *
+ * @param src 源图片
+ * @param file 要保存到的文件
+ * @param format 格式
+ * @param recycle 是否回收
+ * @return {@code true}: 成功
{@code false}: 失败
+ */
+ public static boolean save(final Bitmap src, final File file, final CompressFormat format, final boolean recycle) {
+ if (isEmptyBitmap(src) || !FileUtils.createOrExistsFile(file)) return false;
+ System.out.println(src.getWidth() + ", " + src.getHeight());
+ OutputStream os = null;
+ boolean ret = false;
+ try {
+ os = new BufferedOutputStream(new FileOutputStream(file));
+ ret = src.compress(format, 100, os);
+ if (recycle && !src.isRecycled()) src.recycle();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ CloseUtils.closeIO(os);
+ }
+ return ret;
+ }
+
+ /**
+ * 根据文件名判断文件是否为图片
+ *
+ * @param file 文件
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isImage(final File file) {
+ return file != null && isImage(file.getPath());
+ }
+
+ /**
+ * 根据文件名判断文件是否为图片
+ *
+ * @param filePath 文件路径
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ public static boolean isImage(final String filePath) {
+ String path = filePath.toUpperCase();
+ return path.endsWith(".PNG") || path.endsWith(".JPG")
+ || path.endsWith(".JPEG") || path.endsWith(".BMP")
+ || path.endsWith(".GIF");
+ }
+
+ /**
+ * 获取图片类型
+ *
+ * @param filePath 文件路径
+ * @return 图片类型
+ */
+ public static String getImageType(final String filePath) {
+ return getImageType(FileUtils.getFileByPath(filePath));
+ }
+
+ /**
+ * 获取图片类型
+ *
+ * @param file 文件
+ * @return 图片类型
+ */
+ public static String getImageType(final File file) {
+ if (file == null) return null;
+ InputStream is = null;
+ try {
+ is = new FileInputStream(file);
+ return getImageType(is);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ } finally {
+ CloseUtils.closeIO(is);
+ }
+ }
+
+ /**
+ * 流获取图片类型
+ *
+ * @param is 图片输入流
+ * @return 图片类型
+ */
+ public static String getImageType(final InputStream is) {
+ if (is == null) return null;
+ try {
+ byte[] bytes = new byte[8];
+ return is.read(bytes, 0, 8) != -1 ? getImageType(bytes) : null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 获取图片类型
+ *
+ * @param bytes bitmap的前8字节
+ * @return 图片类型
+ */
+ public static String getImageType(final byte[] bytes) {
+ if (isJPEG(bytes)) return "JPEG";
+ if (isGIF(bytes)) return "GIF";
+ if (isPNG(bytes)) return "PNG";
+ if (isBMP(bytes)) return "BMP";
+ return null;
+ }
+
+ private static boolean isJPEG(final byte[] b) {
+ return b.length >= 2
+ && (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8);
+ }
+
+ private static boolean isGIF(final byte[] b) {
+ return b.length >= 6
+ && b[0] == 'G' && b[1] == 'I'
+ && b[2] == 'F' && b[3] == '8'
+ && (b[4] == '7' || b[4] == '9') && b[5] == 'a';
+ }
+
+ private static boolean isPNG(final byte[] b) {
+ return b.length >= 8
+ && (b[0] == (byte) 137 && b[1] == (byte) 80
+ && b[2] == (byte) 78 && b[3] == (byte) 71
+ && b[4] == (byte) 13 && b[5] == (byte) 10
+ && b[6] == (byte) 26 && b[7] == (byte) 10);
+ }
+
+ private static boolean isBMP(final byte[] b) {
+ return b.length >= 2
+ && (b[0] == 0x42) && (b[1] == 0x4d);
+ }
+
+ /**
+ * 判断bitmap对象是否为空
+ *
+ * @param src 源图片
+ * @return {@code true}: 是
{@code false}: 否
+ */
+ private static boolean isEmptyBitmap(final Bitmap src) {
+ return src == null || src.getWidth() == 0 || src.getHeight() == 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // 下方和压缩有关
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ * 按缩放压缩
+ *
+ * @param src 源图片
+ * @param newWidth 新宽度
+ * @param newHeight 新高度
+ * @return 缩放压缩后的图片
+ */
+ public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight) {
+ return scale(src, newWidth, newHeight, false);
+ }
+
+ /**
+ * 按缩放压缩
+ *
+ * @param src 源图片
+ * @param newWidth 新宽度
+ * @param newHeight 新高度
+ * @param recycle 是否回收
+ * @return 缩放压缩后的图片
+ */
+ public static Bitmap compressByScale(final Bitmap src, final int newWidth, final int newHeight, final boolean recycle) {
+ return scale(src, newWidth, newHeight, recycle);
+ }
+
+ /**
+ * 按缩放压缩
+ *
+ * @param src 源图片
+ * @param scaleWidth 缩放宽度倍数
+ * @param scaleHeight 缩放高度倍数
+ * @return 缩放压缩后的图片
+ */
+ public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight) {
+ return scale(src, scaleWidth, scaleHeight, false);
+ }
+
+ /**
+ * 按缩放压缩
+ *
+ * @param src 源图片
+ * @param scaleWidth 缩放宽度倍数
+ * @param scaleHeight 缩放高度倍数
+ * @param recycle 是否回收
+ * @return 缩放压缩后的图片
+ */
+ public static Bitmap compressByScale(final Bitmap src, final float scaleWidth, final float scaleHeight, final boolean recycle) {
+ return scale(src, scaleWidth, scaleHeight, recycle);
+ }
+
+ /**
+ * 按质量压缩
+ *
+ * @param src 源图片
+ * @param quality 质量
+ * @return 质量压缩后的图片
+ */
+ public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality) {
+ return compressByQuality(src, quality, false);
+ }
+
+ /**
+ * 按质量压缩
+ *
+ * @param src 源图片
+ * @param quality 质量
+ * @param recycle 是否回收
+ * @return 质量压缩后的图片
+ */
+ public static Bitmap compressByQuality(final Bitmap src, @IntRange(from = 0, to = 100) final int quality, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ src.compress(CompressFormat.JPEG, quality, baos);
+ byte[] bytes = baos.toByteArray();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ }
+
+ /**
+ * 按质量压缩
+ *
+ * @param src 源图片
+ * @param maxByteSize 允许最大值字节数
+ * @return 质量压缩压缩过的图片
+ */
+ public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize) {
+ return compressByQuality(src, maxByteSize, false);
+ }
+
+ /**
+ * 按质量压缩
+ *
+ * @param src 源图片
+ * @param maxByteSize 允许最大值字节数
+ * @param recycle 是否回收
+ * @return 质量压缩压缩过的图片
+ */
+ public static Bitmap compressByQuality(final Bitmap src, final long maxByteSize, final boolean recycle) {
+ if (isEmptyBitmap(src) || maxByteSize <= 0) return null;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int quality = 100;
+ src.compress(CompressFormat.JPEG, quality, baos);
+ while (baos.toByteArray().length > maxByteSize && quality > 0) {
+ baos.reset();
+ src.compress(CompressFormat.JPEG, quality -= 5, baos);
+ }
+ if (quality < 0) return null;
+ byte[] bytes = baos.toByteArray();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ }
+
+ /**
+ * 按采样大小压缩
+ *
+ * @param src 源图片
+ * @param sampleSize 采样率大小
+ * @return 按采样率压缩后的图片
+ */
+ public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize) {
+ return compressBySampleSize(src, sampleSize, false);
+ }
+
+ /**
+ * 按采样大小压缩
+ *
+ * @param src 源图片
+ * @param sampleSize 采样率大小
+ * @param recycle 是否回收
+ * @return 按采样率压缩后的图片
+ */
+ public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize, final boolean recycle) {
+ if (isEmptyBitmap(src)) return null;
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = sampleSize;
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ src.compress(CompressFormat.JPEG, 100, baos);
+ byte[] bytes = baos.toByteArray();
+ if (recycle && !src.isRecycled()) src.recycle();
+ return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
+ }
+
+ private static boolean isSpace(final String s) {
+ if (s == null) return true;
+ for (int i = 0, len = s.length(); i < len; ++i) {
+ if (!Character.isWhitespace(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/app/src/main/java/com/fenghoo/seven/utils/Utils.java b/app/src/main/java/com/fenghoo/seven/utils/Utils.java
new file mode 100644
index 0000000..b834edc
--- /dev/null
+++ b/app/src/main/java/com/fenghoo/seven/utils/Utils.java
@@ -0,0 +1,192 @@
+package com.fenghoo.seven.utils;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.util.Base64;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+
+import java.io.ByteArrayInputStream;
+
+import androidx.annotation.NonNull;
+
+/**
+ *
+ * author: Blankj
+ * blog : http://blankj.com
+ * time : 16/12/08
+ * desc : Utils初始化相关
+ *
+ */
+public final class Utils {
+
+ @SuppressLint("StaticFieldLeak")
+ private static Context context;
+
+ private Utils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * 初始化工具类
+ *
+ * @param context 上下文
+ */
+ public static void init(@NonNull final Context context) {
+ Utils.context = context.getApplicationContext();
+ }
+
+ /**
+ * 获取ApplicationContext
+ *
+ * @return ApplicationContext
+ */
+ public static Context getContext() {
+ if (context != null) return context;
+ throw new NullPointerException("u should init first");
+ }
+ public static int dip2px(Context context, float dpValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (dpValue * scale);
+ }
+
+ public static int px2dip(Context context, float pxValue) {
+ final float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (pxValue / scale);
+ }
+
+ public static int getVisiblePercent(View pView) {
+ if (pView != null && pView.isShown()) {
+ DisplayMetrics displayMetrics = pView.getContext().getResources().getDisplayMetrics();
+ int displayWidth = displayMetrics.widthPixels;
+ Rect rect = new Rect();
+ pView.getGlobalVisibleRect(rect);
+ if ((rect.top > 0) && (rect.left < displayWidth)) {
+ double areaVisible = rect.width() * rect.height();
+ double areaTotal = pView.getWidth() * pView.getHeight();
+ return (int) ((areaVisible / areaTotal) * 100);
+ } else {
+ return -1;
+ }
+ }
+ return -1;
+ }
+
+ //is wifi connected
+ public static boolean isWifiConnected(Context context) {
+ if (context.checkCallingOrSelfPermission(Manifest.permission.ACCESS_WIFI_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = connectivityManager.getActiveNetworkInfo();
+ if (info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * 获取对应应用的版本号
+ *
+ * @param context
+ * @return
+ */
+ public static String getAppVersion(Context context) {
+ String version = "1.0.0"; //默认1.0.0版本
+ PackageManager manager = context.getPackageManager();
+ try {
+ PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+ version = info.versionName;
+ } catch (Exception e) {
+ }
+
+ return version;
+ }
+
+
+ public static DisplayMetrics getDisplayMetrics(Context context) {
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ if (windowManager == null) {
+ return displayMetrics;
+ }
+ windowManager.getDefaultDisplay().getMetrics(displayMetrics);
+ return displayMetrics;
+ }
+
+ public static BitmapDrawable decodeImage(String base64drawable) {
+ byte[] rawImageData = Base64.decode(base64drawable, 0);
+ return new BitmapDrawable(null, new ByteArrayInputStream(rawImageData));
+ }
+
+ public static boolean isPad(Context context) {
+
+ //如果能打电话那可能是平板或手机,再根据配置判断
+ if (canTelephone(context)) {
+ //能打电话可能是手机也可能是平板
+ return (context.getResources().getConfiguration().
+ screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+ >= Configuration.SCREENLAYOUT_SIZE_LARGE;
+ } else {
+ return true; //不能打电话一定是平板
+ }
+ }
+
+ private static boolean canTelephone(Context context) {
+ TelephonyManager telephony = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ return (telephony.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) ? false : true;
+ }
+
+ public static boolean containString(String source, String destation) {
+
+ if (source.equals("") || destation.equals("")) {
+ return false;
+ }
+
+ if (source.contains(destation)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 获取view的屏幕属性
+ *
+ * @return
+ */
+ public static final String VIEW_INFO_EXTRA = "view_into_extra";
+ public static final String PROPNAME_SCREENLOCATION_LEFT = "propname_sreenlocation_left";
+ public static final String PROPNAME_SCREENLOCATION_TOP = "propname_sreenlocation_top";
+ public static final String PROPNAME_WIDTH = "propname_width";
+ public static final String PROPNAME_HEIGHT = "propname_height";
+
+ public static Bundle getViewProperty(View view) {
+ Bundle bundle = new Bundle();
+ int[] screenLocation = new int[2];
+ view.getLocationOnScreen(screenLocation); //获取view在整个屏幕中的位置
+ bundle.putInt(PROPNAME_SCREENLOCATION_LEFT, screenLocation[0]);
+ bundle.putInt(PROPNAME_SCREENLOCATION_TOP, screenLocation[1]);
+ bundle.putInt(PROPNAME_WIDTH, view.getWidth());
+ bundle.putInt(PROPNAME_HEIGHT, view.getHeight());
+
+ Log.e("Utils", "Left: " + screenLocation[0] + " Top: " + screenLocation[1]
+ + " Width: " + view.getWidth() + " Height: " + view.getHeight());
+ return bundle;
+ }
+}
diff --git a/app/src/main/res/layout/activity_add_customer.xml b/app/src/main/res/layout/activity_add_customer.xml
index 2bd368f..bdc3deb 100644
--- a/app/src/main/res/layout/activity_add_customer.xml
+++ b/app/src/main/res/layout/activity_add_customer.xml
@@ -5,7 +5,13 @@
android:layout_height="match_parent"
xmlns:omi="http://schemas.android.com/apk/res-auto">
+
+