如何制作带有圆角的视图?


92

我正在尝试在带有圆角的android中进行查看。到目前为止,我发现的解决方案是定义一个带有圆角的形状并将其用作该视图的背景。

这就是我所做的,定义如下所示的可绘制对象:

<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或a MapView。当我ImageView在上面的布局中放置一个图像时,图像的角不会被修剪/裁剪,而是显得完整。

我已经看到了其他解决方法,使其能够像此处说明的那样工作。

但是,是否有一种方法可以为视图设置圆角,并且其所有子视图都包含在具有圆角的主视图中?


如果您使用扩展到LinearLayout的自定义布局,并且在创建其对象时,迭代该Layout的所有子项并将圆角边框背景应用于它们,该怎么办?
MysticMagicϡ

3
android.support.v7.widget.CardView似乎是解决此问题的方法
rocketspacer

此解决方案为我解决了该问题,但需要子类化:https
Aaron

这回答了你的问题了吗?为图像圆角图像android添加边框
user4157124

Answers:


127

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

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

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
我正在使用此布局用于内部具有缩放功能的imageview,但是当我尝试缩放时,此布局无法检测触摸监听器,如何在此布局中添加触摸监听器功能?
穆罕默德·纳菲安·怀达纳

2
我使用了这种方法,效果很好。但是,当我在布局中放入EditText时,键入内容时屏幕不会更新。关闭窗口时,文本会显示出来。关闭键盘后,屏幕将更新。有什么解决办法吗?
萨吉布·阿查里亚

1
当将其与Recycler视图一起使用时,唯一的问题是它不等待完整的项目集被加载。并舍入仅几项。是否有发生这种情况的原因?
艾哈迈德(Ahmed)

2
这不适用于从滑行库加载的使用占位符过渡的图像(或实际上是任何其他动画子级)。我有一个替代解决方案,但是它要求您的背景色是纯色。参见gist.github.com/grennis/da9f86870c45f3b8ae00912edb72e868
Greg Ennis

2
精细而有用的解决方案,但价格昂贵。仅包含六个图片的六个元素的RecyclerView无法平滑滚动。而且它并不取决于设备的功能(三星S9或Wileyfox Swift 2)。 stackoverflow.com/a/41098690/4399323是更好的答案!
瓦伦丁·尤里耶夫'18

71

或者您可以这样使用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>

我已经尝试了很多方法,您的方法是解决我的问题的最佳方法。谢谢!
wizChen

无法删除高程。如果设计需要高程,则可以正常工作。
阿卜杜勒拉曼·沙图

1
@AbdalrahmanShatou developer.android.com/reference/android/support/v7/widget/...有该属性。您是否尝试将它们设置为“ 0”?
rocketspacer

5
一个缺点是它不支持<21个android api
Itai Spector

50

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>

3
@Zach:它以什么方式无法工作?询问原始问题时,您的应用程序是如何工作的?
LarsH

2
clipChildren是无用的,并且图像视图仍在视图外部延伸。
阿卜杜勒拉曼·沙图

1
希望我能为您提供赏金。谢谢
Odys '18

3
我认为如果图像是矩形图像,这种方法将行不通,因为背景将绘制在图像内容的后面。这可能仅适用于具有透明度并且自身具有圆角的图像。
Harsha Vardhan

21

Jaap van Hengstum的答案效果很好,但是我认为它的成本很高,例如,如果将这种方法应用于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);
}

如何使用?
Maksim Kniazev

@MaksimKniazev创建所需布局的子类(例如:FrameLayout),然后将这两种方法粘贴到该子类中,并用所需的角半径值(例如,来自资源)替换“ <whatever_you_want>”。而已。是否对您足够清楚,还是您需要一个示例?

非常感谢,它对于RecyclerView确实更好用!
瓦伦丁·尤里耶夫'18

18

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

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


}

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

2
如果屏幕上没有太多动画,那么效果很好,这会使屏幕一次又一次地绘制;从那以后,它将严重降低性能。应该尝试cardview,它具有有效的圆角方法(不使用剪切路径或canvas.drawRoundRect-在API 17以下无效,但使用porterFilterDuffColor)
Nguyen Tan Dat

15

round.xmldrawable文件夹中创建一个名为的xml文件,然后粘贴以下内容:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
  <solid android:color="#FFFFFF" />
  <stroke android:width=".05dp" android:color="#d2d2d2" />
  <corners android:topLeftRadius="5dp" android:topRightRadius="5dp" android:bottomRightRadius="5dp" android:bottomLeftRadius="5dp"/>
</shape>

然后将round.xmlas用作background任何项目。然后它将给您圆角。


9

在Android L中,您将只能使用View.setClipToOutline来获得这种效果。在以前的版本中,无法仅将随机ViewGroup的内容剪辑为特定形状。

您将不得不考虑一些会给您带来类似效果的事情:

  • 如果在ImageView中仅需要圆角,则可以使用着色器在用作背景的形状上“绘制”图像。以这个库为例。

  • 如果您确实需要剪裁每个孩子,也许您可​​以在布局中查看另一个视图?一个使用您正在使用的任何颜色的背景,中间有一个圆形的“孔”吗?您实际上可以创建一个自定义ViewGroup,在覆盖onDraw方法的每个子级上绘制该形状。


3

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

根据整个Android UI工具包的领先开发者之一盖伊·罗曼(Guy Romain)撰写的这篇文章,有可能制作一个带有圆角的容器(及其所有子视图),但他解释说,这样做太昂贵了(从渲染问题)。

我建议您根据他的帖子进行操作,如果您想要圆角,请ImageView按照此帖子实施圆角。然后,您可以将其放置在具有任何背景的容器中,以获得想要的效果。

最终我也是这样做的。


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>

要么

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>

3

使用以下代码在您的可绘制文件夹下创建一个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>
    </shape>

并将其保留drawablebackground要保留圆角边框的视图。让我们保留一个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>

3

CardViewAndroid 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="wrap_content"
    android:layout_height="wrap_content"
    app:cardCornerRadius="4dp"
    app:cardBackgroundColor="@color/colorPrimary">

    <!-- put your content here -->

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

2

与Jaap van Hengstum的答案不同:

  1. 使用BitmapShader而不是蒙版位图。
  2. 仅创建一次位图。
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);
    }
}

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

2

使用Material Components Library制作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);

在此处输入图片说明


1

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

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

0

尝试将此属性与您的线性布局配合使用,将对以下
工具有所帮助:context =“。youractivity”


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

0

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

-1

在xml中使用带有矩形的形状。根据需要设置底部或上部半径的属性。然后将该xml作为背景应用于您的视图....或...使用渐变从代码中执行此操作。


我已经做到了,但是想知道是否有更好的方法?
2014年

请求者已经在他的问题中提到尝试这种方法。
Michael Krause 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.