Android 自定義View之圓角矩形軌跡圖

一、原理說明

主要是通過計算軌跡的坐標點加入到集合中,然后對集合進行相應截取,傳入canvas中。

二、具體代碼實現(xiàn)


/**
 * 原理是先通過尺寸把各個軌跡的坐標計算出來,然后再截取相應坐標,進行重繪。
 *
 * @author lz
 * @Time 2019-3-27
 */
public class RotateTrackView extends View {

    /**
     * 起始點橫坐標
     */
    private float startXPoint;

    /**
     * 起始點縱坐標
     */
    private float startYPoint;

    /**
     * 寬度
     */
    private float width;

    /**
     * 高度
     */
    private float heigth;

    /**
     * 圓角半徑
     */
    private float radius;

    /**
     * 圖形點集合
     */
    float[] mPoints;

    /**
     * 畫筆
     */
    private Paint mPaint;

    /**
     * 標志位
     */
    private final int LeftUp = 0;
    private final int LeftDown = 1;
    private final int RightDown = 2;
    private final int RightUp = 3;

    /**
     * 停止動畫標志位
     */
    private boolean isPause = false;

    /**
     * 動畫開始位置起點
     */
    private int indexBegin = 0;
    /**
     * 動畫結(jié)束位置,也就是整個動畫的長度
     */
    private int indexEnd = 200;

    private int LENGTH = 200;

    private int TIME = 1;

    /**
     * 截取的軌跡點集合
     */
    private float[] snackPoints;

    public RotateTrackView(Context context) {
        super(context);
    }

