You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java/Kotlin实现上下带曲线的自定义ImageView(ViewPager滑块)

实现上下边缘带曲线的自定义ImageView

Expected Imageview UI

我正在使用ViewPager制作图片滑块,需要为列表项创建一个上下边缘带曲线的自定义ImageView,并且通过API加载图片。目前已能实现自定义矩形ImageView,但不清楚如何添加上下曲线。

已尝试方案:

  • 将SVG设置给ImageView,但动态设置图片后形状发生变化。
  • 继承ImageView实现自定义图片类。

解决方案一:通过ClipPath自定义View实现

直接继承AppCompatImageView,通过裁剪画布实现曲线边缘,适配动态加载的图片:

public class CurvedEdgeImageView extends androidx.appcompat.widget.AppCompatImageView {
    private Path mPath;
    private float mCurveHeight = 30f; // 曲线高度,可根据需求调整

    public CurvedEdgeImageView(Context context) {
        super(context);
        init();
    }

    public CurvedEdgeImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CurvedEdgeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPath = new Path();
        // 关闭硬件加速,避免部分设备ClipPath失效
        setLayerType(LAYER_TYPE_SOFTWARE, null);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mPath.reset();
        // 绘制上边缘二次贝塞尔曲线
        mPath.moveTo(0, mCurveHeight);
        mPath.quadTo(w / 2f, 0, w, mCurveHeight);
        // 连接右侧直线
        mPath.lineTo(w, h - mCurveHeight);
        // 绘制下边缘二次贝塞尔曲线
        mPath.quadTo(w / 2f, h, 0, h - mCurveHeight);
        // 连接左侧直线并闭合路径
        mPath.lineTo(0, mCurveHeight);
        mPath.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.clipPath(mPath);
        super.onDraw(canvas);
    }

    // 提供方法动态调整曲线高度
    public void setCurveHeight(float curveHeight) {
        mCurveHeight = curveHeight;
        requestLayout();
    }
}

使用时直接在布局xml中引用该类,正常用Glide/Picasso加载图片即可,无需额外处理形状适配。


解决方案二:使用ShapeDrawable作为遮罩

如果不想自定义View,可通过给ImageView设置遮罩实现曲线边缘:

  1. 创建遮罩Shape文件(res/drawable/curved_mask.xml):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFFFF"/>
    <path
        android:pathData="M0,30 Q@width/2,0 @width,30 L@width,@height-30 Q@width/2,@height 0,@height-30 Z"/>
</shape>

注:@width和@height需替换为实际ImageView的宽高,或在代码中动态生成PathData。

  1. 代码中设置遮罩:
ImageView imageView = findViewById(R.id.your_image_view);
imageView.setClipToOutline(true);
imageView.setOutlineProvider(new ViewOutlineProvider() {
    @Override
    public void getOutline(View view, Outline outline) {
        Drawable mask = ContextCompat.getDrawable(view.getContext(), R.drawable.curved_mask);
        if (mask != null) {
            mask.setBounds(0, 0, view.getWidth(), view.getHeight());
            mask.getOutline(outline);
        }
    }
});

API 30及以上可直接在xml中配置:

<ImageView
    android:id="@+id/your_image_view"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:clipToOutline="true"
    android:outlineProvider="@drawable/curved_mask"/>

注意事项

  • ClipPath方案需关闭硬件加速,避免部分设备出现显示异常
  • 使用图片加载框架时,确保ImageView尺寸已确定后再加载图片,避免路径计算错误
  • 可通过自定义属性将曲线高度开放到xml中,提升复用性

内容的提问来源于stack exchange,提问作者Rakesh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 02:58:14