How can a color resource be used to change the background colour for a MD3 top app bar in Jetpack Compose?
Understandably, a colors property is available but it's not clear what to use for the above.
Color.kt
val MyColor = Color(0,5,5,255)
MainActivity.kt
MediumTopAppBar(title = {Text(text = "")})
The colors parameter is supposed to be used like so.
There's usually a Default Companion for these things, which provides a convenience function for modifying colors. For example, the default companion for Top bar colors is just TopAppBarDefaults.
Since you are referring to medium bars, we'll use the following
TopAppBarDefaults.mediumTopAppBarColors(
containerColor = Color(...) //Add your own color here, just to clarify.
)
These functions usually provide a containerColor and a contentColor parameter by default.
Solving your problems is... Super-easy, barely an inconvenience.
Related
I need to apply a common font to all the Text() used in my entire app. Currently i am applying it manually to each text using a style or font as follows. How can i specify this as a global theme for the app? In normal xml layouts, i was using a custom TextView widget to achieve this.
Text(
text = stringResource(id = R.string.userName),
style = typography.h2,
fontSize = 20.sp,
)
Jetpack Compose supports theming and a uniform font can be applied by specifying a custom font in app theme. Steps to do this are as follows.
Copy your custom font to res/font directory ex: helvetica_nue.ttf
Create a Kotlin file (Type.kt) and add your Font family object here. Specify the defaultFontFamily as your custom font. If you wish to perform some additional customization you may add your styles to body1 typography, as this is the default typography used for all Text() unless specified.
private val myCustomFont = FontFamily(
Font(R.font.helvetica_nue),
)
val Typography = Typography(
defaultFontFamily = myCustomFont,
)
Create a Kotiln file (Theme.kt or any name) and declare you app theme
#Composable
fun MyApplicationTheme(content: #Composable () -> Unit) {
MaterialTheme(
typography = Typography,
)
}
In your activity/fragment, wrap your apps main Composable within this theme
MyApplicationTheme {
NewsDetailScreen()
}
Now your app will display text in the specified font wherever the theme is applied.
Reference: https://developer.android.com/jetpack/compose/themes/material#typography
If you want to use the same typeface throughout, specify the defaultFontFamily parameter and omit the fontFamily of any TextStyle elements:
One similar approach would be to first create custom implementation over Text composable, lets call it CustomText.
Then for your use case, you can write wrappers over most used text styles, for eg
#Composable
TextH2(text : String) {
//call your own wrapper over framework Text composable
CustomText(text = text, typography = h2)
}
To make things simpler you can wrap font resource, weight and size together and then create specific implementation of this class
for e.g
val h2 = CustomTypography(font = R.font.your-ttf-file, fontSize = 20.sp, fontWeight = Medium)
Above styling data is handled inside one single composable, which in our case is CustomText.
I've this question, maybe trivial, setting up BasicTextField textStyle property,for set text color for example, I can use one of this 3:
textStyle = TextStyle(color = Color.White)
textStyle = LocalTextStyle.current.copy(color = Color.White)
textStyle = MaterialTheme.typography.body1.copy(color = Color.White)
someone could explain me the difference pros/cons? Thank you in advance
textStyle = TextStyle(color = Color.White)
This one is just using hardcoded style for composable. Nothing fancy
textStyle = LocalTextStyle.current.copy(color = Color.White)
This one is copying style that is provided by a CompositionLocal via composable higher up in the hierarchy. The advantage is that you do not hardcode anything about the style in your composable. Instead - higher composable is providing the style dynamically for you. Disadvange - the LocalTextStyle might not be set. Although MaterialTheme containers (like Surface, Button or Scaffold) sets that one for you.
As an example usage - when you create a MaterialTheme Button you can do a simple Text(text = "whatever") and so not set the style. The text style will be provided automatically by the Button itself, so all of the buttons have the same text styles. This is using LocalTextStyle under the hood
textStyle = MaterialTheme.typography.body1.copy(color = Color.White)
This is using a style from a theme (MaterialTheme in this example). The advantage is that you stating you want to use particular style of text. Details about the style are provided by a theme. Cannot think of disadvantages here really, other than binding your composable to the given theme
Problem:
Update the bottomNavBar icon's colour 'programmatically'. There are several ways to update if thru XML (out of scope here as the requirement is to update on the fly).
tried code:
bottomNavigationView.itemIconTintList = getBackgroundColorStates()
// colour state list based on states
private fun getBackgroundColorStates() = ColorStateList(
arrayOf(
intArrayOf(android.R.attr.state_selected),intArrayOf(-android.R.attr.state_selected)),
intArrayOf(ContextCompat.getColor(this, selectedColor), ContextCompat.getColor(this, disabledColour)
)
)
results as below:
whereas the expectation is to change the colour of the icons, not to fill (as below):
Any thoughts on how to update the colour of bottomNavBar menu icon? thanks.
In a project I am creating, I want to have a transparent floating button button in one of my screens.
The button should be of a color RGB(126,26,71), with 75% alpha channel.
But, when I create said button composable, it looks like this:
As you can see, it has slightly white, transparent background as well
This is how the composable is created:
FloatingActionButton(
onClick = {//TODO},
backgroundColor = MediumOpaquePurple, //the color I specified above
elevation = FloatingActionButtonDefaults.elevation(Space8)
)
{
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Delete notifications"
)
}
Weird thing is that when I use the same color for ExtendedFloatingActionButton composable, it looks perfectly fine and there's no white background in it.
Any help apprectiated.
Thanks
For FloatingActionButton the solution is to set elevation(0.dp). Just omitting the call is not enough, because it has a default value of 6.dp.
ExtendedFloatingActionButton acts a bit differently and does need this tweaking.
Using TextInputLayout from Material Design library we can use various end icon modes for password redaction, text clearing and custom mode. Furthermore, if we use any of Widget.MaterialComponents.TextInputLayout.*.ExposedDropdownMenu styles it will automatically apply special end icon mode that displays open and close chevrons.
Example of various icon modes:
Given the variety of use cases for the end icon, we decided to use a loading indicator in the InputTextLayout so that it looks like this:
How should one proceed to implement it?
One can simply set use custom drawable in place of End Icon like this:
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
The problematic part is getting hold of a loading indicator drawable.
Bad Option 1
There is no public drawable resource we can use for a loading indicator.
There is android.R.drawable.progress_medium_material but it is marked private and cannot be resolved in code. Copying the resource and all of its dependent private resources totals into about 6 files (2 drawables + 2 animators + 2 interpolators). That could work but feels quite like a hack.
Bad Option 2
We can use ProgressBar to retrieve its indeterminateDrawable. The problem with this approach is that the drawable is closely tied to the ProgressBar. The indicator is animated only when the ProgressBar is visible, tinting one View will also tint the indicator in the other View and probably additional weird behavior.
In similar situations, we can use Drawable.mutate() to get a new copy of the drawable. Unfortunately the indeterminateDrawable is already mutated and thus mutate() does nothing.
What actually worked to decouple the drawable from the ProgressBar was a call to indeterminateDrawable.constantState.newDrawable(). See documentation for more insight.
Anyway, this still feels like a hack.
Good Option 3
Although the drawable resource is marked private we can resolve certain theme attributes to get the system's default loading indicator drawable. The theme defines progressBarStyle attribute that references style for ProgressBar. Inside of this style is indeterminateDrawable attribute that references themed drawable. In code we can resolve the drawable like this:
fun Context.getProgressBarDrawable(): Drawable {
val value = TypedValue()
theme.resolveAttribute(android.R.attr.progressBarStyleSmall, value, false)
val progressBarStyle = value.data
val attributes = intArrayOf(android.R.attr.indeterminateDrawable)
val array = obtainStyledAttributes(progressBarStyle, attributes)
val drawable = array.getDrawableOrThrow(0)
array.recycle()
return drawable
}
Great, now we have a native loading indicator drawable without hacks!
Extra measures
Animation
Now if you plug in the drawable into this code
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
you will find out that it does not display anything.
Actually, it does display the drawable correctly but the real problem is that it is not being animated. It just happens that at the beginning of the animation the drawable is collapsed into an invisible point.
Unfortunately for us, we cannot convert the drawable to its real type AnimationScaleListDrawable because it is in com.android.internal.graphics.drawable package.
Fortunately for us, we can type it as Animatable and start() it:
(drawable as? Animatable)?.start()
Colors
Another unexpected behavior happens when TextInputLayout receives/loses focus. At such moments it will tint the drawable according to colors defined by layout.setEndIconTintList(). If you don't explicitly specify a tint list, it will tint the drawable to ?colorPrimary. But at the moment when we set the drawable, it is still tinted to ?colorAccent and at a seemingly random moment it will change color.
For that reason I recommend to tint both layout.endIconTintList and drawable.tintList with the same ColorStateList. Such as:
fun Context.fetchPrimaryColor(): Int {
val array = obtainStyledAttributes(intArrayOf(android.R.attr.colorPrimary))
val color = array.getColorOrThrow(0)
array.recycle()
return color
}
...
val states = ColorStateList(arrayOf(intArrayOf()), intArrayOf(fetchPrimaryColor()))
layout.setEndIconTintList(states)
drawable.setTintList(states)
Ultimately we get something like this:
with android.R.attr.progressBarStyle (medium) and android.R.attr.progressBarStyleSmall respectively.
You can use the ProgressIndicator provided by the Material Components Library.
In your layout just use:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/textinputlayout"
...>
<com.google.android.material.textfield.TextInputEditText
.../>
</com.google.android.material.textfield.TextInputLayout>
Then define the ProgressIndicator using:
ProgressIndicatorSpec progressIndicatorSpec = new ProgressIndicatorSpec();
progressIndicatorSpec.loadFromAttributes(
this,
null,
R.style.Widget_MaterialComponents_ProgressIndicator_Circular_Indeterminate);
progressIndicatorSpec.circularInset = 0; // Inset
progressIndicatorSpec.circularRadius =
(int) dpToPx(this, 10); // Circular radius is 10 dp.
IndeterminateDrawable progressIndicatorDrawable =
new IndeterminateDrawable(
this,
progressIndicatorSpec,
new CircularDrawingDelegate(),
new CircularIndeterminateAnimatorDelegate());
Finally apply the drawable to the TextInputLayout:
textInputLayout.setEndIconMode(TextInputLayout.END_ICON_CUSTOM);
textInputLayout.setEndIconDrawable(progressIndicatorDrawable);
It is the util method to convert to dp:
public static float dpToPx(#NonNull Context context, #Dimension(unit = Dimension.DP) int dp) {
Resources r = context.getResources();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
}
You can easily customize the circularRadius and the indicatorColors and all other attributes defined in the ProgressIndicator:
progressIndicatorSpec.indicatorColors = getResources().getIntArray(R.array.progress_colors);
progressIndicatorSpec.growMode = GROW_MODE_OUTGOING;
with this array:
<integer-array name="progress_colors">
<item>#color/...</item>
<item>#color/....</item>
<item>#color/....</item>
</integer-array>
Note: it requires at least the version 1.3.0-alpha02.
This question already has a good answer, however, I would like to post a more concise and simpler solution. If you use androidx you have a class that inherits Drawable - CircularProgressDrawable, so you can use it. This some piece of code I use in my project:
CircularProgressDrawable drawable = new CircularProgressDrawable(requireContext());
drawable.setStyle(CircularProgressDrawable.DEFAULT);
drawable.setColorSchemeColors(Color.GREEN);
inputLayout.setEndIconOnClickListener(view -> {
inputLayout.setEndIconDrawable(drawable);
drawable.start();
//some long running operation starts...
}
Result: