Create color value dynamically - android

My app shows buttons depending on logged user.
Details and Conditions of the app:
The number of buttons that will be shown is unknown (could be 1, 20, 100, etc.)
A different color must be set for each button
I want some control over color values, as text is always white, so text must always be readable
How can I create a dynamic color value under these conditions?

First you need to decide how "readable" the text should be. WCAG 2.1 is a common standard for accessibility requirements, and its minimum contrast requirement is 4.5:1. (The spec definition is here, or for a lighter overview with some nice examples there's this.)
That amount of contrast will guarantee your text can be read by people with "moderately low vision". 3:1 is recommended for "standard text and standard vision", but I'd always recommend going with the accessible ratio - especially since you're using white and random colours, which will vary in readability quite a bit!
WCAG 2.1 also allows for that 3:1 ratio for large-scale text, which is 18pt or 14pt bold. That works out to about 40dp for regular text and 31dp for bold. Depends on the font too, and also since you often use sp instead the user can control how big the fonts are, so it complicates things. But basically, big text = lower contrast requirements
Now you have your contrast level, you can check whether your colour combo meets it or not. There's a nice tool in ColorUtils that does this for you - it uses the WCAG formula for calculating contrast:
fun meetsMinContrast(#ColorInt foreground: Int, #ColorInt background: Int): Boolean {
val minContrast = 4.5
val actual = ColorUtils.calculateContrast(foreground, background)
return actual >= minContrast
}
As for actually generating colours, I don't know the "smart" way to do it, if there is one - you could possibly generate a colour space of valid colours when paired with white, and pick from that, but I don't really know anything about it - maybe someone else can chime in with a better solution!
So for a purely naive random approach:
val colours = generateSequence {
Color.valueOf(
Random.nextInt(0, 255),
Random.nextInt(0, 255),
Random.nextInt(0, 255)
)
}
val accessibleBackgrounds = colours.filter { background ->
meetsMinContrast(Color.WHITE, background)
}
and then you have a stream of valid, random colours you can set on your buttons.
If you don't like the "just keep generating randomly until you hit one that works" approach (which is pretty hacky and could be slow if you're unlucky), you could work with HSV instead:
fun getAccessibleBackground(): Color {
val hsv = floatArrayOf(
Random.nextFloat() * 360f,
Random.nextFloat(),
Random.nextFloat()
)
var colour: Color
while(true) {
colour = Color.HSVtoColor(hsv)
if (meetsMinContrast(Color.WHITE, colour)) return colour
// darken the colour a bit (subtract 1% value) and try again
hsv[2] -= 0.01f
}
}
(I'd be more explicit about error checking and providing a fallback there, especially if you made it work with a foreground colour parameter, but that should always return a valid colour before you need to worry about subtracting from value too much - it's just a simple example)

Related

What is the SpanStyle equivalent of RelativeSizeSpan?

In Compose, we use AnnotatedString as a replacement for Spanned. However, I cant seem to find a way to replicate the behaviour of RelativeSizeSpan with a SpanStyle.
The relevant options I can see for SpanStyle are:
fontSize: TextUnit - not useful because this only accepts absolute text sizes, and I need my span style to scale the original font size by some factor
textGeometricTransform: TextGeometricTransform - not useful because TextGeometricTransform only performs X transformations, and I need the text to be scaled in both X and Y.
Can anyone share some insight?
You can achieve it with SpanStyle, but you need to use the em TextUnit, which is a relative font size unit. It means that 1em is equal to the current font size and 2em is a font two times bigger.
Here is the code demonstrating how it behaves on two Texts with different font size:
val annotatedString = buildAnnotatedString {
append("This is a ")
withStyle(style = SpanStyle(fontSize = 2.em)) {
append("big")
}
append(" text")
}
Column {
Text(annotatedString, fontSize = 20.sp)
Text(annotatedString, fontSize = 40.sp)
}
The word big is 2 times bigger than other words in the same Text.
You can also see that it makes the big word from first Text the same size (2 * 20sp) as the other words in the second Text (40sp).

Using Google's Text Recognition API to detect horizontal lines instead of blocks in images

