How to give hexagon shape to ImageView . Is it possible to do in same way ? If so then how. If this is not possible through this then how this could be achieved ?
<shape xmlns:android="http//schemas.android.com/apk/res/android"
android:shape="hexagon">
<solid android:color="#ffffffff" />
<size android:width="60dp"
android:height="40dp" />
</shape>
Screenshot
Here I can't do masking image because I can not detect which portion of bitmap I should crop to get hexagon shape bitmap. So I am looking for the answer to give hexagon shape to ImageView
Try this View. You might want to adjust it for your specific needs, but it draws a hexagon mask with a border on top of a view. The background resource goes below the mask.
The result:
The code:
HexagonMaskView.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Path;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;
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();
}
}
Update 29.07.2016
A better way to only clip the source image without painting the whole view's background. Switched to an ImageView as a base class to benefit from the scaleType. I also did some code refactoring.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Region;
import android.util.AttributeSet;
import android.widget.ImageView;
public class HexagonMaskView extends ImageView {
private Path hexagonPath;
private Path hexagonBorderPath;
private Paint mBorderPaint;
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() {
this.hexagonPath = new Path();
this.hexagonBorderPath = new Path();
this.mBorderPaint = new Paint();
this.mBorderPaint.setColor(Color.WHITE);
this.mBorderPaint.setStrokeCap(Paint.Cap.ROUND);
this.mBorderPaint.setStrokeWidth(50f);
this.mBorderPaint.setStyle(Paint.Style.STROKE);
}
public void setRadius(float radius) {
calculatePath(radius);
}
public void setBorderColor(int color) {
this.mBorderPaint.setColor(color);
invalidate();
}
private void calculatePath(float radius) {
float halfRadius = radius / 2f;
float triangleHeight = (float) (Math.sqrt(3.0) * halfRadius);
float centerX = getMeasuredWidth() / 2f;
float centerY = getMeasuredHeight() / 2f;
this.hexagonPath.reset();
this.hexagonPath.moveTo(centerX, centerY + radius);
this.hexagonPath.lineTo(centerX - triangleHeight, centerY + halfRadius);
this.hexagonPath.lineTo(centerX - triangleHeight, centerY - halfRadius);
this.hexagonPath.lineTo(centerX, centerY - radius);
this.hexagonPath.lineTo(centerX + triangleHeight, centerY - halfRadius);
this.hexagonPath.lineTo(centerX + triangleHeight, centerY + halfRadius);
this.hexagonPath.close();
float radiusBorder = radius - 5f;
float halfRadiusBorder = radiusBorder / 2f;
float triangleBorderHeight = (float) (Math.sqrt(3.0) * halfRadiusBorder);
this.hexagonBorderPath.reset();
this.hexagonBorderPath.moveTo(centerX, centerY + radiusBorder);
this.hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY + halfRadiusBorder);
this.hexagonBorderPath.lineTo(centerX - triangleBorderHeight, centerY - halfRadiusBorder);
this.hexagonBorderPath.lineTo(centerX, centerY - radiusBorder);
this.hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY - halfRadiusBorder);
this.hexagonBorderPath.lineTo(centerX + triangleBorderHeight, centerY + halfRadiusBorder);
this.hexagonBorderPath.close();
invalidate();
}
@Override
public void onDraw(Canvas c) {
c.drawPath(hexagonBorderPath, mBorderPaint);
c.clipPath(hexagonPath, Region.Op.INTERSECT);
c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
super.onDraw(c);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
calculatePath(Math.min(width / 2f, height / 2f) - 10f);
}
}
Example layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:background="@android:color/holo_green_dark">
<com.scelus.hexagonmaskimproved.HexagonMaskView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/bear"
android:background="@android:color/holo_green_light"/>
</RelativeLayout>
Here is my working code for this, it supports shadows to:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class HexagonImageView extends ImageView {
private Path hexagonPath;
private Path hexagonBorderPath;
private float radius;
private Bitmap image;
private int viewWidth;
private int viewHeight;
private Paint paint;
private BitmapShader shader;
private Paint paintBorder;
private int borderWidth = 4;
public HexagonImageView(Context context) {
super(context);
setup();
}
public HexagonImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setup();
}
public HexagonImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
private void setup() {
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
setBorderColor(Color.WHITE);
paintBorder.setAntiAlias(true);
this.setLayerType(LAYER_TYPE_SOFTWARE, paintBorder);
paintBorder.setShadowLayer(4.0f, 1.0f, 1.0f, Color.BLACK);
hexagonPath = new Path();
hexagonBorderPath = new Path();
}
public void setRadius(float r) {
this.radius = r;
calculatePath();
}
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor) {
if (paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
private void calculatePath() {
float triangleHeight = (float) (Math.sqrt(3) * radius / 2);
float centerX = viewWidth/2;
float centerY = viewHeight/2;
hexagonBorderPath.moveTo(centerX, centerY + radius);
hexagonBorderPath.lineTo(centerX - triangleHeight, centerY + radius/2);
hexagonBorderPath.lineTo(centerX - triangleHeight, centerY - radius/2);
hexagonBorderPath.lineTo(centerX, centerY - radius);
hexagonBorderPath.lineTo(centerX + triangleHeight, centerY - radius/2);
hexagonBorderPath.lineTo(centerX + triangleHeight, centerY + radius/2);
hexagonBorderPath.moveTo(centerX, centerY + radius);
float radiusBorder = radius - borderWidth;
float triangleBorderHeight = (float) (Math.sqrt(3) * radiusBorder / 2);
hexagonPath.moveTo(centerX, centerY + radiusBorder);
hexagonPath.lineTo(centerX - triangleBorderHeight, centerY + radiusBorder/2);
hexagonPath.lineTo(centerX - triangleBorderHeight, centerY - radiusBorder/2);
hexagonPath.lineTo(centerX, centerY - radiusBorder);
hexagonPath.lineTo(centerX + triangleBorderHeight, centerY - radiusBorder/2);
hexagonPath.lineTo(centerX + triangleBorderHeight, centerY + radiusBorder/2);
hexagonPath.moveTo(centerX, centerY + radiusBorder);
invalidate();
}
private void loadBitmap() {
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if (bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
}
@SuppressLint("DrawAllocation")
@Override
public void onDraw(Canvas canvas){
super.onDraw(canvas);
loadBitmap();
// init shader
if (image != null) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
canvas.drawPath(hexagonBorderPath, paintBorder);
canvas.drawPath(hexagonPath, paint);
}
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth * 2);
viewHeight = height - (borderWidth * 2);
radius = height / 2 - borderWidth;
calculatePath();
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
else {
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
}
else {
result = viewHeight;
}
return (result + 2);
}
}
There are a couple things you can try:
You might want to try drawing a 9patch in top of your image.
There's also this short tuto by Romain Guy : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
BitmapShader shader; shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setShader(shader); RectF rect = new RectF(0.0f, 0.0f, width, height); // rect contains the bounds of the shape // radius is the radius in pixels of the rounded corners // paint contains the shader that will texture the shape canvas.drawRoundRect(rect, radius, radius, paint);Instead of using
drawRoundRect()method of canvas, you may try usingdrawPath()to get the desired shape.Hope this puts you on the right direction.
See this example which is creating triangle so you can get logic from it :)
http://looksok.wordpress.com/2013/08/24/android-triangle-arrow-defined-as-an-xml-shape/
Another solution I found but not tested so try this also
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView) findViewById(R.id.text);
Path path = new Path();
float stdW = 100;
float stdH = 100;
float w3 = stdW / 3;
float h2 = stdH / 2;
path.moveTo(0, h2);
h2 -= 6 / 2;
path.rLineTo(w3, -h2); path.rLineTo(w3, 0); path.rLineTo(w3, h2);
path.rLineTo(-w3, h2); path.rLineTo(-w3, 0); path.rLineTo(-w3, -h2);
Shape s = new PathShape(path, stdW, stdH);
ShapeDrawable d = new ShapeDrawable(s);
Paint p = d.getPaint();
p.setColor(0xffeeeeee);
p.setStyle(Style.STROKE);
p.setStrokeWidth(6);
tv.setBackgroundDrawable(d);
}
Source: Google group
Third solution - This might be useful library
PathDrawable is a Drawable that draws simple shapes using Path object.
Its Late to relpy.. But Hope it will help someone...
public Bitmap getHexagonShape(Bitmap scaleBitmapImage) {
// TODO Auto-generated method stub
int targetWidth = 200;
int targetHeight =200;
Bitmap targetBitmap = Bitmap.createBitmap(targetWidth,
targetHeight,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(targetBitmap);
Path path = new Path();
float stdW = 200;
float stdH = 200;
float w3 =stdW / 2;
float h2 = stdH / 2;
float radius=stdH/2-10;
float triangleHeight = (float) (Math.sqrt(3) * radius / 2);
float centerX = stdW/2;
float centerY = stdH/2;
path.moveTo(centerX, centerY + radius);
path.lineTo(centerX - triangleHeight, centerY + radius/2);
path.lineTo(centerX - triangleHeight, centerY - radius/2);
path.lineTo(centerX, centerY - radius);
path.lineTo(centerX + triangleHeight, centerY - radius/2);
path.lineTo(centerX + triangleHeight, centerY + radius/2);
path.moveTo(centerX, centerY + radius);
canvas.clipPath(path);
Bitmap sourceBitmap = scaleBitmapImage;
canvas.drawBitmap(sourceBitmap,
new Rect(0, 0, sourceBitmap.getWidth(),
sourceBitmap.getHeight()),
new Rect(0, 0, targetWidth,
targetHeight), null);
return targetBitmap;
}
public static Bitmap drawableToBitmap (Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable)drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
Call this where you want to use
Drawable drawable = getResources().getDrawable( R.drawable.placeholder );
Bitmap b=getHexagonShape(drawableToBitmap(drawable));
img=(ImageView)findViewById(R.id.imageView);
img.setImageBitmap(b);
The function below reads your image as input bitmap and returns a bitmap which is hexagon in shape
public Bitmap getHexagonShape(Bitmap scaleBitmapImage) {
// TODO Auto-generated method stub
int targetWidth = 600;
int targetHeight = 600;
Bitmap targetBitmap = Bitmap.createBitmap(targetWidth,
targetHeight,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(targetBitmap);
Path path = new Path();
float stdW = 300;
float stdH = 300;
float w3 =stdW / 2;
float h2 = stdH / 2;
path.moveTo(0, (float) (h2*Math.sqrt(3)/2));
path.rLineTo(w3/2, -(float) (h2*Math.sqrt(3)/2)); path.rLineTo(w3, 0); path.rLineTo(w3/2, (float) (h2*Math.sqrt(3)/2));
path.rLineTo(-w3/2, (float) (h2*Math.sqrt(3)/2)); path.rLineTo(-w3, 0); path.rLineTo(-w3/2, -(float) (h2*Math.sqrt(3)/2));
canvas.clipPath(path);
Bitmap sourceBitmap = scaleBitmapImage;
canvas.drawBitmap(sourceBitmap,
new Rect(0, 0, sourceBitmap.getWidth(),
sourceBitmap.getHeight()),
new Rect(0, 0, targetWidth,
targetHeight), null);
return targetBitmap;
}
I don't know if the OP got the answer he was looking for, but here goes.
I've create a custom view, that extends ImageView, that will do the job for you a bit better. The answer here just creates a maks inside the ImageView and forces you to set the picture as the background
My view lets you set the image like a standard bitmap, it handles CenterCrop and scaling of the image. It actually sets the mask outside instead, and with the same border plus drop shadow.
And if that not enough, you can easily create custom shapes to render, just be extending the RenderShape-class. (4 shapes are included in the library: Circle, Triangle, Hexagon and Octagon)
Have a look at my github
Cheers
I've solved it using this code:
private Bitmap getHexagoneCroppedBitmap(Bitmap bitmap, int radius) {
Bitmap finalBitmap;
if (bitmap.getWidth() != radius || bitmap.getHeight() != radius)
finalBitmap = Bitmap.createScaledBitmap(bitmap, radius, radius,
false);
else
finalBitmap = bitmap;
Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(),
finalBitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
Paint paint = new Paint();
final Rect rect = new Rect(0, 0, finalBitmap.getWidth(),
finalBitmap.getHeight());
Point point1_draw = new Point(75, 0);
Point point2_draw = new Point(0, 50);
Point point3_draw = new Point(0, 100);
Point point4_draw = new Point(75, 150);
Point point5_draw = new Point(150, 100);
Point point6_draw = new Point(150, 50);
Path path = new Path();
path.moveTo(point1_draw.x, point1_draw.y);
path.lineTo(point2_draw.x, point2_draw.y);
path.lineTo(point3_draw.x, point3_draw.y);
path.lineTo(point4_draw.x, point4_draw.y);
path.lineTo(point5_draw.x, point5_draw.y);
path.lineTo(point6_draw.x, point6_draw.y);
path.close();
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(finalBitmap, rect, rect, paint);
return output;
}
You can use the Android Shape ImageView by siamed.
https://github.com/siyamed/android-shape-imageview
<com.github.siyamed.shapeimageview.HexagonImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:src="@drawable/neo"
app:siBorderWidth="8dp"
app:siBorderColor="@color/darkgray"/>
Please read the documentation on github, lots of options are available.
来源:https://stackoverflow.com/questions/22601400/how-to-give-hexagon-shape-to-imageview
