BitmapShader的简单应用

先看下别人的效果图
BitmapShader的简单应用_第1张图片
第一眼看到这个的还以为是qq群组头像,然而并不是。画圆形或者圆角或者是椭圆图像大家应该都会自己画。
这个东西呢,稍微复杂了一丁点儿。画圆形图像比较好的实现方式是用BitmapShader,不是使用paint.serXfermode或者是canvas.clip(这个会有锯齿,Google了也没找到解决办法,有知道的请告诉一下)。
单独的1个圆形图片很好实现,如果是左右各一半呢,用canvas.drawPath,然后吧需要画的圆角用path.arcTo这样包裹起来,再配合一下BitmapShader就实现了。

Talk is cheap,show me the fucking source.借助自己写的简单的demo来看下。实现的方式是使用自定义的Drawable的方式。先看下要分割左右一半的Path.arcTo应该怎么写:

 /**
     * 左边右边都是背景和文字
     */
    private void drawNeed_2(Canvas canvas, int interval) {
        float radius = mCenterX;
        float degree = (float) Math.toDegrees(Math.asin(interval / radius));
        float sweepAngle = 180F - 2 * degree;
        Path clipPath = new Path();
        RectF rectF = new RectF(getBounds());
        //画左边文字
        drawTextWithHalfBranch(canvas, clipPath, mCenterX - interval, mCenterY, rectF, 90F + degree, sweepAngle, 0
                , Color.RED, "明哥", Color.YELLOW, 50);
        // 画右边文字
        drawTextWithHalfBranch(canvas, clipPath, mCenterX + interval, mCenterY, rectF, -90F + degree, sweepAngle, 1
                , Color.BLUE, "路飞", Color.YELLOW, 50);
    }

  /**
     * 画二分之一的背景和文字
     */
    private void drawTextWithHalfBranch(Canvas canvas, Path clipPath, float x, float y, RectF rectF, float startAngle, float sweepAngle, int index,
                                        int itemColor, String text, int textColor, float textSize) {
        clipPath.reset();
        clipPath.moveTo(x, y);
        clipPath.arcTo(rectF, startAngle, sweepAngle);
        clipPath.close();
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(itemColor);
        Paint textPaint = new Paint(paint);
        textPaint.setColor(textColor);
        canvas.drawPath(clipPath, paint);

        float textX = 0;
        float textY = 0;
        switch (index) {
            case 0:
                // 左边
                textX = 0 + x / 2F;
                textY = rectF.height() / 2F;
                break;
            case 1:
                // 右边
                textX = x + (rectF.width() - x) / 2F;
                textY = rectF.height() / 2F;
                break;
        }
        textPaint.setTextSize(textSize);
        // 下面让画出的text位于中心点
        Rect bound = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), bound);
        textX = textX - bound.width() / 2F;
        textY = textY + bound.height() / 2F;
        canvas.drawText(text, textX, textY, textPaint);
    }

这里使用了的代码:

 clipPath.moveTo(x, y);
        clipPath.arcTo(rectF, startAngle, sweepAngle);
        clipPath.close();

简单说下怎么取1/4圆角,1/2圆角这个问题:
我画一个简图
BitmapShader的简单应用_第2张图片
点A是起点,当A位移到A1然后旋转90度的画会到AE,但是我们实际需要的终点其实是A2;所以旋转角度应该是90-2*AOA1的夹角,这是1/4的角度;同理可以得到1/2的角度。

下面是代码FuckDrawable

package example.administrator.com.myapplication;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;


/**
 * Created by Tangxb on 2016/11/1.
 */

public class FuckDrawable extends Drawable {
    private Paint mPaint;
    private int mCenterX;
    private int mCenterY;
    private Bitmap[] mInputBitmaps;
    private Bitmap mSourceBitmap;
    // 间隔
    int interval = 2 / 2;

