Android-自定義View-水波加載

一枝缔、前言

在學(xué)習(xí)自定義View中布疙,不可避免的遇到貝賽爾曲線,在一頓學(xué)習(xí)操作之后愿卸,成功的實(shí)現(xiàn)了一個(gè)水波加載拐辽,效果圖如下:(本文適用于有貝賽爾曲線基礎(chǔ)的人學(xué)習(xí))


水波加載

二、正文

開始擼代碼

1)首先是實(shí)現(xiàn)思路

1擦酌、水波的實(shí)現(xiàn)俱诸,在接觸貝賽爾曲線之后,用了二階貝賽爾曲線實(shí)現(xiàn)水波效果
2赊舶、加載過程睁搭,通過屬性動(dòng)畫來實(shí)現(xiàn)這個(gè)過程
3赶诊、實(shí)現(xiàn)順序,先實(shí)現(xiàn)水波后實(shí)現(xiàn)加載過程

2)實(shí)現(xiàn)水波

        int two = width / 2;
        //繪制起點(diǎn)
        path.moveTo(-(x - radius) + dx, (y + radius) - dy);
        for (int i = width; i < getWidth() + width; i += width) {
            //在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線园骆,參數(shù)分別為舔痪,控制點(diǎn)與終點(diǎn)
            path.rQuadTo(two / 2, -height, two, 0);
            //在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線,參數(shù)分別為锌唾,控制點(diǎn)與終點(diǎn)
            path.rQuadTo(two / 2, height, two, 0);
        }
        path.lineTo(getWidth(), getHeight());
        path.lineTo(0, getHeight());
        path.close();

3)水波的動(dòng)畫

        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width);
        valueAnimator.setDuration(2000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                dx = (int) valueAnimator.getAnimatedValue();
                refirsh();
            }
        });
        valueAnimator.start();

3)在實(shí)現(xiàn)水波之后锄码,再實(shí)現(xiàn)加載過程的動(dòng)畫

        final int totalCount = (int) ((rectF.bottom - rectF.top));
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1000);
        valueAnimator.setDuration(3000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int progress = (int) valueAnimator.getAnimatedValue();
                //計(jì)算每次的占比情況
                dy = (float) (progress/maxNumber) * totalCount;
                Log.e("TAG", String.valueOf(dy)+"\t"+maxNumber+"\t"+progress);
                //完成之后調(diào)用接口
                if(dy == totalCount){
                    loading.over();
                }

                refirsh();
            }
        });
        valueAnimator.start();

完整代碼

package com.example.myapplication.Widget;

import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import androidx.annotation.Nullable;

public class BesselViewWidget extends View {
    private Paint paint;
    private Path path;

    public BesselViewWidget(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
        start();

    }

    private void init() {
        paint = new Paint();
        paint.setDither(true);
        paint.setDither(true);
        paint.setStrokeWidth(5);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setColor(Color.BLUE);

        path = new Path();

        paintRect = new Paint();
        paintRect.setDither(true);
        paintRect.setDither(true);
        paintRect.setStrokeWidth(5);
        paintRect.setStyle(Paint.Style.STROKE);
        paintRect.setColor(Color.BLACK);

    }

    /**
     * 水波的寬
     */
    private int width = 700;
    /**
     * 水波的高
     */
    private int height = 20;
    /**
     * 水波預(yù)留的寬
     */
    private int dx = 0;
    /**
     * 水波預(yù)留的高
     */
    private float dy = 0;
    /**
     * 中心點(diǎn)
     */
    private int x;
    private int y;

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        x = (right - left) / 2;
        y = (bottom - top) / 2;
        super.onLayout(changed, left, top, right, bottom);
    }

    private Paint paintRect;
    private RectF rectF;
    private int radius = 150;

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {

        rectF = new RectF(x - radius, y - radius, x + radius, y + radius);
        //創(chuàng)建裁剪區(qū)域
        canvas.clipRect(rectF);

        path.reset();

        int two = width / 2;
        //繪制起點(diǎn)
        path.moveTo(-(x - radius) + dx, (y + radius) - dy);
        for (int i = width; i < getWidth() + width; i += width) {
            //在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線,參數(shù)分別為晌涕,控制點(diǎn)與終點(diǎn)
            path.rQuadTo(two / 2, -height, two, 0);
            //在上一個(gè)點(diǎn)的相對位置上繪制二階貝賽爾曲線滋捶,參數(shù)分別為,控制點(diǎn)與終點(diǎn)
            path.rQuadTo(two / 2, height, two, 0);
        }
        path.lineTo(getWidth(), getHeight());
        path.lineTo(0, getHeight());
        path.close();
        //繪制線
        canvas.drawPath(path, paint);
        //繪制區(qū)域
        canvas.drawRect(rectF, paintRect);
    }

    /**
     * 最大的值
     */
    private float maxNumber = 1000;
    
    /**
     * 設(shè)置進(jìn)度(加載的動(dòng)畫)
     * @param loading
     */
    public void setProgress(final Loading loading){
        final int totalCount = (int) ((rectF.bottom - rectF.top));
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1000);
        valueAnimator.setDuration(3000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int progress = (int) valueAnimator.getAnimatedValue();
                //計(jì)算每次的占比情況
                dy = (float) (progress/maxNumber) * totalCount;
                Log.e("TAG", String.valueOf(dy)+"\t"+maxNumber+"\t"+progress);
                //完成之后調(diào)用接口
                if(dy == totalCount){
                    loading.over();
                }

                refirsh();
            }
        });
        valueAnimator.start();

    }

    private void refirsh() {

        postInvalidate();
    }

    /**
     * 水波的動(dòng)畫
     */
    private void start() {
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width);
        valueAnimator.setDuration(2000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                dx = (int) valueAnimator.getAnimatedValue();
                refirsh();
            }
        });
        valueAnimator.start();
    }

    public interface Loading{
        void over();
    }

}

CSDN地址:Android-自定義View-水波加載

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末余黎,一起剝皮案震驚了整個(gè)濱河市重窟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惧财,老刑警劉巖巡扇,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垮衷,居然都是意外死亡厅翔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門搀突,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刀闷,“玉大人,你說我怎么就攤上這事描姚∩” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵轩勘,是天一觀的道長筒扒。 經(jīng)常有香客問我,道長绊寻,這世上最難降的妖魔是什么花墩? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮澄步,結(jié)果婚禮上冰蘑,老公的妹妹穿的比我還像新娘。我一直安慰自己村缸,他們只是感情好祠肥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著梯皿,像睡著了一般仇箱。 火紅的嫁衣襯著肌膚如雪县恕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天剂桥,我揣著相機(jī)與錄音忠烛,去河邊找鬼。 笑死权逗,一個(gè)胖子當(dāng)著我的面吹牛美尸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斟薇,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼师坎,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奔垦?” 一聲冷哼從身側(cè)響起屹耐,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤尸疆,失蹤者是張志新(化名)和其女友劉穎椿猎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寿弱,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犯眠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了症革。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筐咧。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖噪矛,靈堂內(nèi)的尸體忽然破棺而出量蕊,到底是詐尸還是另有隱情,我是刑警寧澤艇挨,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響拾氓,放射性物質(zhì)發(fā)生泄漏纸厉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一脉漏、第九天 我趴在偏房一處隱蔽的房頂上張望苞冯。 院中可真熱鬧,春花似錦侧巨、人聲如沸舅锄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皇忿。三九已至碉怔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間禁添,已是汗流浹背撮胧。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留老翘,地道東北人芹啥。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像铺峭,于是被迫代替她去往敵國和親墓怀。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349