代码之家  ›  专栏  ›  技术社区  ›  N Sharma

如何为ImageView赋予六边形形状

  •  29
  • N Sharma  · 技术社区  · 11 年前

    如何为 ImageView 。可以用同样的方法吗?如果是,那么如何。如果这不可能实现,那么如何实现?

    <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>
    

    截图

    enter image description here

    在这里,我不能屏蔽图像,因为我无法检测应该裁剪位图的哪个部分以获得六边形位图。因此,我正在寻找给出六边形形状的答案 图片框

    9 回复  |  直到 10 年前
        1
  •  66
  •   SceLus    8 年前

    尝试此视图。您可能希望根据您的特定需要调整它,但它会在视图顶部绘制一个带有边框的六边形遮罩。背景资源位于掩码之下。

    结果:

    enter image description here

    代码:

    六边形MaskView.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();
    }
    }
    

    2016年7月29日更新

    只剪辑源图像而不绘制整个视图背景的更好方法。切换到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_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>
    

    New result

        2
  •  7
  •   lacas    10 年前

    这是我的工作代码,它支持阴影:

    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
  •  4
  •   Spartako    10 年前

    您可以尝试以下几点:

    • 您可能想尝试在图像顶部绘制一个9补丁。

    • 还有这首罗曼·盖伊的短诗: 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);
      

      而不是使用 drawRoundRect() 方法,您可以尝试使用 drawPath() 以获得期望的形状。

      希望这能让你走上正确的方向。

        4
  •  4
  •   MilapTank    10 年前

    请看这个创建三角形的示例,以便从中获得逻辑:)

    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 是使用Path对象绘制简单形状的Drawable。

        5
  •  3
  •   ADT    10 年前

    太晚了。。但希望它能帮助某人。。。

      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
  •  1
  •   uzair_syed    10 年前

    下面的函数将图像作为输入位图读取,并返回六边形位图

    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;
         }
    
        7
  •  1
  •   Pontus Endlessmind Holmberg    10 年前

    我不知道OP是否得到了他想要的答案,但这里有答案。

    我已经创建了一个自定义视图,它扩展了ImageView,这将为您更好地完成工作。 这里的答案只是在ImageView中创建一个标记,并强制您将图片设置为背景

    我的视图允许您像标准位图一样设置图像,它处理图像的CenterCrop和缩放。 实际上,它将遮罩设置在外部,并使用相同的边框加上阴影。

    如果这还不够,您可以很容易地创建要渲染的自定义形状,只需扩展RenderShape类即可。(库中包含4种形状:圆形、三角形、六边形和八角形)

    看一看 at my github

    干杯

        8
  •  1
  •   Aj 27    10 年前

    我使用以下代码解决了问题:

        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
  •  1
  •   Binoy Babu    10 年前

    你可以通过siamed使用Android形状图像视图。

    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"/>
    

    请阅读github上的文档,有很多选项可用。