如何为 ImageView 赋予六边形形状
Posted
技术标签:
【中文标题】如何为 ImageView 赋予六边形形状【英文标题】:How to give hexagon shape to ImageView 【发布时间】:2014-05-01 08:05:48 【问题描述】:如何给ImageView
赋予六边形形状。有可能以同样的方式做吗?如果是,那么如何。如果这是不可能的,那么如何实现呢?
<shape xmlns:android="http//schemas.android.com/apk/res/android"
android:shape="hexagon">
<solid android:color="#ffffffff" />
<size android:
android: />
</shape>
截图
这里我不能做蒙版图像,因为我无法检测到我应该裁剪位图的哪个部分以获得六边形位图。所以我正在寻找将六边形形状赋予ImageView
的答案
【问题讨论】:
嗨,你有办法解决这个问题吗? @Rat-a-tat-a-tatRatatouille 还没有 你看过this @Rat-a-tat-a-tatRatatouille 但它们都是在imageview中绘制形状,而不是将imageview的结构制作为六边形,我想将imageview的形状制作为六边形以便我将设置的任何图像都将设置为六边形 你试过图像遮罩吗??请参考此链接***.com/questions/5299452/… 【参考方案1】:试试这个视图。您可能希望根据您的特定需求对其进行调整,但它会在视图顶部绘制一个带有边框的六边形蒙版。背景资源位于掩码下方。
结果:
代码:
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();
29.07.2016 更新
一种更好的方法,只剪辑源图像而不绘制整个视图的背景。切换到 ImageView 作为基类以受益于 scaleType。我还做了一些代码重构。
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);
示例布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
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_
android:layout_
android:src="@drawable/bear"
android:background="@android:color/holo_green_light"/>
</RelativeLayout>
【讨论】:
谢谢。很好的答案,尽我所能提高你的分数! 如何在 ImageView 中调用它?我正在使用 ImageLoader 加载图像,请您帮帮我。 我在六边形之外得到了一个绿色背景,如何去除它? 有没有办法删除白色边框之外的区域?我假设该区域是蒙版颜色,但如果您删除蒙版颜色,事情就会变得一团糟。关于删除外部区域的任何想法? 它进入全屏模式。如果你想减小尺寸,它是行不通的。【参考方案2】:这是我的工作代码,它支持阴影:
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);
【讨论】:
你的答案比公认的更好【参考方案3】:您可以尝试以下几种方法:
您可能想尝试在图像顶部绘制一个 9patch。
还有 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);
您可以尝试使用drawPath()
来代替画布的drawRoundRect()
方法来获得所需的形状。
希望这能让你朝着正确的方向前进。
【讨论】:
是的,我已经读过了,但我无法实现我为什么在这里问的六边形图像视图【参考方案4】:查看这个创建三角形的示例,以便您可以从中获取逻辑:)
http://looksok.wordpress.com/2013/08/24/android-triangle-arrow-defined-as-an-xml-shape/
我找到了另一个解决方案,但没有测试,所以也试试这个
@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);
来源:Google group
第三种解决方案 - 这可能是有用的库
PathDrawable 是一个 Drawable,它使用 Path 对象绘制简单的形状。
【讨论】:
我尝试了您的解决方案,它使用上述方法绘制六边形路径。我想将 imageview 的形状设置为六边形,这样无论我设置什么图像,它都会像六边形一样设置 嘿,看看这个codebybrian.com/2013/07/28/custom_view_regular_polygons.html 可能对你有帮助 我已经看过了,它也只是在imageview中绘制了六边形,但我想将imageview的形状赋予六边形【参考方案5】:它迟到了..但希望它会帮助某人......
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;
在你想使用的地方调用它
Drawable drawable = getResources().getDrawable( R.drawable.placeholder );
Bitmap b=getHexagonShape(drawableToBitmap(drawable));
img=(ImageView)findViewById(R.id.imageView);
img.setImageBitmap(b);
【讨论】:
【参考方案6】:下面的函数读取您的图像作为输入位图并返回一个六边形的位图
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;
【讨论】:
谢谢..你的代码帮助我..:) 谢谢,但是它的部分六边形在 potraite 和 lanscape 中是完整的六边形【参考方案7】:我不知道 OP 是否得到了他正在寻找的答案,但这里是。
我创建了一个自定义视图,它扩展了 ImageView,它会更好地为您完成这项工作。 这里的答案只是在 ImageView 中创建一个 mak 并强制您将图片设置为背景
我的视图让您可以像标准位图一样设置图像,它可以处理 CenterCrop 和图像的缩放。 它实际上将遮罩设置在外面,并且具有相同的边框和阴影。
如果这还不够,您可以轻松创建要渲染的自定义形状,只需扩展 RenderShape 类即可。 (库中包含 4 种形状:圆形、三角形、六边形和八边形)
看看at my github
干杯
【讨论】:
【参考方案8】:我已经用这个代码解决了:
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;
【讨论】:
【参考方案9】:您可以使用 siamed 的 Android Shape ImageView。
https://github.com/siyamed/android-shape-imageview
<com.github.siyamed.shapeimageview.HexagonImageView
android:layout_
android:layout_
android:layout_margin="8dp"
android:src="@drawable/neo"
app:siBorderWidth="8dp"
app:siBorderColor="@color/darkgray"/>
请阅读 github 上的文档,有很多选项可供选择。
【讨论】:
以上是关于如何为 ImageView 赋予六边形形状的主要内容,如果未能解决你的问题,请参考以下文章
如何为 UITableViews 裁剪 cell.imageview.image 中的图像?