Tint VectorDrawable inside Drawable Resource xml - android

i've had a drawable resource for a selectable Button/ImageView like this:
<selector>
<item android:state_selected="true">
<layer-list>
<item>
<shape android:shape="oval">
<solid android:color="#color/blue"/>
</shape>
</item>
<item>
<bitmap
android:src="#drawable/ic_icon"
android:tint="#color/white"/>
</item>
</layer-list>
</item>
<item>
<layer-list>
<item>
<shape android:shape="oval">
<solid android:color="#color/background_unselected"/>
</shape>
</item>
<item>
<bitmap
android:src="#drawable/ic_icon"
android:tint="#color/icon_unselected"/>
</item>
</layer-list>
</item>
as i've switched to use VectorDrawables the above declaration does not work because i cannot reference a VectorDrawable with a <bitmap> tag.
But as far as i know that's the only way i can tint the icon.
I also cannot apply a Colorfilter in code as this would tint the whole drawable and not just the icon.
Any suggestions?

You can define colors as attributes and reference them in your SVG. Then can load the drawable styled by a theme.
Short example:
attributes.xml
<declare-styleable name="vectorColors">
<attr name="primaryColor" format="color" />
<attr name="secondaryColor" format="color" />
</declare-styleable>
ic_icon.xml
<path
android:fillColor="?attr/primaryColor"
android:pathData="...." />
<path
android:fillColor="?attr/secondaryColor"
android:pathData="...." />
styles.xml
<style name="svgColors">
<item name="primaryColor">#color/someColor</item>
<!-- also can use #RRGGBB way -->
<item name="secondaryColor">#ff0000</item>
</style>
Then load your theme and drawable:
Resources.Theme theme = getResources().newTheme();
theme.applyStyle(R.style.svgColors, false);
VectorDrawableCompat drawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_icon, theme);
imageView.setImageDrawable(drawable);
In your specific selector you should replace
<item>
<bitmap
android:src="#drawable/ic_icon"
android:tint="#color/white"/>
</item>
by <item android:drawable="#drawable/ic_icon">
Reference and full example

Related

Changing the custom state of a custom StateListDrawable (selector)

So I have a custom drawable with selector<> at its root. It uses custom states declared in my attrs.xml:
<declare-styleable name="Status">
<attr name="status_soon" format="boolean"/>
<attr name="status_available" format="boolean"/>
<attr name="status_error" format="boolean"/>
</declare-styleable>
The drawable itself is a simple circle with a solid color (soon = yellow, available = green and error = red, basically).
I have tested individually these layer-lists and they work perfectly fine.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- SOON -->
<item app:status_soon="true">
<layer-list>
<item>
<shape android:shape="oval">
<size android:width="12dp" android:height="12dp" />
<solid android:color="#color/status_soon" />
</shape>
</item>
</layer-list>
</item>
<!-- AVAILABLE -->
<item app:status_available="true">
<layer-list>
<item>
<shape android:shape="oval">
<size android:width="12dp" android:height="12dp" />
<solid android:color="#color/status_available" />
</shape>
</item>
</layer-list>
</item>r-list>
</item>
<!-- ERROR -->
<item app:status_error="true">
<layer-list>
<item>
<shape android:shape="oval">
<size android:width="12dp" android:height="12dp" />
<solid android:color="#color/status_error" />
</shape>
</item>
</layer-list>
</item>
</selector>
Now, my particularity is that I am using this drawable as a compound drawable of a text view.
Example would be: "status: soon + yellow circle"
So I am not using it as a background of my custom view (custom TextView). Because of that I cannot use the usual onCreateDrawableState(extraspace: Int) and refreshDrawableState() functions.
Instead, depending on the status (a simple enum class), I am doing this in my code:
// CustomStatusTextView.kt
private val statusDrawable = ContextCompat.getDrawable(context, R.drawable.status_drawable)
init {
setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, statusDrawable, null)
}
fun setStatus(status: Status) {
when (status) {
Status.SOON -> statusDrawable.state = intArrayOf(R.attr.status_soon)
Status.AVAILABLE -> statusDrawable.state = intArrayOf(R.attr.status_available)
Status.ERROR -> statusDrawable.state = intArrayOf(R.attr.status_error)
}
}
Unfortunately, no drawable is visible at all. What am I doing wrong ?
As stated before, I have tested individually each of the layers of my custom status drawable in order to ensure they actually work.
So, until I find a solution using custom states / attributes, I am going to stick with manually declaring 3 single drawables (one per state - soon, available error) and switching between them depending on the status provided to setStatus(...).
Thanks for the help!

