天天看點

從0開始搞一個錦鯉遊動——畫錦鯉

首先大家可以看一下這個魚的圖案

從0開始搞一個錦鯉遊動——畫錦鯉

一.前期準備

1.建立一個項目

2.将MainActivity的XML檔案寫成

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <ImageView
        android:id="@+id/iv_fish"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>
</RelativeLayout>      

相當于我們後面隻是對​

​ImageView​

​進行塗畫

3.建立fishDrawable類,繼承Drawable

不用多想,就把​

​fishDrawable​

​​類看作圖檔即可

自定義​​

​View​

​​和​

​Drawable​

​​差別不大,主要是​

​Drawable​

​使用起來更簡單一些

public class fishDrawable extends Drawable {
    @Override
    public void draw(@NonNull Canvas canvas) {

    }

    @Override
    public void setAlpha(int alpha) {

    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {

    }

    @Override
    public int getOpacity() {
        return 0;
    }
}      

4.在MainActivity中将ImageView控件與fishDrawable類進行關聯

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //建立關聯
        ImageView fishView = findViewById(R.id.iv_fish);
        fishView.setImageDrawable(new fishDrawable());
    }
}      

二.具體實操

參考比例

從0開始搞一個錦鯉遊動——畫錦鯉

分解圖

從0開始搞一個錦鯉遊動——畫錦鯉

注意:由于代碼添加并不是非常的規律,是以有時可能會忽略非常小一部分代碼。但是大家不用擔心,在這篇文章的最後我會把完整代碼展示,并且會将項目上傳到github,并給對外連結接

1.其餘三個方法進行套公式

除了​

​draw​

​,其餘三個方法都是比較固定的寫法

從0開始搞一個錦鯉遊動——畫錦鯉
從0開始搞一個錦鯉遊動——畫錦鯉
從0開始搞一個錦鯉遊動——畫錦鯉

2.新增兩個方法,固定大小

細心的朋友們可能發現了,我們一開始在​

​XML​

​​中設定​

​ImageView​

​​的大小的時候都是​

​Wrap_Content​

​,那總得固定一個預設大小吧,是以這兩個方法就用上了

(​

​HEAD_RADIUS​

​為魚頭的圓的半徑)

從0開始搞一個錦鯉遊動——畫錦鯉

3.進行一些初始化

public class fishDrawable extends Drawable {
    //建立路徑和畫筆
    private Path mPath;
    private Paint mPaint;

    //設定畫筆的透明度
    private int OTHER_ALPHA = 110;

    //設定魚頭的圓的大小
    private float HEAD_RADIUS = 100;
    
    fishDrawable(){
        init();
    }
    
    //初始化
    private void init() {
        mPath = new Path();
        mPaint = new Paint();

        mPaint.setStyle(Paint.Style.FILL);
        //抗鋸齒
        mPaint.setAntiAlias(true);
        //防抖動
        mPaint.setDither(true);
        //設定顔色
        mPaint.setARGB(OTHER_ALPHA,244,92,71);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {

    }

    //下面三個方法一般為固定寫法
    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public int getIntrinsicWidth() {
        //設定寬度
        return (int) (8.38 * HEAD_RADIUS);
    }

    @Override
    public int getIntrinsicHeight() {
        //設定高度
        return (int) (8.38 * HEAD_RADIUS);
    }
}      

4.确定魚的重心(PointF)

這裡我們中心和重心就不進行區分了,就通俗了解為魚的中心點那個位置。魚頭的中心點是以魚中心點為參考進行繪制的。

//儲存魚的重心
    private PointF middlePoint;
    
    fishDrawable(){
        init();
    }

    //初始化
    private void init() {
        mPath = new Path();
        mPaint = new Paint();

        mPaint.setStyle(Paint.Style.FILL);
        //抗鋸齒
        mPaint.setAntiAlias(true);
        //防抖動
        mPaint.setDither(true);
        //設定顔色
        mPaint.setARGB(OTHER_ALPHA,244,92,71);
        
        //确定魚的重心
        middlePoint = new PointF(4.19f * HEAD_RADIUS,4.19F * HEAD_RADIUS);
    }      

5.寫出核心求坐标算法

此算法為後面所有算法的基礎。利用數學知識,此算法的思想是,

  • 根據一個點的坐标,以及另外一個點到這個點的連線與X軸夾角,以及這條連線的長度,求出另外一個點的坐标。(數學知識,三角函數)

可能光說就有些抽象,我們看下面的圖

從0開始搞一個錦鯉遊動——畫錦鯉

比如我們知道a點,知道ab這條直線的長度,知道角α的大小,就可以求出b點的坐标。這樣說大家應該就了解了。

比如這裡面求b的坐标

從0開始搞一個錦鯉遊動——畫錦鯉

b點的橫坐标就是

​a.x+ab*cosα​

​ b點的縱坐标就是

​a.y+ab*sinα​

OK,知道了原理,我們就開始寫這個算法

private PointF calculatePoint(PointF startPoint,float length,float angle){
        //x坐标的一部分
        float deltaX = (float) (Math.cos(Math.toRadians(angle))*length);

        //y坐标的一部分
        float deltaY = (float) Math.sin(Math.toRadians(angle - 180))*length;

        return new PointF(startPoint.x + deltaX,startPoint.y + deltaY);
    }      

注意:

①​​

​Math.toRadians​

​​是把角度換算為弧度,因為​

​Math.sin/cos​

​​的參數是接收弧度的。

②由于安卓坐标系和數學坐标系的y軸正好相反,是以這裡的求y坐标時要​​

​-180​

​度,以達到正好相反的效果

6.利用魚中心坐标計算魚頭圓心坐标

@Override
    public void draw(@NonNull Canvas canvas) {
        //魚的朝向
        float fishAngle = fishMainAngle;

        //計算魚頭的圓心坐标
        PointF headPoint = calculatePoint(middlePoint,BODY_RADIUS/2,fishAngle);
        //畫魚頭的圓
        canvas.drawCircle(headPoint.x,headPoint.y,HEAD_RADIUS,mPaint);
    }      

效果如下

從0開始搞一個錦鯉遊動——畫錦鯉

7.畫魚鳍

從0開始搞一個錦鯉遊動——畫錦鯉
//設定魚鳍邊界點和魚頭中心點的距離
    private float FIND_FINS_LENGTH = 0.9f * HEAD_RADIUS;
    //魚鳍長度
    private float FINS_LENGTH = 1.3f * HEAD_RADIUS;
    
    @Override
    public void draw(@NonNull Canvas canvas) {
        //畫右魚鳍
        PointF rightFinsPoint = calculatePoint(headPoint,FIND_FINS_LENGTH,fishAngle - 100);
        makeFins(canvas,rightFinsPoint,fishAngle,true);

        //畫左魚鳍
        PointF leftFinsPoint = calculatePoint(headPoint,FIND_FINS_LENGTH,fishAngle + 100);
        makeFins(canvas,leftFinsPoint,fishAngle,false);
    }      

畫魚鳍的算法

private void makeFins(Canvas canvas, PointF startFinsPoint, float fishAngle,boolean isRight) {
        //設定二階貝塞爾曲線控制點的角度
        float controlAngle = 110;//通過看示意圖得知,它要比上面那個110要大

        //求出右魚鳍終點的坐标點
        PointF endFinsPoint = calculatePoint(startFinsPoint,FINS_LENGTH,fishAngle - 180);

        //求出控制點的坐标
        PointF controlPoint = calculatePoint(startFinsPoint,FINS_LENGTH * 1.8f
                ,isRight ? fishAngle - controlAngle : fishAngle + controlAngle);

        //畫線
        mPath.reset();//首先别忘了reset
        mPath.moveTo(startFinsPoint.x,startFinsPoint.y);
        mPath.quadTo(controlPoint.x,controlPoint.y,endFinsPoint.x,endFinsPoint.y);

        canvas.drawPath(mPath,mPaint);
}      

效果

從0開始搞一個錦鯉遊動——畫錦鯉

8.畫魚節肢

//大圓的半徑
    private float BIG_CIRCLE_RADIUS = 0.7f * HEAD_RADIUS;
    //中圓的半徑
    private float MIDDLE_CIRCLE_RADIUS = 0.6f * BIG_CIRCLE_RADIUS;
    //小圓的半徑
    private float SMALL_CIRCLE_RADIUS = 0.4f * MIDDLE_CIRCLE_RADIUS;
    //尋找尾部中圓圓心的線長
    private final float FIND_MIDDLE_CIRCLE_LENGTH = BIG_CIRCLE_RADIUS * (0.6f + 1);
    //尋找尾部小圓圓心的線長
    private final float FIND_SMALL_CIRCLE_LENGTH = MIDDLE_CIRCLE_RADIUS * (0.4f + 2.7f);
    
    @Override
    public void draw(@NonNull Canvas canvas) {
        //畫節肢1
        //首先找到魚身體底部的中心點
        PointF bodyBottomCenterPoint = calculatePoint(headPoint,BODY_LENGTH,fishAngle-180);
        PointF middleCirclePoint = makeSegment(canvas,bodyBottomCenterPoint,MIDDLE_CIRCLE_RADIUS,
                BIG_CIRCLE_RADIUS,FIND_MIDDLE_CIRCLE_LENGTH,fishAngle,true);

        //畫節肢2
        makeSegment(canvas,middleCirclePoint,SMALL_CIRCLE_RADIUS,MIDDLE_CIRCLE_RADIUS,
                FIND_SMALL_CIRCLE_LENGTH,fishAngle,false);

    }      

畫節肢算法

private PointF makeSegment(Canvas canvas, PointF bottomCenterPoint,float smallRadius
            ,float bigRadius,float findSmallCircleLength, float fishAngle,boolean hasBigCircle) {
        //根據梯形下底中心點的坐标,求出上底中心點的坐标
        PointF upperCenterPoint = calculatePoint(bottomCenterPoint,findSmallCircleLength,fishAngle-180);

        //求出梯形四個頂點的坐标
        PointF bottomLeftPoint = calculatePoint(bottomCenterPoint,bigRadius,fishAngle + 90);
        PointF bottomRightPoint = calculatePoint(bottomCenterPoint,bigRadius,fishAngle - 90);
        PointF upperLeftPoint = calculatePoint(upperCenterPoint,smallRadius,fishAngle + 90);
        PointF upperRightPoint = calculatePoint(upperCenterPoint,smallRadius,fishAngle - 90);

        //畫大圓和中圓
        //大圓隻有在節肢1的時候才會畫
        if(hasBigCircle){
            canvas.drawCircle(bottomCenterPoint.x,bottomCenterPoint.y, bigRadius,mPaint);
        }

        canvas.drawCircle(upperCenterPoint.x,upperCenterPoint.y,smallRadius,mPaint);

        //畫梯形
        mPath.reset();
        mPath.moveTo(bottomLeftPoint.x,bottomLeftPoint.y);
        mPath.lineTo(bottomRightPoint.x,bottomRightPoint.y);
        mPath.lineTo(upperRightPoint.x,upperRightPoint.y);
        mPath.lineTo(upperLeftPoint.x,upperLeftPoint.y);
        canvas.drawPath(mPath,mPaint);

        //将中圓的圓心坐标傳回
        return upperCenterPoint;
    }      

效果展示

從0開始搞一個錦鯉遊動——畫錦鯉

9.畫底邊三角形

//尋找大三角形底邊中心點的線長
private final float FIND_TRIANGLE_LENGTH = MIDDLE_CIRCLE_RADIUS * 2.7f;
//畫底邊三角形
@Override
public void draw(@NonNull Canvas canvas) {
//畫底邊三角形
makeTriangle(canvas,middleCirclePoint,FIND_TRIANGLE_LENGTH,BIG_CIRCLE_RADIUS,fishAngle);
makeTriangle(canvas,middleCirclePoint,FIND_TRIANGLE_LENGTH-20,BIG_CIRCLE_RADIUS - 10,fishAngle);
}      

畫三角形的算法

private void makeTriangle(Canvas canvas, PointF middleCirclePoint, float findCenterLength,
                              float halfFdge,float fishAngle) {
        //首先得到三角形底邊中點坐标
        PointF triangleBottomPoint = calculatePoint(middleCirclePoint,findCenterLength,fishAngle + 180);

        //然後計算三角形各個頂點的坐标
        PointF leftPoint = calculatePoint(triangleBottomPoint,halfFdge,fishAngle + 90);
        PointF rightPoint = calculatePoint(triangleBottomPoint,halfFdge,fishAngle - 90);

        //畫線
        mPath.reset();
        mPath.moveTo(middleCirclePoint.x,middleCirclePoint.y);
        mPath.lineTo(leftPoint.x,leftPoint.y);
        mPath.lineTo(rightPoint.x,rightPoint.y);

        canvas.drawPath(mPath,mPaint);
}      

效果展示

從0開始搞一個錦鯉遊動——畫錦鯉

10.畫身體

@Override
    public void draw(@NonNull Canvas canvas) {
        //畫身體
        makeBody(canvas,headPoint,bodyBottomCenterPoint,fishAngle);
    }      

畫身體的算法

private void makeBody(Canvas canvas, PointF headPoint, PointF bodyBottomCenterPoint, float fishAngle) {
        //首先得到身體的四個頂點
        PointF topLeftPoint = calculatePoint(headPoint,HEAD_RADIUS,fishAngle + 90);
        PointF topRightPoint = calculatePoint(headPoint,HEAD_RADIUS,fishAngle - 90);
        PointF bottomLeftPoint = calculatePoint(bodyBottomCenterPoint,BIG_CIRCLE_RADIUS,fishAngle + 90);
        PointF bottomRightPoint = calculatePoint(bodyBottomCenterPoint,BIG_CIRCLE_RADIUS,fishAngle - 90);

        //然後得到控制點的坐标
        PointF controlLeft = calculatePoint(headPoint,BODY_LENGTH*0.56F,fishAngle + 130);
        PointF controlRight = calculatePoint(headPoint,BODY_LENGTH*0.56F,fishAngle - 130);

        mPath.reset();
        mPath.moveTo(topLeftPoint.x,topLeftPoint.y);
        mPath.lineTo(topRightPoint.x,topRightPoint.y);
        mPath.quadTo(controlRight.x,controlRight.y,bottomRightPoint.x,bottomRightPoint.y);
        mPath.lineTo(bottomLeftPoint.x,bottomLeftPoint.y);
        mPath.quadTo(controlLeft.x,controlLeft.y,topLeftPoint.x,topLeftPoint.y);
        canvas.drawPath(mPath,mPaint);
}      

效果展示

全部代碼

public class fishDrawable extends Drawable {
    //建立路徑和畫筆
    private Path mPath;
    private Paint mPaint;

