Reputation: 14506
I want to create an ImageView
that clips its content to inside a polygon (in this case a hexagon). I'm setting the view's layer type to software so I can use canvas.clipPath()
:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType (LAYER_TYPE_SOFTWARE, null);
}
The code that generates the hexagon seems to work fine:
Here's the code that calculates the vertices:
@Override public void calculateVertices () {
width = 2f * radius;
side = 1.5f * radius;
height = (float) Math.sqrt (3f) * radius;
vertices = new Point[6];
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
final float[] p0 = new float[] {center.x - radius, center.y};
final Matrix m = new Matrix ();
final float r = getRotation ();
for (int i = 0; i < vertices.length; i++) {
final float ptRot = rotateBy (r, (float) i * 60f);
final float[] point = new float[2];
if (ptRot != 0f) {
m.reset ();
m.postRotate (ptRot, center.x, center.y);
m.mapPoints (point, p0);
} else {
point[0] = p0[0];
point[1] = p0[1];
}
if (point[0] < minX) {
minX = Math.round (point[0]);
} else if (point[0] > maxX) {
maxX = Math.round (point[0]);
}
if (point[1] < minY) {
minY = Math.round (point[1]);
} else if (point[1] > maxY) {
maxY = Math.round (point[1]);
}
vertices[i] = fromFloat (point);
}
path.reset ();
clipPath.reset ();
path.moveTo (vertices[0].x, vertices[0].y);
clipPath.moveTo (vertices[0].x, vertices[0].y);
for (int i = 1; i < vertices.length; i++) {
path.lineTo (vertices[i].x, vertices[i].y);
clipPath.lineTo (vertices[i].x, vertices[i].y);
}
path.lineTo (vertices[0].x, vertices[0].y);
clipPath.lineTo (vertices[0].x, vertices[0].y);
path.close ();
clipPath.close ();
enclosure.set (minX, minY, maxX, maxY);
}
As you can see above, the same method generates the bounding rectangle as well as the path that defines the polygon and the clipping path of the polygon.
In the constructor of this view, path
and clipPath
are defined as such
path = new Path ();
clipPath = new Path ();
clipPath.setFillType (Path.FillType.INVERSE_EVEN_ODD);
The onDraw
method of the view is overridden as such:
@Override protected void onDraw (final Canvas canvas) {
final int count = canvas.save ();
canvas.clipPath (hexagon.getClipPath ());
super.onDraw (canvas);
hexagon.draw (canvas, selectBackgroundPaint ());
canvas.restoreToCount (count);
}
As soon as I enable the line with canvas.clipPath (hexagon.getClipPath ());
The view displays as such:
2 of the 4 clipping points aren't even on my path!!
What am I doing wrong here? Is there a better way of doing this?
Ultimately I want everything outside of the polygon to be only transparent. Always. Including the selection highlighting.
I appreciate the help. I can't publish too much of the code (Company IP, etc), but if you need more details let me know and I will update.
Upvotes: 1
Views: 2200
Reputation: 10076
i have answer one question exactly you want. @SceLus has also answer which is accepted is so good and and bounty winner so you can get help from it as link may change so i am copy paste that code
HexagonMaskView.java
public class HexagonMaskView extends View {
private Path hexagonPath;
private Path hexagonBorderPath;
private float radius;
private float width, height;
private int maskColor;
public HexagonMaskView(Context context) {
super(context);
init();
}
public HexagonMaskView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public HexagonMaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
hexagonPath = new Path();
hexagonBorderPath = new Path();
maskColor = 0xFF01FF77;
}
public void setRadius(float r) {
this.radius = r;
calculatePath();
}
public void setMaskColor(int color) {
this.maskColor = color;
invalidate();
}
private void calculatePath() {
float triangleHeight = (float) (Math.sqrt(3) * radius / 2);
float centerX = width / 2;
float centerY = height / 2;
hexagonPath.moveTo(centerX, centerY + radius);
hexagonPath.lineTo(centerX - triangleHeight, centerY + radius / 2);
hexagonPath.lineTo(centerX - triangleHeight, centerY - radius / 2);
hexagonPath.lineTo(centerX, centerY - radius);
hexagonPath.lineTo(centerX + triangleHeight, centerY - radius / 2);
hexagonPath.lineTo(centerX + triangleHeight, centerY + radius / 2);
hexagonPath.moveTo(centerX, centerY + radius);
float radiusBorder = radius - 5;
float triangleBorderHeight = (float) (Math.sqrt(3) * radiusBorder / 2);
hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);
hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY
+ radiusBorder / 2);
hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY
- radiusBorder / 2);
hexagonBorderPath.lineTo(centerX, centerY - radiusBorder);
hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY
- radiusBorder / 2);
hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY
+ radiusBorder / 2);
hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);
invalidate();
}
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
c.clipPath(hexagonBorderPath, Region.Op.DIFFERENCE);
c.drawColor(Color.WHITE);
c.save();
c.clipPath(hexagonPath, Region.Op.DIFFERENCE);
c.drawColor(maskColor);
c.save();
}
// getting the view size and default radius
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
radius = height / 2 - 10;
calculatePath();
}
}
Upvotes: 1
Reputation: 1086
Dude here is the solution..
https://github.com/MostafaGazar/CustomShapeImageView For more detail https://coderwall.com/p/hmzf4w
Use this library u will find the circular shape in this library and also easy use.
U can also make your custom shape to this library like cloud etc. this library supports SVG file format and they made the SVG file of Rounded corner Imageview.
Just use and let me know if there is any problem
Upvotes: 0