Is there a way to detect full-sized, horizontal lines (max width) instead of text blocks in images using Google's Text Recognition API? Say, if I wanted to retrieve the total due from a receipt image like this:
... because as of now, the API detects texts in blocks instead in an arbitrary order like this:
... and no, TextBlock's getComponents() only retrieves the Lines within each TextBlock since TextBlock is at the top of the Text hierarchy (TextBlock contains Line) as mentioned in the docs here. If only this API could start off with Lines instead of TextBlocks for an image bitmap's frame...
I even tried resizing the text blocks' bounding box (rectangle) with hard-coded coordinates to hopefully detect the full line of text, "Chicken Bowl... 7.15", but to no avail as shown below:
val textRecognizer = TextRecognizer.Builder(this).build()
if (textRecognizer.isOperational) {
val imageFrame = Frame.Builder()
.setBitmap(imageBitmap)
.build()
val textBlocks = textRecognizer.detect(imageFrame)
for (i in 0 until textBlocks.size()) {
val textBlock = textBlocks.get(textBlocks.keyAt(i))
textBlock.boundingBox.set(97, 1244, 1235, 1292)
val textValue = textBlock.value
Log.d(LOG_TAG, "textValue: " + textValue)
}
}
You are right - the API just gives you the coordinates of the text blocks and of the lines within the blocks. Therefore you have to sort out all lines by yourself.
Before you can start with this, you should rotate the coordinates in a way that the baselines are (more or less) horizontal. Be aware that the coordinates of the bounding boxes are sometimes in a wrong order. You should sort these misleading boxes out, when you calculate the needed rotation angle.
After you rotated all the coordinates, you can start to match all word-bounding-boxes and create the lines that you need. In my code I did this by comparing the vertical center of the boxes. Be aware of fragements with very small or very large height (in comparison to the average height). You have to give them a special treatment.
I can asure you that this works well with receipts as shown in your example.

What does the number next to a color mean?

I'm reading this article about material design. In the list of colors, there is a number next to each color that seems to darken the color as its value goes up.
What does this number means, more precisely?
Edit: As all the answers are about the hex values, I'm adding this edit to clarify the question. My question is about the left hand side numbers like 700, 500, ... not the hex numbers (#3f51b5, ...)
Edit 2: In RGB model, each of the Red, Green or Blue can have a value in scale of 0 - 255. 0 means lack of the color and 255 means the color exists in full power. Is there a numerical meaning for the left hand side numbers? Can I calculate the '700' of a color, assuming '500' of it is #3F51B5? Or these numbers are just name for different shades of color in a palette?
Those values are the relative lightness/darkness or "tint" of the color, where 50 is lightest and 900 is darkest. The Material Design guidelines suggest using the 500 tint as your primary color and the 700 tint as the darker status bar color.
The Annn values are if you're using the color as your accent color.
See https://www.google.com/design/spec/style/color.html#color-ui-color-application
The other answers are correct as well, but I think you are asking about the left hand side numbers. You can use these to specify your theme colors in Angular-Material.
$mdThemingProvider.theme('default')
.primaryPalette('purple', {
'default': '700', // by default use shade from the palette for primary intentions
'hue-1': 'A400', // use shade for the <code>md-hue-1</code> class
'hue-2': '600', // use shade for the <code>md-hue-2</code> class
'hue-3': 'A100' // use shade for the <code>md-hue-3</code> class
})
// If you specify less than all of the keys, it will inherit from the default shades
.accentPalette('deep-purple', {
'default': '200' // use shade 200 for default, and keep all other shades the same
})
The numbers you see in use, correspond the left hand side numbers to set up colors. My site is using variations of the purple theme in this example, and I can set the hue's different from what the Google settings were.
The numbers refer to the darkness of a shade variant (inverse of HSL lightness). The numbers use a scale of 0 to 1000, where 0 is white and 1000 is black.
From the Android documentation for R.color:
system_accent1_0
Lightest shade of the accent color used by the system. White.
system_accent1_10
Shade of the accent system color at 99% lightness.
system_accent1_100
Shade of the accent system color at 90% lightness.
And so on.
The general formula is shadeVariant = 1000 - (lightness * 1000).
(The one curious exception is that the 500 shade variant uses 49% lightness instead of 50%, but this is probably an implementation detail that could be ignored when re-implementing.)
Knowing the formula should additionally make it easy to calculate these values directly. For example, using Polished, you would be able to setLightness(accent, 0.9) to calculate the 100 shade variant of an accent colour yourself in a Node.js app. From there it would be easy to build a utility function that can generate any variant of any colour.
I found some information in this angular.io guide to theming:
In Material Design, each hues in a palette has an identifier number. These identifier numbers include 50, and then each 100 value between 100 and 900. The numbers order hues within a palette from lightest to darkest.
There you have it, the answer to your question: Those numbers are just static identifiers.
As an example of how they can be used, this guide to "Reading hues from palettes" states:
You can use the get-color-from-palette function to get specific hues from a palette by their number identifier.
#use '~#angular/material' as mat;
$my-palette: mat.define-palette(mat.$indigo-palette);
.my-custom-style {
background: mat.get-color-from-palette($my-palette, '500');
color: mat.get-color-from-palette($my-palette, '500-contrast');
}
The number which you are seeing is the HEX (hexadecimal) values for the color tone and the color. You can use it in CSS files instead of writing i.e. black, white or blue.
From WIKI:
"A hex triplet is a six-digit, three-byte hexadecimal number used in HTML, CSS, SVG, and other computing applications to represent colors. The bytes represent the red, green and blue components of the color. One byte represents a number in the range 00 to FF (in hexadecimal notation), or 0 to 255 in decimal notation"
More about it here https://en.wikipedia.org/wiki/Web_colors
The number is codes given to each and all colorssupported by the system. Eachh color code contains symbol "#" and 6 letters or numbers. These numbers are in hexadecimal numeral system. For example "FF" in hexadecimal represents number 255 in Decimal.
Meaning of symbols:
The first two symbols in HTML color code represents the intensity of red color. 00 is the least and FF is the most intense. The third and fourth represents intensity of green and fifth and sixth represents the intensity of blue. So with combining the intensity of red, green and blue we can mix almost any color that our heart desire.
Examples:
#FF0000: With this HTML code we tell browser to show maximum of red and no green and no blue. The result is of course pure red color.
#00FF00 - This results in pure green.

