1. 程式人生 > >Android 自定義View -- 圓形進度條,文字旋轉

Android 自定義View -- 圓形進度條,文字旋轉

最近公司招聘打個廣告:
公司屬於外企福利待遇好,
每週英語課,
關鍵時單身妹子多[色][色]
詳情 [點選全棧JavaScript工程師]

Android 自定義View – 圓形進度條,文字旋轉

作為一名Android開發人員從碼農 -> 程式設計師 -> 工程師進步的過程中必然少不了一些重要的過程, 好久沒有自定義View了也是最近專案需要 ,這是Android版本的定義,如果需要檢視IOS請點選《 IOS Swift自定義View – 圓形進度條,文字旋轉》 ,歡迎大家提出寶貴意見,共同學習和進步,文章暫時沒寫全,以後補上,需要程式碼聯絡本人謝謝
這裡寫圖片描述

效果展示

這裡寫圖片描述

分析

實現的方法有很多種,萬變不離其宗, 我們學習的是方法而不是死記硬背,或者拿來主義

1. 我們先用程式碼在Android中繪製一個圓形

//建立畫筆  
Paint paint = new Paint();
//設定紅色
paint.setColor(Color.RED);
//設定畫筆的鋸齒效果。 true是去除, 畫出的圓會更加光滑
paint.setAntiAlias(true);
//這是畫圓形
canvas.drawCircle(100, 100, 100, paint);

2. 繪製一個矩形,然後在頂部繪製一個“+”,底部繪製一個“-”

// 設定矩形為灰色  
paint.setColor
(Color.GRAY); //設定矩形填滿 paint.setStyle(Paint.Style.FILL); //繪製矩形 canvas.drawRect(60, 90, 160, 100, paint);// 長方形 //繪製文字 canvas.drawText("+", 10, 80, paint); canvas.drawText("-", 10, 80, paint);

3. 分別繪製左右的小球

//繪製小球,和剛才畫圓一樣
canvas.drawCircle(10, 10, 10, paint);

4. 通過旋轉計算滑動的長度(這裡要考驗大家的高中數學了 哈哈哈)

三角函式
這裡寫圖片描述

角度是如何計算呢(這裡我們需要區分在第幾象限)
我們監聽手勢的x,y座標 (與圓心構成三角形,長a,高b)
計算 所以 tan角度 = b / a (第三象限) 其它的也一樣
圓上的點基本可以表示
最近Android Studio 出現問題展示把它解除安裝了 先上swift3.0 的程式碼 原理都一樣應該大家也可以看懂

func LeftRoundAction(_ sender: UIPanGestureRecognizer){
        if(CurrLeftProgress>=0 && CurrLeftProgress<=100){
            let translation = sender.translation(in: self)

            if(sender.state == UIGestureRecognizerState.possible){
            }else if(sender.state == UIGestureRecognizerState.began){
                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

            }else if(sender.state == UIGestureRecognizerState.changed){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.ended){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

                leftEX = translation.x + leftEX
                leftEY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.cancelled){
            }else if(sender.state == UIGestureRecognizerState.failed){
            }else if(sender.state == UIGestureRecognizerState.recognized){
            }

            //兩種情況 y > r or y < r 計算角度 和 新的座標

            var a:CGFloat=0;
            var b:CGFloat=0;

            if(leftY > centerY){
                a = leftY - centerY
                b = centerX - leftX
            }else{
                a = centerX - leftY
                b = centerY - leftX
            }

            let tanA = a / b;

            if(leftY > centerY){
                leftY = centerY + radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) - atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }else{
                leftY = centerY - radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) +  atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }
            leftX = centerX - radius * cos(atan(tanA)) - smallRoundR

            if(angleLeft >= startAngle && angleLeft <= endAngle){
                leftRound?.frame = CGRect(x: leftX, y: leftY, width: smallRoundR * 2, height: smallRoundR * 2)
                //change

                self.bar?.leftProgress(angleRight: angleRight, angleLeft: angleLeft)

                CurrLeftProgress = (angleLeft - startAngle) * maxLeftProgress / (endAngle - startAngle)

                if(CurrLeftProgress>100){
                    CurrLeftProgress = 100
                }else if(CurrLeftProgress<0){
                    CurrLeftProgress = 0
                }
            }
            print("\(TAG)   ------->2  CurrLeftProgress \(CurrLeftProgress)")

            if(sender.state == UIGestureRecognizerState.began){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.changed){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.ended){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }
        }
    }

5. 通過計算使小球只能在圓環上移動

