?android:attr/selectableItemBackground with another existing background - android

I have a 9patch set as the background of my layout. However I still want to provide touch feedback by using the selectableItemBackground attr.
I've tried using a <layer-list> with the 9patch and selectableItemBackground as the android:drawable of the second <item>, however that did not work.
I could also try making a selector and overlay the gradient drawable android uses for selectableItemBackground in list_selector_background_pressed.xml with a <layer-list>. But in 4.4 KitKat the selected background color is actually gray instead of blue in JellyBeans, so I can't really hardcode it :(
There has to be an easier way, right guys? D:

I've tried using a with the 9patch and
selectableItemBackground as the android:drawable of the second ,
however that did not work.
Yes, drawable attribute in a layer-list (or state-list) does not accept an attr value. You would see a Resource.NotFoundException. A look at LayerDrawable's (or StateListDrawable's) source code explains why: the value that you provide is assumed to be a drawable's id.
But, you can retrieve a theme and platform-specific drawable for an attribute in code:
// Attribute array
int[] attrs = new int[] { android.R.attr.selectableItemBackground };
TypedArray a = getTheme().obtainStyledAttributes(attrs);
// Drawable held by attribute 'selectableItemBackground' is at index '0'
Drawable d = a.getDrawable(0);
a.recycle();
Now, you can create a LayerDrawable:
LayerDrawable ld = new LayerDrawable(new Drawable[] {
// Nine Path Drawable
getResources().getDrawable(R.drawable.Your_Nine_Path),
// Drawable from attribute
d });
// Set the background to 'ld'
yourLayoutContainer.setBackground(ld);
You'll also need to set yourLayoutContainer's clickable attribute:
android:clickable="true"

Related

TextInputLayout with loading indicator

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:

Android change color of shape Instance

I have three buttons with same ripple shape.
when I enter activity I change color of single buttons shape.
RippleDrawable bg = (RippleDrawable) button.getBackground();
GradientDrawable gradient = (GradientDrawable) bg.findDrawableByLayerId(R.id.ripple_color);
gradient.setColor(Color.Black);
but when I reenter activity all buttons have this new color.
How to change only this instance of shape instead of modifying shape itself
From the Drawable documentation:
By default, all drawables instances loaded from the same resource share a common state; if you modify the state of one instance, all the other instances will receive the same modification.
So your problem is here:
GradientDrawable gradient = (GradientDrawable) bg.findDrawableByLayerId(R.id.ripple_color);
gradient.setColor(Color.Black);
To avoid the issue mentioned in the documentation, simply call mutate() on the drawable before changing its state.
GradientDrawable gradient =
(GradientDrawable) bg.findDrawableByLayerId(R.id.ripple_color).mutate();
gradient.setColor(Color.Black);

Color set by setColorFilter disappears on prelollipop devices

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.

how to make rounded corners button on runtime even if background is already set in xml

I understand how to make Button has rounded corners using GradientDrawable. The question I have now is if I set my background already in xml and retrieve it by
int[] attrsArray = new int[] {
android.R.attr.background
};
TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);
Drawable background = ta.getDrawable(0);
ta.recycle();
so I have a Drawable type of background now. next thing I want to do is call setBackgroundDrawable() and pass the drawable background, but I won't be able to set rounded corners with Drawable type. I can't just cast it to GradientDrawable either. Is there a way to achieve this? this way makes things a lot easier because I'll be able to give Button any background and have the rounded corner.
Thanks
Not sure if your use case is same as mine but I had to once do something similar and I had an invisible Button with a RoundedImageView behind it. The RoundedImageView was below:
https://github.com/vinc3m1/RoundedImageView
Using that class you can set the radius of the image.

Android ListView ScrollView change edge glow color from blue

Is it possible to change the ScrollView and ListView edge glow color from blue to orange.?
If you want to change color of glow, when you pull listview (the blue glow effect)
use
int glowDrawableId = getResources().getIdentifier("overscroll_glow", "drawable", "android");
int edgeDrawableId = getResources().getIdentifier("overscroll_edge", "drawable", "android");
Drawable androidGlow = ContextCompat.getDrawable(this, glowDrawableId);
Drawable androidEdge = ContextCompat.getDrawable(this, edgeDrawableId);
androidGlow.setColorFilter(getResources().getColor(R.color.white_20), PorterDuff.Mode.SRC_IN);
androidEdge.setColorFilter(getResources().getColor(R.color.white_20), PorterDuff.Mode.SRC_IN);
mode SRC_IN will show only your color. Place this code in Application class if color will be same or before initialisation of each individual listview (if you want different glow of different listviews).
If you asking about color change of fadingedge ( a shadow a top and a bottom of listview)
change color of colorCacheHint inside listview
use the Android Holo Colors Generator by Jérôme Van Der Linden
http://android-holo-colors.com/
The Android Holo Colors Generator allows you to easily create Android
components such as editext or spinner with your own colours for your
Android application. It will generate all necessary nine patch assets
plus associated XML drawables and styles which you can copy straight
into your project.
Select the views you want to generate colors for and add pngs & the theme from the XML it generates
Yes. Create drawable you like and set it for the listview:
android:overScrollHeader="#drawable/header"
android:overScrollFooter="#drawable/footer"

Categories

Resources