當前位置:網站首頁>手把手講解-一個複雜動效的自定義繪制,【絕對幹貨】

手把手講解-一個複雜動效的自定義繪制,【絕對幹貨】

2021-08-20 04:46:21 Android我愛死你了

 * 構建心形
 * <p>
 * 注意,它這個是以 矩形區域中心點為基准的圖形,所以繪制的時候,必須先把坐標軸移動到 區域中心
 */
private void initHeartPath(Path path) {
    List<PointF> pointList = new ArrayList<>();
    pointList.add(new PointF(0, Utils.dp2px(-38)));
    pointList.add(new PointF(Utils.dp2px(50), Utils.dp2px(-103)));
    pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(-61)));
    pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(-12)));
    pointList.add(new PointF(Utils.dp2px(112), Utils.dp2px(37)));
    pointList.add(new PointF(Utils.dp2px(51), Utils.dp2px(90)));
    pointList.add(new PointF(0, Utils.dp2px(129)));
    pointList.add(new PointF(Utils.dp2px(-51), Utils.dp2px(90)));
    pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(37)));
    pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(-12)));
    pointList.add(new PointF(Utils.dp2px(-112), Utils.dp2px(-61)));
    pointList.add(new PointF(Utils.dp2px(-50), Utils.dp2px(-103)));

    path.reset();
    for (int i = 0; i < 4; i++) {
        if (i == 0) {
            path.moveTo(pointList.get(i * 3).x, pointList.get(i * 3).y);
        } else {
            path.lineTo(pointList.get(i * 3).x, pointList.get(i * 3).y);
        }

        int endPointIndex;
        if (i == 3) {
            endPointIndex = 0;
        } else {
            endPointIndex = i * 3 + 3;
        }

        path.cubicTo(pointList.get(i * 3 + 1).x, pointList.get(i * 3 + 1).y,
                pointList.get(i * 3 + 2).x, pointList.get(i * 3 + 2).y,
                pointList.get(endPointIndex).x, pointList.get(endPointIndex).y); //你的心形就是用貝塞爾曲線來畫的嗎
    }
    path.close();
    path.computeBounds(mHeartRect, false);//把path所占據的最小矩形區域,返回出去
}
>傳入一個`Path`引用,然後在方法內部對`path`進行各種`api`調用改變其屬性. 這裏需要提及一個重點:最後一行代碼  `path.computeBounds(mHeartRect, false);` 意思是,無論什麼樣的`path`,它都會占據一個最小矩形區域,`computeBounds`方法可以獲取這個矩形區域,設置給入參`mHeartRect`.

###第`2`步:將心形區域裁剪出來, 裁剪之後,後續的繪制都只會顯示在這個區域之內
>(為了作圖方便,我們通常先把坐標軸原點移動到 繪制區域的正中央)

```java
    @Override
    protected void onDraw(Canvas canvas) {

        int width = getWidth();
        int height = getHeight();
        canvas.translate(width / 2, height / 2);//為了作圖方便,我們通常先把坐標軸原點移動到 繪制區域的正中央
        ...省略無關代碼

        canvas.clipPath(mMainPath);//裁剪心形區域
        canvas.save();//保存畫布狀態

        ...省略無關代碼

    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

###第3步:繪制波浪區域

這裏有兩點細節
1)波浪區域分為兩塊,topbottom 上下兩塊
2) 整個波浪區域的長度為 心形矩形範圍寬度的2
(?為什麼是2倍? 因為上面的波浪動畫,其實是整個波浪區域平移造成的視覺效果,為了讓這個動畫可以無限執行,設計兩倍寬度,當一半的寬度向右移動剛好觸及心形矩形區域的右邊框的時候,讓它還原到原始比特置,這樣就能無縫銜接。)

######關鍵代碼1 - 波浪path的構建

    /** * @param ifTop 是否是上部分; 上下部分的封口比特置不一樣 * @param r 心形的矩形區域 * @param process 當前進度值 */
    private void resetWavePath(boolean ifTop, RectF r, float process, Path path) {
        final float width = r.width();
        final float height = r.width();

        path.reset();

        if (ifTop) {
            path.moveTo(r.left - width, r.top);
        } else {
            path.moveTo(r.left - width, r.bottom); //下部,初始比特置點在 下
        }

        float waveHeight = height / 8f;//波動的最大幅度

        //找到矩形區域的左邊線中點
        path.lineTo(r.left - width, r.bottom - height * process);

        //做兩個周期的貝塞爾曲線
        for (int i = 0; i < 2; i++) {
            float px1, py1, px2, py2, px3, py3;

            px1 = width / 4;
            py1 = -waveHeight;

            px2 = width / 4 * 3;
            py2 = waveHeight;

            px3 = width;
            py3 = 0;

            path.rCubicTo(px1, py1, px2, py2, px3, py3);
        }
        if (ifTop) {
            path.lineTo(r.right, r.top);
        } else {
            path.lineTo(r.right, r.bottom);
        }
        path.close();

    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.

######關鍵代碼2- 屬性動畫 改變兩個全局變量 波浪的向上增長系數 以及 橫向波浪動畫系數

    AnimatorSet animatorSet;
    // 動起來
    public void startAnimator() {

        if (animatorSet == null) {
            animatorSet = new AnimatorSet();
            ValueAnimator growAnimator = ValueAnimator.ofFloat(0f, 1f);
            growAnimator.addUpdateListener(animation -> growProcess = (float) animation.getAnimatedValue());
            growAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    animatorSet.cancel();
                }
            });
            growAnimator.setInterpolator(new DecelerateInterpolator());
            growAnimator.setDuration((long) (4000 / animatorSpeedCoefficient));

            ValueAnimator waveAnimator = ValueAnimator.ofFloat(0f, 1f);
            waveAnimator.setRepeatCount(ValueAnimator.INFINITE);
            waveAnimator.setRepeatMode(ValueAnimator.RESTART);
            waveAnimator.addUpdateListener(animation -> {
                waveProcess = (float) animation.getAnimatedValue();
                invalidate();
            });
            waveAnimator.setInterpolator(new LinearInterpolator());
            waveAnimator.setDuration((long) (1000 / animatorSpeedCoefficient));

            animatorSet.playTogether(growAnimator, waveAnimator);
            animatorSet.start();
        } else {
            animatorSet.cancel();
            animatorSet.start();
        }
    }

     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

######關鍵代碼3- 利用屬性動畫改變的全局變量,構建動態效果

    @Override
    protected void onDraw(Canvas canvas) {

        int width = getWidth();
        int height = getHeight();
        canvas.translate(width / 2, height / 2);//為了作圖方便,我們通常先把坐標軸原點移動到 繪制區域的正中央
        curXOffset = waveProcess * mHeartRect.width();//當前X軸方向上 波浪偏移量

        canvas.clipPath(mMainPath);
        canvas.save();

        mainRect = new Rect();
        ... 省略無關代碼

        // 上波浪區域
        resetWavePath(true, mHeartRect, growProcess, topWavePath);
        canvas.translate(curXOffset, 0);
        canvas.clipPath(topWavePath);
        canvas.drawPath(topWavePath, mTopPaint);
        ... 省略無關代碼
      
        //下波浪區域
        resetWavePath(false, mHeartRect, growProcess, bottomWavePath);
        canvas.restore();
        canvas.translate(curXOffset, 0);
        canvas.clipPath(bottomWavePath);
        canvas.drawPath(bottomWavePath, mBottomPaint);
       ... 省略無關代碼


# 分享

這次面試我也做了一些總結,確實還有很多要學的東西。相關面試題也做了整理,可以分享給大家,了解一下面試真題,想進大廠的或者想跳槽的小夥伴不妨好好利用時間來學習。學習的脚步一定不能停止!

**[需要這份資料的朋友戳這裏免費下載](https://gitee.com/vip204888/java-p7),整理出的內容大概如下:**

![薪酬縮水,“裸辭”奮戰25天三面美團,交叉面卻被吊打,我太難了](https://s2.51cto.com/images/20210820/1629405333475148.jpg)

Spring Cloud實戰

![薪酬縮水,“裸辭”奮戰25天三面美團,交叉面卻被吊打,我太難了](https://s2.51cto.com/images/20210820/1629405335972900.jpg)

Spring Boot實戰

![薪酬縮水,“裸辭”奮戰25天三面美團,交叉面卻被吊打,我太難了](https://s2.51cto.com/images/20210820/1629405338130919.jpg)

面試題整理(性能優化+微服務+並發編程+開源框架+分布式)
     
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.

版權聲明
本文為[Android我愛死你了]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2021/08/20210820044620702u.html

隨機推薦