Android 自定義View -- 圓形進度條,文字旋轉
阿新 • • 發佈:2019-02-14
最近公司招聘打個廣告:
公司屬於外企福利待遇好,
每週英語課,
關鍵時單身妹子多[色][色]
詳情 [點選全棧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)
}
}
}