前言
- 是否还在为创建selector如何起名而烦恼?
- 是否还在为selector文件过多而吐出一口老血?
- 今天将为大家带来全面升级的ShapeView。
正是因为这些原因的纯在,同时也一直困扰着我们,所以今天给大家推荐ShapeView。
(这个版本比起之前的版本内容更加丰富,代码也更加整洁。)
ShapeView 的 Github 地址 觉得不错点个star支持一下~
1. 简介
- 不再创建selector来完成设计直接在布局中配置
- 可以使任何一个View or layout展示出不同形状,并且代替重复创建不同shape文件。
- 对于整个项目而言更加干净优雅,随时切换不同的shape模式
2.实现原理
Paint 的 Xfermode 属性把绘制的内容和 Canvas 内容图层交集结合计算出最终的颜色图像这里的 Xfermode设置的是DST_IN。这个属性的具体作用可以查阅相关资料了解。
3.效果图
4.图形的绘制代码
使用Path的moveTo、lineTo、cubicTo、quadTo 直线与贝塞尔曲线通过指定两个端点坐标来绘制出心形,三角形,圆角矩形,五角星等,再与原内容图层相交剪切成不同形状,将陆续推出更多形状
public abstract class ClipHelper implements ClipPath {
protected final Path path = new Path();
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private ShapeView shapeView;
public ClipHelper(ShapeView view) {
shapeView = view;
}
public void setUpClipPath(int width, int height) {
path.reset();
final Path clipPath = getClipPath(width, height);
if (clipPath != null) {
path.set(clipPath);
}
}
protected final Path getClipPath(int width, int height) {
return createClipPath(width, height);
}
@NonNull
public Bitmap createMask(int width, int height) {
final Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(mask);
canvas.drawPath(path, paint);
return mask;
}
public void setClipPath(Path path, int shapeType, int width, int height) {
switch (shapeType) {
case CIRCLE: //circle
setCirclePath(path, width, height);
break;
case ROUND_RECT://roundRect
RectF rectF = new RectF();
rectF.set(0, 0, width, height);
int radius = shapeView.getRadius();
if (shapeView.getRadius() > 0) {
setRoundRectPath(rectF, path, radius, radius, radius, radius);
} else {
setRoundRectPath(rectF, path, shapeView.getTopLeftRadius(), shapeView.getTopRightRadius(), shapeView.getBottomRightRadius(), shapeView.getBottomLeftRadius());
}
break;
case TRIANGLE://triangle
setTrianglePath(path, width, height);
break;
case HEART://heart
setHeartPath(path, width, height);
break;
case POLYGON: //equilateralPolygon
RectF polygonRectF = new RectF();
polygonRectF.set(0, 0, width, height);
setPolygonPath(polygonRectF, path);
break;
case STAR: //star
//setStarPath(path, width / 2);
//float outR = width / 2f - borderWidthPx *2;
//float inR = outR * sin(18) / sin(180 - 36 - 18) - borderWidthPx;
float outR = width / 2f - shapeView.getBorderWidthPx();
float inR = outR * Utils.sin(18) / Utils.sin(180 - 36 - 18) - shapeView.getBorderWidthPx();
setStarPath(path, outR, inR);
break;
case DIAGONAL:
setDiagonalPath(path, width, height);
break;
}
}
/**
* 三圆形
*/
@Override
public void setCirclePath(Path path, int width, int height) {
//xy为圆的圆心 radius为圆的半径 Diection.CW 顺时针方向
path.addCircle(width / 2f, height / 2f, Math.min(width / 2f, height / 2f), Path.Direction.CW);
}
/**
* 带圆角的矩形
*/
@Override
public void setRoundRectPath(RectF rect, Path path, float topLeftDiameter, float topRightDiameter, float bottomRightDiameter, float bottomLeftDiameter) {
topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;
float borderWidth = shapeView.getBorderWidthPx() / 2;
path.moveTo(rect.left + topLeftDiameter + borderWidth, rect.top + borderWidth);
path.lineTo(rect.right - topRightDiameter - borderWidth, rect.top + borderWidth);
path.quadTo(rect.right - borderWidth, rect.top + borderWidth, rect.right - borderWidth, rect.top + topRightDiameter + borderWidth);
path.lineTo(rect.right - borderWidth, rect.bottom - bottomRightDiameter - borderWidth);
path.quadTo(rect.right - borderWidth, rect.bottom - borderWidth, rect.right - bottomRightDiameter - borderWidth, rect.bottom - borderWidth);
path.lineTo(rect.left + bottomLeftDiameter + borderWidth, rect.bottom - borderWidth);
path.quadTo(rect.left + borderWidth, rect.bottom - borderWidth, rect.left + borderWidth, rect.bottom - bottomLeftDiameter - borderWidth);
path.lineTo(rect.left + borderWidth, rect.top + topLeftDiameter + borderWidth);
path.quadTo(rect.left + borderWidth, rect.top + borderWidth, rect.left + topLeftDiameter + borderWidth, rect.top + borderWidth);
path.close();
}
@Override
public void setTriangleBroadPath(Path path, int width, int height) {
float borderWidth = shapeView.getBorderWidthPx();
path.moveTo(0 + borderWidth, shapeView.getPercentLeft() * height + borderWidth / 2);
path.lineTo(shapeView.getPercentBottom() * width - borderWidth / 2, height - borderWidth);
path.lineTo(width - borderWidth, shapeView.getPercentBottom() * height + borderWidth / 2);
path.close();
}
/**
* 三角形
*/
@Override
public void setTrianglePath(Path path, int width, int height) {
float borderWidth = shapeView.getBorderWidthPx() / 2;
path.moveTo(0 + borderWidth, shapeView.getPercentLeft() * height + borderWidth);
path.lineTo(shapeView.getPercentBottom() * width - borderWidth, height - borderWidth);
path.lineTo(width - borderWidth, shapeView.getPercentRight() * height + borderWidth);
path.close();
}
/**
* 心形
*/
@Override
public void setHeartPath(Path path, int width, int height) {
int borderWidth = shapeView.getBorderWidthPx() / 2;
path.moveTo(0.5f * width, 0.16f * height + borderWidth);
path.cubicTo(0.15f * width + borderWidth, -shapeView.getRadian() * height + borderWidth, -0.4f * width + borderWidth, 0.45f * height + borderWidth, 0.5f * width, height - borderWidth);
// path.moveTo(0.5f * width, height);
path.cubicTo(width + 0.4f * width - borderWidth, 0.45f * height + borderWidth, width - 0.15f * width - borderWidth, -shapeView.getRadian() * height + borderWidth, 0.5f * width, 0.16f * height + borderWidth);
path.close();
}
/**
* 正多边形
*/
@Override
public void setPolygonPath(RectF rect, Path path) {
int sides = shapeView.getSides();
if (sides < 3) {
return;
}
float r = (rect.right - rect.left) / 2 - shapeView.getBorderWidthPx();
float mX = (rect.right + rect.left) / 2;
float my = (rect.top + rect.bottom) / 2;
for (int i = 0; i <= sides; i++) {
// - 0.5 : Turn 90 °
float alpha = Double.valueOf(((2f / sides) * i - shapeView.getTurn()) * Math.PI).floatValue();
float nextX = mX + Double.valueOf(r * Math.cos(alpha)).floatValue();
float nextY = my + Double.valueOf(r * Math.sin(alpha)).floatValue();
if (i == 0) {
path.moveTo(nextX, nextY);
} else {
path.lineTo(nextX, nextY);
}
}
path.close();
}
/**
* 星星
*/
@Override
public void setStarPath(Path path, float outR, float inR) {
int borderWidthPx = shapeView.getBorderWidthPx();
float radius = outR;
float radian = Utils.degree2Radian(36);// 36为五角星的角度
float radius_in = (float) (radius * Math.sin(radian / 2) / Math
.cos(radian)); // 中间五边形的半径
path.moveTo((float) (radius * Math.cos(radian / 2) + borderWidthPx), borderWidthPx);// 此点为多边形的起点
path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in
* Math.sin(radian) + borderWidthPx),
(float) (radius - radius * Math.sin(radian / 2) + borderWidthPx));
path.lineTo((float) (radius * Math.cos(radian / 2) * 2 + borderWidthPx),
(float) (radius - radius * Math.sin(radian / 2) + borderWidthPx));
path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in
* Math.cos(radian / 2) + borderWidthPx),
(float) (radius + radius_in * Math.sin(radian / 2) + borderWidthPx));
path.lineTo(
(float) (radius * Math.cos(radian / 2) + radius
* Math.sin(radian) + borderWidthPx), (float) (radius + radius
* Math.cos(radian) + borderWidthPx));
path.lineTo((float) (radius * Math.cos(radian / 2) + borderWidthPx), radius + radius_in + borderWidthPx);
path.lineTo(
(float) (radius * Math.cos(radian / 2) - radius
* Math.sin(radian) + borderWidthPx), (float) (radius + radius
* Math.cos(radian) + borderWidthPx));
path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in
* Math.cos(radian / 2) + borderWidthPx),
(float) (radius + radius_in * Math.sin(radian / 2) + borderWidthPx));
path.lineTo(borderWidthPx, (float) (radius - radius * Math.sin(radian / 2) + borderWidthPx));
path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in
* Math.sin(radian) + borderWidthPx),
(float) (radius - radius * Math.sin(radian / 2) + borderWidthPx));
path.close();
}
/**
* 对角线图形
*/
@Override
public void setDiagonalPath(Path path, int width, int height) {
final float perpendicularHeight = (float) (width * Math.tan(Math.toRadians(shapeView.getDiagonalAngle())));
final boolean isDirectionLeft = shapeView.getDiagonalDirection() == DIRECTION_LEFT;
int paddingLeft = shapeView.getPaddingLeft();
int paddingRight = shapeView.getPaddingRight();
int paddingTop = shapeView.getPaddingTop();
int paddingBottom = shapeView.getPaddingBottom();
switch (shapeView.getDiagonalPosition()) {
case POSITION_BOTTOM:
if (isDirectionLeft) {
path.moveTo(paddingLeft, paddingRight);
path.lineTo(width - paddingRight, paddingTop);
path.lineTo(width - paddingRight, height - perpendicularHeight - paddingBottom);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
} else {
path.moveTo(width - paddingRight, height - paddingBottom);
path.lineTo(paddingLeft, height - perpendicularHeight - paddingBottom);
path.lineTo(paddingLeft, paddingTop);
path.lineTo(width - paddingRight, paddingTop);
path.close();
}
break;
case POSITION_TOP:
if (isDirectionLeft) {
path.moveTo(width - paddingRight, height - paddingBottom);
path.lineTo(width - paddingRight, paddingTop + perpendicularHeight);
path.lineTo(paddingLeft, paddingTop);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
} else {
path.moveTo(width - paddingRight, height - paddingBottom);
path.lineTo(width - paddingRight, paddingTop);
path.lineTo(paddingLeft, paddingTop + perpendicularHeight);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
}
break;
case POSITION_RIGHT:
if (isDirectionLeft) {
path.moveTo(paddingLeft, paddingTop);
path.lineTo(width - paddingRight, paddingTop);
path.lineTo(width - paddingRight - perpendicularHeight, height - paddingBottom);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
} else {
path.moveTo(paddingLeft, paddingTop);
path.lineTo(width - paddingRight - perpendicularHeight, paddingTop);
path.lineTo(width - paddingRight, height - paddingBottom);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
}
break;
case POSITION_LEFT:
if (isDirectionLeft) {
path.moveTo(paddingLeft + perpendicularHeight, paddingTop);
path.lineTo(width - paddingRight, paddingTop);
path.lineTo(width - paddingRight, height - paddingBottom);
path.lineTo(paddingLeft, height - paddingBottom);
path.close();
} else {
path.moveTo(paddingLeft, paddingTop);
path.lineTo(width - paddingRight, paddingTop);
path.lineTo(width - paddingRight, height - paddingBottom);
path.lineTo(paddingLeft + perpendicularHeight, height - paddingBottom);
path.close();
}
break;
}
}
}
复制代码
最后
感谢阅读~附上github地址ShapView 感觉需要的朋友欢迎star,fork,有问题可以提issue。,你的鼓励我是最大的动力,随后希望可以有更多的分享和总结。