Since Android released the new Splash Screen API with Android 12, a lot of apps had issues with duplicate splash screens, lack of customization, etc.
Right now, it is possible to set the background color and icon in the middle of it, but is it possible to customize it a bit more? Since right now we are limited to use single-colored background and non-resizable logo icon which doesn't look quite good.
What I'm trying to achieve is a custom splash screen, with an image drawable as background (or layer-list with 2 items - one background image and one centered logo), as it could be used before Android 12.
Did someone succeed to achieve this type of behavior?
There is a workaround to set windowIsTranslucent attribute to true and show only the second splash (the right one), but it introduces bad UX since it seems like the app is not responding for a few seconds.
Short answer is No, but here in my answer you can find more info:
Android: How to set a drawable as a windowSplashScreenBackground parameter in the new SplashScreen API?
I did something like this.
First remove default drawable
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#color/...</item>
<item name="windowSplashScreenAnimatedIcon">#android:color/transparent</item>
<item name="postSplashScreenTheme">#style/AppTheme</item>
</style>
Then inflate your custom splash view
class MainActivity : AppCompatActivity(), SplashScreen.OnExitAnimationListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val installSplashScreen = installSplashScreen()
installSplashScreen.setOnExitAnimationListener(this)
setContentView(R.layout.activity_main)
}
override fun onSplashScreenExit(splashScreenViewProvider: SplashScreenViewProvider) {
val view = splashScreenViewProvider.view
if (view is ViewGroup) {
val binding = ActivityMainSplashBinding.inflate(layoutInflater,view, true)
// Do what you want with your inflated view
animate(view) {
// Remove splash
splashScreenViewProvider.remove()
}
}
}
private fun animate(view: View, doOnFinish: () -> Unit) {
view.animate()
.withEndAction(doOnFinish)
.start()
}
}
At the end remember to call splashScreenViewProvider.remove() to remove splash.
I'm using NativeScript with Angular, and I can't seem to change the StatusBar color in my project or set it to transparent (any of these would be fine). Instead, it is not totally transparent, but is translucent black, so the background scrolls behind it but it is darkened by the StatusBar. I would like to set it to transparent or to change the color (to the same one as the page background)
What I've tried:
Changing the "ns_primaryDark" and "ns_primary" colors in App_Resources/Android/src/main/res/values/colors.xml (works on launch screen if i set the TranslucentStatus" to false;
Setting <item name="android:windowTranslucentStatus"></item> in <style name="AppThemeBase" parent="Theme.AppCompat.Light.NoActionBar"> in App_Resources/Android/src/main/res/values/styles.xml doesn't make any difference, despite working fine on <style name="LaunchScreenThemeBase" parent="Theme.AppCompat.Light.NoActionBar"> (the launch screen);
Setting <item name="android:windowLightStatusBar">true</item> changes the text color to black and works on both the launch screen and the main app;
Using the code below in any component's constructor doesn't change anything in my app, but it worked on the other project i tried (details below), setting the color to black:
let window = app.android.startActivity.getWindow();
window.setStatusBarColor(new Color("black").android);
One thing i also tried was doing these steps on another project i had downloaded (https://github.com/NativeScript/nativescript-ui-samples/tree/master/chart this one, to be exact) and it worked, so i thought the template i used in my application might be "overlaying" any settings for the StatusBar.
I then tried to make a new project with the same template as mine and i figured out that it didn't work either. Is the template my problem? If so, is there any way to get around it?
The template i used is this one: https://github.com/NativeScript/nativescript-app-templates/tree/master/packages/template-tab-navigation-ng (also works with "tns create my-app-name --template tns-template-tab-navigation-ng").
Big thanks in advance.
Add the following code to your main.ts, that should do the job.
import * as application from "tns-core-modules/application";
declare var android;
application.android.on(application.AndroidApplication.activityCreatedEvent, (event) => {
const activity = event.activity;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
activity.getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
const parent = activity.findViewById(android.R.id.content);
for (let i = 0; i < parent.getChildCount(); i++) {
const childView = parent.getChildAt(i);
if (childView instanceof android.view.ViewGroup) {
childView.setFitsSystemWindows(true);
childView.setClipToPadding(true);
}
}
});
It's pretty much as in the library. If you like to use the library, you will have to include it in your app grad and access the StatusBarUtil class.
You can use this library to transparent your status bar Library
compile 'com.jaeger.statusbarutil:library:1.4.0'
To make any specific activity to transparent you can simply use this
StatusBarUtil.setTransparent(Activity activity)
It will look like this
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:
I have a TabLayout which has icons. The idea is to change the colors runtime.
I have and xml drawable file with states: state_pressed, state_selected and default with the same white picture so I can put color later.
I take the drawables for different states:
Drawable[] drawables = stateListDrawable.getConstantState();
and the for every drawable state I put color from another array:
drawables[i].setColorFilter(colors[i], PorterDuff.Mode.MULTIPLY);
The issue is that the color is visible in the beginning, but when I start to click on the icons all the icons becomes white again and I lose the tint.
Everything is working as expected on lollipop and above.
Use the tint method from the v4 Support library
drawables[i] = DrawableCompat.wrap(drawables[i])
DrawableCompat.setTint(drawables[i], colors[i])
I have found my solution, which does not look clean at all, but at least it is working :)
I have created CustomStateListDrawable which extends from StateListDrawable and added the drawables for the different states. Then I have overridden all the methods in the class to see which ones are called and tried to change the colors there. The called late enough(my changes will not be overridden after I make them) was getState(). I have created also a ColorStateList object to hold my colors so the code will look like this:
private ColorStateList colorStateList;
public int[] getState() {
if (colorStateList != null) {
// Resolve the color for the current state
int color = colorStateList.getColorForState(super.getState(), 0);
// Get the current drawable and changed its color.
if (getCurrent() != null) {
getCurrent().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
}
}
return super.getState();
}
Basically every time when there is a change in the state I get the current drawable and change its color.
In the new AppCompat library, we can tint the button this way:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/follow"
android:id="#+id/button_follow"
android:backgroundTint="#color/blue_100"
/>
How can I set the tint of the button programmatically in my code?
I'm basically trying to implement a conditional coloring of the button based on some user input.
According to the documentation the related method to android:backgroundTint is setBackgroundTintList(ColorStateList list)
Update
Follow this link to know how create a Color State List Resource.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:color="#your_color_here" />
</selector>
then load it using
setBackgroundTintList(contextInstance.getResources().getColorStateList(R.color.your_xml_name));
where contextInstance is an instance of a Context
using AppCompart
btnTag.setSupportButtonTintList(ContextCompat.getColorStateList(Activity.this, R.color.colorPrimary));
You could use
button.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.id.blue_100)));
But I would recommend you to use a support library drawable tinting which just got released yesterday:
Drawable drawable = ...;
// Wrap the drawable so that future tinting calls work
// on pre-v21 devices. Always use the returned drawable.
drawable = DrawableCompat.wrap(drawable);
// We can now set a tint
DrawableCompat.setTint(drawable, Color.RED);
// ...or a tint list
DrawableCompat.setTintList(drawable, myColorStateList);
// ...and a different tint mode
DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_OVER);
You can find more in this blog post (see section "Drawable tinting")
Seems like views have own mechanics for tint management, so better will be put tint list:
ViewCompat.setBackgroundTintList(
editText,
ColorStateList.valueOf(errorColor));
here's how to do it in kotlin:
view.background.setTint(ContextCompat.getColor(context, textColor))
In properly extending dimsuz's answer by providing a real code situation, see the following code snippet:
Drawable buttonDrawable = button.getBackground();
buttonDrawable = DrawableCompat.wrap(buttonDrawable);
//the color is a direct color int and not a color resource
DrawableCompat.setTint(buttonDrawable, Color.RED);
button.setBackground(buttonDrawable);
This solution is for the scenario where a drawable is used as the button's background. It works on pre-Lollipop devices as well.
The simple way to do it
in Java
myButton.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.id.white)));
in Kotlin
myButton.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.id.white)))
Have you tried something like this?
button.setBackgroundTintList(getResources().getColorStateList(R.id.blue_100));
note that getResources() will only work in an activity. But it can be called on every context too.
this is easily handled in the new Material Button from material design library, first, add the dependency:
implementation 'com.google.android.material:material:1.1.0-alpha07'
then in your XML, use this for your button:
<com.google.android.material.button.MaterialButton
android:id="#+id/accept"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/i_accept"
android:textSize="18sp"
app:backgroundTint="#color/grayBackground_500" />
and when you want to change the color, here's the code in Kotlin, It's not deprecated and it can be used prior to Android 21:
accept.backgroundTintList = ColorStateList.valueOf(ResourcesCompat.getColor(resources,
R.color.colorPrimary, theme))
You can use DrawableCompat e.g.
public static Drawable setTint(Drawable drawable, int color) {
final Drawable newDrawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(newDrawable, color);
return newDrawable;
}
The way I managed to get mine to work was by using CompoundButtonCompat.setButtonTintList(button, colour).
To my understanding this works regardless of android version.
I had a similar problem. I wished to colour a complex drawable background for a view based on a color (int) value.
I succeeded by using the code:
ColorStateList csl = new ColorStateList(new int[][]{{}}, new int[]{color});
textView.setBackgroundTintList(csl);
Where color is an int value representing the colour required.
This represents the simple xml ColorStateList:
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:color="color here"/>
</selector>
Hope this helps.
If you are using Kotlin and Material Design, you can change color of your MaterialButton like this:
myButton.background.setTintList(ContextCompat.getColorStateList(context, R.color.myColor))
You can improve it even better by creating an extension function for your MaterialButton in order to make you code more readable and your coding little more convenient:
fun MaterialButton.changeColor(color: Int) {
this.background.setTintList(ContextCompat.getColorStateList(context, color))
}
Then, you can use your function everywhere like this:
myButton.changeColor(R.color.myColor)
You can use something like that:
myButton.backgroundTintList = AppCompatResources.getColorStateList(context, R.color.primary_variant)
For ImageButton you can use:
favoriteImageButton.setColorFilter(Color.argb(255, 255, 255, 255)); // White Tint
The suggested answer here doesn't work properly on android 5.0 if your XML based color state list references themed attributes..
For instance, I have an xml color state list like so:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorPrimary" android:state_enabled="true"/>
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
</selector>
Using this as my backgroundTint from xml works just fine on android 5.0 and everything else. However if I try to set this in code like this:
(Don't do this)
myButton.setSupportButtonTintList(ContextCompat.getColorStateList(myButton.getContext(), R.color.btn_tint_primary));
It actually doesn't matter if I pass the Activity or the button's context to ContextCompat.getColorStateList() method, neither will give me the proper color state list with respect to the theme the button is within. This is because using theme attributes in color state lists wasn't supported until api 23 and ContextCompat does not do anything special to resolve these. Instead you must use AppCompatResources.getColorStateList() which does its own resource parsing/theme attribute resolution on devices < API 23.
Instead, you must use this:
myButton.setSupportBackgroundTintList(AppCompatResources.getColorStateList(myButton.getContext(), R.color.btn_tint_primary));
TLDR: use AppCompatResources and not -ContextCompat- if you'll need resolved themed resources across all API versions of android.
For more information on the topic, see this article.
In addition to Shayne3000's answer you can also use a color resource (not only an int color). Kotlin version:
var indicatorViewDrawable = itemHolder.indicatorView.background
indicatorViewDrawable = DrawableCompat.wrap(indicatorViewDrawable)
val color = ResourcesCompat.getColor(context.resources, R.color.AppGreenColor, null) // get your color from resources
DrawableCompat.setTint(indicatorViewDrawable, color)
itemHolder.indicatorView.background = indicatorViewDrawable
There are three options for it using setBackgroundTintList
int myColor = Color.BLACK;
button.setBackgroundTintList(new ColorStateList(EMPTY, new int[] { myColor }));
button.setBackgroundTintList(ColorStateList.valueOf(myColor));
button.setBackgroundTintList(contextInstance.getResources().getColorStateList(R.color.my_color));
If you dont want to care about different versions of android, you can use this code, for basicaly any view. Worked for me.
val states = arrayOf(intArrayOf(android.R.attr.state_enabled))
val colors = intArrayOf(Color.GREEN) // your color here
val colorStateList = ColorStateList(states, colors)
ViewCompat.setBackgroundTintList(yourButtonHere,colorStateList)
Kotlin version, wish a nice day to everyone reading this ;)
btw. if you created some drawable background shape this should override only only the tint color.
checkbox.ButtonTintList = ColorStateList.ValueOf(Android.Color.White);
Use ButtonTintList instead of BackgroundTintList
Tint can be added to button like:
filterBtn.setBackgroundTintList(ContextCompat.getColorStateList(context,R.color.colorPrimary))
simple we can also use for an imageview
imageView.setColorFilter(ContextCompat.getColor(context,
R.color.COLOR_YOUR_COLOR));
With Kotlin,
checkbox.buttonTintList = AppCompatResources.getColorStateList(context, color.colorPrimary)