如何在android中创建带圆角的视图

时间:2014-09-27 12:41:53

标签: android android-layout android-view android-shape

我正在尝试在圆形边缘的android中创建一个视图。到目前为止我找到的解决方案是定义一个带圆角的形状,并将其用作该视图的背景。

这就是我所做的,定义一个drawable,如下所示

    

<padding
android:top="2dp"
android:bottom="2dp"/>
<corners android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>

现在我用它作为我的布局的背景,如下所示

<LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        android:clipChildren="true"
        android:background="@drawable/rounded_corner">

这完全正常,我可以看到视图有圆角。

但我的布局中还有许多其他子视图说一个ImageView或一个MapView。当我在上面的布局中放置一个ImageView时,图像的角不会被裁剪/裁剪,而是显示为满。

我已经看到了其他解决方法,使其像解释here那样工作。

  

但是有没有一种方法可以为视图和所有视图设置圆角   子视图包含在已舍入的主视图中   角?

感谢。

20 个答案:

答案 0 :(得分:105)

另一种方法是创建一个自定义布局类,如下所示。此布局首先将其内容绘制到屏幕外位图,使用圆角矩形屏蔽屏幕外位图,然后在实际画布上绘制屏幕外位图。

我尝试了它似乎工作(至少对我的简单测试用例)。与常规布局相比,它当然会影响性能。

package com.example;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.widget.FrameLayout;

public class RoundedCornerLayout extends FrameLayout {
    private final static float CORNER_RADIUS = 40.0f;

    private Bitmap maskBitmap;
    private Paint paint, maskPaint;
    private float cornerRadius;

    public RoundedCornerLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

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

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        setWillNotDraw(false);
    }

    @Override
    public void draw(Canvas canvas) {
        Bitmap offscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas offscreenCanvas = new Canvas(offscreenBitmap);

        super.draw(offscreenCanvas);

        if (maskBitmap == null) {
            maskBitmap = createMask(canvas.getWidth(), canvas.getHeight());
        }

        offscreenCanvas.drawBitmap(maskBitmap, 0f, 0f, maskPaint);
        canvas.drawBitmap(offscreenBitmap, 0f, 0f, paint);
    }

    private Bitmap createMask(int width, int height) {
        Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
        Canvas canvas = new Canvas(mask);

        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);

        canvas.drawRect(0, 0, width, height, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, paint);

        return mask;
    }
}

像普通布局一样使用它:

<com.example.RoundedCornerLayout
    android:layout_width="200dp"
    android:layout_height="200dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/test"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:background="#ff0000"
        />

</com.example.RoundedCornerLayout>

答案 1 :(得分:40)

shape.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#f6eef1" />

    <stroke
        android:width="2dp"
        android:color="#000000" />

    <padding
        android:bottom="5dp"
        android:left="5dp"
        android:right="5dp"
        android:top="5dp" />

    <corners android:radius="5dp" />

</shape>

并在你的布局中

<LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        android:clipChildren="true"
        android:background="@drawable/shape">

        <ImageView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:src="@drawable/your image"
             android:background="@drawable/shape">

</LinearLayout>

答案 2 :(得分:37)

或者您可以像android.support.v7.widget.CardView这样使用:

<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    card_view:cardBackgroundColor="@color/white"
    card_view:cardCornerRadius="4dp">

    <!--YOUR CONTENT-->
</android.support.v7.widget.CardView>

答案 3 :(得分:16)

如果在向布局添加触摸侦听器时遇到问题。将此布局用作父布局。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.widget.FrameLayout;

public class RoundedCornerLayout extends FrameLayout {
    private final static float CORNER_RADIUS = 6.0f;
    private float cornerRadius;

    public RoundedCornerLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

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

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, CORNER_RADIUS, metrics);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        int count = canvas.save();

        final Path path = new Path();
        path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), cornerRadius, cornerRadius, Path.Direction.CW);
        canvas.clipPath(path, Region.Op.REPLACE);

        canvas.clipPath(path);
        super.dispatchDraw(canvas);
        canvas.restoreToCount(count);
    }


}

as

<?xml version="1.0" encoding="utf-8"?>
<com.example.view.RoundedCornerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:id="@+id/patentItem"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingRight="20dp">
        ... your child goes here
    </RelativeLayout>
</com.example.view.RoundedCornerLayout>

答案 4 :(得分:10)

Jaap van Hengstum's answer效果很好,但我认为它很昂贵,如果我们在Button上应用此方法,触摸效果会丢失,因为视图会呈现为位图。

对我来说,最好的方法和最简单的方法是在视图上应用蒙版,如:

@Override
protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
    super.onSizeChanged(width, height, oldWidth, oldHeight);

    float cornerRadius = <whatever_you_want>;
    this.path = new Path();
    this.path.addRoundRect(new RectF(0, 0, width, height), cornerRadius, cornerRadius, Path.Direction.CW);
}

@Override
protected void dispatchDraw(Canvas canvas) {
    if (this.path != null) {
        canvas.clipPath(this.path);
    }
    super.dispatchDraw(canvas);
}

