Skip to content

Commit 6917bcf

Browse files
add HexText for setting color using text input
1 parent 215f04c commit 6917bcf

File tree

5 files changed

+211
-22
lines changed

5 files changed

+211
-22
lines changed

app/src/main/java/com/smarttoolfactory/composecolorpicker/MainActivity.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
66
import androidx.compose.animation.ExperimentalAnimationApi
7-
import androidx.compose.foundation.Canvas
8-
import androidx.compose.foundation.layout.*
7+
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.fillMaxSize
99
import androidx.compose.material.*
1010
import androidx.compose.runtime.Composable
1111
import androidx.compose.runtime.rememberCoroutineScope
1212
import androidx.compose.ui.Modifier
13-
import androidx.compose.ui.geometry.Rect
1413
import androidx.compose.ui.graphics.Color
15-
import androidx.compose.ui.graphics.Path
16-
import androidx.compose.ui.graphics.drawscope.Stroke
1714
import androidx.compose.ui.unit.dp
18-
import com.google.accompanist.pager.*
15+
import com.google.accompanist.pager.ExperimentalPagerApi
16+
import com.google.accompanist.pager.HorizontalPager
17+
import com.google.accompanist.pager.PagerState
18+
import com.google.accompanist.pager.rememberPagerState
1919
import com.smarttoolfactory.composecolorpicker.demo.*
2020
import com.smarttoolfactory.composecolorpicker.ui.theme.ComposeColorPickerTheme
2121
import kotlinx.coroutines.launch
@@ -84,6 +84,7 @@ private fun HomeContent() {
8484
4 -> GradientAngleDemo()
8585
5 -> HSVHSLGradientDemo()
8686
6 -> ColorfulSliderDemo()
87+
7 -> HexConversionDemo()
8788
else -> ColorModeConversionDemo()
8889
}
8990
}
@@ -98,5 +99,6 @@ internal val tabList =
9899
"Gradient Angle",
99100
"HSV&HSL Gradients",
100101
"Colorful Sliders",
102+
"Hex Conversions",
101103
"Color Mode Conversions"
102104
)

app/src/main/java/com/smarttoolfactory/composecolorpicker/demo/ColorPickerDemo.kt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.smarttoolfactory.composecolorpicker.demo
33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.*
55
import androidx.compose.foundation.rememberScrollState
6+
import androidx.compose.foundation.shape.RoundedCornerShape
67
import androidx.compose.foundation.verticalScroll
78
import androidx.compose.material.ButtonDefaults
89
import androidx.compose.material.OutlinedButton
@@ -12,10 +13,10 @@ import androidx.compose.ui.Alignment
1213
import androidx.compose.ui.Modifier
1314
import androidx.compose.ui.graphics.Color
1415
import androidx.compose.ui.text.font.FontWeight
16+
import androidx.compose.ui.text.style.TextAlign
1517
import androidx.compose.ui.unit.dp
1618
import androidx.compose.ui.unit.sp
1719
import com.smarttoolfactory.colorpicker.dialog.*
18-
import com.smarttoolfactory.colorpicker.ui.Blue400
1920
import com.smarttoolfactory.colorpicker.util.colorToHex
2021
import com.smarttoolfactory.colorpicker.widget.ColorDisplayRoundedRect
2122
import com.smarttoolfactory.composecolorpicker.ui.theme.backgroundColor
@@ -47,9 +48,19 @@ fun ColorPickerDemo() {
4748
initialColor = previousColor,
4849
currentColor = color
4950
)
50-
Spacer(modifier = Modifier.height(10.dp))
51-
Text(text = hexString, fontSize = 18.sp, color = Blue400, fontWeight = FontWeight.Bold)
52-
Spacer(modifier = Modifier.height(10.dp))
51+
Spacer(modifier = Modifier.height(12.dp))
52+
53+
Text(
54+
text = hexString,
55+
fontSize = 20.sp,
56+
color = color,
57+
textAlign = TextAlign.Center,
58+
fontWeight = FontWeight.Bold,
59+
modifier = Modifier
60+
.background(color = Color(0x66000000), shape = RoundedCornerShape(12.dp))
61+
.padding(6.dp)
62+
)
63+
Spacer(modifier = Modifier.height(8.dp))
5364

