自定义View实现水波纹下载进度效果

本文介绍了一种自定义Android视图WaveView的实现方法,该视图可以绘制波动效果并显示进度百分比。通过XML属性自定义波浪的颜色、高度、宽度及文字样式。

attr属性声明文件:

 <declare-styleable name="WaveView">
        <attr name="wave_text_color" format="color|reference"></attr>
        <attr name="wave_text_size" format="dimension"></attr>
        <attr name="wave_width" format="dimension"></attr>
        <attr name="wave_high" format="dimension"></attr>
        <attr name="wave_color" format="color|reference"></attr>
    </declare-styleable>
自定WaveView类:

package com.baiboss.test.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;

import com.baiboss.test.R;

/**
 * Created by zq on 2018/1/22.
 */

public class WaveView extends View {
    private int mWidth;
    private int mHeight;

    private Path mPath;
    private Paint mPathPaint;
    private Paint mTextPaint;

    private static final int WAVE_SPEED = 3;//移动速度
    private int mWaveHigh = 30;// 振幅
    private int mWaveWidth = 100;// 波长

    private float maxProgress = 1000;
    private float currentProgress = 0;
    private float currentY;

    private int distance;
    private Bitmap backgroundBitmap;

    public WaveView(Context context) {
        this(context, null, 0);
    }

    public WaveView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WaveView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int index = typedArray.getIndex(i);
            switch (index) {
                case R.styleable.WaveView_wave_text_color:
                    mTextPaint.setColor(typedArray.getColor(index, Color.BLACK));
                    break;
                case R.styleable.WaveView_wave_text_size:
                    mTextPaint.setTextSize(typedArray.getDimension(index, 40));
                    break;
                case R.styleable.WaveView_wave_color:
                    mPathPaint.setColor(typedArray.getColor(index, Color.GREEN));
                    break;
                case R.styleable.WaveView_wave_high:
                    mWaveHigh = typedArray.getDimensionPixelSize(index, 30);
                    break;
                case R.styleable.WaveView_wave_width:
                    mWaveWidth = typedArray.getDimensionPixelSize(index, 100);
                    break;
            }
        }
        typedArray.recycle();
    }

    public void setMaxProgress(int maxProgress) {
        this.maxProgress = maxProgress;
    }

    public void setCurrentProgress(int currentProgress) {
        this.currentProgress = currentProgress;
        if (currentProgress <= maxProgress) {
            invalidate();
        }
    }

    private void init() {
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPath = new Path();
        mPathPaint = new Paint();
        mPathPaint.setAntiAlias(true);
        mPathPaint.setStyle(Paint.Style.FILL);
        backgroundBitmap = getBackgroundBitmap();
    }

    private Bitmap getBackgroundBitmap() {
        Drawable background = getBackground();
        if (background != null) {
            if (background instanceof BitmapDrawable) {
                return ((BitmapDrawable) background).getBitmap();
            }
            int intrinsicWidth = background.getIntrinsicWidth();
            int intrinsicHeight = background.getIntrinsicHeight();
            Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            background.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
            background.draw(canvas);
            return bitmap;
        }
        return null;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }

    private Bitmap createBitmap() {
        Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);

        /**
         * currentMidY与currentY的关系:
         * currentMidY变化区间为[mHeight,0]
         * currentY变化区间为[mHeight + mWaveHigh / 2,-mWaveHigh / 2]
         */
        float currentMidY = mHeight * (maxProgress - currentProgress) / maxProgress;
        currentY = currentMidY + currentMidY * mWaveHigh / mHeight - mWaveHigh / 2;

        /**
         *   当mWidth不是波长的整数倍时,需要+1
         *   当波纹平移时,会额外需要一个波长作为过渡,再+1
         */
        int waveNum = mWidth / mWaveWidth + 2;

        mPath.reset();
        mPath.moveTo(0 - distance, currentY);
        int num = 0;
        for (int i = 0; i < waveNum; i++) {
            mPath.quadTo(mWaveWidth * (num + 1) / 4 - distance, currentY - mWaveHigh, mWaveWidth * (num + 2) / 4 - distance, currentY);
            mPath.quadTo(mWaveWidth * (num + 3) / 4 - distance, currentY + mWaveHigh, mWaveWidth * (num + 4) / 4 - distance, currentY);
            num += 4;
        }
        distance += WAVE_SPEED;
        distance = distance % mWaveWidth;
        mPath.lineTo(mWidth, mHeight);
        mPath.lineTo(0, mHeight);
        mPath.close();
        canvas.drawPath(mPath, mPathPaint);
        if (backgroundBitmap != null) {
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
            Bitmap scale = Bitmap.createScaledBitmap(backgroundBitmap, this.mWidth, mHeight, false);
            canvas.drawBitmap(scale, 0, 0, paint);
        } else {
            throw new RuntimeException("background is null");
        }
        String percent = currentProgress * 100f / maxProgress + "%";
        float textwidth = mTextPaint.measureText(percent);
        Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
        float y = (mHeight - (fontMetricsInt.bottom - fontMetricsInt.top)) / 2 - fontMetricsInt.top;
        canvas.drawText(percent, (mWidth - textwidth) / 2, y, mTextPaint);
        return bitmap;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(createBitmap(), 0, 0, null);
    }
}

layout布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
              android:padding="16dp">

    <com.baiboss.test.widget.WaveView
        android:id="@+id/waveview"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@drawable/wave_bg"
        app:wave_color="@color/wave_color"
        app:wave_high="@dimen/wave_high"
        app:wave_text_color="@color/login_text_color"
        app:wave_text_size="@dimen/verification_code_size"
        app:wave_width="@dimen/wave_width"/>

</LinearLayout>
MainActivity主要部分:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);

    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mWaveView.setCurrentProgress(progress++);
            handler.postDelayed(this, 100);
        }
    }, 100);
}

效果:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值