畫圖不容易,還是喜歡在紙上用筆胡亂畫
長(width) 高(height) 半徑(r)
X座標:± r * cos角度 + width / 2
Y座標:± r * sin角度 + height / 2

func LeftRoundAction(_ sender: UIPanGestureRecognizer){
        if(CurrLeftProgress>=0 && CurrLeftProgress<=100){
            let translation = sender.translation(in: self)

            if(sender.state == UIGestureRecognizerState.possible){
            }else if(sender.state == UIGestureRecognizerState.began){
                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

            }else if(sender.state == UIGestureRecognizerState.changed){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.ended){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

                leftEX = translation.x + leftEX
                leftEY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.cancelled){
            }else if(sender.state == UIGestureRecognizerState.failed){
            }else if(sender.state == UIGestureRecognizerState.recognized){
            }

            //兩種情況 y > r or y < r 計算角度 和 新的座標

            var a:CGFloat=0;
            var b:CGFloat=0;

            if(leftY > centerY){
                a = leftY - centerY
                b = centerX - leftX
            }else{
                a = centerX - leftY
                b = centerY - leftX
            }

            let tanA = a / b;

            if(leftY > centerY){
                leftY = centerY + radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) - atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }else{
                leftY = centerY - radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) +  atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }
            leftX = centerX - radius * cos(atan(tanA)) - smallRoundR

            if(angleLeft >= startAngle && angleLeft <= endAngle){
                leftRound?.frame = CGRect(x: leftX, y: leftY, width: smallRoundR * 2, height: smallRoundR * 2)
                //change

                self.bar?.leftProgress(angleRight: angleRight, angleLeft: angleLeft)

                CurrLeftProgress = (angleLeft - startAngle) * maxLeftProgress / (endAngle - startAngle)

                if(CurrLeftProgress>100){
                    CurrLeftProgress = 100
                }else if(CurrLeftProgress<0){
                    CurrLeftProgress = 0
                }
            }
            print("\(TAG)   ------->2  CurrLeftProgress \(CurrLeftProgress)")

            if(sender.state == UIGestureRecognizerState.began){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.changed){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.ended){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }
        }
    }

說明:上述教大家一些圖形的基本繪製,和一些原理,幫助大家學習,真正的這種程式碼和效果在下面。

全部程式碼

//
//  SeekBarView.swift
//  Pacsafe
//
//  Created by NOA-Labs on 11/30/16.
//  Copyright © 2016 NOA-Labs. All rights reserved.
//

import UIKit

class SeekBarView: UIView {

    let TAG:String = "SeekBarView"

    @IBOutlet var contentView: UIView!

    var gestureRight:UIPanGestureRecognizer? = nil
    var gestureLeft:UIPanGestureRecognizer? = nil

    var seekbarDelegate:SeekBarViewDelegate? = nil


    var leftRound:RoundView?
    var rightRound:RoundView?

    var bar:BarView?

    //小球的半徑
    let smallRoundR:CGFloat = 21

    //起始角度
    let startAngle:CGFloat = 0.08
    let endAngle:CGFloat = 3.0

    //右邊的角度
    var angleRight:CGFloat = 0
    //左邊的角度
    var angleLeft:CGFloat = 0
    //長度
    var width:CGFloat = 0
    //寬度
    var height:CGFloat = 0

    var leftX:CGFloat = 0,leftY:CGFloat = 0;
    var leftEX:CGFloat = 0,leftEY:CGFloat = 0;

    var rightX:CGFloat = 0,rightY:CGFloat = 0;
    var rightEX:CGFloat = 0,rightEY:CGFloat = 0;

    var centerX:CGFloat = 0,centerY:CGFloat = 0;

    var radius:CGFloat = 0

    let maxLeftProgress:CGFloat = 100
    let maxRightProgress:CGFloat = 100