5465
val buttonModifier = Modifier
5566
.fillMaxWidth()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.smarttoolfactory.composecolorpicker.demo
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.border
5+
import androidx.compose.foundation.layout.*
6+
import androidx.compose.foundation.rememberScrollState
7+
import androidx.compose.foundation.verticalScroll
8+
import androidx.compose.material.Text
9+
import androidx.compose.runtime.*
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.graphics.Color
12+
import androidx.compose.ui.text.font.FontWeight
13+
import androidx.compose.ui.unit.dp
14+
import androidx.compose.ui.unit.sp
15+
import com.smarttoolfactory.colorpicker.util.colorToHex
16+
import com.smarttoolfactory.colorpicker.util.colorToHexAlpha
17+
import com.smarttoolfactory.colorpicker.widget.HexAlphaTextField
18+
import com.smarttoolfactory.composecolorpicker.ui.theme.backgroundColor
19+
20+
@Composable
21+
fun HexConversionDemo() {
22+
Column(
23+
modifier = Modifier
24+
.background(backgroundColor)
25+
.fillMaxSize()
26+
.padding(8.dp)
27+
.verticalScroll(rememberScrollState())
28+
) {
29+
30+
var color by remember {
31+
mutableStateOf(
32+
Color.hsl(0f, 0.5f, 0.5f)
33+
)
34+
}
35+
36+
var hexString by remember { mutableStateOf(colorToHexAlpha(color)) }
37+
38+
Text(
39+
"HEX: $hexString",
40+
color = color,
41+
fontWeight = FontWeight.Bold,
42+
fontSize = 20.sp
43+
)
44+
Spacer(modifier = Modifier.height(10.dp))
45+
46+
HexAlphaTextField(
47+
modifier = Modifier
48+
.wrapContentWidth()
49+
.border(1.dp, Color.Red),
50+
hexString = hexString,
51+
onTextChange = {
52+
hexString = it
53+
}, onColorChange = {
54+
color = it
55+
}
56+
)
57+
}
58+
}
59+

colorpicker/src/main/java/com/smarttoolfactory/colorpicker/util/HexConversionUtil.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import androidx.compose.ui.graphics.Color
66
HEX-ColorInt Conversion
77
*/
88
fun hexToColorInt(colorString: String): Int {
9-
return android.graphics.Color.parseColor(colorString)
9+
val completeColorString = if (colorString.first() == '#') colorString else "#$colorString"
10+
println("🌈 hexToColor: $colorString, completeColorString: $completeColorString")
11+
return android.graphics.Color.parseColor(completeColorString)
1012
}
1113

