Androidに角の丸いビューを作成しようとしています。これまでに見つけた解決策は、角が丸い形状を定義し、そのビューの背景として使用することです。
これが私がやったことです、以下に示すようにドロウアブルを定義します
<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を配置すると、画像の角はクリップ/トリミングされず、完全に表示されます。
私は here で説明したような動作をさせるための別の回避策を見ました。
しかし、ビューの角を丸く設定する方法はありますか?そのすべての子ビューは角の丸いメインビュー内に含まれていますか?
ありがとう。
別のアプローチは、以下のようなカスタムレイアウトクラスを作成することです。このレイアウトは、最初にそのコンテンツをオフスクリーンビットマップに描画し、オフスクリーンビットマップを角丸長方形でマスクしてから、実際のキャンバスにオフスクリーンビットマップを描画します。
私はそれを試しましたが、(少なくとも私の簡単なテストケースでは)うまくいくようです。もちろん、通常のレイアウトと比較してパフォーマンスに影響します。
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>
または、次のように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>
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>
そしてあなたの内側のレイアウト
<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>
タッチリスナーをレイアウトに追加するときに問題が発生した場合。このレイアウトを親レイアウトとして使用します。
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>
Jaap van Hengstumの答え はうまく機能しますが、高価であると思います。たとえば、このメソッドをボタンに適用すると、ビューがビットマップとしてレンダリングされるため、タッチ効果が失われます。
私にとって最良の方法で最も簡単な方法は、次のようにビューにマスクを適用することです。
@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);
}
drawable
フォルダーにround.xml
というxmlファイルを作成し、このコンテンツを貼り付けます。
<?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>
次に、round.xml
をbackground
として任意のアイテムに使用します。次に、角を丸くします。
Android Lでは、 View.setClipToOutline を使用してその効果を得ることができます。以前のバージョンでは、ランダムなViewGroupのコンテンツを特定の形状にクリップする方法はありません。
同様の効果が得られるものを考える必要があります。
ImageViewで角丸のみが必要な場合は、シェーダーを使用して、背景として使用している図形に画像を「ペイント」できます。例として this library をご覧ください。
すべての子供を切り抜く必要がある場合、レイアウトを別のビューで表示できますか?使用している色に関係なく背景があり、中央に丸い「穴」がありますか?実際には、onDrawメソッドをオーバーライドするすべての子にその形状を描画するカスタムViewGroupを作成できます。
CardView
は、Android Studio 3.0.1のAPI 27で機能しました。 colorPrimary
はres/values/colors.xml
ファイルで参照されており、単なる例です。 0dp
のlayout_widthの場合、親の幅まで伸びます。必要に応じて、制約と幅/高さを構成する必要があります。
<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>
次のコードを使用して、描画可能フォルダーの下に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>
そして、丸い角の境界線を保持したいビューの背景として、このドロウアブルを保持します。 LinearLayoutのために保持しましょう
<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>
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);
}
}
このチュートリアルとその下のすべての議論に従ってください- http://www.curious-creature.org/2012/12/11/ Android-recipe-1-image-with-rounded-corners /
Android UIツールキット全体の主要な開発者の1人であるGuy Romainが書いたこの投稿によると、角の丸いコンテナ(およびそのすべての子ビュー)を作成することは可能ですが、あまりにも高価です(レンダリングの問題のパフォーマンスから)。
彼の投稿に従って行くことをお勧めします。角を丸くしたい場合は、この投稿に従って丸い角ImageView
を実装します。その後、任意の背景を持つコンテナ内に配置することができ、希望する効果が得られます。
それも最終的に私がやったことです。
提供されたチュートリアルリンクは、子要素のlayout_widthおよびlayout_heightプロパティをmatch_parentに設定する必要があることを示唆しているようです。
<ImageView
Android:layout_width="match_parent"
Android:layout_height="match_parent">
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);
}
}
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;
}