How to set dropdown arrow in spinner with underline from image

List item
my code is in the style
this is inside the style.xml
<style name="SpinnerTheme">
<item name="android:background" >#drawable/bg_spinner</item>
</style>
bg_spinner.xml
<item>
<layer-list>
<item >
<bitmap android:gravity="bottom" android:src="#drawable/under_line" />
</item>
<item android:right="5dp">
<bitmap android:gravity="center_vertical|right" android:src="#drawable/dropdown" />
</item>
</layer-list>
</item>
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/SpinnerTheme"
android:layout_marginTop="15dp"
android:hint="Sex"
android:id="#+id/spinnerSex" />
date,month and year are in full width,and background image also less width, but when it comes sex its not showing full width.
i need the under line in sex full width, please do the needfull
You can draw line by xml code. like below
<item >
<layer-list>
<item>
<shape android:shape="rectangle" >
<solid android:color="#android:color/transparent" />
<!-- background color of box -->
</shape>
</item>
<item
android:left="-2dp"
android:right="-2dp"
android:top="-2dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke
android:width="1dp"
android:color="#323232" />
<!-- color of stroke -->
</shape>
</item>
<item android:right="5dp">
<bitmap android:gravity="center_vertical|right" android:src="#drawable/downlarrow" />
</item>
</layer-list>
</item>
output looks something like this
replace your xml code with this code:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<!-- Draw a 2dp width border around shape -->
<stroke
android:color="#000000"
android:width="2dp"
/>
</shape>
</item>
<!-- Overlap the left, top and right border using background color -->
<item
android:bottom="2dp"
>
<shape android:shape="rectangle">
<solid android:color="#ffffff"/>
</shape>
</item>
Full XML Code is:
Keep below code in your drawable directory. File name can be like this: "bg_spinner.xml"
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<layer-list>
<item>
<shape android:shape="rectangle">
<solid android:color="#android:color/transparent" />
<!-- background color of box -->
</shape>
</item>
<item android:left="-2dp" android:right="-2dp" android:top="-2dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke android:width="1dp" android:color="#323232" />
<!-- color of stroke -->
</shape>
</item>
<item android:right="5dp">
<bitmap android:gravity="center_vertical|right" android:src="#mipmap/arrow_down" />
</item>
</layer-list>
</item>
</selector>
In above code change bitmap src to your arrow image from drawable or mipmap directory. You can take nice arrow from "File -> New -> Vector Asset"
In your style.xml file keep below code:
<style name="SpinnerLineTheme" parent="android:Widget.Spinner">
<item name="android:background">#drawable/bg_spinner</item>
<item name="android:elevation">0dp</item>
<item name="android:popupBackground">#color/colorWhite</item>
</style>
And call the theme from spinner like below:
<Spinner
android:id="#+id/mySpinner"
style="#style/SpinnerLineTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#color/colorWhite"
android:dropDownWidth="300dp"
android:dropDownVerticalOffset="60dp"
android:gravity="start"
android:paddingEnd="30dp"
android:spinnerMode="dropdown" />
PS: Inspired by #Jayanth answer, as edit request queue is full.

ripple drawable crashes the app on Android API 19

