Android裁剪图片为波浪形或者曲线形的ImageView

如果需要做一个自定义的波浪效果的进度条,裁剪图片,对ImageView的图片进行裁剪,比如下面2张图,如何实现?

先看下面的效果,看到其实只需要对第一张高亮的图片进行处理即可,灰色状态的作为背景图。

1、实现原理

这里首先想到的是自定义View,在Android中,使用二阶贝塞尔曲线可以实现我们想要的效果,关键的API就是Path的rQuadTo()方法。

java 复制代码
        path.reset()
        path.moveTo(startX, startY)
        path.quadTo(currentX, currentY, endX, endY)
        canvas.drawPath(path, curvePaint)

startX和startY,endX和endY为两个固定点,currentX和currentY就是控制点,通过改变控制点的位置来改变二阶贝塞尔曲线的形状。

a点和b点就是固定点,c点是控制点,我们可以改变c点的位置来改变曲线的形状。

在Android中,使用cubicTo来实现三阶贝塞尔。上面的需求方案使用二阶即可实现,三阶的可以自行了解。

2、事例分析

首先可以通过自定义一个贝赛尔曲线来了解一下:

java 复制代码
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
 
public class BezierCurveView extends View {
 
    private Paint paint;
    private Path path;
 
    public BezierCurveView(Context context) {
        super(context);
        init();
    }
 
    public BezierCurveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
 
    public BezierCurveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    private void init() {
        paint = new Paint();
        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5f);
 
        path = new Path();
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
 
        int height = getHeight();
        int width = getWidth();

        float dx = width / 4f;
        float dy = height / 4f;

        path.reset();

        path.moveTo(0, height / 2f); // 起点

        path.rQuadTo(dx, -dy, 2 * dx, 0f); // 控制点、终点
        path.rQuadTo(dx, dy, 2 * dx, 0f); // 控制点、终点
        path.rQuadTo(dx, -dy, 2 * dx, 0f); // 控制点、终点
        path.rQuadTo(dx, dy, 2 * dx, 0f); // 控制点、终点

        path.lineTo(width, height);
        path.lineTo(0f, height);

        path.close();
        canvas.drawPath(path, paint);
    }
}

要使用这个自定义View,你需要在布局文件中引用一下:

XML 复制代码
<com.example.myapp.BezierCurveView
    android:layout_width="match_parent"
    android:layout_height="200dp" />

看下效果:

3、使用贝塞尔裁剪ImageView

1)自定义ImageView

java 复制代码
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.util.AttributeSet;

import androidx.annotation.Nullable;

/**
 * 图片裁剪为贝赛尔曲线边缘ImageView
 */
public class BezierImageView extends androidx.appcompat.widget.AppCompatImageView {
    private static final String TAG = "ArcImageView";

    public BezierImageView (Context context) {
        this(context, null);
    }

    public BezierImageView (Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BezierImageView (Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        path = new Path();
    }

    private final Path path;
    /**
     * 进度值(0~100),默认100
     */
    private int progress = 100;

    public void setProgress(int progress) {
        this.progress = progress;
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        final int width = getWidth();
        final int height = getHeight();

        float dx = width / 4f;
        float dy = height / 8f;
        float yOffset = (100 - progress) / 100f * height;
        float[] p1 = {0f, yOffset};
        float[] p2 = {dx, -dy};
        float[] p3 = {2 * dx, 0f};
        float[] p4 = {dx, dy};
        float[] p5 = {2 * dx, 0f};
        float[] p6 = {dx, -dy};
        float[] p7 = {2 * dx, 0f};
        float[] p8 = {dx, dy};
        float[] p9 = {2 * dx, 0f};

        path.reset();
        path.moveTo(p1[0], p1[1]);
        path.rQuadTo(p2[0], p2[1], p3[0], p3[1]);
        path.rQuadTo(p4[0], p4[1], p5[0], p5[1]);
        path.rQuadTo(p6[0], p6[1], p7[0], p7[1]);
        path.rQuadTo(p8[0], p8[1], p9[0], p9[1]);

        path.lineTo(width, height);
        path.lineTo(0f, height);

        path.close();
        canvas.clipPath(path);
        super.onDraw(canvas);
    }
}

2)如何使用?

XML 复制代码
    <com.....BezierImageView 
        android:id="@+id/bg"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:scaleType="fitCenter"
        android:src="@drawable/bg_cover" />
java 复制代码
bg.setProgress(50);

直接调用setProgress()方法,传入要裁剪的百分比即可。

效果如下所示:

4、使用sin()正弦函数实现波浪/曲线效果

基于3中,直接修改onDraw()中的关键代码即可:

java 复制代码
@Override
    protected void onDraw(Canvas canvas) {
        final int width = getWidth();
        final int height = getHeight();

        // 变量描述,可自己调整:
        // offset:Y轴偏移量
        // yHeight:可控制Y轴高度(值越小百分比显示越大)
        // countF:控制波数(越大波数越少)
        // xOffset:可控制波长幅度(值越大幅度越大,越小越平缓)
        float offset = 0.1f, yHeight = 1.0f, countF = 60.0f, xOffset = 0.04f;

        // 方案1:
        path.moveTo(0, height);
        for (int i = 0; i < width; i++) {
            path.lineTo(i, (float) (height * ((yHeight - offset) + Math.sin(i / countF + Math.PI) * xOffset)));
        }

        path.lineTo(width, height);
        path.lineTo(0f, height);

        path.close();
        canvas.clipPath(path);
        super.onDraw(canvas);
    }

使用sin()函数结合lineTo()方法,同样可以实现波浪曲线效果:

相关推荐
救救孩子把7 分钟前
深入理解 Java 对象的内存布局
java
落落落sss10 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节16 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭22 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由29 分钟前
速成java记录(上)
java·速成
代码敲上天.31 分钟前
数据库语句优化
android·数据库·adb
一直学习永不止步35 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明35 分钟前
面试知识储备-多线程
java·面试·职场和发展
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript