自定義小太陽控制項

来源:https://www.cnblogs.com/LiuZhen/archive/2018/01/26/8359611.html
-Advertisement-
Play Games

你沒看錯,右上角的那個大圓就是傳說中的太陽,^_^ 這個動畫的難點在於這個“食物”的繪製上吧,不用懷疑,你還是沒看錯,那些小點就是傳說中的食物 首先一步步來,看到這種效果,第一個想到的就是一個普通的小圓,而這個大圓就用貝塞爾繪製,至於為什麼用貝塞爾而不是直接繪製一個半圓呢,因為食物是繞著半圓的,緊貼 ...


 

 

 

 

 

你沒看錯,右上角的那個大圓就是傳說中的太陽,^_^

這個動畫的難點在於這個“食物”的繪製上吧,不用懷疑,你還是沒看錯,那些小點就是傳說中的食物

首先一步步來,看到這種效果,第一個想到的就是一個普通的小圓,而這個大圓就用貝塞爾繪製,至於為什麼用貝塞爾而不是直接繪製一個半圓呢,因為食物是繞著半圓的,緊貼著,你需要拿這個半圓做參照物,不然隨便改改佈局就亂套了,

然後是小圓也需要圍著半圓繞圈,這兩個都需要一個已知的point

接下來就開始繪製半圓了,小圓就直接被忽略了

半圓採用 drawPath.cubicTo 來繪製,需要四個點,其中有兩個控制點

具體怎麼繪製看後面的代碼

接下來繪製食物,這下可能一部分人會有點困惑,食物怎麼繪製,怎麼讓食物緊貼著這個半圓去繪製,那我是不是可以照著這個半圓把Y軸的坐標改改,這樣另外一個半圓就出現了,只是需要改變下樣式,因為食物是一個個小點

這裡設置paint的effect就可以改變繪製的樣式了

就這樣食物出現了

然後就是一個難點了,小圓怎麼沿著食物一路吃下去呢,很顯然不能直接按照食物的繪製方法來繪製了,因為小圓是會動的,需要加入動畫,而不是一下子全繪製,這樣就需要知道食物的坐標了,這樣才能讓小圓繞著食物吃下去

這裡需要用到 Evaluator 根據控制點去計算出它本身的一個繪製坐標,貝塞爾是一個公式,可以推算出它的一個坐標點

package com.fragmentapp.view;

import android.graphics.PointF;

/**
 * Created by liuzhen on 2018/1/24.
 */

public class BezierUtil {

    /**
     * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
     *
     * @param t  曲線長度比例
     * @param p0 起始點
     * @param p1 控制點
     * @param p2 終止點
     * @return t對應的點
     */
    public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
        PointF point = new PointF();
        float temp = 1 - t;
        point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
        point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
        return point;
    }

    /**
     * B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3, t ∈ [0,1]
     *
     * @param t  曲線長度比例
     * @param p0 起始點
     * @param p1 控制點1
     * @param p2 控制點2
     * @param p3 終止點
     * @return t對應的點
     */
    public static PointF CalculateBezierPointForCubic(float t, PointF p0, PointF p1, PointF p2, PointF p3) {
        PointF point = new PointF();
        float temp = 1 - t;
        point.x = p0.x * temp * temp * temp + 3 * p1.x * t * temp * temp + 3 * p2.x * t * t * temp + p3.x * t * t * t;
        point.y = p0.y * temp * temp * temp + 3 * p1.y * t * temp * temp + 3 * p2.y * t * t * temp + p3.y * t * t * t;
        return point;
    }

}
View Code
package com.fragmentapp.view;

import android.animation.TypeEvaluator;
import android.graphics.PointF;

/**
 * Created by liuzhen on 2018/1/24.
 */

public class PathEvaluator implements TypeEvaluator<PointF> {

    private PointF mControlPoint1,mControlPoint2;

//    public PathEvaluator(PointF controlPoint) {
//        this.mControlPoint1 = controlPoint;
//    }
    public PathEvaluator(PointF controlPoint1,PointF controlPoint2) {
        this.mControlPoint1 = controlPoint1;
        this.mControlPoint2 = controlPoint2;
    }

    @Override
    public PointF evaluate(float t, PointF startValue, PointF endValue) {
        return BezierUtil.CalculateBezierPointForCubic(t, startValue, mControlPoint1,mControlPoint2, endValue);
    }

}
View Code

 

有了這些坐標那小圓就可以沿著食物吃下去了,但是隨著吃的動作又會發現怎麼吃這個問題了,這顯然也不是很好操作,本人想了一會,發現這條路行不通,技術還沒達標,於是另闢蹊徑,用另外的辦法去做了,想想是繪製,那就乾脆在繪製一條背景色的線

這樣食物就會出現被吃掉的效果了,所以在上面有個clears.add的操作

在給小圓添加一張嘴吧

控制angle這個參數,小圓終於可以吃了,然後為了追求更高的一個層次,又添加了一個移動的動畫,然後動畫開始的時候先讓半圓有個上升的過程,這樣頓時看起來高大上一些了