    //設定畫筆的透明度
    private int OTHER_ALPHA = 110;

    //儲存魚的重心
    private PointF middlePoint;

    //設定魚的主要角度,與X軸的夾角
    private float fishMainAngle = 0;

    /**
     * 與魚的長度有關的所有值
     */
    //設定魚頭的圓的大小
    private float HEAD_RADIUS = 100f;
    //設定魚身的大小
    private float BODY_LENGTH = 3.2f*HEAD_RADIUS;
    //設定魚鳍邊界點和魚頭中心點的距離
    private float FIND_FINS_LENGTH = 0.9f * HEAD_RADIUS;
    //魚鳍長度
    private float FINS_LENGTH = 1.3f * HEAD_RADIUS;
    //大圓的半徑
    private float BIG_CIRCLE_RADIUS = 0.7f * HEAD_RADIUS;
    //中圓的半徑
    private float MIDDLE_CIRCLE_RADIUS = 0.6f * BIG_CIRCLE_RADIUS;
    //小圓的半徑
    private float SMALL_CIRCLE_RADIUS = 0.4f * MIDDLE_CIRCLE_RADIUS;
    //尋找尾部中圓圓心的線長
    private final float FIND_MIDDLE_CIRCLE_LENGTH = BIG_CIRCLE_RADIUS * (0.6f + 1);
    //尋找尾部小圓圓心的線長
    private final float FIND_SMALL_CIRCLE_LENGTH = MIDDLE_CIRCLE_RADIUS * (0.4f + 2.7f);
    //尋找大三角形底邊中心點的線長
    private final float FIND_TRIANGLE_LENGTH = MIDDLE_CIRCLE_RADIUS * 2.7f;

