aboutsummaryrefslogtreecommitdiff
path: root/colorpicker
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2018-02-23 20:52:21 -0500
committerGitHub <noreply@github.com>2018-02-23 20:52:21 -0500
commit3d7c85bd97261116a090a7202b0e0ed2625b5d73 (patch)
treef8d6409ef847a1ca0c0ba3640a27984703de470e /colorpicker
parent20f0d085d6940be30b076a8cff3de25fe4a6e21a (diff)
downloadkau-3d7c85bd97261116a090a7202b0e0ed2625b5d73.tar.gz
kau-3d7c85bd97261116a090a7202b0e0ed2625b5d73.tar.bz2
kau-3d7c85bd97261116a090a7202b0e0ed2625b5d73.zip
Misc (#140)
* Nullify task * Rewrite circle view * Add better encapsulation * Update annotations * Update kpref annotations * Begin writing tests for color picker * Add color selection tests * Update changelog * Cleanup
Diffstat (limited to 'colorpicker')
-rw-r--r--colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt17
-rw-r--r--colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt23
-rw-r--r--colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerView.kt190
3 files changed, 117 insertions, 113 deletions
diff --git a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt
index c74cba1..fe7a7a6 100644
--- a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt
+++ b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt
@@ -45,6 +45,8 @@ class CircleView @JvmOverloads constructor(context: Context, attrs: AttributeSet
private val outerPaint: Paint = Paint().apply { isAntiAlias = true }
private val whitePaint: Paint = Paint().apply { isAntiAlias = true; color = Color.WHITE }
private val innerPaint: Paint = Paint().apply { isAntiAlias = true }
+ val colorSelected: Boolean
+ get() = selected
private var selected: Boolean = false
var withBorder: Boolean = false
set(value) {
@@ -107,25 +109,16 @@ class CircleView @JvmOverloads constructor(context: Context, attrs: AttributeSet
fun animateSelected(selected: Boolean) {
if (this.selected == selected) return
- this.selected = true // We need to draw the other bands
+ this.selected = selected // We need to draw the other bands
val range = if (selected) Pair(-borderWidthSmall, borderWidthLarge) else Pair(borderWidthLarge, -borderWidthSmall)
- val animator = ValueAnimator.ofFloat(range.first, range.second)
- with(animator) {
+ ValueAnimator.ofFloat(range.first, range.second).apply {
reverse()
duration = 150L
addUpdateListener { animation ->
whiteOuterBound = animation.animatedValue as Float
invalidate()
+
}
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- this@CircleView.selected = selected
- }
-
- override fun onAnimationCancel(animation: Animator) {
- this@CircleView.selected = selected
- }
- })
start()
}
}
diff --git a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt
index a3dcc2d..ccebb71 100644
--- a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt
+++ b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt
@@ -31,19 +31,26 @@ class ColorBuilder : ColorContract {
interface ColorContract {
var title: String?
- var titleRes: Int @StringRes set
+ @setparam:StringRes
+ var titleRes: Int
var allowCustom: Boolean
var allowCustomAlpha: Boolean
var isAccent: Boolean
- var defaultColor: Int @StringRes set
- var doneText: Int @StringRes set
- var backText: Int @StringRes set
- var cancelText: Int @StringRes set
+ @setparam:StringRes
+ var defaultColor: Int
+ @setparam:StringRes
+ var doneText: Int
+ @setparam:StringRes
+ var backText: Int
+ @setparam:StringRes
+ var cancelText: Int
+ @setparam:StringRes
var presetText: Int
- @StringRes set
- var customText: Int @StringRes set
+ @setparam:StringRes
+ var customText: Int
var dynamicButtonColors: Boolean
- var circleSizeRes: Int @DimenRes set
+ @setparam:DimenRes
+ var circleSizeRes: Int
var colorCallback: ((selectedColor: Int) -> Unit)?
var colorsTop: IntArray?
var colorsSub: Array<IntArray>?
diff --git a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerView.kt b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerView.kt
index 778b775..dfb0773 100644
--- a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerView.kt
+++ b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerView.kt
@@ -26,21 +26,23 @@ import java.util.*
internal class ColorPickerView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ScrollView(context, attrs, defStyleAttr) {
- var selectedColor: Int = -1
- var isInSub: Boolean = false
- var isInCustom: Boolean = false
- var circleSize: Int = context.dimen(R.dimen.kau_color_circle_size).toInt()
+ val selectedColor: Int
+ get() = _selectedColor
+ private var _selectedColor: Int = -1
+ private var isInSub: Boolean = false
+ private var isInCustom: Boolean = false
+ private var circleSize: Int = context.dimen(R.dimen.kau_color_circle_size).toInt()
@SuppressLint("PrivateResource")
- val backgroundColor = context.resolveColor(R.attr.md_background_color,
+ private val backgroundColor = context.resolveColor(R.attr.md_background_color,
if (context.resolveColor(android.R.attr.textColorPrimary).isColorDark) Color.WHITE else 0xff424242.toInt())
- val backgroundColorTint = backgroundColor.colorToForeground()
- lateinit var dialog: MaterialDialog
- lateinit var builder: ColorContract
- lateinit var colorsTop: IntArray
- var colorsSub: Array<IntArray>? = null
- var topIndex: Int = -1
- var subIndex: Int = -1
- var colorIndex: Int
+ private val backgroundColorTint = backgroundColor.colorToForeground()
+ private lateinit var dialog: MaterialDialog
+ private lateinit var builder: ColorContract
+ private lateinit var colorsTop: IntArray
+ private var colorsSub: Array<IntArray>? = null
+ private var topIndex: Int = -1
+ private var subIndex: Int = -1
+ private var colorIndex: Int
get() = if (isInSub) subIndex else topIndex
set(value) {
if (isInSub) subIndex = value
@@ -54,24 +56,29 @@ internal class ColorPickerView @JvmOverloads constructor(
}
}
- val gridView: FillGridView by bindView(R.id.md_grid)
- val customFrame: LinearLayout by bindView(R.id.md_colorChooserCustomFrame)
- val customColorIndicator: View by bindView(R.id.md_colorIndicator)
- val hexInput: EditText by bindView(R.id.md_hexInput)
- val alphaLabel: TextView by bindView(R.id.md_colorALabel)
- val alphaSeekbar: SeekBar by bindView(R.id.md_colorA)
- val alphaValue: TextView by bindView(R.id.md_colorAValue)
- val redSeekbar: SeekBar by bindView(R.id.md_colorR)
- val redValue: TextView by bindView(R.id.md_colorRValue)
- val greenSeekbar: SeekBar by bindView(R.id.md_colorG)
- val greenValue: TextView by bindView(R.id.md_colorGValue)
- val blueSeekbar: SeekBar by bindView(R.id.md_colorB)
- val blueValue: TextView by bindView(R.id.md_colorBValue)
+ private val gridView: FillGridView by bindView(R.id.md_grid)
+ private val customFrame: LinearLayout by bindView(R.id.md_colorChooserCustomFrame)
+ private val customColorIndicator: View by bindView(R.id.md_colorIndicator)
+ private val hexInput: EditText by bindView(R.id.md_hexInput)
+ private val alphaLabel: TextView by bindView(R.id.md_colorALabel)
+ private val alphaSeekbar: SeekBar by bindView(R.id.md_colorA)
+ private val alphaValue: TextView by bindView(R.id.md_colorAValue)
+ private val redSeekbar: SeekBar by bindView(R.id.md_colorR)
+ private val redValue: TextView by bindView(R.id.md_colorRValue)
+ private val greenSeekbar: SeekBar by bindView(R.id.md_colorG)
+ private val greenValue: TextView by bindView(R.id.md_colorGValue)
+ private val blueSeekbar: SeekBar by bindView(R.id.md_colorB)
+ private val blueValue: TextView by bindView(R.id.md_colorBValue)
- var customHexTextWatcher: TextWatcher? = null
- var customRgbListener: SeekBar.OnSeekBarChangeListener? = null
+ private var customHexTextWatcher: TextWatcher? = null
+ private var customRgbListener: SeekBar.OnSeekBarChangeListener? = null
init {
+ init()
+ }
+
+ @SuppressLint("PrivateResource")
+ private fun init() {
View.inflate(context, R.layout.md_dialog_colorchooser, this)
}
@@ -79,29 +86,33 @@ internal class ColorPickerView @JvmOverloads constructor(
this.builder = builder
this.dialog = dialog
this.colorsTop = with(builder) {
- if (colorsTop != null) colorsTop!!
- else if (isAccent) ColorPalette.ACCENT_COLORS
- else ColorPalette.PRIMARY_COLORS
+ when {
+ colorsTop != null -> colorsTop!!
+ isAccent -> ColorPalette.ACCENT_COLORS
+ else -> ColorPalette.PRIMARY_COLORS
+ }
}
this.colorsSub = with(builder) {
- if (colorsTop != null) colorsSub
- else if (isAccent) ColorPalette.ACCENT_COLORS_SUB
- else ColorPalette.PRIMARY_COLORS_SUB
+ when {
+ colorsTop != null -> colorsSub
+ isAccent -> ColorPalette.ACCENT_COLORS_SUB
+ else -> ColorPalette.PRIMARY_COLORS_SUB
+ }
}
- this.selectedColor = builder.defaultColor
+ this._selectedColor = builder.defaultColor
if (builder.allowCustom) {
if (!builder.allowCustomAlpha) {
alphaLabel.gone()
alphaSeekbar.gone()
alphaValue.gone()
- hexInput.hint = String.format("%06X", selectedColor)
+ hexInput.hint = String.format("%06X", _selectedColor)
hexInput.filters = arrayOf(InputFilter.LengthFilter(6))
} else {
- hexInput.hint = String.format("%08X", selectedColor)
+ hexInput.hint = String.format("%08X", _selectedColor)
hexInput.filters = arrayOf(InputFilter.LengthFilter(8))
}
}
- if (findColor(builder.defaultColor) || !builder.allowCustom) isInCustom = true //when toggled this will be false
+ if (findColor(_selectedColor) || !builder.allowCustom) isInCustom = true // when toggled this will be false
toggleCustom()
}
@@ -127,21 +138,20 @@ internal class ColorPickerView @JvmOverloads constructor(
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
- selectedColor = try {
+ _selectedColor = try {
Color.parseColor("#$s")
} catch (e: IllegalArgumentException) {
Color.BLACK
}
-
- customColorIndicator.setBackgroundColor(selectedColor)
+ customColorIndicator.setBackgroundColor(_selectedColor)
if (alphaSeekbar.isVisible) {
- val alpha = Color.alpha(selectedColor)
+ val alpha = Color.alpha(_selectedColor)
alphaSeekbar.progress = alpha
alphaValue.text = String.format(Locale.CANADA, "%d", alpha)
}
- redSeekbar.progress = Color.red(selectedColor)
- greenSeekbar.progress = Color.green(selectedColor)
- blueSeekbar.progress = Color.blue(selectedColor)
+ redSeekbar.progress = Color.red(_selectedColor)
+ greenSeekbar.progress = Color.green(_selectedColor)
+ blueSeekbar.progress = Color.blue(_selectedColor)
isInSub = false
topIndex = -1
subIndex = -1
@@ -150,7 +160,7 @@ internal class ColorPickerView @JvmOverloads constructor(
override fun afterTextChanged(s: Editable?) {}
}
- hexInput.setText(selectedColor.toHexString(builder.allowCustomAlpha, false))
+ hexInput.setText(_selectedColor.toHexString(builder.allowCustomAlpha, false))
hexInput.addTextChangedListener(customHexTextWatcher)
customRgbListener = object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
@@ -181,11 +191,11 @@ internal class ColorPickerView @JvmOverloads constructor(
blueSeekbar.setOnSeekBarChangeListener(customRgbListener)
if (alphaSeekbar.isVisible)
alphaSeekbar.setOnSeekBarChangeListener(customRgbListener)
- hexInput.setText(selectedColor.toHexString(alphaSeekbar.isVisible, false))
+ hexInput.setText(_selectedColor.toHexString(alphaSeekbar.isVisible, false))
gridView.fadeOut(onFinish = { gridView.gone() })
customFrame.fadeIn()
} else {
- findColor(selectedColor)
+ findColor(_selectedColor)
if (builder.allowCustom) dialog.setActionButton(DialogAction.NEUTRAL, builder.customText)
dialog.setActionButton(DialogAction.NEGATIVE, if (isInSub) builder.backText else builder.cancelText)
gridView.fadeIn(onStart = this::invalidateGrid)
@@ -201,9 +211,9 @@ internal class ColorPickerView @JvmOverloads constructor(
}
fun refreshColors() {
- if (!isInCustom) findColor(selectedColor)
- //Ensure that our tinted color is still visible against the background
- val visibleColor = if (selectedColor.isColorVisibleOn(backgroundColor)) selectedColor else backgroundColorTint
+ if (!isInCustom) findColor(_selectedColor)
+ // Ensure that our tinted color is still visible against the background
+ val visibleColor = if (_selectedColor.isColorVisibleOn(backgroundColor)) _selectedColor else backgroundColorTint
if (builder.dynamicButtonColors) {
dialog.getActionButton(DialogAction.POSITIVE).setTextColor(visibleColor)
dialog.getActionButton(DialogAction.NEGATIVE).setTextColor(visibleColor)
@@ -222,11 +232,8 @@ internal class ColorPickerView @JvmOverloads constructor(
topIndex = -1
subIndex = -1
colorsTop.forEachIndexed { index, topColor ->
- if (findSubColor(color, index)) {
- topIndex = index
- return true
- }
- if (topColor == color) { // If no sub colors exists and top color matches
+ // First check for sub colors, then if the top color matches
+ if (findSubColor(color, index) || topColor == color) {
topIndex = index
return true
}
@@ -235,14 +242,8 @@ internal class ColorPickerView @JvmOverloads constructor(
}
private fun findSubColor(@ColorInt color: Int, topIndex: Int): Boolean {
- if (colorsSub == null || colorsSub!!.size <= topIndex) return false
- colorsSub!![topIndex].forEachIndexed { index, subColor ->
- if (subColor == color) {
- subIndex = index
- return true
- }
- }
- return false
+ subIndex = colorsSub?.getOrNull(topIndex)?.indexOfFirst { color == it } ?: -1
+ return subIndex != -1
}
private fun invalidateGrid() {
@@ -256,50 +257,53 @@ internal class ColorPickerView @JvmOverloads constructor(
inner class ColorGridAdapter : BaseAdapter(), OnClickListener, OnLongClickListener {
override fun onClick(v: View) {
- if (v.tag != null && v.tag is String) {
- val tags = (v.tag as String).split(":")
- if (colorIndex == tags[0].toInt()) {
- colorIndex = tags[0].toInt() //Go to sub list if exists
- return
- }
- if (colorIndex != -1) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(false)
- selectedColor = tags[1].toInt()
- refreshColors()
- val currentSub = isInSub
- colorIndex = tags[0].toInt()
- if (currentSub == isInSub) (gridView.getChildAt(colorIndex) as CircleView).animateSelected(true)
- //Otherwise we are invalidating our grid, so there is no point in animating
- }
+ val (pos, color) = v.tagData ?: return
+ if (colorIndex == pos && isInSub)
+ return
+ circleAt(colorIndex)?.animateSelected(false)
+ _selectedColor = color
+ colorIndex = pos
+ refreshColors()
+ if (isInSub)
+ circleAt(colorIndex)?.animateSelected(true)
+ // Otherwise we are invalidating our grid, so there is no point in animating
}
- override fun onLongClick(v: View): Boolean {
- if (v.tag != null && v.tag is String) {
- val tag = (v.tag as String).split(":")
- val color = tag[1].toInt()
- (v as CircleView).showHint(color)
- return true
+ private fun circleAt(index: Int): CircleView? =
+ if (index == -1) null
+ else gridView.getChildAt(index) as? CircleView
+
+ private val View.tagData: Pair<Int, Int>?
+ get() {
+ val tags = (tag as? String)?.split(":") ?: return null
+ val pos = tags[0].toIntOrNull() ?: return null
+ val color = tags[1].toIntOrNull() ?: return null
+ return pos to color
}
- return false
+
+ override fun onLongClick(v: View): Boolean {
+ val (_, color) = v.tagData ?: return false
+ (v as? CircleView)?.showHint(color) ?: return false
+ return true
}
- override fun getItem(position: Int): Any = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position]
+ override fun getItem(position: Int): Int = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position]
override fun getCount(): Int = if (isInSub) colorsSub!![topIndex].size else colorsTop.size
override fun getItemId(position: Int): Long = position.toLong()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
- val view: CircleView = if (convertView == null)
- CircleView(context).apply { layoutParams = AbsListView.LayoutParams(circleSize, circleSize) }
- else
- convertView as CircleView
- val color: Int = if (isInSub) colorsSub!![topIndex][position] else colorsTop[position]
+ val view: CircleView = convertView as? CircleView ?: CircleView(context).apply {
+ layoutParams = AbsListView.LayoutParams(circleSize, circleSize)
+ setOnClickListener(this@ColorGridAdapter)
+ setOnLongClickListener(this@ColorGridAdapter)
+ }
+ val color: Int = getItem(position)
return view.apply {
setBackgroundColor(color)
isSelected = colorIndex == position
tag = "$position:$color"
- setOnClickListener(this@ColorGridAdapter)
- setOnLongClickListener(this@ColorGridAdapter)
}
}
}