Shape Drawable gradientRadius only works in pixels. Isn't this useless? % values don't work

I want to use shape drawable with a radial gradient as a background for a View. According to the javadoc the radius of the gradient can be set as a specific value (presumably pixels) or as a percent:
android:gradientRadius
Radius of the gradient, used only with radial gradient.
May be a floating point value, such as "1.2".
May be a fractional value, which is a floating point number appended
with either % or %p, such as "14.5%". The % suffix always means a
percentage of the base size; the optional %p suffix provides a size
relative to some parent container.
Correct me if I'm wrong, but using a pixel value here is completely useless since this gradient will look totally different from one screen density to another (tested, and yes this is true). I tried to go with the % and %p values, but they didn't work at all.
I dove into the Android source code to see how gradientRadius is being processed and found this in the GradientDrawable Class:
TypedValue tv = a.peekValue(
com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
boolean radiusRel = tv.type == TypedValue.TYPE_FRACTION;
st.mGradientRadius = radiusRel ?
tv.getFraction(1.0f, 1.0f) : tv.getFloat();
} else ...
}
Ah HA! So all adding % or %p does is divide my value by 100. (I tested this because the documentation for TypeValue.getFraction() is even more unclear). 65% became 0.65. Makes sense, but serves no useful purpose.
So what is the best way to use gradientRadius?
PS.
I have added my background programmatically using a GradientDrawable and I am getting the desired results. I used GradientDrawable.setGradientRadius() with a value relative to the view width and get a consistent Gradient across devices.
To work with all the screen size you have to set radius in dp. You can calculate px from dp by this method
public float pxfromDp(float dp) {
return dp * getResources().getDisplayMetrics().density;
}
So for example you want to set GradientDrawable.setGradientRadius(pxfromDp(7));

How do I use access colours and gradients on Android?

So i'm having a problem with using decimal colors in android.
I'm getting color codes from an external database.
For Example:
16777215 is white
16711680 is red
Now I want to use this colors to create a GradientDrawable.
int color = myDbReader.getColor(); //returns the decimal color code
new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[] {color, 0 });
The code example produces always a completley white gradient object.
I tried to google how to convert the decimal colors in the right way.. But i didn't find anything.
Can anyone give me a hint how to use the decimal colors in the right way?
You should read about android colours.
Android colors are 32bit unsigned integers, not signed ones!
Also Android colors are using Alpha bit, please read the article and the solution will be clear.
Even if SO is extremely good source of information you should google it before asking, it was the first link on Google when I entered "colour android".
BTW, I'm not an android developer.
Android colors are ARGB units, so your color should actually consist of 4 bytes with 1 of them for the alpha value. In order to convert you can have something like this
0xFF000000 & yourColor
which will set the opacity of your color to 100%(alpha byte is set). Another option is to use Color.parseColor after converting your integer to a hexadecimal string. Also do not refer to colors by some magic constants. The static fields of the Color class are designed so that you don't have to do that.
So based on your suggestions I spent last night with reading about the different types of Color codes and finally found the solution:
The given numbers are access color codes so i only have to:
int color = myDbReader.getColor(); //returns the decimal color code
gradient = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[] {Color.rgb((value/256/256)%256, (value/256)%256, value%256), 0 });
Thanks four your help!

Categories

Resources