    fishDrawable(){
        init();
    }

    //初始化
    private void init() {
        mPath = new Path();
        mPaint = new Paint();

        mPaint.setStyle(Paint.Style.FILL);
        //抗鋸齒
        mPaint.setAntiAlias(true);
        //防抖動
        mPaint.setDither(true);
        //設定顔色
        mPaint.setARGB(OTHER_ALPHA,244,92,71);

        middlePoint = new PointF(4.19f * HEAD_RADIUS,4.19F * HEAD_RADIUS);
    }



    @Override
    public void draw(@NonNull Canvas canvas) {
        //魚的朝向
        float fishAngle = fishMainAngle;

        //計算魚頭的圓心坐标
        PointF headPoint = calculatePoint(middlePoint,BODY_LENGTH/2,fishAngle);
        //畫魚頭的圓
        canvas.drawCircle(headPoint.x,headPoint.y,HEAD_RADIUS,mPaint);

        //畫右魚鳍
        PointF rightFinsPoint = calculatePoint(headPoint,FIND_FINS_LENGTH,fishAngle - 100);
        makeFins(canvas,rightFinsPoint,fishAngle,true);

        //畫左魚鳍
        PointF leftFinsPoint = calculatePoint(headPoint,FIND_FINS_LENGTH,fishAngle + 100);
        makeFins(canvas,leftFinsPoint,fishAngle,false);

        //畫節肢1
        //首先找到魚身體底部的中心點
        PointF bodyBottomCenterPoint = calculatePoint(headPoint,BODY_LENGTH,fishAngle-180);
        PointF middleCirclePoint = makeSegment(canvas,bodyBottomCenterPoint,MIDDLE_CIRCLE_RADIUS,
                BIG_CIRCLE_RADIUS,FIND_MIDDLE_CIRCLE_LENGTH,fishAngle,true);
        //畫節肢2
        makeSegment(canvas,middleCirclePoint,SMALL_CIRCLE_RADIUS,MIDDLE_CIRCLE_RADIUS,
                FIND_SMALL_CIRCLE_LENGTH,fishAngle,false);

        //畫底邊三角形
        makeTriangle(canvas,middleCirclePoint,FIND_TRIANGLE_LENGTH,BIG_CIRCLE_RADIUS,fishAngle);
        makeTriangle(canvas,middleCirclePoint,FIND_TRIANGLE_LENGTH-20,BIG_CIRCLE_RADIUS - 10,fishAngle);

        //畫身體
        makeBody(canvas,headPoint,bodyBottomCenterPoint,fishAngle);
    }

    private void makeBody(Canvas canvas, PointF headPoint, PointF bodyBottomCenterPoint, float fishAngle) {
        //首先得到身體的四個頂點
        PointF topLeftPoint = calculatePoint(headPoint,HEAD_RADIUS,fishAngle + 90);
        PointF topRightPoint = calculatePoint(headPoint,HEAD_RADIUS,fishAngle - 90);
        PointF bottomLeftPoint = calculatePoint(bodyBottomCenterPoint,BIG_CIRCLE_RADIUS,fishAngle + 90);
        PointF bottomRightPoint = calculatePoint(bodyBottomCenterPoint,BIG_CIRCLE_RADIUS,fishAngle - 90);

        //然後得到控制點的坐标
        PointF controlLeft = calculatePoint(headPoint,BODY_LENGTH*0.56F,fishAngle + 130);
        PointF controlRight = calculatePoint(headPoint,BODY_LENGTH*0.56F,fishAngle - 130);

        mPath.reset();
        mPath.moveTo(topLeftPoint.x,topLeftPoint.y);
        mPath.lineTo(topRightPoint.x,topRightPoint.y);
        mPath.quadTo(controlRight.x,controlRight.y,bottomRightPoint.x,bottomRightPoint.y);
        mPath.lineTo(bottomLeftPoint.x,bottomLeftPoint.y);
        mPath.quadTo(controlLeft.x,controlLeft.y,topLeftPoint.x,topLeftPoint.y);
        canvas.drawPath(mPath,mPaint);
    }

