带波浪动画的心形进度条示例

本文介绍了一种基于Android的心形进度条动画实现方法,通过自定义View绘制心形及内部填充进度,实现了升降动画及波浪效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该程序能够根据相应的百分比示数,对心形进度条进行填充。填充过程有升降动画,并在填充水柱的表面有持续性的波浪效果。

首先来看一下效果图:(初学markdown,不知道怎么缩小图片,悲剧中)

这里写图片描述

下面是布局文件的xml代码,可以看出,中间图像的所有内容都在HeartWave当中。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.baili.bluetest.HeartWave
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/heartWave"
        />

    <EditText
        android:layout_width="170dp"
        android:layout_height="72dp"
        android:layout_marginTop="365dp"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:inputType="number"
        android:textSize="25dp"
        android:gravity="center"
        android:background="@drawable/edit_text_border"
        android:id="@+id/etNum"
        />

    <Button
        android:layout_width="180dp"
        android:layout_height="80dp"
        android:layout_marginTop="360dp"
        android:layout_marginLeft="180dp"
        android:text="Submit"
        android:textSize="30dp"
        android:onClick="btSub"
        />

</RelativeLayout>

接下来是heartwave的代码。大概的原理是,我先画一个心形,心形的下面有一个画布,当它上移时,让内容裁剪到心形当中。最后再在画布的顶端画一个随时间不断前移的波浪。

public class HeartWave extends View {

    int i=0;
    int score=60;
    Paint paint = new Paint();

    public HeartWave(Context context, AttributeSet attrs) {
        super(context, attrs);  //没有第二项就无法在layout里加载
    }

    @Override
    protected void onDraw(Canvas canvas){

        paint.setTextSize(150);
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setTypeface(Typeface.SANS_SERIF); //共有3种字体,这一种还算华丽
        canvas.drawColor(Color.WHITE);  //先把底部画成白色
        canvas.translate(540, 400);

        Path path = new Path(); //path的定义一定要写在onDraw里面,不然动画会非常慢,原因不明
        Path path1 = new Path();
        Path path2 = new Path();

        float x;
        float y;
        path.moveTo(-400, 0);
        for(x=-2;x<=2;x+=0.001){
            y=-(float)Math.sqrt(-x*x + 2*Math.abs(x));
            path.lineTo(200*x,200*y);
        }
        path.lineTo(400, 0);    //为了解决for循环画不到边缘的问题

        path.moveTo(-400, 0);
        for(x=-2;x<=2;x+=0.001){
            y=(float)Math.sqrt(1-Math.sqrt(Math.abs(x)/2))*3;
            path.lineTo(200*x,200*y);
        }
        path.lineTo(400, 0);
        canvas.clipPath(path);  //总体来说clipPath比较智能,能够神奇地把图像向内侧切

        canvas.translate(0, -score * 8);    //这一招很绝妙,哈哈
        paint.setColor(Color.MAGENTA);
        paint.setStyle(Paint.Style.FILL);
        path1.moveTo(-400, 1400);   //在容器的最下方放一个大的方形,上涨多少就往上拉多少
        path1.lineTo(400, 1400);
        path1.lineTo(400, 600);
        path1.lineTo(-400, 600);
        path1.lineTo(-400, 1400);
        canvas.drawPath(path1, paint);

        canvas.translate(i, 0); //每次前进一点点,形成动画。放弃属性动画,太麻烦
        path2.moveTo(-1200, 600);
        path2.quadTo(-1000, 570, -800, 600);
        path2.moveTo(-400, 600);
        path2.quadTo(-200, 570, 0, 600);
        canvas.drawPath(path2, paint);  //这里不得不感叹,fillPath太智能了,能够非常准确地填充图形内侧

        paint.setColor(Color.WHITE);    //下半弧的颜色要与背景色相同
        path2.reset();
        path2.moveTo(-800, 600);
        path2.quadTo(-600, 630, -400, 600);
        path2.moveTo(0, 600);
        path2.quadTo(200, 630, 400, 600);
        canvas.drawPath(path2, paint);

        canvas.translate(-i, score * 8);    //最后绘制心形,这是为了让动画不要覆盖内边框,画之前当然要先返回中心
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        paint.setColor(Color.RED);
        canvas.drawPath(path, paint);

        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.GREEN);
        canvas.drawText("" + score, 0, 200, paint);

        i += 5;
        if (i==800) i=0;

        postInvalidateDelayed(1);   //在view内部循环相当于使用了surfaceView的独立线程,至今未遇到被破坏的情形
    }

}

最后是java部分,这部分的功能很简单,只是负责当submit后数值传递到view

public class MainActivity extends Activity {

    private static final String ACTIVITY_TAG="MainActivity";
    private HeartWave heartWave;
    private EditText etNum;
    private int percent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.heart);
        heartWave = (HeartWave) findViewById(R.id.heartWave);
        etNum = (EditText) findViewById(R.id.etNum);
    }

    //没有这一段,标题栏就不会有“更多”按钮
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    public void btSub(View view){
        if (etNum.getText().toString().equals("")){
            return;
        }
        percent = Integer.parseInt(etNum.getText().toString().trim());
        etNum.setText("");

        if (percent > 100) return;   //小于0做不到,就不加了

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {

                while (heartWave.score != percent){
                    if (heartWave.score < percent){
                        heartWave.score += 1;
                    }

                    else if (heartWave.score > percent){
                        heartWave.score -= 1;
                    }

                    try {
                        Thread.sleep(30);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
    }

}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值