最後stop

使用就是兩個方法,一個start動畫,一個開始吃的動畫操作,兩個方法,在合適的時候調用就行了,直接上代碼

package com.fragmentapp.view.refresh;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathDashPathEffect;
import android.graphics.PathEffect;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.fragmentapp.R;
import com.fragmentapp.helper.TimeUtil;
import com.fragmentapp.view.PathEvaluator;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by liuzhen on 2018/1/24.
 */

public class SunHeadView extends View implements IHeadView{

    private int mWidth;
    private int mHeight;

    private Paint effectPaint,facePaint,clearPaint,defPaint;//這裡定義多個屬性都是為了能夠自定義不同的樣式
    private RectF rectF = null;

    private float angle,loadAngle = 45;

    private ValueAnimator faceVa,arcVa;

    private int left,top;

    private boolean isDraw = false;

    private Path path,foodPath;
    private PointF startPoint = null,movePoint1 = null,movePoint2 = null,endPoint = null;
    private List<PointF> clears = null;

    private PathEffect effect = null;

    private int faceRadius = 30,foodRadius = 3;

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

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

    public SunHeadView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){

        path = new Path();

        foodPath = new Path();
        foodPath.addCircle(0, 0, foodRadius, Path.Direction.CCW);

        effectPaint = new Paint();
        effectPaint.setAntiAlias(true);
        effectPaint.setStyle(Paint.Style.STROKE);
        effectPaint.setColor(getResources().getColor(R.color.color_a9a05c));

        effect = new PathDashPathEffect(foodPath, 12, -1, PathDashPathEffect.Style.ROTATE);
        effectPaint.setPathEffect(effect);

        facePaint = new Paint();
        facePaint.setAntiAlias(true);
        facePaint.setStyle(Paint.Style.FILL);
        facePaint.setColor(getResources().getColor(R.color.color_a9a05c));

        defPaint = new Paint();
        defPaint.setAntiAlias(true);
        defPaint.setStyle(Paint.Style.FILL);
        defPaint.setColor(getResources().getColor(R.color.color_a9a05c));

        rectF = new RectF(0,0,0,0);
        startPoint = new PointF();
        movePoint1 = new PointF();
        movePoint2 = new PointF();
        endPoint = new PointF();

        clearPaint = new Paint();
        clearPaint.setAntiAlias(true);
        clearPaint.setStyle(Paint.Style.FILL);
        clearPaint.setColor(getResources().getColor(R.color.white));

        clears = new ArrayList<>();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();

            this.left = mWidth / 2;
            this.top = mHeight / 3;

            rectF.set(startPoint.x - faceRadius/2,startPoint.y - faceRadius,startPoint.x + faceRadius/2,startPoint.y);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (!isDraw) return;
        //繪製“食物”
        foodPath.reset();
        foodPath.moveTo(startPoint.x,startPoint.y);
        foodPath.cubicTo(movePoint1.x,movePoint1.y,movePoint2.x,movePoint2.y,endPoint.x,endPoint.y);
        canvas.drawPath(foodPath, effectPaint);
        //繪製大球
        path.reset();
        path.moveTo(startPoint.x + faceRadius/2,startPoint.y);
        path.cubicTo(movePoint1.x,movePoint1.y + faceRadius/2,movePoint2.x,movePoint2.y + faceRadius/2,endPoint.x - faceRadius/2,endPoint.y);
        canvas.drawPath(path, defPaint);
        //吃掉“食物”
        for (PointF f : clears) {
            RectF rectF = new RectF(f.x-foodRadius*2,f.y-foodRadius*2,f.x+foodRadius*2,f.y+foodRadius*2);
            canvas.drawOval(rectF,clearPaint);
        }

        //繪製小球,需要在最後面繪製
        canvas.drawArc(rectF, angle, 360 - angle * 2, true, facePaint);

    }

    @Override
    public View getView() {
        return this;
    }

    /**開始動畫*/
    public void upAnim(){
        if (faceVa != null)
            faceVa.cancel();
        faceVa = null;

        effectPaint.setColor(getResources().getColor(R.color.color_a9a05c));
        facePaint.setColor(getResources().getColor(R.color.color_a9a05c));
        clearPaint.setColor(getResources().getColor(R.color.white));
        defPaint.setColor(getResources().getColor(R.color.color_a9a05c));

        startPoint.set(mWidth*1/2 + faceRadius*2,mHeight + faceRadius);
        movePoint1.set(mWidth*2/3, 0);
        movePoint2.set(mWidth*5/6, 0);
        endPoint.set(mWidth - faceRadius*2,mHeight + faceRadius);

        clears.clear();

        PathEvaluator bezierEvaluator = new PathEvaluator(movePoint1,movePoint2);
        arcVa = ValueAnimator.ofObject(bezierEvaluator, startPoint, endPoint);
        arcVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//饒球移動
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                PointF point = (PointF) valueAnimator.getAnimatedValue();
//                if (point.x + faceRadius <= endPoint.x)
                clears.add(new PointF(point.x,point.y));//保存移動的坐標
                //faceRadius/2是為了讓小球的中心點剛好在大球的中間
                rectF.set(point.x - faceRadius/2,point.y - faceRadius/2,point.x + faceRadius/2,point.y + faceRadius/2);

                postInvalidate();
            }
        });
        arcVa.setInterpolator(new LinearInterpolator());
        arcVa.setDuration(2000);
        arcVa.setRepeatMode(ValueAnimator.RESTART);
        arcVa.start();

        rectF.set(startPoint.x - faceRadius/2,startPoint.y - faceRadius,startPoint.x + faceRadius/2,startPoint.y);
        angle = loadAngle;
        faceVa = ValueAnimator.ofFloat(loadAngle , 0);

        faceVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//吃食物動作
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                angle = (float)valueAnimator.getAnimatedValue();
                postInvalidate();
            }
        });
        faceVa.setInterpolator(new LinearInterpolator());
        faceVa.setDuration(500);
        faceVa.setRepeatMode(ValueAnimator.RESTART);
        faceVa.setRepeatCount(-1);
        faceVa.start();
    }

    @Override
    public void startAnim() {//前奏
        effectPaint.setColor(getResources().getColor(R.color.transparent));
        facePaint.setColor(getResources().getColor(R.color.transparent));
        clearPaint.setColor(getResources().getColor(R.color.transparent));

        isDraw = true;
        faceVa = ValueAnimator.ofFloat(0 , mHeight + faceRadius);//大球落下

        faceVa.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//吃食物動作
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float val = (float)valueAnimator.getAnimatedValue();

                startPoint.set(mWidth*1/2 + faceRadius*2,val);
                movePoint1.set(mWidth*2/3, 0);
                movePoint2.set(mWidth*5/6, 0);
                endPoint.set(mWidth - faceRadius*2,val);

                postInvalidate();
            }
        });
        faceVa.setInterpolator(new LinearInterpolator());
        faceVa.setDuration(1000);
        faceVa.setRepeatMode(ValueAnimator.RESTART);
        faceVa.start();
    }

    @Override
    public void stopAnim() {
        if (arcVa != null)
            arcVa.cancel();
        if (faceVa != null)
            faceVa.cancel();
        arcVa = null;
        faceVa = null;
        isDraw = false;
    }
}
View Code