    public RotateTrackView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    public RotateTrackView(Context context, float startXPoint, float startYPoint, float width, float heigth, float radius) {
        this(context);
        this.startXPoint = startXPoint;
        this.startYPoint = startYPoint;
        this.heigth = heigth;
        this.width = width;
        this.radius = radius;

        // 初始化畫筆
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(8);

        // 軌跡計算
        ArrayList<Float> mPointLists = new ArrayList<>();
        addXPoints(width, radius, startXPoint, startYPoint, mPointLists);
        addCircleArgle(RightUp, radius, mPointLists);
        addYPoints(heigth, radius, mPointLists.get(mPointLists.size() - 1), mPointLists.get(mPointLists.size() - 2), mPointLists);
        addCircleArgle(RightDown, radius, mPointLists);
        reduceXPoints(width, radius, mPointLists.get(mPointLists.size() - 2), mPointLists.get(mPointLists.size() - 1), mPointLists);
        addCircleArgle(LeftDown, radius, mPointLists);
        reduceYPoints(heigth, radius, mPointLists.get(mPointLists.size() - 1), mPointLists.get(mPointLists.size() - 2), mPointLists);
        addCircleArgle(LeftUp, radius, mPointLists);

        // 將list集合轉(zhuǎn)換成canvas接受的float數(shù)組
        mPoints = new float[mPointLists.size()];
        for (int i = 0; i < mPointLists.size(); i++) {
            mPoints[i] = mPointLists.get(i);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (snackPoints.length == LENGTH) {
            canvas.drawPoints(snackPoints, mPaint);
        }
    }

    /**
     * x軸向右計算
     *
     * @param length
     * @param radius
     * @param addNumber
     * @param constantNumber
     * @param list
     */
    private void addXPoints(float length, float radius, float addNumber, float constantNumber, List<Float> list) {
        for (int i = 1; i < (length - 2 * radius); i++) {
            float pointX = addNumber + i;
            float pointY = constantNumber;
            list.add(pointX);
            list.add(pointY);
        }
    }

    private void reduceXPoints(float length, float radius, float addNumber, float constantNumber, List<Float> list) {
        for (int i = 1; i < (length - 2 * radius); i++) {
            float pointX = addNumber - i;
            float pointY = constantNumber;
            list.add(pointX);
            list.add(pointY);
        }
    }

    private void addYPoints(float length, float radius, float addNumber, float constantNumber, List<Float> list) {
        for (int i = 1; i < (length - 2 * radius); i++) {
            float pointX = addNumber + i;
            float pointY = constantNumber;
            list.add(pointY);
            list.add(pointX);
        }
    }

    private void reduceYPoints(float length, float radius, float addNumber, float constantNumber, List<Float> list) {
        for (int i = 1; i < (length - 2 * radius); i++) {
            float pointX = addNumber - i;
            float pointY = constantNumber;
            list.add(pointY);
            list.add(pointX);
        }
    }

    /**
     * 由于上下左右四個圓的軌跡坐標計算方式是不一樣的,需要單獨處理
     *
     * @param position
     * @param radius
     * @param list
     */
    private void addCircleArgle(int position, float radius, List<Float> list) {
        int argle = 0;
        float x = list.get(list.size() - 2);
        float y = list.get(list.size() - 1);
        for (int i = 0; i < 90; i += 10) {
            argle = i + 10;

            switch (position) {

                case RightUp:
                    float x1 = (float) (x + Math.sin((Math.PI / 180) * argle) * radius);
                    float y1 = (float) (y + (radius - Math.cos((Math.PI / 180) * argle) * radius));
                    list.add(x1);
                    list.add(y1);
                    break;

                case RightDown:
                    float x2 = (float) (x - (radius - Math.cos((Math.PI / 180) * argle) * radius));
                    float y2 = (float) (y + (Math.sin((Math.PI / 180) * argle) * radius));
                    list.add(x2);
                    list.add(y2);
                    break;

                case LeftDown:
                    float x3 = (float) (x - (Math.sin((Math.PI / 180) * argle) * radius));
                    float y3 = (float) (y - (radius - Math.cos((Math.PI / 180) * argle) * radius));
                    list.add(x3);
                    list.add(y3);
                    break;

                case LeftUp:
                    float x4 = (float) (x + (radius - Math.cos((Math.PI / 180) * argle) * radius));
                    float y4 = (float) (y - (Math.sin((Math.PI / 180) * argle) * radius));
                    list.add((float) (x + (radius - Math.cos((Math.PI / 180) * argle) * radius)));
                    list.add((float) (y - (Math.sin((Math.PI / 180) * argle) * radius)));
                    break;

                default:
                    break;
            }
        }
    }

    /**
     * 開始動畫
     */
    public void startAnim() {
        Thread thread = new Thread() {
            @Override
            public void run() {
                super.run();
                while (!isPause) {
                    RotateTrackView.this.post(new Runnable() {
                        @Override
                        public void run() {
                            invalidate();
                        }
                    });
                    indexBegin += 2;
                    indexEnd += 2;
                    if (indexEnd < mPoints.length) {
                        snackPoints = (Arrays.copyOfRange(mPoints, indexBegin, indexEnd));
                    } else if (indexBegin == mPoints.length) {
                        indexBegin = 0;
                        indexEnd = LENGTH;
                    } else {
                        float[] floats2 = Arrays.copyOfRange(mPoints, indexBegin, mPoints.length);
                        float[] floats3 = Arrays.copyOf(floats2, LENGTH);
                        int length = floats2.length;
                        for (int i = 0; i < (LENGTH - floats2.length); i++) {
                            floats3[length] = mPoints[i];
                            length++;
                        }
                        snackPoints = floats3;

                    }
                    try {
                        Thread.sleep(TIME);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
    }

    /**
     * 停止動畫
     */
    public void stopAnim() {
        isPause = true;
    }

    /**
     * 設置軌跡粗細
     */
    public void setLinesWidth(int width) {
        mPaint.setStrokeWidth(width);
    }
}

三、使用方法

public class MainActivity extends AppCompatActivity {

    private RotateTrackView rotateTrackView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 創(chuàng)建RotateTrackView, 設置起始坐標、寬、高、圓角半徑
        rotateTrackView = new RotateTrackView(this, 500, 600, 250, 180, 30);
        setContentView(rotateTrackView);
        // 設置畫筆粗細
        rotateTrackView.setLinesWidth(20);
        // 開始動畫
        rotateTrackView.startAnim();
    }
}

四、效果展示

RotateTrackView.gif

五、下載地址

https://github.com/liuzheng1101/RotateTrackView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容