android - ImageView 裁剪为多边形

标签 android android-canvas android-custom-view

我想创建一个 ImageView 将其内容剪辑到多边形(在本例中为六边形)内。我将 View 的图层类型设置为软件,这样我就可以使用 canvas.clipPath():

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    setLayerType (LAYER_TYPE_SOFTWARE, null);
}

生成六边形的代码似乎工作正常:

Without clipping

这是计算顶点的代码:

@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);
}

如上所示,同样的方法生成边界矩形以及定义多边形的路径和多边形的裁剪路径。

在这个 View 的构造函数中,pathclipPath 是这样定义的

path = new Path ();
clipPath = new Path ();
clipPath.setFillType (Path.FillType.INVERSE_EVEN_ODD);

View 的 onDraw 方法被重写为:

@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);
}

只要我用 canvas.clipPath (hexagon.getClipPath ()); 启用该行, View 就会显示如下:

Clipping enabled

4 个剪切点中的 2 个甚至不在我的路径上!!

我在这里做错了什么?有更好的方法吗?

最终我希望多边形之外的所有东西都只是透明的。总是。包括选择突出显示。

感谢您的帮助。我不能发布太多代码(公司 IP 等),但如果您需要更多详细信息,请告诉我,我会更新。

最佳答案

我已经回答了一个你想要的问题。 @SceLus 也有 answer被接受的非常好,并且是赏金赢家,因此您可以从中获得帮助,因为链接可能会更改,所以我正在复制粘贴该代码

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();
}
}

enter image description here

关于android - ImageView 裁剪为多边形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24836921/

相关文章:

android - 软键盘不隐藏在 fragment 中。如何隐藏键盘?

android - 高效构建Android系统服务组件

android - 一台蓝牙设备具有多个应用程序

android - 在自定义 ScrollView onScrollChanged 中更改 View 高度

Android 数据绑定(bind) - 找不到接受参数类型 'long' 的 <> 的 getter

android - attrs.xml 中用于自定义 View 的同名属性

java - Android 2.3 无法在未调用 Looper.prepare() (AsyncTask) 的线程内创建处理程序

android - 使位图的某些区域在触摸时透明 --> 再次

java - 如何设置 Canvas 使用 @dimen 指定尺寸

java - Android 存储位图数组