Jak mogę utworzyć układ z zaokrąglonymi narożnikami? Chcę zastosować do mnie zaokrąglone rogi LinearLayout
.
Jak mogę utworzyć układ z zaokrąglonymi narożnikami? Chcę zastosować do mnie zaokrąglone rogi LinearLayout
.
Odpowiedzi:
1: Zdefiniuj layout_bg.xml w drawables:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<stroke android:width="3dp" android:color="#B1BCBE" />
<corners android:radius="10dp"/>
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>
2: Dodaj layout_bg.xml
jako tło do swojego układu
android:background="@drawable/layout_bg"
Element shape doesn't have the required attribute android:layout_height
?
Element shape doesn't have the required attribute android:layout_height
Przyczyna błędu została pokazana, ponieważ layout_bg.xml nie znajdował się w folderze rozwijalnym.
W przypadku interfejsu API 21+ użyj widoków klipu
Zaokrąglone obcinanie obrysu zostało dodane do View
klasy w API 21. Zobacz ten dokument szkoleniowy lub to odniesienie, aby uzyskać więcej informacji.
Ta wbudowana funkcja sprawia, że zaokrąglone rogi są bardzo łatwe do wdrożenia. Działa na dowolnym widoku lub układzie i obsługuje prawidłowe obcinanie.
Oto co należy zrobić:
android:background="@drawable/round_outline"
android:clipToOutline="true"
Niestety wydaje się, że wystąpił błąd i ten atrybut XML obecnie nie jest rozpoznawany. Na szczęście możemy ustawić przycinanie w Javie:
View.setClipToOutline(true)
Jak to wygląda:
Specjalna uwaga na temat ImageViews
setClipToOutline()
działa tylko wtedy, gdy tło Widoku jest ustawione na kształt, który można rysować. Jeśli ten kształt tła istnieje, widok traktuje kontur tła jako obramowanie do celów wycinania i cieniowania.
Oznacza to, że jeśli chcesz zaokrąglać rogi w ImageView za pomocą setClipToOutline()
, twój obraz musi pochodzić android:src
zamiast android:background
(ponieważ tło jest używane dla zaokrąglonego kształtu). Jeśli MUSISZ użyć tła do ustawienia obrazu zamiast src, możesz użyć tego obejścia widoków zagnieżdżonych:
src=@drawable...
zamiast background=@drawable
? Możesz to zrobić lub zagnieździć ImageView w widoku kontenera, który zawiera kontur kształtu.
Oto kopia pliku XML, aby utworzyć rysunek z białym tłem, czarną ramką i zaokrąglonymi narożnikami:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ffffffff"/>
<stroke android:width="3dp"
android:color="#ff000000"
/>
<padding android:left="1dp"
android:top="1dp"
android:right="1dp"
android:bottom="1dp"
/>
<corners android:bottomRightRadius="7dp" android:bottomLeftRadius="7dp"
android:topLeftRadius="7dp" android:topRightRadius="7dp"/>
</shape>
zapisz go jako plik xml w katalogu do rysowania, użyj go tak, jakbyś używał dowolnego tła do rysowania (ikony lub pliku zasobów), używając jego nazwy zasobu (R.drawable.your_xml_name)
<corners android:radius="7dp" />
Użyj CardView w bibliotece wsparcia dla Androida v7. Choć jest trochę ciężki, rozwiązuje wszystkie problemy i jest dość łatwy. W przeciwieństwie do metody rysowania tła, może skutecznie przycinać widoki podrzędne.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardBackgroundColor="@android:color/transparent"
card_view:cardCornerRadius="5dp"
card_view:cardElevation="0dp"
card_view:contentPadding="0dp">
<YOUR_LINEARLAYOUT_HERE>
</android.support.v7.widget.CardView>
Zrobiłem w ten sposób:
Sprawdź zrzut ekranu:
Utwórz plik do rysowania o nazwie custom_rectangle.xml
w folderze do rysowania :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="@android:color/white" />
<corners android:radius="10dip" />
<stroke
android:width="1dp"
android:color="@android:color/white" />
</shape>
Teraz zastosuj tło prostokąta w widoku :
mView.setBackground(R.drawlable.custom_rectangle);
Gotowy
Myślę, że lepszym sposobem na to jest połączenie dwóch rzeczy:
zrób bitmapę układu, jak pokazano tutaj .
Zrobić zaokrąglony rysunek z mapy bitowej, jak pokazano tutaj
ustaw drawable na imageView.
Będzie to obsługiwać przypadki, których inne rozwiązania nie rozwiązały, na przykład zawartość o narożnikach.
Myślę, że jest też trochę bardziej przyjazny dla GPU, ponieważ pokazuje jedną warstwę zamiast 2.
Jedynym lepszym sposobem jest stworzenie całkowicie spersonalizowanego widoku, ale to dużo kodu i może zająć dużo czasu. Myślę, że to, co tu zasugerowałem, jest najlepsze z obu światów.
Oto fragment tego, jak można to zrobić:
RoundedCornersDrawable.java
/**
* shows a bitmap as if it had rounded corners. based on :
* http://rahulswackyworld.blogspot.co.il/2013/04/android-drawables-with-rounded_7.html
* easy alternative from support library: RoundedBitmapDrawableFactory.create( ...) ;
*/
public class RoundedCornersDrawable extends BitmapDrawable {
private final BitmapShader bitmapShader;
private final Paint p;
private final RectF rect;
private final float borderRadius;
public RoundedCornersDrawable(final Resources resources, final Bitmap bitmap, final float borderRadius) {
super(resources, bitmap);
bitmapShader = new BitmapShader(getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
final Bitmap b = getBitmap();
p = getPaint();
p.setAntiAlias(true);
p.setShader(bitmapShader);
final int w = b.getWidth(), h = b.getHeight();
rect = new RectF(0, 0, w, h);
this.borderRadius = borderRadius < 0 ? 0.15f * Math.min(w, h) : borderRadius;
}
@Override
public void draw(final Canvas canvas) {
canvas.drawRoundRect(rect, borderRadius, borderRadius, p);
}
}
CustomView.java
public class CustomView extends ImageView {
private View mMainContainer;
private boolean mIsDirty=false;
// TODO for each change of views/content, set mIsDirty to true and call invalidate
@Override
protected void onDraw(final Canvas canvas) {
if (mIsDirty) {
mIsDirty = false;
drawContent();
return;
}
super.onDraw(canvas);
}
/**
* draws the view's content to a bitmap. code based on :
* http://nadavfima.com/android-snippet-inflate-a-layout-draw-to-a-bitmap/
*/
public static Bitmap drawToBitmap(final View viewToDrawFrom, final int width, final int height) {
// Create a new bitmap and a new canvas using that bitmap
final Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bmp);
viewToDrawFrom.setDrawingCacheEnabled(true);
// Supply measurements
viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(canvas.getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(canvas.getHeight(), MeasureSpec.EXACTLY));
// Apply the measures so the layout would resize before drawing.
viewToDrawFrom.layout(0, 0, viewToDrawFrom.getMeasuredWidth(), viewToDrawFrom.getMeasuredHeight());
// and now the bmp object will actually contain the requested layout
canvas.drawBitmap(viewToDrawFrom.getDrawingCache(), 0, 0, new Paint());
return bmp;
}
private void drawContent() {
if (getMeasuredWidth() <= 0 || getMeasuredHeight() <= 0)
return;
final Bitmap bitmap = drawToBitmap(mMainContainer, getMeasuredWidth(), getMeasuredHeight());
final RoundedCornersDrawable drawable = new RoundedCornersDrawable(getResources(), bitmap, 15);
setImageDrawable(drawable);
}
}
EDYCJA: znalazłem fajną alternatywę, opartą na bibliotece „RoundKornersLayouts” . Mieć klasę, która będzie używana dla wszystkich klas układu, które chcesz rozszerzyć, aby ją zaokrąglić:
//based on https://github.com/JcMinarro/RoundKornerLayouts
class CanvasRounder(cornerRadius: Float, cornerStrokeColor: Int = 0, cornerStrokeWidth: Float = 0F) {
private val path = android.graphics.Path()
private lateinit var rectF: RectF
private var strokePaint: Paint?
var cornerRadius: Float = cornerRadius
set(value) {
field = value
resetPath()
}
init {
if (cornerStrokeWidth <= 0)
strokePaint = null
else {
strokePaint = Paint()
strokePaint!!.style = Paint.Style.STROKE
strokePaint!!.isAntiAlias = true
strokePaint!!.color = cornerStrokeColor
strokePaint!!.strokeWidth = cornerStrokeWidth
}
}
fun round(canvas: Canvas, drawFunction: (Canvas) -> Unit) {
val save = canvas.save()
canvas.clipPath(path)
drawFunction(canvas)
if (strokePaint != null)
canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, strokePaint)
canvas.restoreToCount(save)
}
fun updateSize(currentWidth: Int, currentHeight: Int) {
rectF = android.graphics.RectF(0f, 0f, currentWidth.toFloat(), currentHeight.toFloat())
resetPath()
}
private fun resetPath() {
path.reset()
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW)
path.close()
}
}
Następnie w każdej z niestandardowych klas układu dodaj kod podobny do tego:
class RoundedConstraintLayout : ConstraintLayout {
private lateinit var canvasRounder: CanvasRounder
constructor(context: Context) : super(context) {
init(context, null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(context, attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
init(context, attrs, defStyle)
}
private fun init(context: Context, attrs: AttributeSet?, defStyle: Int) {
val array = context.obtainStyledAttributes(attrs, R.styleable.RoundedCornersView, 0, 0)
val cornerRadius = array.getDimension(R.styleable.RoundedCornersView_corner_radius, 0f)
val cornerStrokeColor = array.getColor(R.styleable.RoundedCornersView_corner_stroke_color, 0)
val cornerStrokeWidth = array.getDimension(R.styleable.RoundedCornersView_corner_stroke_width, 0f)
array.recycle()
canvasRounder = CanvasRounder(cornerRadius,cornerStrokeColor,cornerStrokeWidth)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(FrameLayout.LAYER_TYPE_SOFTWARE, null)
}
}
override fun onSizeChanged(currentWidth: Int, currentHeight: Int, oldWidth: Int, oldheight: Int) {
super.onSizeChanged(currentWidth, currentHeight, oldWidth, oldheight)
canvasRounder.updateSize(currentWidth, currentHeight)
}
override fun draw(canvas: Canvas) = canvasRounder.round(canvas) { super.draw(canvas) }
override fun dispatchDraw(canvas: Canvas) = canvasRounder.round(canvas) { super.dispatchDraw(canvas) }
}
Jeśli chcesz obsługiwać atrybuty, użyj tego, jak napisano w bibliotece:
<resources>
<declare-styleable name="RoundedCornersView">
<attr name="corner_radius" format="dimension"/>
<attr name="corner_stroke_width" format="dimension"/>
<attr name="corner_stroke_color" format="color"/>
</declare-styleable>
</resources>
Inna alternatywa, która może być łatwiejsza w przypadku większości zastosowań: użyj MaterialCardView. Pozwala dostosować zaokrąglone rogi, kolor i szerokość obrysu oraz wysokość.
Przykład:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:clipChildren="false" android:clipToPadding="false"
tools:context=".MainActivity">
<com.google.android.material.card.MaterialCardView
android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center"
app:cardCornerRadius="8dp" app:cardElevation="8dp" app:strokeColor="#f00" app:strokeWidth="2dp">
<ImageView
android:layout_width="match_parent" android:layout_height="match_parent" android:background="#0f0"/>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>
A wynik:
Zauważ, że na krawędziach obrysu występuje niewielki problem z artefaktami (pozostawia tam kilka pikseli zawartości), jeśli go używasz. Możesz to zauważyć, jeśli powiększysz. Zgłosiłem tutaj ten problem .
EDYCJA: wydaje się być naprawiona, ale nie na IDE. Zgłoszone tutaj .
Spróbuj tego...
1. stwórz xml do rysowania (custom_layout.xml):
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#FFFFFF" />
<stroke
android:width="2dp"
android:color="#FF785C" />
<corners android:radius="10dp" />
</shape>
2. dodaj swoje tło widoku
android:background="@drawable/custom_layout"
Jeśli chcesz, aby Twój układ był zaokrąglony, najlepiej użyć CardView, ponieważ zapewnił wiele funkcji, aby projekt był piękny.
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".3"
android:text="@string/quote_code"
android:textColor="@color/white"
android:textSize="@dimen/text_head_size" />
</LinearLayout>
</android.support.v7.widget.CardView>
Za pomocą tego card_view: cardCornerRadius = "5dp" możesz zmienić promień.
Najlepszym i najprostszym sposobem byłoby wykorzystanie card_background rozciągliwej w układzie. Jest to również zgodne z wytycznymi Google dotyczącymi projektowania materiałów. Po prostu dołącz to do siebie LinearLayout:
android:background="@drawable/card_background"
Dodaj to do katalogu, który można wyciągnąć i nazwij go card_background.xml :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#BDBDBD"/>
<corners android:radius="5dp"/>
</shape>
</item>
<item
android:left="0dp"
android:right="0dp"
android:top="0dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#ffffff"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
Użyj CardView, aby uzyskać zaokrąglone krawędzie dla dowolnych układów. Użyj card_view: cardCornerRadius = "5dp" dla widoku karty, aby uzyskać zaokrąglone krawędzie układu.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
card_view:cardCornerRadius="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="15dp"
android:weightSum="1">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".3"
android:text="@string/quote_code"
android:textColor="@color/white"
android:textSize="@dimen/text_head_size" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight=".7"
android:text="@string/quote_details"
android:textColor="@color/white"
android:textSize="@dimen/text_head_size" />
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Lepszym sposobem na to byłoby:
background_activity.xml
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="fill">
<color android:color="@color/black"/>
</item>
<item>
<shape android:gravity="fill">
<solid android:color="@color/white"/>
<corners android:radius="10dip"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
</shape>
</item>
</layer-list>
To zadziała również poniżej API 21 i da ci coś takiego:
Jeśli chcesz nieco więcej wysiłku, lepiej kontrolować, użyj android.support.v7.widget.CardView
tego cardCornerRadius
atrybutu (i ustaw elevation
atrybut, 0dp
aby pozbyć się towarzyszącego mu cienia za pomocą cardView). Będzie to również działać z poziomu interfejsu API już od 15.
Funkcja programowego ustawiania promienia narożnika
static void setCornerRadius(GradientDrawable drawable, float topLeft,
float topRight, float bottomRight, float bottomLeft) {
drawable.setCornerRadii(new float[] { topLeft, topLeft, topRight, topRight,
bottomRight, bottomRight, bottomLeft, bottomLeft });
}
static void setCornerRadius(GradientDrawable drawable, float radius) {
drawable.setCornerRadius(radius);
}
Za pomocą
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(Color.GREEN);
setCornerRadius(gradientDrawable, 20f);
//or setCornerRadius(gradientDrawable, 20f, 40f, 60f, 80f);
view.setBackground(gradientDrawable);
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<stroke android:width="3dip" android:color="#B1BCBE" />
<corners android:radius="10dip"/>
<padding android:left="3dip" android:top="3dip" android:right="3dip" android:bottom="3dip" />
</shape>
@ David, po prostu wstaw padding taką samą wartość jak obrys, aby ramka była widoczna, bez względu na rozmiar obrazu
Wziąłem odpowiedź @gauravsapiens z moimi komentarzami w środku, aby dać ci rozsądne zrozumienie, jakie parametry będą miały wpływ.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background color -->
<solid android:color="@color/white" />
<!-- Stroke around the background, width and color -->
<stroke android:width="4dp" android:color="@color/drop_shadow"/>
<!-- The corners of the shape -->
<corners android:radius="4dp"/>
<!-- Padding for the background, e.g the Text inside a TextView will be
located differently -->
<padding android:left="10dp" android:right="10dp"
android:bottom="10dp" android:top="10dp" />
</shape>
Jeśli chcesz tylko utworzyć kształt, który zaokrągli rogi, usuń wypełnienie, a obrys się spełni. Jeśli usuniesz bryłę, utworzysz zaokrąglone rogi na przezroczystym tle.
Dla lenistwa stworzyłem pod spodem kształt, który jest po prostu białym tłem z zaokrąglonymi rogami - ciesz się! :)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background color -->
<solid android:color="@color/white" />
<!-- The corners of the shape -->
<corners android:radius="4dp"/>
</shape>
Stwórz swój plik xml w wersji do rysowania, layout_background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="@color/your_colour" />
<stroke
android:width="2dp"
android:color="@color/your_colour" />
<corners android:radius="10dp" />
</shape>
<--width, color, radius should be as per your requirement-->
a następnie dodaj to do swojego layout.xml
android:background="@drawable/layout_background"
Za pomocą Biblioteki komponentów materiałów możesz używać MaterialShapeDrawable
do rysowania niestandardowych kształtów .
Wystarczy umieścić LinearLayout w układzie xml:
<LinearLayout
android:id="@+id/linear_rounded"
android:layout_width="match_parent"
android:layout_height="wrap_content"
..>
<!-- content ..... -->
</LinearLayout>
Następnie w kodzie możesz zastosować ShapeAppearanceModel
. Coś jak:
float radius = getResources().getDimension(R.dimen.default_corner_radius);
LinearLayout linearLayout= findViewById(R.id.linear_rounded);
ShapeAppearanceModel shapeAppearanceModel = new ShapeAppearanceModel()
.toBuilder()
.setAllCorners(CornerFamily.ROUNDED,radius)
.build();
MaterialShapeDrawable shapeDrawable = new MaterialShapeDrawable(shapeAppearanceModel);
//Fill the LinearLayout with your color
shapeDrawable.setFillColor(ContextCompat.getColorStateList(this,R.color.secondaryLightColor));
ViewCompat.setBackground(linearLayout,shapeDrawable);
Uwaga :: wymaga wersji 1.1.0 biblioteki komponentów materiałów.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="@dimen/_10sdp"
android:shape="rectangle">
<solid android:color="@color/header" />
<corners
android:bottomLeftRadius="@dimen/_5sdp"
android:bottomRightRadius="@dimen/_5sdp"
android:topLeftRadius="@dimen/_5sdp"
android:topRightRadius="@dimen/_5sdp" />