答案 5 :(得分:8)

在Android L中,您只需使用View.setClipToOutline即可获得此效果。在以前的版本中,无法仅以某种形状剪切随机ViewGroup的内容。

你必须考虑能给你类似效果的东西:

  • 如果您只需要在ImageView中使用圆角,则可以使用着色器在您用作背景的形状上“绘制”图像。请查看this library示例。

  • 如果您真的需要剪裁每个孩子,也许您可​​以另外查看您的布局?一个背景是你正在使用的颜色,中间是一个圆形的“洞”?实际上,您可以创建一个自定义ViewGroup,在覆盖onDraw方法的每个子项上绘制该形状。

答案 6 :(得分:8)

在drawable文件夹中创建一个xml round.xml

<solid android:color="#FFFFFF" />
<stroke android:width=".05dp" android:color="#d2d2d2" />
<corners android:topLeftRadius="5dp" android:topRightRadius="5dp" android:bottomLeftRadius="5dp" android:bottomRightRadius="5dp" />

然后使用round.xml作为项目的背景。然后它将给出圆角

答案 7 :(得分:3)

使用材料成分库,制作带有圆角的View的最佳方法是使用 MaterialShapeDrawable

创建具有自定义圆角的ShapeAppearanceModel:

ShapeAppearanceModel shapeAppearanceModelLL1 = new ShapeAppearanceModel()
        .toBuilder()
        .setAllCorners(CornerFamily.ROUNDED,radius16)
        .build();

创建一个MaterialShapeDrawable

MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);

如果您还想为深色主题应用heightOverlay,请使用以下方法:

MaterialShapeDrawable shapeDrawableLL1 = MaterialShapeDrawable.createWithElevationOverlay(this, 4.0f);
shapeDrawableLL1.setShapeAppearanceModel(shapeAppearanceModelLL1);

可选:应用于shapeDrawable背景颜色和笔触

shapeDrawableLL1.setFillColor(
       ContextCompat.getColorStateList(this,R.color...));
 shapeDrawableLL1.setStrokeWidth(2.0f);
 shapeDrawableLL1.setStrokeColor(
       ContextCompat.getColorStateList(this,R.color...));

最后在您的LinearLayout(或其他视图)中将shapeDrawable用作背景:

LinearLayout linearLayout1= findViewById(R.id.ll_1);
ViewCompat.setBackground(linearLayout1,shapeDrawableLL1);

enter image description here

答案 8 :(得分:3)

您可以像这样使用androidx.cardview.widget.CardView

<androidx.cardview.widget.CardView
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="wrap_content"       
        app:cardCornerRadius="@dimen/dimen_4"
        app:cardElevation="@dimen/dimen_4"
        app:contentPadding="@dimen/dimen_10">

       ...

</androidx.cardview.widget.CardView>

OR

shape.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#f6eef1" />

    <stroke
        android:width="2dp"
        android:color="#000000" />

    <padding
        android:bottom="5dp"
        android:left="5dp"
        android:right="5dp"
        android:top="5dp" />

    <corners android:radius="5dp" />

</shape>

并在您的布局之内

<LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/shape">

        ...

</LinearLayout>

答案 9 :(得分:2)

按照本教程及其下的所有讨论 - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/

根据整个Android UI工具包的领先开发人员之一Guy Romain撰写的这篇文章,可以制作一个带圆角的容器(以及他所有的孩子视图),但他解释说它太贵了(从渲染问题的表现)。

我建议你按照他的帖子去,如果你想要圆角,那么根据这篇文章实现圆角ImageView。然后,你可以将它放在任何背景的容器中,你会得到你想要的效果。

这也是我最终也做的。

答案 10 :(得分:2)

public class RoundedCornerLayout extends FrameLayout {
    private double mCornerRadius;

    public RoundedCornerLayout(Context context) {
        this(context, null, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }

    public double getCornerRadius() {
        return mCornerRadius;
    }

    public void setCornerRadius(double cornerRadius) {
        mCornerRadius = cornerRadius;
    }

    @Override
    public void draw(Canvas canvas) {
        int count = canvas.save();

        final Path path = new Path();
        path.addRoundRect(new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), (float) mCornerRadius, (float) mCornerRadius, Path.Direction.CW);
        canvas.clipPath(path, Region.Op.REPLACE);

        canvas.clipPath(path);
        super.draw(canvas);
        canvas.restoreToCount(count);
    }
}

答案 11 :(得分:1)

