Estoy tratando de hacer una vista en Android con bordes redondeados. La solución que encontré hasta ahora es definir una forma con esquinas redondeadas y usarla como fondo de esa vista.
Esto es lo que hice, definir un dibujable como se indica a continuación:
<padding
android:top="2dp"
android:bottom="2dp"/>
<corners android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
Ahora usé esto como fondo para mi diseño de la siguiente manera:
<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">
Esto funciona perfectamente bien, puedo ver que la vista tiene bordes redondeados.
Pero mi diseño tiene muchas otras vistas secundarias, por ejemplo, ImageView o MapView
. Cuando coloco un ImageView
diseño dentro del diseño anterior, las esquinas de la imagen no se recortan / recortan, sino que aparecen llenas.
He visto otras soluciones para que funcione como se explica aquí .
Pero, ¿existe un método para establecer esquinas redondeadas para una vista y todas sus vistas secundarias están contenidas dentro de esa vista principal que tiene esquinas redondeadas?
Respuestas:
Otro enfoque es crear una clase de diseño personalizada como la que se muestra a continuación. Este diseño primero dibuja su contenido en un mapa de bits fuera de la pantalla, enmascara el mapa de bits fuera de la pantalla con un rectángulo redondeado y luego dibuja el mapa de bits fuera de la pantalla en el lienzo real.
Lo probé y parece funcionar (al menos para mi caso de prueba simple). Por supuesto, afectará el rendimiento en comparación con un diseño normal.
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 esto como un diseño normal:
<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>
fuente
O puedes usar un me
android.support.v7.widget.CardView
gusta:<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>
fuente
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>
y dentro de tu diseño
<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>
fuente
La respuesta de Jaap van Hengstum funciona muy bien, sin embargo, creo que es costosa y si aplicamos este método en un botón, por ejemplo, el efecto táctil se pierde ya que la vista se representa como un mapa de bits.
Para mí el mejor método y el más sencillo consiste en aplicar una máscara en la vista, así:
@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); }
fuente
Si tiene problemas al agregar oyentes táctiles al diseño. Utilice este diseño como diseño principal.
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); } }
como
<?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>
fuente
Cree un archivo xml llamado
round.xml
en ladrawable
carpeta y pegue este contenido:<?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>
luego use el
round.xml
comobackground
para cualquier artículo. Entonces te dará esquinas redondeadas.fuente
En Android L, podrá usar View.setClipToOutline para obtener ese efecto. En versiones anteriores, no hay forma de recortar el contenido de un ViewGroup aleatorio en una forma determinada.
Tendrás que pensar en algo que te dé un efecto similar:
Si solo necesita esquinas redondeadas en ImageView, puede usar un sombreador para 'pintar' la imagen sobre la forma que está usando como fondo. Eche un vistazo a esta biblioteca para ver un ejemplo.
Si realmente necesita que se recorten todos los niños, ¿quizás pueda otra vista sobre su diseño? ¿Uno con un fondo de cualquier color que estés usando y un 'agujero' redondo en el medio? De hecho, podría crear un ViewGroup personalizado que dibuje esa forma sobre cada niño anulando el método onDraw.
fuente
Cree un archivo xml en su carpeta dibujable con el siguiente código. (El nombre del archivo que creé es round_corner.xml)
esquina_ redondeada.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>
Y mantenga esto
drawable
comobackground
para la vista en la que desea mantener el borde de la esquina redondeada. Dejémoslo por unLinearLayout
<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>
fuente
siga este tutorial y toda la discusión debajo de él - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-dered-corners/
De acuerdo con esta publicación escrita por Guy Romain, uno de los desarrolladores líderes de todo el kit de herramientas de la interfaz de usuario de Android, es posible hacer un contenedor (y todas las vistas de sus hijos) con esquinas redondeadas, pero explicó que es demasiado caro (por las actuaciones de problemas de renderizado).
Te recomendaré que vayas de acuerdo con su publicación, y si quieres esquinas redondeadas, implementa las esquinas redondeadas de
ImageView
acuerdo con esta publicación. luego, puedes colocarlo dentro de un contenedor con cualquier fondo y obtendrás el efecto que deseas.eso es lo que hice también eventualmente.
fuente
Puedes usar un me
androidx.cardview.widget.CardView
gusta así:<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>
O
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>
y dentro de tu diseño
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/shape"> ... </LinearLayout>
fuente
El
CardView
trabajó para mí en el API 27 en Android Studio 3.0.1. SecolorPrimary
hizo referencia a él en elres/values/colors.xml
archivo y es solo un ejemplo. Porque el layout_width0dp
se extenderá hasta el ancho del padre. Tendrá que configurar las restricciones y el ancho / alto según sus necesidades.<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>
fuente
Con la Biblioteca de componentes de materiales, la mejor manera de hacer un
View
con esquinas redondeadas es usar elMaterialShapeDrawable
.Cree un ShapeAppearanceModel con esquinas redondeadas personalizadas:
ShapeAppearanceModel shapeAppearanceModelLL1 = new ShapeAppearanceModel() .toBuilder() .setAllCorners(CornerFamily.ROUNDED,radius16) .build();
Crea un
MaterialShapeDrawable
:MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);
Si desea aplicar también una superposición de elevación para el tema oscuro, use esto:
MaterialShapeDrawable shapeDrawableLL1 = MaterialShapeDrawable.createWithElevationOverlay(this, 4.0f); shapeDrawableLL1.setShapeAppearanceModel(shapeAppearanceModelLL1);
Opcional: aplicar al shapeDrawable un color de fondo y un trazo
shapeDrawableLL1.setFillColor( ContextCompat.getColorStateList(this,R.color...)); shapeDrawableLL1.setStrokeWidth(2.0f); shapeDrawableLL1.setStrokeColor( ContextCompat.getColorStateList(this,R.color...));
Finalmente, aplique shapeDrawable como fondo en su
LinearLayout
(u otra vista):fuente
Diferencia con la respuesta de Jaap van Hengstum:
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); } }
fuente
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); } }
fuente
El enlace del tutorial que proporcionó parece sugerir que debe establecer las propiedades layout_width y layout_height de sus elementos secundarios en match_parent.
<ImageView android:layout_width="match_parent" android:layout_height="match_parent">
fuente
Para crear una imagen de esquina redondeada usando 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)
o si desea usarlo en un archivo xml:
<com.google.android.material.imageview.ShapeableImageView android:id="@+id/thumb" android:layout_width="80dp" android:layout_height="60dp" app:shapeAppearanceOverlay="@style/circleImageView" />
en style.xml agregue esto:
<style name="circleImageView" parent=""> <item name="cornerFamily">rounded</item> <item name="cornerSize">10%</item> </style>
fuente
pruebe esta propiedad con su diseño lineal, ayudará a las
herramientas: contexto = ". su actividad"
fuente
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; }
fuente
Por si quieres doblar alguna esquina concreta.
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 } }
fuente
Use la forma en xml con rectángulo, establezca la propiedad de radio inferior o superior como desee, luego aplique ese xml como fondo a su vista ... o ... use degradados para hacerlo desde el código.
fuente