Как сделать в Android с закругленными углами


Я пытаюсь сделать вид в 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 внутри вышеупомянутого макета, углы изображения не обрезаются/обрезаются, вместо этого он выглядит полным.

Я видел другие обходные пути, чтобы заставить его работать, как пояснил здесь.

но есть ли способ установить закругленные углы для вида и всех его дочерние представления содержатся в том главном представлении, которое округлено углы?

спасибо.

15 51

15 ответов:

другой подход заключается в создании пользовательского класса макета, как показано ниже. Этот макет сначала рисует свое содержимое в закадровом растровом изображении, маскирует закадровое растровое изображение округлым прямоугольником, а затем рисует закадровое растровое изображение на фактическом холсте.

я попробовал это, и это, кажется, работает (по крайней мере для моих простых тестах). Это, конечно, повлияет на производительность по сравнению с обычным макетом.

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>

формы.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>

или вы можете использовать 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>

Если у вас возникли проблемы при добавлении касания слушателей к макету. Используйте этот макет в качестве родительского макета.

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>

создать xml-раунд.xml в drawable folder

<solid android:color="#FFFFFF" />
<stroke android:width=".05dp" android:color="#d2d2d2" />
<corners android:topLeftRadius="5dp" android:topRightRadius="5dp" android:bottomLeftRadius="5dp" android:bottomRightRadius="5dp" />

затем использовать круглый.XML в качестве фона для элементов..Тогда это даст закругленные углы

В Android L вы сможете просто использовать вид.setClipToOutline чтобы получить этот эффект. В предыдущих версиях не было возможности просто обрезать содержимое случайной группы просмотра в определенной форме.

вам придется придумать что-то, что даст вам подобный эффект:

  • Если вам нужны только закругленные углы в ImageView, вы можете использовать шейдер, чтобы "нарисовать" изображение поверх формы, которую вы используете в качестве фона. Взгляните на библиотека для примера.

  • Если вам действительно нужно, чтобы каждый ребенок был обрезан, может быть, вы можете еще один вид на ваш макет? Один с фоном любого цвета, который вы используете, и круглое "отверстие" в середине? Вы можете фактически создать пользовательскую группу представлений, которая рисует эту фигуру над каждым дочерним элементом, переопределяющим метод onDraw.

ответ Яапа Ван Хенгстума отлично работает, однако я думаю, что это дорого, и если мы применяем этот метод на кнопку, например, сенсорный эффект теряется, так как вид отображается как растровое изображение.

для меня лучший метод и самый простой заключается в применении маски на вид, например:

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

этот учебник и вся дискуссия под ним - http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/

согласно этому посту, написанному Гаем Роменом, одним из ведущих разработчиков всего инструментария Android UI, можно сделать контейнер (и все его дочерние представления) с закругленными углами, но он объяснил, что это слишком дорого (из выступлений рендеринга вопросы.)

Я буду рекомендовать вам идти в соответствии со своим постом, и если вы хотите закругленные углы, то реализовать закругленные углы ImageView согласно этому сообщению. затем вы можете поместить его в контейнер с любым фоном, и вы получите желаемый эффект.

это то, что я сделал также и в конце концов.

ссылка на учебник, которую вы предоставили, похоже, предполагает, что вам нужно установить свойства layout_width и layout_height ваших дочерних элементов в match_parent.

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

попробуйте это свойство с вашей линейной компоновкой это поможет
инструменты:контекст=".ваша активность"

отличие от ответа Яапа Ван Хенгстума:

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

The CardView работал для меня в API 27 В Android Studio 3.0.1. Элемент colorPrimary была ссылка в res/values/colors.xml файл и это только пример. Для layout_width из 0dp он будет растягиваться до ширины родителя. Вам нужно будет настроить ограничения и ширину / высоту в соответствии с вашими потребностями.

<android.support.v7.widget.CardView
    android:id="@+id/cardView"
    android:layout_width="0dp"
    android:layout_height="200dp"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:layout_marginTop="8dp"
    app:cardCornerRadius="4dp"
    app:cardBackgroundColor="@color/colorPrimary">

    <!-- put your content here -->

</android.support.v7.widget.CardView>
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;
    }

использовать форму в xml с прямоугольником.установите свойство нижнего или верхнего радиуса как хотите.затем примените этот xml в качестве фона для представления ur....или...использовать градиенты, чтобы сделать это из кода.