我正在使用曲线裁剪图像侧圆角想要删除图像中出现的线条,如您在帖子中提到的输出屏幕中所示
使用quadTo裁剪圆弧形状,然后应用arcTo使角落变圆(平滑),如参考图像中所述
参考图片
ArcLayout.java
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
public class ArcLayout extends FrameLayout {
private ArcLayoutSettings settings;
private int height = 0;
private int width = 0;
private Path clipPathOne;
private Path clipPathOneCurv;
private Path clipPathTwoCurv;
private float radius = 0, margin = 0;
private final RectF cornerRect = new RectF();
public ArcLayout(Context context) {
super(context);
init(context, null);
}
public ArcLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public void init(Context context, AttributeSet attrs) {
settings = new ArcLayoutSettings(context, attrs);
settings.setElevation(ViewCompat.getElevation(this));
radius = settings.getRadius();
margin = settings.getMargin();
/**
* If hardware acceleration is on (default from API 14), clipPathOne worked correctly
* from API 18.
*
* So we will disable hardware Acceleration if API < 18
*
* https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported
* Section #Unsupported Drawing Operations
*/
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
private Path createClipPath(int position) {
final Path path = new Path();
float arcHeight = settings.getArcHeight();
switch (position) {
case ArcLayoutSettings.POSITION_BOTTOM_CURVE:
if (settings.isCropInside()) {
path.moveTo(0, 0);
path.lineTo(0, height);
path.quadTo(width / 2, height - 2 * arcHeight, width, height);
path.lineTo(width, 0);
path.close();
} else {
path.moveTo(0, 0);
path.lineTo(0, height - arcHeight);
path.quadTo(width / 2, height + arcHeight, width, height - arcHeight);
path.lineTo(width, 0);
path.close();
}
break;
case ArcLayoutSettings.POSITION_TOP_CURVE:
if (settings.isCropInside()) {
path.moveTo(0, height);
path.lineTo(0, 0);
path.quadTo(width / 2, 2 * arcHeight, width, 0);
path.lineTo(width, height);
path.close();
} else {
path.moveTo(0, arcHeight);
path.quadTo(width / 2, -arcHeight, width, arcHeight);
path.lineTo(width, height);
path.lineTo(0, height);
path.close();
}
break;
case ArcLayoutSettings.POSITION_LEFT_CURVE:
if (settings.isCropInside()) {
path.moveTo(width, 0);
path.lineTo(0, 0);
path.quadTo(arcHeight * 2, height / 2, 0, height);
path.lineTo(width, height);
path.close();
} else {
path.moveTo(width, 0);
path.lineTo(arcHeight, 0);
path.quadTo(-arcHeight, height / 2, arcHeight, height);
path.lineTo(width, height);
path.close();
}
break;
case ArcLayoutSettings.POSITION_RIGHT_CURVE:
if (settings.isCropInside()) {
path.moveTo(0, 0);
path.lineTo(width, 0);
path.quadTo(width - arcHeight * 2, height / 2, width, height);
path.lineTo(0, height);
path.close();
} else {
path.moveTo(0, 0);
path.lineTo(width - arcHeight, 0);
path.quadTo(width + arcHeight, height / 2, width - arcHeight, height);
path.lineTo(0, height);
path.close();
}
break;
}
return path;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
calculateLayout();
}
}
private void calculateLayout() {
if (settings == null) {
return;
}
height = getMeasuredHeight();
width = getMeasuredWidth();
if (radius > 0 && settings.getRounded_corn_pos() != ArcLayoutSettings.POSITION_DEFAULT) {
clipPathOne = roundedCornerPath(radius, settings.getRounded_corn_pos());
}
if (settings.getArc_position() != ArcLayoutSettings.POSITION_DEFAULT) {
if (width > 0 && height > 0) {
if (settings.getArc_position() == ArcLayoutSettings.POSITION_BOTH_CURVE) {
clipPathOneCurv = createClipPath(ArcLayoutSettings.POSITION_TOP_CURVE);
clipPathTwoCurv = createClipPath(ArcLayoutSettings.POSITION_BOTTOM_CURVE);
} else {
clipPathOneCurv = createClipPath(settings.getArc_position());
}
ViewCompat.setElevation(this, settings.getElevation());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !settings.isCropInside()) {
ViewCompat.setElevation(this, settings.getElevation());
setOutlineProvider(new ViewOutlineProvider() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void getOutline(View view, Outline outline) {
if (settings.getArc_position() == ArcLayoutSettings.POSITION_BOTH_CURVE) {
outline.setConvexPath(clipPathOneCurv);
outline.setConvexPath(clipPathTwoCurv);
} else {
//outline.setConvexPath(clipPathOneCurv);
}
}
});
}
}
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
super.dispatchDraw(canvas);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
if (clipPathOneCurv != null) {
canvas.drawPath(clipPathOneCurv, paint);
}
if (clipPathTwoCurv != null) {
canvas.drawPath(clipPathTwoCurv, paint);
}
if (clipPathOne != null) {
canvas.drawPath(clipPathOne, paint);
}
canvas.restoreToCount(saveCount);
}
private Path roundedCornerPath(float cornerRadius, int position) {
//path.rewind();
Path path = new Path();
final int width = getWidth();
final int height = getHeight();
final float twoRadius = cornerRadius * 2;
cornerRect.set(-cornerRadius, -cornerRadius, cornerRadius, cornerRadius);
if (position == ArcLayoutSettings.POSITION_TOP_LEFT_ROUND || position == ArcLayoutSettings.POSITION_TOP_ROUND || position == ArcLayoutSettings.POSITION_ALL_ROUND) {
cornerRect.offsetTo(0f, settings.getArcHeight());
path.arcTo(cornerRect, 180f, 90f);
} else {
path.moveTo(0f, 0f);
}
if (position == ArcLayoutSettings.POSITION_TOP_RIGHT_ROUND || position == ArcLayoutSettings.POSITION_TOP_ROUND || position == ArcLayoutSettings.POSITION_ALL_ROUND) {
cornerRect.offsetTo(width - twoRadius, settings.getArcHeight());
path.arcTo(cornerRect, 270f, 90f);
} else {
path.lineTo(width, 0f);
}
if (position == ArcLayoutSettings.POSITION_BOTTOM_RIGHT_ROUND || position == ArcLayoutSettings.POSITION_BOTTOM_ROUND || position == ArcLayoutSettings.POSITION_ALL_ROUND) {
cornerRect.offsetTo(width - twoRadius, height - twoRadius);
path.arcTo(cornerRect, 0f, 90f);
} else {
path.lineTo(width, height);
}
if (position == ArcLayoutSettings.POSITION_BOTTOM_LEFT_ROUND || position == ArcLayoutSettings.POSITION_BOTTOM_ROUND || position == ArcLayoutSettings.POSITION_ALL_ROUND) {
cornerRect.offsetTo(0f, height - twoRadius);
path.arcTo(cornerRect, 90f, 90f);
} else {
path.lineTo(0f, height);
}
path.close();
return path;
}
}
rounded_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="match_parent"
android:layout_centerInParent="true"
android:background="@color/colorPrimaryDark"
android:gravity="center">
<com.example.sonalsb.curved_img.ArcLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:arc_cropDirection="cropOutside"
app:corner_position="top"
app:corner_radius="12dp"
app:arc_position="top"
app:arc_height="8dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:adjustViewBounds="true"
android:src="@drawable/cartoon" />
</com.example.sonalsb.curved_img.ArcLayout>
</RelativeLayout>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ArcLayout">
<attr name="arc_height" format="dimension" />
<attr name="corner_radius" format="dimension"/>
<attr name="corner_margin" format="dimension"/>
<attr name="corner_position">
<enum name="top_left" value="0"/>
<enum name="top_right" value="1"/>
<enum name="bottom_left" value="2"/>
<enum name="bottom_right" value="3"/>
<enum name="top" value="4"/>
<enum name="bottom" value="5"/>
<enum name="left" value="6"/>
<enum name="right" value="7"/>
<enum name="empty" value="9"/>
<enum name="all" value="10"/>
</attr>
<attr name="arc_position">
<enum name="bottom" value="0"/>
<enum name="top" value="1"/>
<enum name="left" value="2"/>
<enum name="right" value="3"/>
<enum name="both" value="4"/>
<enum name="empty" value="99"/>
</attr>
<attr name="arc_cropDirection" format="enum">
<enum name="cropInside" value="0" />
<enum name="cropOutside" value="1"/>
</attr>
</declare-styleable>
</resources>
输出