Próbuję zrobić widok na Androidzie z zaokrąglonymi krawędziami. Rozwiązaniem, które znalazłem do tej pory, jest zdefiniowanie kształtu z zaokrąglonymi narożnikami i użycie go jako tła tego widoku.
Oto, co zrobiłem, zdefiniuj rysunek, jak podano poniżej:
<padding
android:top="2dp"
android:bottom="2dp"/>
<corners android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
Teraz użyłem tego jako tła dla mojego układu, jak poniżej:
<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">
Działa to doskonale, widzę, że widok ma zaokrąglone krawędzie.
Ale mój układ ma wiele innych widoków podrzędnych, na przykład ImageView lub MapView
. Kiedy umieszczam ImageView
wewnątrz powyższego układu, rogi obrazu nie są przycinane / przycinane, zamiast tego wydaje się, że jest pełny.
Widziałem inne obejścia, aby działało tak, jak to wyjaśniono tutaj .
Ale czy istnieje metoda ustawiania zaokrąglonych rogów widoku, a wszystkie jego widoki podrzędne są zawarte w tym widoku głównym, który ma zaokrąglone rogi?
Odpowiedzi:
Innym podejściem jest utworzenie niestandardowej klasy układu, takiej jak ta poniżej. Ten układ najpierw rysuje zawartość do mapy bitowej poza ekranem, maskuje bitmapę poza ekranem zaokrąglonym prostokątem, a następnie rysuje bitmapę poza ekranem na rzeczywistym płótnie.
Wypróbowałem to i wydaje się, że działa (przynajmniej w moim prostym przypadku testowym). Wpłynie to oczywiście na wydajność w porównaniu do zwykłego układu.
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; } }
Użyj tego jak normalnego układu:
<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>
źródło
Lub możesz użyć
android.support.v7.widget.CardView
podobnego:<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>
źródł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>
i wewnątrz ciebie układ
<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>
źródło
Odpowiedź Jaapa van Hengstuma działa świetnie, ale myślę, że jest droga i jeśli zastosujemy tę metodę na przykład do przycisku, efekt dotyku zostanie utracony, ponieważ widok jest renderowany jako mapa bitowa.
Dla mnie najlepsza i najprostsza metoda polega na nałożeniu maski na widok, tak:
@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); }
źródło
Jeśli masz problem z dodawaniem detektorów dotykowych do układu. Użyj tego układu jako układu nadrzędnego.
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); } }
tak jak
<?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>
źródło
Utwórz plik xml o nazwie
round.xml
wdrawable
folderze i wklej następującą zawartość:<?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>
następnie użyj
round.xml
asbackground
do dowolnego elementu. Wtedy da ci zaokrąglone rogi.źródło
W Androidzie L będziesz mógł po prostu użyć View.setClipToOutline, aby uzyskać ten efekt. W poprzednich wersjach nie było sposobu, aby po prostu przyciąć zawartość losowej grupy ViewGroup do określonego kształtu.
Będziesz musiał pomyśleć o czymś, co dałoby podobny efekt:
Jeśli potrzebujesz tylko zaokrąglonych rogów w ImageView, możesz użyć modułu cieniującego, aby „pomalować” obraz na kształcie, którego używasz jako tła. Spójrz na przykład w tej bibliotece .
Jeśli naprawdę potrzebujesz, aby wszystkie dzieci zostały obcięte, może możesz spojrzeć na swój układ z innej perspektywy? Taką z tłem w jakimkolwiek używanym kolorze i okrągłą „dziurą” pośrodku? W rzeczywistości można utworzyć niestandardową grupę ViewGroup, która rysuje ten kształt na wszystkich elementach potomnych, nadpisując metodę onDraw.
źródło
Utwórz plik xml w folderze do rysowania za pomocą następującego kodu. (Nazwa utworzonego przeze mnie pliku to 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>
I zachowaj to
drawable
jakbackground
dla widoku, do którego chcesz zachować zaokrągloną krawędź narożną. Zatrzymajmy to naLinearLayout
<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>
źródło
postępuj zgodnie z tym samouczkiem i całą dyskusją pod nim - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
zgodnie z tym postem napisanym przez Guya Romaina, jednego z czołowych programistów całego zestawu narzędzi Android UI, możliwe jest wykonanie kontenera (i wszystkich jego widoków podrzędnych) z zaokrąglonymi rogami, ale wyjaśnił, że jest to zbyt drogie (z wykonania problemy z renderowaniem).
Radzę ci postępować zgodnie z jego postem, a jeśli chcesz zaokrąglone rogi, zaimplementuj zaokrąglone rogi
ImageView
zgodnie z tym postem. następnie możesz umieścić go w pojemniku z dowolnym tłem, a uzyskasz pożądany efekt.to też ostatecznie zrobiłem.
źródło
Możesz użyć
androidx.cardview.widget.CardView
podobnego:<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>
LUB
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>
i wewnątrz ciebie układ
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/shape"> ... </LinearLayout>
źródło
CardView
Pracował dla mnie w API 27 w Android Studio 3.0.1.colorPrimary
Został wymieniony wres/values/colors.xml
pliku i jest tylko przykładem. Dla parametru layout_width0dp
rozciągnie się do szerokości rodzica. Będziesz musiał skonfigurować ograniczenia i szerokość / wysokość do swoich potrzeb.<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>
źródło
W przypadku Biblioteki komponentów materiałów najlepszym sposobem na utworzenie
View
z zaokrąglonymi narożnikami jest użycieMaterialShapeDrawable
.Utwórz model ShapeAppearanceModel z niestandardowymi zaokrąglonymi narożnikami:
ShapeAppearanceModel shapeAppearanceModelLL1 = new ShapeAppearanceModel() .toBuilder() .setAllCorners(CornerFamily.ROUNDED,radius16) .build();
Utwórz
MaterialShapeDrawable
:MaterialShapeDrawable shapeDrawableLL1 = new MaterialShapeDrawable(shapeAppearanceModeLL1);
Jeśli chcesz zastosować także ElevationOverlay dla ciemnego motywu, użyj tego:
MaterialShapeDrawable shapeDrawableLL1 = MaterialShapeDrawable.createWithElevationOverlay(this, 4.0f); shapeDrawableLL1.setShapeAppearanceModel(shapeAppearanceModelLL1);
Opcjonalnie: zastosuj do shapeDrawable kolor tła i obrys
shapeDrawableLL1.setFillColor( ContextCompat.getColorStateList(this,R.color...)); shapeDrawableLL1.setStrokeWidth(2.0f); shapeDrawableLL1.setStrokeColor( ContextCompat.getColorStateList(this,R.color...));
Na koniec zastosuj shapeDrawable jako tło w swoim
LinearLayout
(lub innym widoku):źródło
Różnica w stosunku do odpowiedzi Jaapa van Hengstuma:
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); } }
źródło
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); } }
źródło
Podane łącze do samouczka wydaje się sugerować, że musisz ustawić właściwości layout_width i layout_height elementów podrzędnych na match_parent.
<ImageView android:layout_width="match_parent" android:layout_height="match_parent">
źródło
Aby utworzyć obraz z okrągłym rogiem za pomocą 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)
lub jeśli chcesz go użyć w pliku xml:
<com.google.android.material.imageview.ShapeableImageView android:id="@+id/thumb" android:layout_width="80dp" android:layout_height="60dp" app:shapeAppearanceOverlay="@style/circleImageView" />
w style.xml dodaj to:
<style name="circleImageView" parent=""> <item name="cornerFamily">rounded</item> <item name="cornerSize">10%</item> </style>
źródło
wypróbuj tę właściwość w swoim układzie liniowym, pomoże ona
narzędziom: context = ". youractivity"
źródło
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; }
źródło
Na wypadek, gdybyś chciał zaokrąglić jakiś konkretny róg.
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 } }
źródło
Użyj kształtu w xml z prostokątem. Ustaw właściwość dolnego lub górnego promienia według potrzeb. Następnie zastosuj ten xml jako tło do widoku ur ... lub ... użyj gradientów, aby zrobić to z kodu.
źródło