この記事の内容は、カラー セレクターを 0 から 1 まで開発し、最初にレンダリングを行うことです。
関連するソース コード ファイルは記事の最後に添付されます。
その効果を図に示します。この記事では、このようなコントロールをカスタマイズする方法について説明します。コードは記事の最後にあります。
考える
(1) 実装方法
(2) ビジネス層に渡すデータ形式
(3) 実装内容
以上の 3 点を考慮する必要があります。
(1) この種のレイアウトは通常のコントロールではサポートされていないため、ここで選択した実装方法はカスタム ビューであることをお勧めします。
(2) ビジネス レイヤーの場合、行う必要があるのは、カラー データをコントロールに渡し、コントロールが色を選択した後、カラー データがそのままビジネス レイヤーに返されることです。ここでは、文字列形式が選択されています。カラー値。「#ffffff」のように渡します。
(3) 実装にあたっては、キャンバスを使って円弧を正しく描く方法、実際の角度の計算方法、象限間隔のデータ変換、座標データの変換、クリックイベントの座標計算など、考えるべき問題がたくさんあります。
さて、よく考えてから作業を始めましょう。
実装プロセス
##### (1) クラスの定義
同様のコントロールを作成するには、考え方の概要を理解する必要があります。つまり、コントロール ライブラリがどのようなメソッドを外部に公開するのか、また、外部からのデータをどのように処理する必要があるのかということです。外側を内側に。したがって、最初に抽象メソッドを定義する必要があります。
(1) 制御抽象クラス:
interface LibColorCirclePickerApi {
fun setColor(color: Array<Array<String>>)
fun setLibColorCirclePickerListener(listener: LibColorCirclePickerListener)
fun removeLibColorCirclePickerListener()
}
(2) 外部監視クラス:
interface LibColorCirclePickerListener {
fun selInfo(info: LibColorSelInfo)
}
これらのメソッドを定義すると、継承を通じて実装できます。
#####(2) 描画色の値を実現するためのビューのカスタマイズ
キャンバス描画の知識が必要となりますので、予めご了承ください。
図に示すように、図面は 2 つのレベルに分かれています。1 つは中央の円、もう 1 つはカラー ブロックです。
描画する前に、その後の描画を容易にするために次の変数を決定する必要があります。
(1) 描画の中心座標点、x、y 点。ここでは x/2、y/2 をとります。
(2) 描画半径、ここでは一般に最短辺を描画半径とします。
(3) 中心円の半径を描き、(2)に一定の比率を掛けて中心円の半径を求めます。
(4) 中心円を除いた残りの半径長さを均等に分割し、各層のカラーブロックの半径長さを計算します。
(5) 関連する中間変数値(カラーブロック座標セットなど)を初期化します
関連する変数を初期化した後、描画を開始します。
中心円を描く
中心の円については、キャンバスの円描画メソッドを直接呼び出すだけで、冗長になる必要はありません。
カラーブロックを描く
カラー ブロックの場合は、まずカラー ブロックのレイヤー数を決定する必要があります。これにより、カラー ブロックの各レイヤーの半径の長さが決まります。ここでの層の数は、外部から渡される 2 次元配列によって決まります。受信する 2 次元配列の場合、1 つの次元がレベルを決定し、2 つの次元は 1 つの次元の下のカラー ブロックの配列になります。
以上のことから、「カラーブロックの各層の半径長さ」は、「残りの半径」を「カラーブロックの層数」で割ることで求めることができる。
次に、360 を「レイヤーごとのカラー ブロックの数」で割って、各カラー ブロックが占める角度を取得します。
最後にブラシのストローク幅を設定することで描画できます。
最後に、描画プロセス中に生成された「半径範囲値」、「角度範囲値」、および「カラー値」は、後続のタッチ イベント呼び出しのために中間変数を通じて保存されます。
ps:
ここにはペイントのストローク幅に関する実装の詳細があります。実際には描画座標の両端の拡大に基づいています。たとえば、(x, y) (0,10) のように Y 座標の両端で直線を描く場合、ストローク幅が 10 の場合、直角の辺の幅は次のようになります。 (0,5)-(0,15) 中間に基づいています。
したがって、ストローク幅を設定して描画した後、描画実装で位置を予約する必要があります。
タッチイベント
タッチ イベントの場合は、onTouchEvent で処理する必要があり、親クラスのメソッドは次のとおりです。
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
}
MotionEvent.ACTION_MOVE -> {
}
MotionEvent.ACTION_UP -> {
val currentX = event.x
val currentY = event.y
dealWithUpEvent(currentX, currentY)
}
MotionEvent.ACTION_CANCEL -> {
}
}
return true
}
ここで、true を返すことにより、すべてのイベントがこのビューによってインターセプトされ、処理されます。
クリックしたときに中心座標と X、Y 座標が得られたと想像してください。「象限間隔」ロジックを導き出すことは可能でしょうか? 次に、数式tanメソッドを通じて、対応する角度を取得できます。
角度と象限がわかったので、最終的な実際のクリック角度とクリック座標点の半径を計算できます。コアコードは次のとおりです。
/**
* 处理手势抬起事件
* */
private fun dealWithUpEvent(currentX: Float, currentY: Float) {
//判断象限
val trainX = (currentX - mCenterX)
val trainY = (mCenterY - currentY)
//求半径长度
val x = abs(abs(currentX) - abs(mCenterX)).toDouble()
val y = abs(abs(mCenterY) - abs(currentY)).toDouble()
//半径
val trainRadius = sqrt((x.pow(2.0) + y.pow(2.0)))
//角度
val angle = atan(abs(x) / abs(y)) * 180 / PI
//计算后,再根据象限,转换最终的角度
var trainAngle = 0f
if (trainX <= 0 && trainY >= 0) {
trainAngle = (90 - angle + 270).toFloat()
} else if (trainX >= 0 && trainY >= 0) {
trainAngle = angle.toFloat()
} else if (trainX <= 0 && trainY <= 0) {
trainAngle = (angle + 180).toFloat()
} else if (trainX >= 0 && trainY <= 0) {
trainAngle = (90 - angle + 90).toFloat()
} else {
return
}
// Log.d("dealWithUpEvent", "angle $angle + 转换角度 $trainAngle 半径: $trainRadius")
//通过象限,角度,半径,三个条件,确定具体的位置
val filterList: MutableList<LibColorInnerPosInfo> = ArrayList()
this.mColorPosInfo.filter {
it.startAngle <= trainAngle && it.endAngle >= trainAngle
}.filter {
it.startRadius >= trainRadius && it.endRadius <= trainRadius
}.filter {
!it.color.isNullOrBlank()
}.toCollection(filterList)
if (filterList.size != 1) {
return
}
mListener?.selInfo(LibColorSelInfo().apply {
this.colorStr = filterList.first().color
})
}
ここまででプロセス全体を説明しましたが、まだ理解できない点がある場合は、ソース コードをダウンロードして理解してください。
ソースコード
抽出コード:naf2
それだけです - - - - - - - - - - - - - - - - - - - - - - - - --------------