    private void makeTriangle(Canvas canvas, PointF middleCirclePoint, float findCenterLength,
                              float halfFdge,float fishAngle) {
        //首先得到三角形底邊中點坐标
        PointF triangleBottomPoint = calculatePoint(middleCirclePoint,findCenterLength,fishAngle + 180);

        //然後計算三角形各個頂點的坐标
        PointF leftPoint = calculatePoint(triangleBottomPoint,halfFdge,fishAngle + 90);
        PointF rightPoint = calculatePoint(triangleBottomPoint,halfFdge,fishAngle - 90);

        //畫線
        mPath.reset();
        mPath.moveTo(middleCirclePoint.x,middleCirclePoint.y);
        mPath.lineTo(leftPoint.x,leftPoint.y);
        mPath.lineTo(rightPoint.x,rightPoint.y);

        canvas.drawPath(mPath,mPaint);
    }

    private PointF makeSegment(Canvas canvas, PointF bottomCenterPoint,float smallRadius
            ,float bigRadius,float findSmallCircleLength, float fishAngle,boolean hasBigCircle) {
        //根據梯形下底中心點的坐标,求出上底中心點的坐标
        PointF upperCenterPoint = calculatePoint(bottomCenterPoint,findSmallCircleLength,fishAngle-180);

        //求出梯形四個頂點的坐标
        PointF bottomLeftPoint = calculatePoint(bottomCenterPoint,bigRadius,fishAngle + 90);
        PointF bottomRightPoint = calculatePoint(bottomCenterPoint,bigRadius,fishAngle - 90);
        PointF upperLeftPoint = calculatePoint(upperCenterPoint,smallRadius,fishAngle + 90);
        PointF upperRightPoint = calculatePoint(upperCenterPoint,smallRadius,fishAngle - 90);

        //畫大圓和中圓
        //大圓隻有在節肢1的時候才會畫
        if(hasBigCircle){
            canvas.drawCircle(bottomCenterPoint.x,bottomCenterPoint.y, bigRadius,mPaint);
        }

        canvas.drawCircle(upperCenterPoint.x,upperCenterPoint.y,smallRadius,mPaint);

        //畫梯形
        mPath.reset();
        mPath.moveTo(bottomLeftPoint.x,bottomLeftPoint.y);
        mPath.lineTo(bottomRightPoint.x,bottomRightPoint.y);
        mPath.lineTo(upperRightPoint.x,upperRightPoint.y);
        mPath.lineTo(upperLeftPoint.x,upperLeftPoint.y);
        canvas.drawPath(mPath,mPaint);

        //将中圓的圓心坐标傳回
        return upperCenterPoint;
    }

    private void makeFins(Canvas canvas, PointF startFinsPoint, float fishAngle,boolean isRight) {
        //設定二階貝塞爾曲線控制點的角度
        float controlAngle = 110;//通過看示意圖得知,它要比上面那個110要大

        //求出右魚鳍終點的坐标點
        PointF endFinsPoint = calculatePoint(startFinsPoint,FINS_LENGTH,fishAngle - 180);

        //求出控制點的坐标
        PointF controlPoint = calculatePoint(startFinsPoint,FINS_LENGTH * 1.8f
                ,isRight ? fishAngle - controlAngle : fishAngle + controlAngle);

        //畫線
        mPath.reset();//首先别忘了reset
        mPath.moveTo(startFinsPoint.x,startFinsPoint.y);
        mPath.quadTo(controlPoint.x,controlPoint.y,endFinsPoint.x,endFinsPoint.y);

        canvas.drawPath(mPath,mPaint);
    }

    private PointF calculatePoint(PointF startPoint,float length,float angle){
        //x坐标的一部分
        float deltaX = (float) (Math.cos(Math.toRadians(angle))*length);

        //y坐标的一部分
        float deltaY = (float) Math.sin(Math.toRadians(angle - 180))*length;

        return new PointF(startPoint.x + deltaX,startPoint.y + deltaY);
    }

    //下面三個方法一般為固定寫法
    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    //設定寬度和高度
    @Override
    public int getIntrinsicWidth() {
        return (int) (8.38 * HEAD_RADIUS);
    }

    @Override
    public int getIntrinsicHeight() {
        return (int) (8.38 * HEAD_RADIUS);
    }
}      

三.總結

繼續閱讀