一、需求
1. 多圖拼接展示(目前最多支援四張圖)
2. 可展示文字
3. 可展示成圓角、方角、圓形
4. 可自定義分割線寬度、顏色
5. 可設定是否包含邊框
二、預覽
三、程式碼
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;
import java.util.List;
/**
* Describe : https://github.com/NFLeo
* Created by Leo on 2018/12/13 on 13:57.
*/
class AvatarUtil {
static Builder getBuilder(Context context) {
return new Builder(context);
}
public static class Builder {
private Context mContext;
private List<Object> mList; // 資料來源
private int mWidth = 50; // 控制元件寬度
private int mHeight = 50; // 控制元件高度
private int mShape = Shape.CIRCLE; // 控制元件形狀
private int mRoundAngel = 10; // 圓角大小
private int mMarginWidth = 4; // 圖片間隙
private int mMarginColor = R.color.gray; // 圖片間隙顏色
private boolean hasEdge = true; // 是否包含邊緣
private float mTextSize = 50; // 文字大小
private int mTextColor = R.color.colorPrimary; // 文字顏色
private int mBackGroundColor = R.color.colorAccent; // 文字背景顏色
private Builder(Context context) {
this.mContext = context;
}
/**
* 設定資料來源
*/
Builder setList(List<Object> mList) {
this.mList = mList;
return this;
}
/**
* 設定圖片尺寸
*/
Builder setBitmapSize(int mWidth, int mHeight) {
if (mWidth > 0) {
this.mWidth = mWidth;
}
if (mHeight > 0) {
this.mHeight = mHeight;
}
return this;
}
/**
* 設定展示型別(圓形、圓角、方形)
*/
Builder setShape(int mShape) {
this.mShape = mShape;
return this;
}
/**
* 設定圓角角度
* 當shape設定為Shape.Round時讀取改屬性
*
* @param mRoundAngel 圓角角度
*/
Builder setRoundAngel(int mRoundAngel) {
this.mRoundAngel = mRoundAngel;
return this;
}
/**
* 設定分割線寬度
*/
Builder setMarginWidth(int mMarginWidth) {
this.mMarginWidth = mMarginWidth;
return this;
}
/**
* 設定分割線顏色
*/
public Builder setMarginColor(int mMarginColor) {
this.mMarginColor = mMarginColor;
return this;
}
/**
* 設定文字大小
*/
public Builder setTextSize(int mTextSize) {
this.mTextSize = mTextSize;
return this;
}
/**
* 設定文字顏色
*/
public Builder setTextColor(int mTextColor) {
this.mTextColor = mTextColor;
return this;
}
public Builder setHasEdge(boolean hasEdge) {
this.hasEdge = hasEdge;
return this;
}
Bitmap create() {
final Bitmap result = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawPath(drawShapePath(), paint);
float[] marginPath;
final int listSize = mList.size();
switch (listSize) {
case 1:
startDraw(canvas, mList.get(0), DrawPosition.WHOLE);
break;
case 2:
startDraw(canvas, mList.get(0), DrawPosition.LEFT);
startDraw(canvas, mList.get(1), DrawPosition.RIGHT);
marginPath = new float[]{mWidth / 2, 0, mWidth / 2, mHeight};
drawMarginLine(canvas, marginPath);
break;
case 3:
startDraw(canvas, mList.get(0), DrawPosition.LEFT);
startDraw(canvas, mList.get(1), DrawPosition.RIGHT_TOP);
startDraw(canvas, mList.get(2), DrawPosition.RIGHT_BOTTOM);
marginPath = new float[]{mWidth / 2, 0,
mWidth / 2, mHeight,
mWidth / 2, mHeight / 2,
mWidth, mHeight / 2};
drawMarginLine(canvas, marginPath);
break;
default:
startDraw(canvas, mList.get(0), DrawPosition.LEFT_TOP);
startDraw(canvas, mList.get(1), DrawPosition.LEFT_BOTTOM);
startDraw(canvas, mList.get(2), DrawPosition.RIGHT_TOP);
startDraw(canvas, mList.get(3), DrawPosition.RIGHT_BOTTOM);
marginPath = new float[]{mWidth / 2, 0,
mWidth / 2, mHeight,
0, mHeight / 2,
mWidth, mHeight / 2};
drawMarginLine(canvas, marginPath);
break;
}
// 僅方形支援邊緣 且單個文字不支援邊緣
if (hasEdge && mShape == Shape.SQUARE && !(mList.size() == 1 && mList.get(0) instanceof String)) {
drawEdge(canvas);
}
return result;
}
/**
* 根據邊角配置繪製畫布path
*/
private Path drawShapePath() {
Path mPath = new Path();
switch (mShape) {
case Shape.ROUND:
mPath.addRoundRect(new RectF(0, 0, mHeight, mWidth), mRoundAngel, mRoundAngel, Path.Direction.CCW);
break;
case Shape.SQUARE:
mPath.addRect(new RectF(0, 0, mHeight, mWidth), Path.Direction.CCW);
break;
case Shape.CIRCLE:
int radius = Math.max(mWidth, mHeight) / 2;
mPath.addCircle(mWidth / 2, mHeight / 2, radius, Path.Direction.CCW);
break;
}
return mPath;
}
/**
* 根據資料來源型別區分繪製圖片或文字
*/
private void startDraw(Canvas canvas, Object resource, int position) {
if (resource instanceof Bitmap) {
drawBitmap(canvas, (Bitmap) resource, position);
} else if (resource instanceof String) {
drawText(canvas, (String) resource, position);
}
}
/**
* 繪製圖片
* 最多支援四張圖
*/
private void drawBitmap(Canvas canvas, Bitmap bitmap, int mode) {
int left, top;
int x, y, width, height;
int dstWidth, dstHeight;
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
if (mode == DrawPosition.WHOLE) {
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, mWidth, mHeight, false);
canvas.drawBitmap(bmp, 0, 0, paint);
} else if (mode == DrawPosition.LEFT) {
dstWidth = mWidth;
dstHeight = mHeight;
x = mWidth / 4 + mMarginWidth / 4;
y = 0;
width = mWidth / 2 - mMarginWidth / 4;
height = mHeight;
left = 0;
top = 0;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 裁取中間部分(從x點裁取置頂距離)
Bitmap dstBmp = Bitmap.createBitmap(bmp, x, y, width, height);
// 繪圖
canvas.drawBitmap(dstBmp, left, top, paint);
} else if (mode == DrawPosition.RIGHT) {
dstWidth = mWidth;
dstHeight = mHeight;
x = mWidth / 4 + mMarginWidth / 4;
y = 0;
width = mWidth / 2 - mMarginWidth / 4;
height = mHeight;
left = mWidth / 2 + mMarginWidth / 4;
top = 0;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 裁取中間部分(從x點裁取置頂距離)
Bitmap dstBmp = Bitmap.createBitmap(bmp, x, y, width, height);
// 繪圖
canvas.drawBitmap(dstBmp, left, top, paint);
} else if (mode == DrawPosition.LEFT_TOP) {
dstWidth = mWidth / 2 - mMarginWidth / 4;
dstHeight = mHeight / 2 - mMarginWidth / 4;
left = 0;
top = 0;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 繪圖
canvas.drawBitmap(bmp, left, top, paint);
} else if (mode == DrawPosition.LEFT_BOTTOM) {
dstWidth = mWidth / 2 - mMarginWidth / 4;
dstHeight = mHeight / 2 - mMarginWidth / 4;
left = 0;
top = mHeight / 2 + mMarginWidth / 4;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 繪圖
canvas.drawBitmap(bmp, left, top, paint);
} else if (mode == DrawPosition.RIGHT_TOP) {
dstWidth = mWidth / 2 - mMarginWidth / 4;
dstHeight = mHeight / 2 - mMarginWidth / 4;
left = mWidth / 2 + mMarginWidth / 4;
top = 0;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 繪圖
canvas.drawBitmap(bmp, left, top, paint);
} else if (mode == DrawPosition.RIGHT_BOTTOM) {
dstWidth = mWidth / 2 - mMarginWidth / 4;
dstHeight = mHeight / 2 - mMarginWidth / 4;
left = mWidth / 2 + mMarginWidth / 4;
top = mHeight / 2 + mMarginWidth / 4;
// 比例縮放
Bitmap bmp = Bitmap.createScaledBitmap(bitmap, dstWidth, dstHeight, false);
// 繪圖
canvas.drawBitmap(bmp, left, top, paint);
}
}
/**
* 繪製文字
*/
private void drawText(Canvas canvas, String text, int mode) {
float bgLeft = 0, bgTop = 0, bgRight = 0, bgBottom = 0;
float textSize = mTextSize;
Paint textBgPaint = new Paint();
textBgPaint.setColor(ContextCompat.getColor(mContext, mBackGroundColor));
textBgPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
if (mode == DrawPosition.WHOLE) {
bgLeft = 0;
bgTop = 0;
bgRight = mWidth;
bgBottom = mHeight;
textSize = mWidth / 2;
} else if (mode == DrawPosition.LEFT) {
bgLeft = 0;
bgTop = 0;
bgRight = mWidth / 2 - mMarginWidth / 4;
bgBottom = mHeight;
textSize = mWidth / 4;
} else if (mode == DrawPosition.RIGHT) {
bgLeft = mWidth / 2 + mMarginWidth / 4;
bgTop = 0;
bgRight = mWidth;
bgBottom = mHeight;
textSize = mWidth / 4;
} else if (mode == DrawPosition.LEFT_TOP) {
bgLeft = 0;
bgTop = 0;
bgRight = mWidth / 2 - mMarginWidth/ 4;
bgBottom = mHeight / 2 - mMarginWidth/ 4;
textSize = mWidth / 5;
} else if (mode == DrawPosition.LEFT_BOTTOM) {
bgLeft = 0;
bgTop = mHeight / 2 + mMarginWidth/ 4;
bgRight = mWidth / 2 - mMarginWidth/ 4;
bgBottom = mHeight;
textSize = mWidth / 5;
} else if (mode == DrawPosition.RIGHT_TOP) {
bgLeft = mWidth / 2 + mMarginWidth/ 4;
bgTop = 0;
bgRight = mWidth;
bgBottom = mHeight / 2 - mMarginWidth/ 4;
textSize = mWidth / 5;
} else if (mode == DrawPosition.RIGHT_BOTTOM) {
bgLeft = mWidth / 2 + mMarginWidth/ 4;
bgTop = mHeight / 2 + mMarginWidth/ 4;
bgRight = mWidth;
bgBottom = mHeight;
textSize = mWidth / 5;
}
RectF rect = new RectF(bgLeft, bgTop, bgRight, bgBottom);
canvas.drawRect(rect, textBgPaint);
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setAntiAlias(true);
textPaint.setColor(ContextCompat.getColor(mContext, mTextColor));
textPaint.setTextSize(Math.min(mTextSize, textSize));
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
int baseline = (int) ((bgBottom + bgTop - fontMetrics.bottom - fontMetrics.top) / 2);
canvas.drawText(text, rect.centerX(), baseline, textPaint);
}
/**
* 繪製邊緣線
*/
private void drawEdge(Canvas canvas) {
Paint edgePaint = new Paint();
edgePaint.setStrokeWidth(mMarginWidth);
edgePaint.setStyle(Paint.Style.STROKE);
edgePaint.setColor(ContextCompat.getColor(mContext, mMarginColor));
Path mPath = new Path();
mPath.moveTo(0, 0);
mPath.lineTo(0, mHeight);
mPath.lineTo(mWidth, mHeight);
mPath.lineTo(mWidth, 0);
mPath.close();
canvas.drawPath(mPath, edgePaint);
}
/**
* 繪製分割線
*/
private void drawMarginLine(Canvas canvas, float[] path) {
Paint marginPaint = new Paint();
marginPaint.setStrokeWidth(mMarginWidth / 2);
marginPaint.setColor(ContextCompat.getColor(mContext, mMarginColor));
canvas.drawLines(path, marginPaint);
}
}
public interface Shape {
int ROUND = 0X33;
int CIRCLE = 0X11;
int SQUARE = 0X22;
}
interface DrawPosition {
int WHOLE = 0;
int LEFT = 1;
int RIGHT = 2;
int LEFT_TOP = 3;
int LEFT_BOTTOM = 4;
int RIGHT_TOP = 5;
int RIGHT_BOTTOM = 6;
}
}
四、使用
1. 複製該類到專案
2. 呼叫以下方法
// 注:目標圖轉換成Bitmap,多Bitmap拼接都放在子執行緒操作
List<Object> bitmapList = new ArrayList()
bitmapList.add(bitmap) // 新增圖片轉換完成的bitmap
bitmapList.add("錢") // 可新增需展示的文字
Bitmap avatar = AvatarUtil.getBuilder(AvatarActivity.this)
.setShape(shape)
.setMarginWidth(width)
.setRoundAngel(height)
.setList(bitmapList)
.setTextSize(textSize) // px
.setTextColor(R.color.black) // 需傳入color檔案下的顏色值
.setBitmapSize(size, size)
.setHasEdge(hasEdge)
.create();
// 放到主執行緒
ivAvatar.setImageBitmap(avatar)
可以用rxjava執行緒切換
五、說明
注:
1. 拼接最多支援四張,需要更多可自行計算
2. 尺寸傳值(圖片尺寸、文字尺寸、間隔尺寸),需外部計算成px
3. 顏色傳值,需傳入color檔案中的色值,例如R.color.black