原本项目中要实现camu的曝光多边形动画,做好后,产品后来决定不用了。所以源码我就贴出来了。
支持任意多边形
import android.content.Context;
import android.graphics.*;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.lang.ref.WeakReference;
public class PolygonImageView extends View {
private static final String TAG = PolygonImageView.class.getSimpleName();
// 当前UI的宽高
private int mWidth;
private int mHeight;
// 画笔
private Paint paint;
// 绘制目标圆位图画笔
private static int sPolygonColor = Color.BLUE;
private WeakReference<Bitmap> mWeakSrcOriBitmap;
private WeakReference<Bitmap> mWeakDstOriBitmap;
// 多边形默认边宽度
private static float sPolygonWidth = 0f;
// 多边形边数
private static int sPolygonNumber = -1;
// 多边形内角角度
private static float sPolygonInnerAngel = 180 - (360f / sPolygonNumber);
// 多边形内部每一份三角弧形的角度 = 圆周角度 / 多边形边数
private static float sPolygonInnerRadianAngel = 360f / sPolygonNumber;
// 多边形内部每一份三角弧形的的间隔,默认2px
private static int sPolygonInnerRadianGap = 2;
private PorterDuffXfermode mPorterDuffXferMode;
private static int sCanvasFlag = Canvas.MATRIX_SAVE_FLAG |
Canvas.CLIP_SAVE_FLAG |
Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
Canvas.FULL_COLOR_LAYER_SAVE_FLAG |
Canvas.CLIP_TO_LAYER_SAVE_FLAG;
public PolygonImageView(Context context) {
super(context);
}
public PolygonImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 初始化多边形view的参数
*
* @param polygonNumber 多边形的边数
* @param polygonWidth 多边形边宽
* @param polygonColor 多边形颜色 eg:Color.RED
* @param polygonGap 多边形内部每一份三角弧形的的间隔,默认2px
*/
public void initPolygon(int polygonNumber, int polygonWidth, int polygonColor, int polygonGap) {
if (polygonNumber < 3)
throw new RuntimeException("this [polygonNumber < 3] can't compose to a polygon!");
if (polygonWidth < 0)
throw new RuntimeException("this polygonWidth can't be negative!");
if (polygonGap < 0) {
Log.i(TAG, "this sPolygonInnerRadianGap can't be negative, will use the default value of 2px");
polygonGap = 2;
}
sPolygonNumber = polygonNumber;
sPolygonWidth = polygonWidth;
sPolygonColor = polygonColor;
sPolygonInnerRadianGap = polygonGap;
sPolygonInnerAngel = 180 - (360f / polygonNumber);
sPolygonInnerRadianAngel = 360f / sPolygonNumber;
initPaint();
mPorterDuffXferMode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
private void initPaint() {
if (paint == null) {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Style.FILL);
paint.setShader(null);
}
}
/**
* 调整多变形的宽度
*
* @param widthDelta 宽度的差值 增大为正数,减小为负数
*/
public void adjustPolygonWidth(float widthDelta) {
sPolygonWidth = sPolygonWidth + widthDelta;
if (sPolygonWidth >= mWidth >> 1) {
sPolygonWidth = mWidth >> 1;
}
if (sPolygonWidth <= 0) {
sPolygonWidth = 0;
}
if (mWeakDstOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
mWeakDstOriBitmap.clear();
}
if (mWeakSrcOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
mWeakSrcOriBitmap.clear();
}
invalidate();
}
// create a bitmap with a circle, used for the "dst" image
private Bitmap makeDst(int w, int h) {
Bitmap bm;
if (mWeakDstOriBitmap != null && mWeakDstOriBitmap.get() != null) {
return mWeakDstOriBitmap.get();
} else {
bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
Canvas c = new Canvas(bm);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(sPolygonColor);
float half = w >> 1;
Log.i(TAG, "makeDst half = " + half);
c.drawCircle(half, half, half, dstPaint);
//bitmap缓存起来,避免每次分配内存
mWeakDstOriBitmap = null;
mWeakDstOriBitmap = new WeakReference<Bitmap>(bm);
return bm;
}
// create a bitmap with a rect, used for the "src" image
private Bitmap makeSrc(int mWidth, int mHeight) {
Log.i(TAG, "makeSrc sPolygonWidth = " + sPolygonWidth);
Bitmap bm;
// 从缓存中获取bitmap,避免每次创建
if (mWeakSrcOriBitmap != null && mWeakSrcOriBitmap.get() != null) {
return mWeakSrcOriBitmap.get();
} else {
bm = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
}
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(Color.RED);
float halfWidth = mWidth >> 1;
float halfHeight = mHeight >> 1;
// 获取多边形内角三角形的右三角形的底边距离
// 圆心
// /|\
// / | \
// / | \
// / | \
// /____|____\
// |-计算|
// |-右边|
// |-三角|
// |-形这|
// |-段的|
// |-距离|
double polygonRadian = (180 - sPolygonInnerAngel) / 2 * Math.PI / 180;
float b = (float) (halfWidth * Math.tan(polygonRadian));
Path path = new Path();
// 移动到多边形内三角形顶点
path.moveTo(halfWidth - sPolygonWidth, halfHeight);
// 连接多边形内三角形右下角点
path.lineTo(halfWidth + b - sPolygonWidth, mHeight);
// 连接多边形内三角形左下角点
path.lineTo(halfWidth - b - sPolygonWidth, mHeight);
// 回连到多边形内三角形顶点
path.lineTo(halfWidth - sPolygonWidth, halfHeight);
path.close();
c.drawPath(path, p);
//bitmap缓存起来,避免每次分配内存
mWeakSrcOriBitmap = null;
mWeakSrcOriBitmap = new WeakReference<Bitmap>(bm);
return bm;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (sPolygonNumber == -1)
throw new RuntimeException("you must call initPolygon() method first!");
if (mWidth == 0 || mHeight == 0) {
mWidth = getWidth();
mHeight = getHeight();
}
Bitmap srcB = makeSrc(mWidth, mHeight);
Bitmap dstB = makeDst(mWidth, mHeight);
// 不能用int,因为有些多边形的角度带有小数点,用int会导致整体角度不准
float degree = 0;
for (int i = 0; i < sPolygonNumber; i++) {
int sc = canvas.saveLayer(0, 0, mWidth, mHeight, null, sCanvasFlag);
// 绘制圆
canvas.drawBitmap(dstB, 0, 0, paint);
// 旋转每个三角弧形
canvas.rotate(degree, mWidth >> 1, mHeight >> 1);
// 累加下一次旋转的角度
degree = degree + sPolygonInnerRadianAngel;
// 设置显示为取两个图形相交的部分
Log.i(TAG, "degree = " + degree);
paint.setXfermode(mPorterDuffXferMode);
canvas.save();
// 多边形每部分的间隙
canvas.translate(0, sPolygonInnerRadianGap);
// 绘制平移间隙后的三角形
canvas.drawBitmap(srcB, 0, 0, paint);
canvas.restore();
paint.setXfermode(null);
canvas.restoreToCount(sc);
}
}
}