I am using a custom ripple drawable
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="#android:integer/config_shortAnimTime"
android:color="#android:color/white">
<item android:id="#android:id/mask">
<shape android:shape="rectangle" >
<solid android:color="#android:color/white" />
</shape>
</item>
</ripple>
but it crashes the app on API 19
android.content.res.Resources$NotFoundException: File res / drawable /
ripple_effect_square2.xml from drawable resource ID #0x7f02017d
at android.content.res.Resources.loadDrawable(Resources.java:2101)
at android.content.res.Resources.getDrawable(Resources.java:700)
at android.view.View.setBackgroundResource(View.java:15303)
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line # 2: invalid drawable tag ripple
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java: 933)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java: 877)
at android.content.res.Resources.loadDrawable(Resources.java: 2097)
at android.content.res.Resources.getDrawable(Resources.java: 700) 
at android.view.View.setBackgroundResource(View.java: 15303) 
What should i do to prevent crashing ?
The RippleDrawable was added in API 21, so it's not available on earlier SDKs.
You can move your drawable file to res/drawable-v21 to ensure it doesn't crash on earlier releases.
I had the same problem.
Mina Samy's answer solved my problem also.
Atef Hares asked for an alternative in older versions.
What works for me is to use a selector instead of ripple
for example:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#ccffffff" />
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle">
<stroke android:color="#android:color/white" />
</shape>
</item>
<item android:drawable="#color/playColor" />
</selector>
where
<color name="playColor">#E8EAF6</color>
Ripple is only available from Lollipop (Android API 21). Here is an example for backwards compatibility which includes states and no default background (Transparent):
Create two drawable xml files with the same name, one will be placed in the drawable-v21 folder, and the other in the normal drawable folder.
drawable-v21/ripple_red_solid.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#android:color/red">
<item android:id="#android:id/mask">
<color android:color="#android:color/red" />
</item>
<item>
<selector>
<item android:state_selected="true">
<color android:color="#android:color/red" />
</item>
<item android:state_activated="true">
<color android:color="#android:color/red" />
</item>
<item android:state_focused="true">
<color android:color="#android:color/red" />
</item>
</selector>
</item>
</ripple>
drawable/ripple_red_solid.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape android:shape="rectangle">
<solid android:color="#android:color/red" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#android:color/red" />
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="rectangle">
<solid android:color="#android:color/red" />
</shape>
</item>
</selector>
And then simply use it on the view like:
<LinearLayout
...
android:background="#drawable/ripple_red_solid" />

Ripples on a shape with a transparent background

I am using the following shape in my app
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval" >
<solid android:color="#color/primary_light"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
<item android:state_focused="true">
<shape android:shape="oval">
<stroke android:color="#color/primary_light" android:width="5dp"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="#android:color/transparent"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
</selector>
It is there in my drawable folder. Now I am updating my app to Lollipop and I wish to give a ripple feedback on the circular button that I used.
So in drawable-v21 folder I changed it to a ripple selector:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/primary_light">
<item>
<selector>
<item android:state_focused="true">
<shape android:shape="oval">
<stroke android:color="#color/primary_light" android:width="5dp"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
<item android:id="#android:id/mask">
<shape android:shape="oval">
<solid android:color="#android:color/transparent"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
</selector>
</item>
</ripple>
But unfortunately the ripple effect is not generated by using the above drawable in Lollipop. Is it because of the <solid android:color="#android:color/transparent"/>?
Can anyone show me where I have go wrong? Thanks
After a few trial and errors it seems I have misunderstood the hierarchy of the selector and item.
The following works perfectly.
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/primary_light">
<item android:id="#android:id/mask">
<shape android:shape="oval">
<solid android:color="#android:color/white"/>
<size android:height="80dp" android:width="80dp"/>
</shape>
</item>
</ripple>
This works with a transparent background:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"
android:exitFadeDuration="#android:integer/config_shortAnimTime">
<!-- Use this to define the shape of the ripple effect (rectangle, oval, ring or line). The color specified here isn't used anyway -->
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="3dp"/>
<!-- This color is needed to be set - doesn't affect anything -->
<solid android:color="#android:color/white"/>
</shape>
</item>
<!-- This is the background for your button -->
<item>
<!-- Use the shape you want here -->
<shape android:shape="rectangle">
<!-- Use the solid tag to define the background color you want -->
<solid android:color="#android:color/transparent"/>
</shape>
</item>
</ripple>
This was modified a bit from this answer: Transparent ripple
You can have a ripple effect on transparent or semi-transparent background when the ripple mask is specified. The ripple mask is a Drawable but only the alpha value is used setting the ripple alpha per pixel. Here is an example for creating programmatically.
var background = new android.graphics.drawable.GradientDrawable();
background.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
background.setColor(0x77FFFFFF); // semi-transparent background
background.setCornerRadius(10);
background.setStroke(3, 0xFF1144FF); // border color
var mask = new android.graphics.drawable.GradientDrawable();
mask.setShape(android.graphics.drawable.GradientDrawable.RECTANGLE);
mask.setColor(0xFF000000); // the color is irrelevant here, only the alpha
mask.setCornerRadius(5); // you probably want the same corner as the background
var rippleColorLst = android.content.res.ColorStateList.valueOf(
android.graphics.Color.argb(255,50,150,255)
);
// aaaand
var ripple = new android.graphics.drawable.RippleDrawable(rippleColorLst,background,mask);
yourButton.setBackground(ripple);
(Sorry for the JavaScript/NativeScript code, probably one can understand it)
Following code works for custom shape with ripple effect for transparent button -
main_activity_buttons_ripple_with_background.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:color="#888888"
tools:targetApi="lollipop">
<!-- Use this to define the shape of the ripple effect (rectangle, oval, ring or line). The color specified here isn't used anyway -->
<item android:id="#android:id/mask">
<shape android:shape="rectangle">
<corners android:radius="25dp" />
<!-- This color is needed to be set - doesn't affect anything -->
<solid android:color="#000" />
</shape>
</item>
<!-- This is the background for your button -->
<item>
<!-- Use the shape you want here -->
<shape android:shape="rectangle">
<!-- Use the solid tag to define the background color you want -->
<solid android:color="#android:color/transparent" />
<stroke
android:width="1dp"
android:color="#FFF" />
<corners android:radius="25dp" />
</shape>
</item>
</ripple>
styles.xml
<style name="MainActivityButtonsStyle">
<item name="android:background">#drawable/main_activity_buttons_ripple_with_background</item>
<item name="android:textAllCaps">false</item>
<item name="android:layout_margin">15dp</item>
<item name="android:paddingLeft">20dp</item>
<item name="android:paddingRight">20dp</item>
<item name="android:layout_centerHorizontal">true</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">50dp</item>
<item name="android:textColor">#FFF</item>
<item name="android:textSize">18sp</item>
</style>
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:layout_alignParentTop="true"
style="#style/MainActivityButtonsStyle"
android:text="#string/button_search_by_id" />
</RelativeLayout>
</LinearLayout>

