使用裁剪来圆化 ViewGroup 的角
Use clipping to round corners of ViewGroup
我有一个 RelativeLayout
需要圆化左上角和右上角。我可以使用在 XML 中定义的带有角 topLeftRadius 和 topRightRadius 的可绘制背景来执行此操作。但是......这个RelativeLayout
还需要有一个背景,它是一个带有平铺位图和形状组合的图层列表,并且平铺位图在drawable XML中没有角参数。所以我的想法是用下面的代码制作一个RelativeLayout
:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
path.reset();
rect.set(0, 0, w, h);
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
path.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
遗憾的是没有剪裁发生,我期待它剪裁我的 RelativeLayout 的所有四个角,但没有发生任何事情。 "onSizeChanged" 和 "dispatchDraw" 方法都被调用了,我测试过。我也试过关闭硬件加速,但没有任何效果。
我的 RelativeLayout
是更大布局的一部分,该布局在 FrameLayout
的子类中膨胀,然后该子类在 RecyclerView
中连续使用,如果这改变了一切。
已定义此布局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<com.playground.RoundedRelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="@color/colorPrimary" />
</FrameLayout>
其中 RoundedRelativeLayout
具有以下实现:
public class RoundedRelativeLayout extends RelativeLayout {
private RectF rectF;
private Path path = new Path();
private float cornerRadius = 15;
public RoundedRelativeLayout(Context context) {
super(context);
}
public RoundedRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
rectF = new RectF(0, 0, w, h);
resetPath();
}
@Override
public void draw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.draw(canvas);
canvas.restoreToCount(save);
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
private void resetPath() {
path.reset();
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
path.close();
}
}
您将获得以下输出:
实施是无耻地从 RoundKornerLayouts 项目中窃取的。
这是 azizbekian 答案的 Kotlin 版本:
class RoundedRelativeLayout(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {
private lateinit var rectF: RectF
private val path = Path()
private var cornerRadius = 15f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
rectF = RectF(0f, 0f, w.toFloat(), h.toFloat())
resetPath()
}
override fun draw(canvas: Canvas) {
val save = canvas.save()
canvas.clipPath(path)
super.draw(canvas)
canvas.restoreToCount(save)
}
override fun dispatchDraw(canvas: Canvas) {
val save = canvas.save()
canvas.clipPath(path)
super.dispatchDraw(canvas)
canvas.restoreToCount(save)
}
private fun resetPath() {
path.reset()
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW)
path.close()
}
}
编辑
作为奖励,这里介绍了如何将 cornerRadius 添加为您可以设置的额外 xml 属性,只需将其添加到 res/values/styleable.xml
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundedRelativeLayout">
<attr name="cornerRadius" format="float"/>
</declare-styleable>
</resources>
然后将此初始化方法添加到 RoundedRelativeLayout
class:
init {
val ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundedRelativeLayout)
cornerRadius = ta.getFloat(R.styleable.RoundedRelativeLayout_cornerRadius, 15f)
ta.recycle()
}
现在,当您使用布局时,您可以像这样在 xml 中设置 cornerRadius:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" <-- Make sure you include this line
android:layout_width="80dp"
android:layout_height="80dp">
.
.
.
<your.package.name.RoundedRelativeLayout
android:id="@+id/roundedRect"
app:cornerRadius="24"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
.
.
.
我有一个 RelativeLayout
需要圆化左上角和右上角。我可以使用在 XML 中定义的带有角 topLeftRadius 和 topRightRadius 的可绘制背景来执行此操作。但是......这个RelativeLayout
还需要有一个背景,它是一个带有平铺位图和形状组合的图层列表,并且平铺位图在drawable XML中没有角参数。所以我的想法是用下面的代码制作一个RelativeLayout
:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
path.reset();
rect.set(0, 0, w, h);
path.addRoundRect(rect, radius, radius, Path.Direction.CW);
path.close();
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
遗憾的是没有剪裁发生,我期待它剪裁我的 RelativeLayout 的所有四个角,但没有发生任何事情。 "onSizeChanged" 和 "dispatchDraw" 方法都被调用了,我测试过。我也试过关闭硬件加速,但没有任何效果。
我的 RelativeLayout
是更大布局的一部分,该布局在 FrameLayout
的子类中膨胀,然后该子类在 RecyclerView
中连续使用,如果这改变了一切。
已定义此布局:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorAccent">
<com.playground.RoundedRelativeLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="@color/colorPrimary" />
</FrameLayout>
其中 RoundedRelativeLayout
具有以下实现:
public class RoundedRelativeLayout extends RelativeLayout {
private RectF rectF;
private Path path = new Path();
private float cornerRadius = 15;
public RoundedRelativeLayout(Context context) {
super(context);
}
public RoundedRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
rectF = new RectF(0, 0, w, h);
resetPath();
}
@Override
public void draw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.draw(canvas);
canvas.restoreToCount(save);
}
@Override
protected void dispatchDraw(Canvas canvas) {
int save = canvas.save();
canvas.clipPath(path);
super.dispatchDraw(canvas);
canvas.restoreToCount(save);
}
private void resetPath() {
path.reset();
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
path.close();
}
}
您将获得以下输出:
实施是无耻地从 RoundKornerLayouts 项目中窃取的。
这是 azizbekian 答案的 Kotlin 版本:
class RoundedRelativeLayout(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs) {
private lateinit var rectF: RectF
private val path = Path()
private var cornerRadius = 15f
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
rectF = RectF(0f, 0f, w.toFloat(), h.toFloat())
resetPath()
}
override fun draw(canvas: Canvas) {
val save = canvas.save()
canvas.clipPath(path)
super.draw(canvas)
canvas.restoreToCount(save)
}
override fun dispatchDraw(canvas: Canvas) {
val save = canvas.save()
canvas.clipPath(path)
super.dispatchDraw(canvas)
canvas.restoreToCount(save)
}
private fun resetPath() {
path.reset()
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW)
path.close()
}
}
编辑
作为奖励,这里介绍了如何将 cornerRadius 添加为您可以设置的额外 xml 属性,只需将其添加到 res/values/styleable.xml
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundedRelativeLayout">
<attr name="cornerRadius" format="float"/>
</declare-styleable>
</resources>
然后将此初始化方法添加到 RoundedRelativeLayout
class:
init {
val ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundedRelativeLayout)
cornerRadius = ta.getFloat(R.styleable.RoundedRelativeLayout_cornerRadius, 15f)
ta.recycle()
}
现在,当您使用布局时,您可以像这样在 xml 中设置 cornerRadius:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" <-- Make sure you include this line
android:layout_width="80dp"
android:layout_height="80dp">
.
.
.
<your.package.name.RoundedRelativeLayout
android:id="@+id/roundedRect"
app:cornerRadius="24"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
.
.
.