如何为ImageView提供六边形

时间:2014-03-24 04:59:18

标签: android imageview android-imageview

如何为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

这里我不能做掩蔽图像,因为我无法检测到应该裁剪哪个位图部分以获得六边形形状位图。所以我正在寻找给ImageView

六边形的答案

9 个答案:

答案 0 :(得分:58)

试试此视图。您可能希望根据您的特定需求进行调整,但它会在视图顶部绘制带边框的六边形蒙版。后台资源低于掩码。

结果:

enter image description here

代码:

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

答案 1 :(得分:7)

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

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


}

答案 2 :(得分: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对象绘制简单的形状。

答案 3 :(得分:4)

您可以尝试以下几种方法:

  • 您可能想尝试在图片顶部绘制一个9patch。

  • Romain Guy还有这个短暂的tuto: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 :(得分:3)

回复已经晚了..但希望它会帮助别人......

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

我不知道OP是否得到了他正在寻找的答案,但现在就去了。

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

我的视图允许您将图像设置为标准位图,它可以处理CenterCrop和图像缩放。 它实际上将面具设置在外面,并使用相同的边框加上投影。

如果这还不够,您可以轻松创建自定义形状进行渲染,只需扩展RenderShape类。 (库中包含4种形状:圆形,三角形,六边形和八边形)

看看at my github

干杯

答案 7 :(得分:1)

我用这段代码解决了它:

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

答案 8 :(得分:1)

您可以使用siamed的Android Shape ImageView。

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上的文档,有很多选项。