How to make a view with rounded corners?

前端 未结 20 1014
遇见更好的自我
遇见更好的自我 2020-11-27 11:08

I am trying to make a view in android with rounded edges. The solution I found so far is to define a shape with rounded corners and use it as the background of that view.

相关标签:
20条回答
  • 2020-11-27 11:36

    Another approach is to make a custom layout class like the one below. This layout first draws its contents to an offscreen bitmap, masks the offscreen bitmap with a rounded rect and then draws the offscreen bitmap on the actual canvas.

    I tried it and it seems to work (at least for my simple testcase). It will of course affect performance compared to a regular layout.

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

    Use this like a normal layout:

    <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>
    
    0 讨论(0)
  • 2020-11-27 11:37

    If you are having problem while adding touch listeners to the layout. Use this layout as parent layout.

    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>
    
    0 讨论(0)
  • 2020-11-27 11:37

    The tutorial link you provided seems to suggest that you need to set the layout_width and layout_height properties, of your child elements to match_parent.

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
    0 讨论(0)
  • 2020-11-27 11:38

    follow this tutorial and all the discussion beneath it - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/

    according to this post written by Guy Romain, one of the leading developers of the entire Android UI toolkit, it is possible to make a container (and all his child views) with rounded corners, but he explained that it too expensive (from performances of rendering issues).

    I'll recommend you to go according to his post, and if you want rounded corners, then implement rounded corners ImageView according to this post. then, you could place it inside a container with any background, and you'll get the affect you wish.

    that's what I did also also eventually.

    0 讨论(0)
  • 2020-11-27 11:40

    Jaap van Hengstum's answer works great however I think it is expensive and if we apply this method on a Button for example, the touch effect is lost since the view is rendered as a bitmap.

    For me the best method and the simplest one consists in applying a mask on the view, like that:

    @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);
    }
    
    0 讨论(0)
  • 2020-11-27 11:41

    With the Material Components Library the best way to make a View with rounded corners is to use the MaterialShapeDrawable.

    Create a ShapeAppearanceModel with custom rounded corners:

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

    Create a MaterialShapeDrawable:

    MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);
    

    If you want to apply also an elevationOverlay for the dark theme use this:

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

    Optional: apply to the shapeDrawable a background color and a stroke

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

    Finally apply the shapeDrawable as background in your LinearLayout (or other view):

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

    0 讨论(0)
提交回复
热议问题