    var CurrLeftProgress:CGFloat = 67
    var CurrRightProgress:CGFloat = 70


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initFromXIB()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initFromXIB()
    }

    func initFromXIB() {
        let bundle = Bundle(for: type(of: self))
        //nibName是你定義的xib檔名
        let nib = UINib(nibName: "SeekBarView", bundle: bundle)
        contentView = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
        contentView.frame = bounds

        gestureRight = UIPanGestureRecognizer(target: self, action: #selector(SeekBarView.RightRoundAction))

        gestureLeft = UIPanGestureRecognizer(target: self, action: #selector(SeekBarView.LeftRoundAction))

        self.addSubview(contentView)

        print("\(TAG)   ------->  initFromXIB \(self.frame)")

        // 根據當前的進度 計算應該旋轉的角度
        angleRight=startAngle + (endAngle - startAngle) / maxRightProgress * CurrRightProgress
        angleLeft=startAngle + (endAngle - startAngle) / maxLeftProgress * CurrLeftProgress
    }

    func RightRoundAction(_ sender: UIPanGestureRecognizer){

        if(CurrRightProgress>=0 && CurrRightProgress<=100){
            let translation = sender.translation(in: self)

            if(sender.state == UIGestureRecognizerState.possible){
            }else if(sender.state == UIGestureRecognizerState.began){
                rightX = translation.x + rightEX
                rightY = translation.y + rightEY
            }else if(sender.state == UIGestureRecognizerState.changed){
                rightX = translation.x + rightEX
                rightY = translation.y + rightEY
            }else if(sender.state == UIGestureRecognizerState.ended){
                rightX = translation.x + rightEX
                rightY = translation.y + rightEY

                rightEX = translation.x + rightEX
                rightEY = translation.y + rightEY
            }else if(sender.state == UIGestureRecognizerState.cancelled){
            }else if(sender.state == UIGestureRecognizerState.failed){
            }else if(sender.state == UIGestureRecognizerState.recognized){
            }

            //兩種情況 y > r or y < r 計算角度 和 新的座標

            var a:CGFloat=0;
            var b:CGFloat=0;

            if(rightY > centerY){
                a = rightY - centerY
                b = rightX - centerX
            }else{
                a = centerY - rightY
                b = rightX - centerX
            }

            let tanA = a / b;

            if(rightY > centerY){
                rightY = centerY + radius * sin(atan(tanA)) - smallRoundR

                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) - atan(tanA)

                if (abs(self.angleRight - angle) < 0.3 || self.angleRight == 0){
                    self.angleRight = angle
                }else{
                    return
                }
            }else{
                rightY = centerY - radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) + atan(tanA)

                if (abs(self.angleRight - angle) < 0.3 || self.angleRight == 0){
                    self.angleRight = angle
                }else{
                    return
                }
            }
            rightX = centerX + radius * cos(atan(tanA)) - smallRoundR



            if(angleRight >= startAngle && angleRight <= endAngle){
                rightRound?.frame = CGRect(x:rightX, y:rightY, width: smallRoundR * 2, height: smallRoundR * 2)

                bar?.leftProgress(angleRight: angleRight, angleLeft: angleLeft)
            }


            CurrRightProgress = (angleRight - startAngle) * maxRightProgress / (endAngle - startAngle)

            print("\(TAG)   ------->1  CurrRightProgress \(CurrRightProgress)")
            if(CurrRightProgress>100){
                CurrRightProgress = 100
            }else if(CurrRightProgress<0){
                CurrRightProgress = 0
            }

            if(sender.state == UIGestureRecognizerState.began){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.REIGHT, progress: CurrRightProgress)
            }else if(sender.state == UIGestureRecognizerState.changed){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.REIGHT, progress: CurrRightProgress)
            }else if(sender.state == UIGestureRecognizerState.ended){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.REIGHT, progress: CurrRightProgress)
            }
        }
    }

    func LeftRoundAction(_ sender: UIPanGestureRecognizer){
        if(CurrLeftProgress>=0 && CurrLeftProgress<=100){
            let translation = sender.translation(in: self)

            if(sender.state == UIGestureRecognizerState.possible){
            }else if(sender.state == UIGestureRecognizerState.began){
                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

            }else if(sender.state == UIGestureRecognizerState.changed){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.ended){

                leftX = translation.x + leftEX
                leftY = translation.y + leftEY

                leftEX = translation.x + leftEX
                leftEY = translation.y + leftEY
            }else if(sender.state == UIGestureRecognizerState.cancelled){
            }else if(sender.state == UIGestureRecognizerState.failed){
            }else if(sender.state == UIGestureRecognizerState.recognized){
            }

            //兩種情況 y > r or y < r 計算角度 和 新的座標

            var a:CGFloat=0;
            var b:CGFloat=0;

            if(leftY > centerY){
                a = leftY - centerY
                b = centerX - leftX
            }else{
                a = centerX - leftY
                b = centerY - leftX
            }

            let tanA = a / b;

            if(leftY > centerY){
                leftY = centerY + radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) - atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }else{
                leftY = centerY - radius * sin(atan(tanA)) - smallRoundR
                let angle = CGFloat(M_PI_2 - M_PI_2 * 0.025) +  atan(tanA)

                if (abs(self.angleLeft - angle) < 0.3 || self.angleLeft == 0){
                    self.angleLeft = angle
                }else{
                    return
                }
            }
            leftX = centerX - radius * cos(atan(tanA)) - smallRoundR

            if(angleLeft >= startAngle && angleLeft <= endAngle){
                leftRound?.frame = CGRect(x: leftX, y: leftY, width: smallRoundR * 2, height: smallRoundR * 2)
                //change

                self.bar?.leftProgress(angleRight: angleRight, angleLeft: angleLeft)

                CurrLeftProgress = (angleLeft - startAngle) * maxLeftProgress / (endAngle - startAngle)

                if(CurrLeftProgress>100){
                    CurrLeftProgress = 100
                }else if(CurrLeftProgress<0){
                    CurrLeftProgress = 0
                }
            }
            print("\(TAG)   ------->2  CurrLeftProgress \(CurrLeftProgress)")

            if(sender.state == UIGestureRecognizerState.began){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.changed){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }else if(sender.state == UIGestureRecognizerState.ended){
                self.seekbarDelegate?.fetchChangeProgress(direction: SeekBarViewDirectionStyle.LEFT, progress: CurrLeftProgress)
            }
        }
    }

    override func draw(_ rect: CGRect) {
        print("\(TAG)   ------->2  draw \(self.frame)")
    }

    override func draw(_ layer: CALayer, in ctx: CGContext) {

        self.width = frame.width
        self.height = frame.height


        // 繪製靜態的背景
        let progress = ProgressBarView(frame:CGRect.init(x: 0, y: 0, width:self.width, height:self.height))
        self.contentView.addSubview(progress)


        //根據當前的角度 計算當前的座標

        if(self.width>self.height){
            self.radius = self.height / 2
        }else{
            self.radius = self.width / 2
        }

        self.leftX = self.width / 2 - self.radius * sin(angleLeft) - smallRoundR
        //Y軸需要區分上半部分和下半部分
        if(angleLeft<1.504){// 下半部分
            self.leftY = self.height / 2 + self.radius * cos(angleLeft + CGFloat(M_PI_2 * 0.025)) - smallRoundR
        }else{//上半部分
            self.leftY = self.height / 2 - self.radius * cos(CGFloat(M_PI) - angleLeft - CGFloat(M_PI_2 * 0.025)) - smallRoundR
        }

        self.rightX = self.width / 2 + self.radius * sin(angleRight) - smallRoundR

        //Y軸需要區分上半部分和下半部分
        if(angleRight<1.504){// 下半部分
            self.rightY = self.height / 2 + self.radius * cos(angleRight + CGFloat(M_PI_2 * 0.025)) - smallRoundR
        }else{//上半部分
            self.rightY = self.height / 2 - self.radius * cos(CGFloat(M_PI) - angleRight - CGFloat(M_PI_2 * 0.025)) - smallRoundR
        }

        print("\(TAG)   ------->1  angleLeft \(self.angleLeft)    angleRight:\(angleRight)")

        leftEX = leftX
        leftEY = leftY

        rightEX = rightX
        rightEY = rightY


        centerY = self.height / 2
        centerX = self.width / 2

        print("\(TAG)   ------->1  frame:\(self.frame)")

        // 繪製動態的當前進度
        if(bar == nil){
            bar = BarView(frame:CGRect.init(x: 0, y: 0, width: self.width, height: self.height))
            bar?.setProgress(angleRight:angleRight,angleLeft:angleLeft)
            bar?.backgroundColor = UIColor.clear
            self.contentView.addSubview(bar!)
        }else{
            bar?.setProgress(angleRight:angleRight,angleLeft:angleLeft)
        }

        //draw round
        if(leftRound == nil){
            leftRound = RoundView(frame:CGRect(x: leftX, y: leftY, width: smallRoundR * 2, height: smallRoundR * 2))
            leftRound?.backgroundColor=UIColor.clear
            self.addSubview(leftRound!)

            leftRound?.isUserInteractionEnabled = true
            leftRound?.addGestureRecognizer(gestureLeft!)
        }else{
            leftRound?.frame = CGRect(x: leftX, y: leftY, width: smallRoundR * 2, height: smallRoundR * 2)
        }

        if(rightRound == nil){
            rightRound = RoundView(frame:CGRect(x: rightX, y: rightY, width: smallRoundR * 2, height: smallRoundR * 2))
            self.addSubview(rightRound!)
            rightRound?.backgroundColor=UIColor.clear

            rightRound?.isUserInteractionEnabled = true
            rightRound?.addGestureRecognizer(gestureRight!)
        }else{
            rightRound?.frame = CGRect(x: rightX, y: rightY, width: smallRoundR * 2, height: smallRoundR * 2)
        }
    }
}