You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

需求:实现带指定边框的多边形ImageView(已完成六边形遮罩)

Bordered Hexagon ImageView Implementation

Got it, let's build out that bordered polygon ImageView you're working on. You already have a solid foundation with your HexagonMaskView—let's extend it to add customizable border support, clean up the incomplete code, and make it flexible for XML configuration.

Complete Custom View Code

First, here's the full, working version of your custom ImageView with border functionality:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import androidx.appcompat.widget.AppCompatImageView;

public class HexagonMaskView extends AppCompatImageView {
    private Path hexagonPath;
    private Path hexagonBorderPath;
    private Paint borderPaint;
    private float borderWidth = 4f; // Default border width
    private int borderColor = Color.WHITE; // Default border color

    public HexagonMaskView(Context context) {
        super(context);
        init(null);
    }

    public HexagonMaskView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public HexagonMaskView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        // Load custom attributes from XML
        if (attrs != null) {
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.HexagonMaskView);
            borderWidth = a.getDimension(R.styleable.HexagonMaskView_hexagonBorderWidth, borderWidth);
            borderColor = a.getColor(R.styleable.HexagonMaskView_hexagonBorderColor, borderColor);
            a.recycle();
        }

        // Initialize border paint
        borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        borderPaint.setColor(borderColor);
        borderPaint.setStyle(Paint.Style.STROKE);
        borderPaint.setStrokeWidth(borderWidth);

        // Disable hardware acceleration to avoid clipPath issues
        setLayerType(LAYER_TYPE_SOFTWARE, null);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        calculateHexagonPaths(w, h);
    }

    private void calculateHexagonPaths(int viewWidth, int viewHeight) {
        hexagonPath = new Path();
        hexagonBorderPath = new Path();
        float halfWidth = viewWidth / 2f;
        float halfHeight = viewHeight / 2f;
        float radius = Math.min(halfWidth, halfHeight) - (borderWidth / 2f);

        // Calculate hexagon vertices
        RectF rect = new RectF(halfWidth - radius, halfHeight - radius, halfWidth + radius, halfHeight + radius);
        float angle = 0;
        for (int i = 0; i < 6; i++) {
            float x = (float) (halfWidth + radius * Math.cos(Math.toRadians(angle)));
            float y = (float) (halfHeight + radius * Math.sin(Math.toRadians(angle)));
            if (i == 0) {
                hexagonPath.moveTo(x, y);
                hexagonBorderPath.moveTo(x, y);
            } else {
                hexagonPath.lineTo(x, y);
                hexagonBorderPath.lineTo(x, y);
            }
            angle += 60;
        }
        hexagonPath.close();
        hexagonBorderPath.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // Save canvas state before clipping
        canvas.save();
        // Clip the canvas to the hexagon shape to mask the image
        canvas.clipPath(hexagonPath);
        // Draw the original image (super.onDraw handles this)
        super.onDraw(canvas);
        // Restore canvas to draw the border outside the clipped area
        canvas.restore();
        // Draw the hexagon border
        canvas.drawPath(hexagonBorderPath, borderPaint);
    }

    // Public methods to update border properties dynamically
    public void setBorderWidth(float width) {
        borderWidth = width;
        borderPaint.setStrokeWidth(width);
        calculateHexagonPaths(getWidth(), getHeight());
        invalidate();
    }

    public void setBorderColor(int color) {
        borderColor = color;
        borderPaint.setColor(color);
        invalidate();
    }
}

Step-by-Step Explanation

Let's break down the key additions and fixes:

  • Custom XML Attributes: We added hexagonBorderColor and hexagonBorderWidth so you can configure the border directly in your layout XML, no hardcoding needed. To use these, add this to your res/values/attrs.xml file:

    <declare-styleable name="HexagonMaskView">
        <attr name="hexagonBorderColor" format="color" />
        <attr name="hexagonBorderWidth" format="dimension" />
    </declare-styleable>
    
  • Path Calculation: In onSizeChanged, we calculate both the inner hexagon path (for masking the image) and the border path. We adjust the radius to account for the border width so the border doesn't get clipped.

  • Canvas Handling: We save the canvas state before clipping, draw the masked image, then restore the canvas to draw the border outside the clipped area. This ensures the border is fully visible around the hexagon.

  • Dynamic Updates: The setBorderWidth and setBorderColor methods let you adjust the border properties at runtime if needed.

Usage in Layout XML

Here's how to use your new bordered hexagon ImageView in a layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">

    <com.yourpackage.HexagonMaskView
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:src="@drawable/your_profile_image"
        android:scaleType="centerCrop"
        app:hexagonBorderColor="#FF5722"
        app:hexagonBorderWidth="6dp" />

</LinearLayout>

Key Notes

  • We disabled hardware acceleration with setLayerType(LAYER_TYPE_SOFTWARE, null) because clipPath can have issues with hardware acceleration on older Android versions.
  • Using centerCrop as the scaleType ensures your image fills the hexagon without stretching awkwardly.

内容的提问来源于stack exchange,提问作者fazal ur-rehman-sharif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:20:24