先看下别人的效果图
第一眼看到这个的还以为是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圆角这个问题:
我画一个简图
点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, GlideAnimation super 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, GlideAnimation super 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, GlideAnimation super 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, GlideAnimation super 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);
}