    public FuckDrawable(Bitmap... inputBitmaps) {
        mInputBitmaps = inputBitmaps;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);
        mCenterX = getBounds().width() / 2;
        mCenterY = getBounds().height() / 2;
        generateSourceBitmap(getBounds());
    }

    @Override
    public void draw(Canvas canvas) {
        if (mSourceBitmap != null && !mSourceBitmap.isRecycled()) {
            canvas.drawBitmap(mSourceBitmap, 0, 0, mPaint);
        }
    }

    private void generateSourceBitmap(Rect bounds) {
        if (mInputBitmaps != null && mInputBitmaps.length > 0) {
            final int bitmapCount = mInputBitmaps.length;
            mSourceBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(mSourceBitmap);
            Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            drawPaint.setFilterBitmap(true);
            if (bitmapCount == 1) {
                float radius = mCenterX;
                drawBitmapWithCircle(canvas, mInputBitmaps[0], mCenterX, mCenterY, radius, drawPaint);
            } else {
                // 改变这里测试(只用一个need即可)
//                drawNeed_2(canvas, interval);
//                drawNeed_3(canvas, interval, drawPaint);
                drawNeed_4(canvas, interval, drawPaint);
            }
        }
    }

    /**
     * 左边2个图片,右边一个背景和文字
     */
    private void drawNeed_3(Canvas canvas, int interval, Paint drawPaint) {
        float radius = mCenterX;
        float degree = (float) Math.toDegrees(Math.asin(interval / radius));
        float sweepAngle = 90F - 2 * degree;
        Path clipPath = new Path();
        RectF rectF = new RectF(getBounds());
        // 左上
        drawBitmapWithFourDegree(canvas, mInputBitmaps[0], clipPath, mCenterX - interval, mCenterY - interval, rectF, 180F + degree, sweepAngle
                , mCenterX - interval, mCenterY - interval, radius, drawPaint);
        // 左下
        drawBitmapWithFourDegree(canvas, mInputBitmaps[1], clipPath, mCenterX - interval, mCenterY + interval, rectF, 90F + degree, sweepAngle
                , mCenterX - interval, mCenterY + interval, radius, drawPaint);
        // 画右边文字
        drawTextWithHalfBranch(canvas, clipPath, mCenterX + interval, mCenterY, rectF, -90F + degree, 180F - 2 * degree, 1
                , Color.RED, "明哥", Color.YELLOW, 50);
    }

    /**
     * 左边右边都是背景和文字
     */
    private void drawNeed_2(Canvas canvas, int interval) {
        float radius = mCenterX;
        float degree = (float) Math.toDegrees(Math.asin(interval / radius));
        float sweepAngle = 180F - 2 * degree;
        Path clipPath = new Path();
        RectF rectF = new RectF(getBounds());
        //画左边文字
        drawTextWithHalfBranch(canvas, clipPath, mCenterX - interval, mCenterY, rectF, 90F + degree, sweepAngle, 0
                , Color.RED, "明哥", Color.YELLOW, 50);
        // 画右边文字
        drawTextWithHalfBranch(canvas, clipPath, mCenterX + interval, mCenterY, rectF, -90F + degree, sweepAngle, 1
                , Color.BLUE, "路飞", Color.YELLOW, 50);
    }

    /**
     * 画4个角
     */
    private void drawNeed_4(Canvas canvas, int interval, Paint drawPaint) {
        float radius = mCenterX;
        float degree = (float) Math.toDegrees(Math.asin(interval / radius));
        float sweepAngle = 90F - 2 * degree;
        Path clipPath = new Path();
        RectF rectF = new RectF(getBounds());
        // 左上
        // 画图
        drawBitmapWithFourDegree(canvas, mInputBitmaps[0], clipPath, mCenterX - interval, mCenterY - interval, rectF, 180F + degree, sweepAngle
                , mCenterX - interval, mCenterY - interval, radius, drawPaint);
        //画文字
        drawTextWithFourBranch(canvas, clipPath, mCenterX - interval, mCenterY - interval, rectF, 180F + degree, sweepAngle, 0
                , Color.RED, "明哥", Color.YELLOW, 50);
        // 左下
        drawBitmapWithFourDegree(canvas, mInputBitmaps[1], clipPath, mCenterX - interval, mCenterY + interval, rectF, 90F + degree, sweepAngle
                , mCenterX - interval, mCenterY + interval, radius, drawPaint);
        // 右上
        drawBitmapWithFourDegree(canvas, mInputBitmaps[2], clipPath, mCenterX + interval, mCenterY - interval, rectF, -90F + degree, sweepAngle
                , mCenterX + interval, mCenterY - interval, radius, drawPaint);
        // 右下
        drawBitmapWithFourDegree(canvas, mInputBitmaps[3], clipPath, mCenterX + interval, mCenterY + interval, rectF, 0F + degree, sweepAngle
                , mCenterX + interval, mCenterY + interval, radius, drawPaint);
    }

    /**
     * 画二分之一的背景和文字
     */
    private void drawTextWithHalfBranch(Canvas canvas, Path clipPath, float x, float y, RectF rectF, float startAngle, float sweepAngle, int index,
                                        int itemColor, String text, int textColor, float textSize) {
        clipPath.reset();
        clipPath.moveTo(x, y);
        clipPath.arcTo(rectF, startAngle, sweepAngle);
        clipPath.close();
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(itemColor);
        Paint textPaint = new Paint(paint);
        textPaint.setColor(textColor);
        canvas.drawPath(clipPath, paint);

        float textX = 0;
        float textY = 0;
        switch (index) {
            case 0:
                // 左边
                textX = 0 + x / 2F;
                textY = rectF.height() / 2F;
                break;
            case 1:
                // 右边
                textX = x + (rectF.width() - x) / 2F;
                textY = rectF.height() / 2F;
                break;
        }
        textPaint.setTextSize(textSize);
        // 下面让画出的text位于中心点
        Rect bound = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), bound);
        textX = textX - bound.width() / 2F;
        textY = textY + bound.height() / 2F;
        canvas.drawText(text, textX, textY, textPaint);
    }

    /**
     * 画四分之一的背景和文字
     */
    private void drawTextWithFourBranch(Canvas canvas, Path clipPath, float x, float y, RectF rectF, float startAngle, float sweepAngle, int index,
                                        int itemColor, String text, int textColor, float textSize) {
        clipPath.reset();
        clipPath.moveTo(x, y);
        clipPath.arcTo(rectF, startAngle, sweepAngle);
        clipPath.close();
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(itemColor);
        Paint textPaint = new Paint(paint);
        textPaint.setColor(textColor);
        canvas.drawPath(clipPath, paint);

        float textX = 0;
        float textY = 0;
        switch (index) {
            case 0:
                // 左上
                textX = 0 + x / 2F;
                textY = 0 + y / 2F;
                break;
            case 1:
                // 左下
                textX = 0 + x / 2F;
                textY = y + (rectF.height() - y) / 2F;
                break;
            case 2:
                // 右上
                textX = x + (rectF.width() - x) / 2F;
                textY = 0 + y / 2F;
                break;
            case 3:
                // 右下
                textX = x + (rectF.width() - x) / 2F;
                textY = y + (rectF.height() - y) / 2F;
                break;
        }
        textPaint.setTextSize(textSize);
        // 下面让画出的text位于中心点
        Rect bound = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), bound);
        textX = textX - bound.width() / 2F;
        textY = textY + bound.height() / 2F;
        canvas.drawText(text, textX, textY, textPaint);
    }

    /**
     * 画四分之一的圆半角
     */
    private void drawBitmapWithFourDegree(Canvas canvas, Bitmap bitmap, Path clipPath, float x, float y, RectF rectF, float startAngle, float sweepAngle
            , int centerX, int centerY, float radius, Paint drawPaint) {
        clipPath.reset();
        clipPath.moveTo(x, y);
        clipPath.arcTo(rectF, startAngle, sweepAngle);
        clipPath.close();
        drawBitmapWithPath(canvas, bitmap, clipPath, centerX, centerY, radius, drawPaint);
    }

    /**
     * 使用Path画图
     */
    private void drawBitmapWithPath(Canvas canvas, Bitmap bitmap, Path path, int centerX, int centerY, float radius, Paint drawPaint) {
        final int halfBitmapWidth = bitmap.getWidth() / 2;
        final int halfBitmapHeight = bitmap.getHeight() / 2;

        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        Matrix shaderMatrix = new Matrix();
        float minSize = bitmap.getWidth() > bitmap.getHeight() ? bitmap.getHeight() : bitmap.getWidth();
        float scale = radius * 2 / minSize;
        // 先缩放
        shaderMatrix.setScale(scale, scale);
        // 由于缩放的中心点是(0,0)缩放之后,要让中心位移到需要的位置(具体可以用radius=100,minSize=400来验证一下)
        shaderMatrix.postTranslate(centerX - (halfBitmapWidth * scale), centerY - (halfBitmapHeight * scale));
        bitmapShader.setLocalMatrix(shaderMatrix);

        drawPaint.setShader(bitmapShader);
        canvas.drawPath(path, drawPaint);
        drawPaint.setShader(null);
    }

    /**
     * 画圆形图
     */
    private void drawBitmapWithCircle(Canvas canvas, Bitmap bitmap, int centerX, int centerY, float radius, Paint drawPaint) {
        final int halfBitmapWidth = bitmap.getWidth() / 2;
        final int halfBitmapHeight = bitmap.getHeight() / 2;

        BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        Matrix shaderMatrix = new Matrix();
        float minSize = bitmap.getWidth() > bitmap.getHeight() ? bitmap.getHeight() : bitmap.getWidth();
        float scale = radius * 2 / minSize;
        shaderMatrix.setScale(scale, scale);
        shaderMatrix.postTranslate(centerX - (halfBitmapWidth * scale), centerY - (halfBitmapHeight * scale));
        bitmapShader.setLocalMatrix(shaderMatrix);

        drawPaint.setShader(bitmapShader);
        canvas.drawCircle(centerX, centerY, radius, drawPaint);
        drawPaint.setShader(null);
    }

    public void recycle() {
        if (mInputBitmaps != null) {
            for (int i = 0; i < mInputBitmaps.length; i++) {
                Bitmap bitmap = mInputBitmaps[i];
                if (bitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle();
                }
            }
            mInputBitmaps = null;
        }

        if (mSourceBitmap != null && !mSourceBitmap.isRecycled()) {
            mSourceBitmap.recycle();
            mSourceBitmap = null;
        }
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }
}

上面的注释写得很详细了,看下Activity里面配合Glide来加载网络图片:

private void combineWithGlide() {
        int needW = 500;
        int needH = 500;
        // 下载原始图片大小
//        needH = needW = Target.SIZE_ORIGINAL;
        final WeakHashMap> hashMap = new WeakHashMap<>();
        Glide.with(this).load(IMAGE_URL1).asBitmap().into(new SimpleTarget(needW, needH) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimationsuper Bitmap> glideAnimation) {
                hashMap.put(IMAGE_URL1, new SoftReference<>(resource));
                judgeDoWork(hashMap);
            }
        });
        Glide.with(this).load(IMAGE_URL2).asBitmap().into(new SimpleTarget(needW, needH) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimationsuper Bitmap> glideAnimation) {
                hashMap.put(IMAGE_URL2, new SoftReference<>(resource));
                judgeDoWork(hashMap);
            }
        });
        Glide.with(this).load(IMAGE_URL3).asBitmap().into(new SimpleTarget(needW, needH) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimationsuper Bitmap> glideAnimation) {
                hashMap.put(IMAGE_URL3, new SoftReference<>(resource));
                judgeDoWork(hashMap);
            }
        });
        Glide.with(this).load(IMAGE_URL4).asBitmap().into(new SimpleTarget(needW, needH) {
            @Override
            public void onResourceReady(Bitmap resource, GlideAnimationsuper Bitmap> glideAnimation) {
                hashMap.put(IMAGE_URL4, new SoftReference<>(resource));
                judgeDoWork(hashMap);
            }
        });
    }

    /**
     * 下载完4个图片才设置背景
     *
     * @param hashMap
     */
    private void judgeDoWork(WeakHashMap> hashMap) {
        int needSize = 4;
        if (hashMap.size() != needSize) return;
        Bitmap[] bitmap = new Bitmap[needSize];
        bitmap[0] = hashMap.get(IMAGE_URL1).get();
        bitmap[1] = hashMap.get(IMAGE_URL2).get();
        bitmap[2] = hashMap.get(IMAGE_URL3).get();
        bitmap[3] = hashMap.get(IMAGE_URL4).get();
        FuckDrawable fuckDrawable = new FuckDrawable(bitmap);
        imageView04.setImageDrawable(fuckDrawable);
    }

简单滴上一个效果咯:
BitmapShader的简单应用_第3张图片

你可能感兴趣的:(Android)