110

我正在尝试在具有圆角边缘的 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在上面的布局中放置一个时,图像的角落没有被剪裁/裁剪,而是看起来很完整。

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

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

4

20 回答 20

138

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

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

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>
于 2014-10-05T08:57:06.590 回答
95

或者你可以使用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>
于 2016-05-13T01:45:49.540 回答
66

形状.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>
于 2014-10-03T06:46:29.400 回答
25

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);
}
于 2016-12-12T10:26:21.190 回答
22

在文件夹中创建一个名为 xmlround.xmldrawable文件并粘贴以下内容:

<?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.xmlasbackground用于任何项目。然后它会给你圆角。

于 2014-10-04T05:00:13.283 回答
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>
于 2016-02-08T10:04:50.773 回答
11

使用 Material Components Library 制作View圆角的最佳方法是使用MaterialShapeDrawable.

创建一个带有自定义圆角的 ShapeAppearanceModel:

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

创建一个MaterialShapeDrawable

MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);

如果您还想为深色主题应用elevationOverlay,请使用:

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

在此处输入图像描述

于 2020-06-08T13:29:07.167 回答
10

在 Android L 中,您只需使用View.setClipToOutline即可获得该效果。在以前的版本中,无法将随机 ViewGroup 的内容裁剪为特定形状。

你将不得不想出一些能给你带来类似效果的东西:

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

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

于 2014-10-03T15:55:27.093 回答
7

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

圆角.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>
于 2018-11-08T09:35:23.677 回答
6

如果您想绕过某个特定的角落。

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

可以在具有如下子项的 LinearLayout 上使用:

在此处输入图像描述

对此:

在此处输入图像描述

于 2020-10-21T13:32:58.967 回答
5

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

    <!-- put your content here -->

</android.support.v7.widget.CardView>
于 2017-12-03T06:20:05.570 回答
4

你可以使用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>

或者

形状.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>
于 2020-02-11T10:35:46.513 回答
4

使用 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>
于 2020-06-19T12:29:18.967 回答
3

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

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

我建议你按照他的帖子去,如果你想要圆角,然后ImageView根据这个帖子实现圆角。然后,你可以把它放在一个任何背景的容器里,你会得到你想要的效果。

这也是我最终所做的。

于 2014-10-03T18:29:45.417 回答
3
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);
    }
}
于 2016-06-16T12:16:49.157 回答
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);
    }
}
于 2015-09-17T11:04:46.093 回答
1

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

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent">
于 2014-09-27T12:53:48.210 回答
0

用你的线性布局试试这个属性,它会帮助
工具:context=".youractivity"

于 2014-09-27T13:22:22.707 回答
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;
    }
于 2016-05-09T11:32:05.353 回答
-1

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

于 2014-09-30T18:16:08.810 回答