主頁 > 移動端開發 > Android 自定義圓形取色盤

Android 自定義圓形取色盤

2022-08-16 08:45:47 移動端開發

概述 

這是一個自定義色盤,根據點,直線和圓的幾何學加上hsv顏色模型完成

 技術點

幾何:

圓的標準方程式:
(x-a)2+(y-b)2=r2

直線一般方程式:

已知直線上的兩點P1(X1,Y1) P2(X2,Y2), P1 P2兩點不重合, AX+BY+C=0 A = Y2 - Y1 B = X1 - X2 C = X2*Y1 - X1*Y2

點與圓的位置關系:

點在圓內、點在圓上、點在圓外,假設圓的半徑為r,點到圓心的距離為d,則有:d<r點在圓內,d=r點在圓上,d>r點在圓外,
點P(x1,y1) 與圓(x-a)2+(y-b)2=r2的位置關系:
當(x-a)2+(y-b)2>r2時,則點P在圓外,
當(x-a)2+(y-b)2=r2時,則點P在圓上,
當(x-a)2+(y-b)2<r2時,則點P在圓內,

直線的斜率:

一條直線與某平面直角坐標系橫軸正半軸方向的夾角的正切值即該直線相對于該坐標系的斜率
斜率 k=tanα(α傾斜角)
與x軸垂直的直線不存在斜率
直線的斜率公式為:k=y2-y1)/(x2-x1)或(y1-y2)/(x1-x2)

象限:

(x,y) 是象限中的一點
第一象限中的點:x > 0, y > 0
第二象限中的點:x < 0, y > 0
第三象限中的點:x < 0, y < 0
第四象限中的點:x > 0, y < 0
值得注意的是原點和坐標軸上的點不屬于任何象限,

解方程:

 

 

 Android:

自定義View、ComposeShader、SweepGradient、RadialGradient、Paint,

上原始碼

代碼都比較簡單這里就不介紹了

  1 class RoundColorPaletteHSV360 constructor(context: Context, attrs: AttributeSet?, defStyleAttr:Int): View(context,attrs,defStyleAttr) {
  2 
  3     constructor(context: Context):this(context,null,0)
  4 
  5     constructor(context: Context,attrs: AttributeSet?):this(context,attrs,0)
  6 
  7     //取色范圍半徑
  8     var radius:Float = 0f
  9         private set(value){
 10             field = value-stroke-gap-colorRadius
 11         }
 12 
 13     //取色圓半徑
 14     var colorRadius = 50f
 15         set(value){
 16             if (value >= 0)
 17                 field = value
 18         }
 19 
 20     //取色圓邊框半徑
 21     var colorStroke = 8f
 22         set(value){
 23             if (value >= 0)
 24                 field = value
 25         }
 26 
 27     //取色圓邊框顏色
 28     var colorStrokeColor = Color.BLACK
 29 
 30     //取色顏色
 31     var color = Color.WHITE
 32 
 33     //邊框半徑
 34     var stroke = 24f
 35         set(value){
 36             if (value >= 0)
 37                 field = value
 38         }
 39 
 40     //邊框顏色
 41     var strokeColor = Color.YELLOW
 42 
 43     //間隙半徑
 44     var gap = 4f
 45         set(value){
 46             if (value >= 0)
 47                 field = value
 48         }
 49 
 50     var isOutOfBounds:Boolean = false
 51 
 52     private val paint = Paint()
 53     private val colors1:IntArray
 54     private val positions1 :FloatArray
 55     private val colors2:IntArray
 56     private val positions2 :FloatArray
 57     private var xColor: Float = 0f
 58     private var yColor: Float = 0f
 59     private var colorChangeCallBack: ColorChangeCallBack? = null
 60 
 61     init {
 62 //        <declare-styleable name="RoundColorPaletteHSV360">
 63 //        <attr name="colorRadius" format="dimension"/>
 64 //        <attr name="colorStroke" format="dimension"/>
 65 //        <attr name="gap" format="dimension"/>
 66 //        <attr name="stroke" format="dimension"/>
 67 //        <attr name="colorStrokeColor" format="color"/>
 68 //        <attr name="strokeColor" format="color"/>
 69 //        <attr name="isOutOfBounds" format="boolean"/>
 70 //        </declare-styleable>
 71         val  typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundColorPaletteHSV360)
 72         colorRadius = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_colorRadius,50f)
 73         colorStroke = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_colorStroke,8f)
 74         gap = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_gap,4f)
 75         stroke = typedArray.getDimension(R.styleable.RoundColorPaletteHSV360_stroke,24f)
 76         colorStrokeColor = typedArray.getColor(R.styleable.RoundColorPaletteHSV360_colorStrokeColor,Color.BLACK)
 77         strokeColor = typedArray.getColor(R.styleable.RoundColorPaletteHSV360_strokeColor,Color.YELLOW)
 78         isOutOfBounds = typedArray.getBoolean(R.styleable.RoundColorPaletteHSV360_isOutOfBounds,false)
 79         typedArray.recycle()
 80 
 81         val colorCount1 = 360
 82         val colorCount2 = 255
 83         val colorAngleStep = 360 / colorCount1
 84         positions1 = FloatArray(colorCount1+1){i-> i/(colorCount1*1f)}
 85         var hsv = floatArrayOf(0f, 1f, 1f)
 86         colors1 = IntArray(colorCount1+1){ i->
 87             hsv[0] = 360 - i * colorAngleStep % 360f
 88             Color.HSVToColor(hsv)
 89         }
 90         hsv = floatArrayOf(0f, 0f, 1f)
 91         positions2 = FloatArray(colorCount2+1){i-> i/(colorCount2*1f)}
 92         colors2 = IntArray(colorCount2+1){ i->
 93             Color.HSVToColor(255 * (colorCount2 -i) / colorCount2 , hsv)
 94         }
 95 
 96     }
 97 
 98     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
 99         val width = MeasureSpec.getSize(widthMeasureSpec)/2f