最後,歡迎收藏

GitHub:https://github.com/1024477951/FragmentApp


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 ALTER TABLE 'table_name' DROP 'id'; 2 ALTER TABLE 'table_name' ADD 'id' int(11) PRIMARY KEY NOT NULL AUTO_INCREMENT FIRST; ...
  • 前言 最近在嘗試一個日誌系統graylog來收集mysql的慢查詢日誌提,供後續的分析、監控和報警等。測試步驟已經到日誌已成功收集到graylog,測試時需要刷一些慢查詢日誌出來。為了刷比較多的日誌和不對測試環境造成較大的影響,想到了使用mysql的sleep函數結合event來做刷慢日誌。 MyS ...
  • 一、基本查詢 (表:emp 列:ename ,depton,sal 、表示換行) 1.查當前用戶 SQL> show user 2.查表結構 SQL> desc emp (emp:表名) --查詢出名稱、類型、是否為空 3.清屏 SQL> host cls 4.設置行寬 SQL> show line ...
  • 轉載地址為:http://blog.51cto.com/hades02/1641652 首先在命令行輸入 show global variables like '%general%' ,然後出現下麵的視窗,欄位general_log為開關,一般預設是為OFF的,所以還沒開啟監視,然後general_ ...
  • Redis簡介 Redis是一個開源的使用ANSI C語言編寫、支持網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API. Redis是一個開源(BSD許可),記憶體存儲的數據結構伺服器,可用作資料庫,高速緩存和消息隊列代理。它支持字元串、哈希表、列表、集合、有序集合, ...
  • DDL 資料庫定義語言(庫的操作)(看懂,工作時使用較少)1.創建一個庫 create database 庫名稱 [character set 碼表名稱 collate 字元校對集名稱] create database day15; 1>看到Query OK, 1 row affected (0.0 ...
  • 1、數據倉庫.就與我們之前學過的純文本,properties,XML這些技術一樣.用來保存數據.並提供對數據進行增刪改查的操作.我們以後做項目時, 項目中的數據都是保存在資料庫中的. 2、資料庫的特點 2.1、實現數據共用 2.2、減少數據的冗餘度 2.3、數據實現集中控制 2.4、數據一致性,完整 ...
  • 前幾天幫用戶解決了一個問題,記錄下 有個用戶用我們的芝麻小客服,配置都是正確的,但是一直收不到用戶發的消息,無法自動回覆消息。 後來發現原來是有bug,如果發現你登陸小程式的客服系統,然後,別人發送消息,你都看不到,那麼可能是bug,需要向官方反饋。 這裡就有一個用戶反饋的,官方解決了,參考下麵這個 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...