与Jaap van Hengstum的答案不同:

  1. 使用 BitmapShader 代替掩码位图。
  2. 仅创建一次位图。
  3. public class RoundedFrameLayout extends FrameLayout {
        private Bitmap mOffscreenBitmap;
        private Canvas mOffscreenCanvas;
        private BitmapShader mBitmapShader;
        private Paint mPaint;
        private RectF mRectF;
    
        public RoundedFrameLayout(Context context) {
            super(context);
            init();
        }
    
        public RoundedFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public RoundedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            setWillNotDraw(false);
        }
    
        @Override
        public void draw(Canvas canvas) {
            if (mOffscreenBitmap == null) {
                mOffscreenBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
                mOffscreenCanvas = new Canvas(mOffscreenBitmap);
                mBitmapShader = new BitmapShader(mOffscreenBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
                mPaint.setShader(mBitmapShader);
                mRectF = new RectF(0f, 0f, canvas.getWidth(), canvas.getHeight());
            }
            super.draw(mOffscreenCanvas);
    
            canvas.drawRoundRect(mRectF, 8, 8, mPaint);
        }
    }
    

答案 12 :(得分:1)

您提供的教程链接似乎建议您需要将子元素的layout_width和layout_height属性设置为match_parent。

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

答案 13 :(得分:1)

要使用com.google.android.material:material:1.2.0-beta01创建圆角图像

 float radius = context.getResources().getDimension(R.dimen.border_radius_hug);
    shapeAppearanceModel = new ShapeAppearanceModel()
            .toBuilder()
            .setAllCorners(CornerFamily.ROUNDED,radius)
            .build();

imageView.setShapeAppearanceModel(shapeAppearanceModel)

或者如果您想在xml文件中使用它:

  <com.google.android.material.imageview.ShapeableImageView
            android:id="@+id/thumb"
            android:layout_width="80dp"
            android:layout_height="60dp"
            app:shapeAppearanceOverlay="@style/circleImageView"
            />

在style.xml中添加以下内容:

<style name="circleImageView" parent="">
      <item name="cornerFamily">rounded</item>
      <item name="cornerSize">10%</item>
</style>

答案 14 :(得分:1)

CardView在Android Studio 3.0.1的API 27中为我工作。 colorPrimary文件中引用了res/values/colors.xml,这只是一个示例。对于0dp的layout_width,它将延伸到父级的宽度。您必须根据需要配置约束和宽度/高度。

<android.support.v7.widget.CardView
    android:id="@+id/cardView"
    android:layout_width="0dp"
    android:layout_height="200dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    app:cardCornerRadius="4dp"
    app:cardBackgroundColor="@color/colorPrimary">

    <!-- put your content here -->

</android.support.v7.widget.CardView>

答案 15 :(得分:1)

使用以下代码在您的可绘制文件夹下创建一个xml文件。 (我创建的文件的名称为rounded_corner.xml)

rounded_corner.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <!-- view background color -->
    <solid
        android:color="#a9c5ac" >
    </solid>

    <!-- view border color and width -->
    <stroke
        android:width="3dp"
        android:color="#1c1b20" >
    </stroke>

    <!-- If you want to add some padding -->
    <padding
        android:left="4dp"
        android:top="4dp"
        android:right="4dp"
        android:bottom="4dp"    >
    </padding>

    <!-- Here is the corner radius -->
    <corners
        android:radius="10dp"   >
    </corners>

并将此可绘制对象用作要保留圆角边框的视图的背景。让我们将其保留为LinearLayout

<LinearLayout android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/rounded_corner"
        android:layout_centerInParent="true">

        <TextView android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Hi, This layout has rounded corner borders ..."
            android:gravity="center"
            android:padding="5dp"/>

    </LinearLayout>

答案 16 :(得分:0)

如果您想绕一些特定的角。

fun setCorners() {
        
        val mOutlineProvider = object : ViewOutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {

                val left = 0
                val top = 0;
                val right = view.width
                val bottom = view.height
                val cornerRadiusDP = 16f
                val cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDP, resources.displayMetrics).toInt()

                // all corners
                outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat())

                /* top corners
                outline.setRoundRect(left, top, right, bottom+cornerRadius, cornerRadius.toFloat())*/

                /* bottom corners
                outline.setRoundRect(left, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

                /* left corners
                outline.setRoundRect(left, top, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* right corners
                outline.setRoundRect(left - cornerRadius, top, right, bottom, cornerRadius.toFloat())*/

                /* top left corner
                outline.setRoundRect(left , top, right+ cornerRadius, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* top right corner
                outline.setRoundRect(left - cornerRadius , top, right, bottom + cornerRadius, cornerRadius.toFloat())*/

                /* bottom left corner
                outline.setRoundRect(left, top - cornerRadius, right + cornerRadius, bottom, cornerRadius.toFloat())*/

                /* bottom right corner
                outline.setRoundRect(left - cornerRadius, top - cornerRadius, right, bottom, cornerRadius.toFloat())*/

            }
        }

        myView.apply {
            outlineProvider = mOutlineProvider
            clipToOutline = true
        }
    }

答案 17 :(得分:0)

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {

        Bitmap roundedBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(roundedBitmap);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return roundedBitmap;
    }

答案 18 :(得分:0)

尝试使用您的线性布局此属性,它将有助于 工具:上下文= “youractivity”

答案 19 :(得分:-1)

在xml中使用shape with rectangle.set需要底部或上半径的属性。然后将xml作为背景应用于ur view ....或者......使用渐变来从代码中执行此操作。