Android - make an arrow shape with xml

I want to make a button for my shape like this one
Is there a way to do this with xml ?
Like setting some points, in my case I have 5..
What you need is to create a shape xml file in your project's drawable-xxx folder and then use this shape as background for a button.
Here is the shape file called arrow_shape.xml:
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Colored rectangle-->
<item>
<shape android:shape="rectangle">
<size
android:width="100dp"
android:height="40dp" />
<solid android:color="#5EB888" />
<corners android:radius="0dp"/>
</shape>
</item>
<!-- This rectangle for the top arrow edge -->
<!-- Its color should be the same as the layout's background -->
<item
android:top="-40dp"
android:bottom="65dp"
android:right="-30dp">
<rotate
android:fromDegrees="45">
<shape android:shape="rectangle">
<solid android:color="#ffffff" />
</shape>
</rotate>
</item>
<!-- This rectangle for the lower arrow edge -->
<!-- Its color should be the same as the layout's background -->
<item
android:top="65dp"
android:bottom="-40dp"
android:right="-30dp">
<rotate
android:fromDegrees="-45">
<shape android:shape="rectangle">
<solid android:color="#ffffff" />
</shape>
</rotate>
</item>
</layer-list>
Then use it as button's background, for example
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/arrow_shape"/>
Here is the screenshot:
More info on Layer-List you can find here.
EDIT:
Keep in mind though that I used certain values for the shape's width and height. If you change those you might need to change the values of the top, bottom and right attributes. So, in that case consider using different values in your project's values directory.
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:width="50dp" android:height="10dp" android:right="6dp">
<shape>
<solid android:color="#android:color/holo_green_dark"/>
<corners
android:bottomLeftRadius="2dp"
android:topLeftRadius="2dp"/>
</shape>
</item>
<item android:width="7dp" android:height="7dp"
android:left="50dp">
<rotate android:fromDegrees="45"
android:pivotX="0"
android:pivotY="0">
<shape>
<solid android:color="#android:color/holo_green_dark"/>
</shape>
</rotate>
</item>
</layer-list>
With the Material Components library you can just use the standard MaterialButton defining the shapeAppearanceOverlay attribute:
Something like:
<com.google.android.material.button.MaterialButton
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlay.Button.Triangle"
... />
And in your style define:
<style name="ShapeAppearanceOverlay.Button.Triangle" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopLeft">0dp</item>
<item name="cornerSizeBottomLeft">0dp</item>
<item name="cornerFamilyTopRight">cut</item>
<item name="cornerFamilyBottomRight">cut</item>
<item name="cornerSizeTopRight">50%</item>
<item name="cornerSizeBottomRight">50%</item>
</style>

Categories

Resources