100         val height = MeasureSpec.getSize(heightMeasureSpec)/2f
101         radius = if(width-height<0) width else height
102         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
103     }
104 
105     @SuppressLint("DrawAllocation")
106     override fun onDraw(canvas: Canvas?) {
107         createColorWheel(canvas)
108         createColorRadius(canvas,xColor,yColor)
109         super.onDraw(canvas)
110     }
111 
112     //色盤
113     private fun createColorWheel(canvas: Canvas?){
114         paint.reset()
115         paint.isAntiAlias = true
116         paint.shader = ComposeShader(
117             SweepGradient(
118                 width / 2f,
119                 height / 2f,
120                 colors1,
121                 positions1
122             ), RadialGradient(
123                 width / 2f,
124                 height / 2f,
125                 radius-colorRadius,
126                 colors2,
127                 positions2,
128                 Shader.TileMode.CLAMP
129             ),  PorterDuff.Mode.SRC_OVER)
130         canvas?.drawCircle(width / 2f,height / 2f,radius,paint)
131         paint.shader = null
132         paint.style = Paint.Style.STROKE
133         paint.strokeWidth = stroke.toFloat()
134         paint.color = strokeColor
135         canvas?.drawCircle(width / 2f,height / 2f,radius+gap+stroke/2,paint)
136     }
137 
138     //取色圓
139     private fun createColorRadius(canvas: Canvas?, rx: Float, ry: Float){
140         var x = rx
141         var y = ry
142         if(x==0f || y==0f ){
143             x = width / 2f
144             y = height / 2f
145         }
146         paint.reset()
147         paint.isAntiAlias = true
148         paint.color = color
149         canvas?.drawCircle(x,y,colorRadius,paint)
150         paint.style = Paint.Style.STROKE
151         paint.color = colorStrokeColor
152         paint.strokeWidth = colorStroke
153         canvas?.drawCircle(x,y,colorRadius+colorStroke/2,paint)
154     }
155 
156     fun setColorChangeCallBack(colorChangeCallBack: ColorChangeCallBack){
157         this.colorChangeCallBack = colorChangeCallBack
158     }
159 
160     override fun onTouchEvent(event: MotionEvent?): Boolean {
161         event?.let {
162             val pointToCircle = pointToCircle(it.x, it.y)
163             if( pointToCircle <= radius - if(isOutOfBounds) 0f else (colorRadius-colorStroke)){
164                 xColor =  it.x
165                 yColor =  it.y
166                 color =  Color.HSVToColor(floatArrayOf((angdeg(it.x,it.y)),pointToCircle/radius,1f))
167                 colorChangeCallBack?.onChange(color)
168             }else{
169                 findPoint( it.x, it.y)
170             }
171             invalidate()
172         }
173         return true
174     }
175 
176     //點到圓心距離
177     private fun pointToCircle(x: Float = width / 2f, y: Float = height / 2f ) =
178         sqrt((x - width / 2f)*(x - width / 2f) + (y-height / 2f)*(y-height / 2f))
179 
180     //離目標最近的點 待開發
181     private fun findPoint(x1: Float = width / 2f, y1: Float = height / 2f ){
182 //      直線一般方程
183 //      以圓心為坐標0,0 重新計算點(a,b)坐標
184         val a = y1 - height / 2f
185         val b = x1 - width / 2f
186         val r = radius - if(isOutOfBounds) 0f else (colorRadius-colorStroke)
187         //r^2/((b/a)^2+1)的開平方
188         yColor = sqrt( (r * r) / ((b / a) * (b / a) + 1))
189         //判斷開平方的正負值
190         if(a<0) yColor = -yColor
191         xColor =  (b*yColor)/a + width / 2f
192         yColor += height / 2f
193         color =  Color.HSVToColor(floatArrayOf((angdeg(xColor,yColor)),1f,1f))
194         colorChangeCallBack?.onChange(color)
195 
196     }
197 
198 
199     //角度
200     private fun angdeg(x: Float = width / 2f, y: Float = height / 2f ):Float{
201         var angdeg = 0
202 
203         //特殊角度, 與x軸垂直不存在斜率
204         if(x - width / 2f == 0f && y - height / 2f < 0){
205             angdeg = 90
206         }else{
207             //到圓心的斜率
208             val k = ((y-height / 2f)*(y-height / 2f))/((x - width / 2f)*(x - width / 2f))
209             //二分法
210             var min = 0.00
211             var max = 90.00
212             while(max-min>1){
213                 val deg = min + (max - min) / 2
214                 if(k>tan(toRadians(deg))) min = deg else max = deg
215             }
216             angdeg = (max-1).toInt()
217         }
218 
219         if((x - width / 2f <= 0f && y - height / 2f <= 0f)) {//第二象限 90~180
220             angdeg = 180 - angdeg
221         }else if((x - width / 2f <= 0f && y - height / 2f >= 0f)) {//第三象限 180~270
222             angdeg += 180
223         }else if((x - width / 2f >= 0f && y - height / 2f >= 0f)) {//第四象限 270~360
224             angdeg = 360 - angdeg
225         }
226 
227         return angdeg.toFloat()
228     }
229 
230     interface ColorChangeCallBack{
231         fun onChange(@ColorInt color: Int)
232     }
233 }
View Code

 

# 圓形取色盤

#### 倉庫地址
https://gitee.com/yangguizhong/circular-color-plate


#### 安裝教程

gradle 7.0以上的在 settings.gradle 添加:

```
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {url "https://gitee.com/yangguizhong/circular-color-plate/raw/master"}
jcenter() // Warning: this repository is going to shut down soon
}
}
```
gradle 7.0以下的在根 build.gradle 添加:

```
allprojects {
repositories {
google()
mavenCentral()
maven {url "https://gitee.com/yangguizhong/circular-color-plate/raw/master"}
}
}
```
build.gradle 添加:

```
implementation 'com.xiaotie.colorPicker:colorPicker:1.0.0'
```


#### 使用說明


```
<com.wifiled.ipixels.view.RoundColorPaletteHSV360
android:id="@+id/color_picker"
android:layout_
android:layout_height="300dp"
app:gap="1dp"//取色背景和背景邊框的間隙
app:colorRadius="20dp"//取色圓的半徑
app:colorStroke="3dp"//取色圓的邊框寬度
app:stroke="8dp"//背景邊框的寬度
app:strokeColor="#FFF7A6"//背景邊框的顏色
app:isOutOfBounds="true"//是否超出邊框
app:colorStrokeColor="#2E3C3C"//取色圓的邊框寬度
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
```
```
colorPickerView.setColorChangeCallBack(object:RoundColorPaletteHSV360.ColorChangeCallBack{
override fun onChange(color: Int) {
//回傳的顏色
}

})

```

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/501964.html

標籤:Android

上一篇:Android 知識體系

下一篇:Android 知識體系

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more