1214
/*
@@ -36,3 +38,10 @@ fun hexToARGB(colorString: String, argbIn: IntArray) {
3638
fun hexToColor(colorString: String): Color {
3739
return Color(hexToColorInt(colorString))
3840
}
41+
42+
43+
// Regex for checking if this string is a 6 char hex or 8 char hex
44+
val hexWithAlphaRegex = "^#?([0-9a-fA-F]{6}|[0-9a-fA-F]{8})\$".toRegex()
45+
val hexRegex = "^#?([0-9a-fA-F]{6})\$".toRegex()
46+
// Check only on char if it's in range of 0-9, a-f, A-F
47+
val hexRegexSingleChar = "[a-fA-F0-9]".toRegex()
Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,130 @@
11
package com.smarttoolfactory.colorpicker.widget
22

3+
import androidx.compose.foundation.shape.RoundedCornerShape
34
import androidx.compose.foundation.text.BasicTextField
5+
import androidx.compose.material.*
46
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
58
import androidx.compose.ui.graphics.Color
6-
import com.smarttoolfactory.colorpicker.util.argbToHex
9+
import androidx.compose.ui.graphics.Shape
10+
import androidx.compose.ui.graphics.SolidColor
11+
import androidx.compose.ui.text.AnnotatedString
12+
import androidx.compose.ui.text.TextStyle
13+
import androidx.compose.ui.text.input.OffsetMapping
14+
import androidx.compose.ui.text.input.TransformedText
15+
import androidx.compose.ui.text.input.VisualTransformation
16+
import com.smarttoolfactory.colorpicker.util.hexRegexSingleChar
17+
import com.smarttoolfactory.colorpicker.util.hexToColor
18+
import com.smarttoolfactory.colorpicker.util.hexWithAlphaRegex
719

820
@Composable
9-
fun HexText(color: Color, onTextChange: (Color) -> Unit) {
10-
val hexString = argbToHex(
11-
color.alpha,
12-
color.red,
13-
color.green,
14-
color.blue
15-
)
16-
BasicTextField(
17-
value = hexString,
21+
fun HexAlphaTextField(
22+
modifier: Modifier = Modifier,
23+
hexString: String,
24+
textStyle: TextStyle = LocalTextStyle.current,
25+
colors: TextFieldColors = TextFieldDefaults.textFieldColors(
26+
focusedIndicatorColor = Color.Transparent,
27+
unfocusedIndicatorColor = Color.Transparent,
28+
disabledIndicatorColor = Color.Transparent
29+
),
30+
shape: Shape = RoundedCornerShape(25),
31+
onTextChange: (String) -> Unit,
32+
onColorChange: (Color) -> Unit
33+
) {
34+
35+
TextField(
36+
modifier = modifier,
37+
visualTransformation = HexVisualTransformation(),
38+
singleLine = true,
39+
textStyle = textStyle,
40+
colors = colors,
41+
shape = shape,
42+
value = hexString.removePrefix("#"),
1843
onValueChange = {
1944

45+
if (it.length <= 8) {
46+
47+
if (it.isNotEmpty()) {
48+
val lastChar = it.last()
49+
val isHexChar = hexRegexSingleChar.matches(lastChar.toString())
50+
if (isHexChar) {
51+
onTextChange(it)
52+
}
53+
} else {
54+
onTextChange(it)
55+
}
56+
57+
// Hex String with 6 or 8 chars matches a Color
58+
if (hexWithAlphaRegex.matches(it)) {
59+
onColorChange(hexToColor(it))
60+
}
61+
}
2062
}
2163
)
22-
}
64+
}
65+
66+
@Composable
67+
internal fun BasicHexTextField(
68+
modifier: Modifier = Modifier,
69+
value: String,
70+
textStyle: TextStyle = LocalTextStyle.current,
71+
textColor: Color,
72+
backgroundColor: Color,
73+
cursorColor: Color = Color.Black,
74+
hint: String,
75+
shape: Shape = RoundedCornerShape(25),
76+
onValueChange: (String) -> Unit,
77+
) {
78+
79+
Surface(
80+
shape = shape,
81+
color = backgroundColor,
82+
contentColor = textColor
83+
) {
84+
BasicTextField(
85+
modifier = modifier,
86+
textStyle = textStyle,
87+
value = value,
88+
cursorBrush = SolidColor(cursorColor),
89+
onValueChange = onValueChange
90+
) {
91+
if (value.isEmpty()) {
92+
Text(hint)
93+
}
94+
}
95+
}
96+
}
97+
98+
class HexVisualTransformation : VisualTransformation {
99+
100+
override fun filter(text: AnnotatedString): TransformedText {
101+
102+
val trimmed = if (text.text.length >= 8) text.text.substring(0..7) else text.text
103+
104+
val output = if (trimmed.isEmpty()) {
105+
trimmed
106+
} else {
107+
"#$trimmed"
108+
}
109+
110+
return TransformedText(AnnotatedString(output), hexOffsetMapping)
111+
}
112+
113+
private val hexOffsetMapping = object : OffsetMapping {
114+
115+
override fun originalToTransformed(offset: Int): Int {
116+
117+
// when empty return only 1 char for #
118+
if (offset == 0) return offset
119+
if (offset <= 7) return offset + 1
120+
return 9
121+
}
122+
123+
override fun transformedToOriginal(offset: Int): Int {
124+
if (offset == 0) return offset
125+
// #ffABCABC
126+
if (offset <= 8) return offset - 1
127+
return 8
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)