仿釘釘頭像(有頭像顯示圖片拼接,無圖顯示暱稱)

weixin_34337265發表於2018-12-14

一、需求

  1. 多圖拼接展示(目前最多支援四張圖)
  2. 可展示文字
  3. 可展示成圓角、方角、圓形
  4. 可自定義分割線寬度、顏色
  5. 可設定是否包含邊框

二、預覽

2362769-cac6909bba32ca30.jpg
預覽圖.jpg

三、程式碼

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

相關文章