DrawableLeft with xml icon - android

How to use drawableLeft with an xml icon? I have the following button:
<Button
android:id="#+id/vitimas"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:drawableLeft="#drawable/ic_person_black_24dp"
android:drawableStart="#drawable/ic_person_black_24dp"
android:background="#drawable/botao_verde"
android:text="VĂ­timas"/>
In old APIs such as 16, the app stops working due to drawableLeft, I tried to use an ImageButton but the same happens, if I use app: srcCompat it works, however the icon is not stay in left, I need it to be stay in left and the text in the middle
The icon is from the Vector Asset package.

AppCompatTextView supports app:drawableLeftCompat, app:drawableTopCompat, app:drawableRightCompat, app:drawableBottomCompat, app:drawableStartCompat and app:drawableEndCompat compound drawables, supporting backported drawable types such as VectorDrawableCompat.
Include this in your gradle file
implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
implementation 'androidx.appcompat:appcompat-resources:1.1.0-rc01'
In your text view you can use
app:drawableLeftCompat
app:drawableStartCompat

Best way I've found:
static public void setLeftDrawable(View view, Context c, int drawable) {
Drawable leftDrawable = AppCompatResources.getDrawable(c, drawable);
if (view instanceof TextInputEditText) {
((TextInputEditText) view).setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
} else if (view instanceof BootstrapEditText) {
((BootstrapEditText) view).setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
} else if (view instanceof EditText) {
((EditText) view).setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
} else if (view instanceof TextView) {
((TextView) view).setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null);
}
}
Then just call passing the View, the Context and the Drawable, very easy!

add this in your gradle file
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}

Older versions still do not support vector drawables in views such as TextView.
For that, you need to use the method setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) to add a drawable to a TextView, CheckboxView, and the like.
TextView tv = findViewById(R.id.tv);
tv.setCompoundDrawablesWithIntrinsicBounds(R.drawable.icon, 0, 0, 0);
Do not nest views just to get this effect, it's bad for performance and unnecessary.
for a list of similar methods see the docs.

Here I added code for gradle:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
after adding this code to your application class:
#Override
public void onCreate() {
super.onCreate();
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
after this create one layer file for the vector drawable :
ic_person_black.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_person_black_24dp"/></layer-list>
and now you are able to add this file to drawable left.
android:drawableLeft="#drawable/ic_person_black.xml"

Related

Setting the Color of a TextView Drawable

Im trying to change the color of a TextView Drawable in Xamarin.
In Java you can do it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView txt = (TextView) findViewById(R.id.my_textview);
setTextViewDrawableColor(txt, R.color.my_color);
}
private void setTextViewDrawableColor(TextView textView, int color) {
for (Drawable drawable : textView.getCompoundDrawables()) {
if (drawable != null) {
drawable.setColorFilter(new PorterDuffColorFilter(getColor(color), PorterDuff.Mode.SRC_IN));
}
}
}
How i can do something like this in Xamarin.Android?
Try below solution
private void setTextViewDrawableColor(TextView textView, int color) {
for (Drawable drawable : textView.getCompoundDrawables()) {
if (drawable != null) {
drawable.setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(textView.getContext(), color), PorterDuff.Mode.SRC_IN));
}
}
}
I am using this in kotlin:
tv.getCompoundDrawables()[0].setTint(//color)
Please, notice that if you set drawables in your layout file via android:drawableStart or android:drawableEnd instead of android:drawableLeft and android:drawableRight respectively you should use TextView.getCompoundDrawablesRelative(). Otherwise you can get empty array of drawables.
private void setTextViewDrawableColor(TextView textView, int color) {
for (Drawable drawable : textView.getCompoundDrawablesRelative()) {
if (drawable != null) {
drawable.setColorFilter(new PorterDuffColorFilter(ContextCompat.getColor(textView.getContext(), color), PorterDuff.Mode.SRC_IN));
}
}
}
// index of drawable
val left = 0
val start = left
val top = 1
val right = 2
val end = right
val bottm = 3
// color int
val color = Color.RED
// apply tint for target drawable
textView.compoundDrawables.getOrNull(left)?.setTint(color)
// apply tint for all drawables
textView.compoundDrawables?.forEach { it?.setTint(color) }
NOTE!
if in XML layout you use android:stratDrawable or android:endDrawable you have to work with textView.compoundDrawablesRelative array, textView.compoundDrawables contains drawables when they have been added with android:leftDrawable or android:rightDrawable attributes.
I solve this problem adding in xml definition this line:
android:drawableTint="#color/red"
A complete example:
<TextView
android:id="#+id/tv_element"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:drawableStart="#drawable/ic_icon"
android:drawableTint="#color/color"
android:visibility="visible" />
There's built in support for this through TextViewCompat.setCompoundDrawableTintList(textView, colors)
val color = ContextCompat.getColor(context, R.color.foo)
val colorList = ColorStateList.valueOf(color)
TextViewCompat.setCompoundDrawableTintList(textView, colorList)
If you want to change the tint color of the drawable of any view (tested on API 29) :
private fun setTintColor(textView: TextView, color: Int) {
DrawableCompat.setTint(DrawableCompat.wrap(textView.background).mutate(),
ContextCompat.getColor(this, color))
}
For Kotlin. Use the below extension for the TextView drawable. It supports below and above API level of 23.
private fun TextView.setTextViewDrawableColor(color: Int) {
for (drawable in this.compoundDrawablesRelative) {
drawable?.mutate()
drawable?.colorFilter = PorterDuffColorFilter(
color, PorterDuff.Mode.SRC_IN
)
}
}
Note: You can also use this function in the RecyclerView item as well, It will not override the same color for each item
I faced the problem where I am changing the color of the compound drawable and except one rest of all the colors are constant. Confusing for me.!!!
the solution that worked for me is
// Pass through the each drawable and update the tint if drawable is set.
textView.compoundDrawables.filterNotNull().forEach { drawable ->
drawable.mutate()
drawable.setTint(drawableColor)
}
Without the mutate(), the things were working partially. I got the more details here Drawable Mutations .
For the interest of the reader, I am providing a quick summery below.
Android Drawables are the drawing containers. Such as BitmapDrawable is used to display the images, ShapeDrawable is used to display the shapes and gradients.
Drawables are used extensively in the Android ecosystem thus they are optimized. So when views are created the different instances are spawned but the drawables associated with the view share the common state, called "Constant state". For example, if a drawable is a BitmapDrawable then the same bitmap is used with the all the corresponding copies or views. Advantage: It simply saves huge amount of memory.
Problem: As the same drawable is shared across the various views. Any change in the state of the drawable such as alpha, transformation etc. will impact all the places where it is used.
Solution: The mutate() method when called on a drawable, the constant state of the drawable is duplicated to allow you to change any property without affecting other drawables. Note: Bitmap is still shared.

Can't apply a colorFilter to text selection handles

I'm trying to bring material text selection handles to my app. I got drawables from the SDK for middle/right/left handle (bitmaps) and text cursor (9-patch), and set:
<item name="android:textSelectHandleLeft">#drawable/text_select_handle_left_mtrl_alpha</item>
<item name="android:textSelectHandleRight">#drawable/text_select_handle_right_mtrl_alpha</item>
<item name="android:textSelectHandle">#drawable/text_select_handle_middle_mtrl_alpha</item>
<item name="android:textCursorDrawable">#drawable/text_cursor_mtrl_alpha</item>
It works as expected. However, in Lollipop these drawables are tinted with a particular color in XML using the android:tint attribute, which I can't use on API<21. So I'm trying to set a color filter at runtime.
Text cursor does not get tinted. I think this might be due to it being a 9 patch. How can a 9-patch drawable be filtered at runtime? I tried probably all of PorterDuff.Modes.
Right/left handles are black, while middle handle is white.
I.e., non of them is green as I would like. Why?
As you can see above, I set up four ImageView below my edit text, and they instead get tinted.
private void setUpTextCursors() {
Drawable left = getResources().getDrawable(R.drawable.text_select_handle_left_mtrl_alpha);
Drawable right = getResources().getDrawable(R.drawable.text_select_handle_right_mtrl_alpha);
Drawable middle = getResources().getDrawable(R.drawable.text_select_handle_middle_mtrl_alpha);
Drawable cursor = getResources().getDrawable(R.drawable.text_cursor_mtrl_alpha);
ColorFilter cf = new PorterDuffColorFilter(mGreenColor, PorterDuff.Mode.SRC_IN);
/**
* tint my ImageViews, but no effect on edit text handles
*/
left.setColorFilter(cf);
right.setColorFilter(cf);
middle.setColorFilter(cf);
/**
* no effect whatsoever
*/
cursor.setColorFilter(cf);
}
Looks like here we have both a 9-patch tinting issue - since filter fails even on test ImageViews - and an issue related to the fact that none of the applied filters get considered by the text selection manager.
Relevant source code about that is from the TextView class and from this Editor hidden helper class which I found somehow. Spent some time on it but still can't tell why my filters are ignored.
To #pskink: let cursor be the filtered drawable, I can have:
<ImageView
android:id="#id/1"
android:src="#drawable/cursor_drawable" />
<ImageView
android:id="#id/2" />
The first won't be tinted, but if I call imageView2.setBackground(cursor), then it's tinted.
Also if I have
<item name="android:textSelectHandle">#drawable/cursor_drawable</item>
this affects the edit selection (because I override the default cursor) but it's not tinted, again.
you need to override the default Resources used by your Activity:
// your activity source file
Resources res;
#Override
public Resources getResources() {
if (res == null) {
res = new TintResources(super.getResources());
}
return res;
}
the custom Resources class will override getDrawable() method so you can intercept creating your Drawables and set up the color filter, for example:
class TintResources extends Resources {
public TintResources(Resources resources) {
super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
}
#Override
public Drawable getDrawable(int id) throws NotFoundException {
Drawable d = super.getDrawable(id);
if (id == R.drawable.text_cursor_material) {
// setup #drawable/text_cursor_material
d.setColorFilter(0xff00aa00, PorterDuff.Mode.SRC_IN);
}
return d;
}
}
the same way you can setup other Drawables (#drawable/text_select_handle_*_material), note you need that not direct way since EditText doesn't have getter methods for accessing those Drawables
This is just a partial answer, and we can also consider it quite bad, since it's a workaround. I was able to load just the handles (i.e., the BitmapDrawables) inside the edittext (or any other selection stuff) by pointing at XML files rather than at raw png files. I.e. I set:
<item name="android:textSelectHandleLeft">#drawable/text_select_handle_left_material</item>
<item name="android:textSelectHandleRight">#drawable/text_select_handle_right_material</item>
<item name="android:textSelectHandle">#drawable/text_select_handle_middle_material</item>
<item name="android:textCursorDrawable">#drawable/text_cursor_material</item>
where these are xml drawables like:
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/text_select_handle_left_mtrl_alpha" />
or
<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/text_cursor_mtrl_alpha" />
If I filter these drawables, I found them tinted both in views and in selections. So I altered my method like such:
private void setUpTextCursors() {
ColorFilter cf = new PorterDuffColorFilter(mColorControlActivated, PorterDuff.Mode.SRC_IN);
BitmapDrawable left = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_left_material);
BitmapDrawable middle = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_middle_material);
BitmapDrawable right = (BitmapDrawable) getResources().getDrawable(R.drawable.text_select_handle_right_material);
// NinePatchDrawable cursor = (NinePatchDrawable) getResources().getDrawable(R.drawable.text_cursor_material);
left.setColorFilter(cf);
right.setColorFilter(cf);
middle.setColorFilter(cf);
// cursor.setColorFilter(cf); this does not work: cursor still white!
}
However, while this works for left, right, and middle, something is still wrong with the 9-patch cursor, because I can't get it tinted.

How can I change the transparency (alpha) of a view on pre-SDK-11 on Android?

How can I change the transparency (alpha) of a view on pre-SDK-11 on Android?
Before you suggest using a background colour with some transparency, please note that this method does not include all elements in the view such as the text of a button or the child views of a view group.
The ViewHelper of NineOldAndroids is what I use, it is a static helper class and a real gem! Many here recommend NineOldAndroids but I have seen no mention of the ViewHelper. It is really easy to use.
import com.nineoldandroids.view.ViewHelper;
...
ViewHelper.setAlpha(myView, .2f);
You can also use it to set other properties like x, y etc, very handy when setting up for animations or building your UI. Many thanks to Jake Wharton for sharing his work with the community!
EDIT - example below refers for Android pre-SDK11, but I just found out about an amazingly great library called Nine Old Androids, The amazing thing it does is enabling all animation capabilities of Android 3.0 for all API versions!!!
Previous answer
I actually encountered this kind of problem when wanted to set alpha dynamically on a complex layout.
I created an override of onSetAlpha() and added another recursive function that checks every kind of view for background image, drawables and text colors.
#Override
public boolean onSetAlpha(int alpha)
{
return onSetAlpha(alpha, theLayoutYouWantToSetAlphaTo);
}
public boolean onSetAlpha(int alpha, View view)
{
if (view instanceof ViewGroup)
{
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
{
onSetAlpha(alpha, ((ViewGroup) view).getChildAt(i));
if (((ViewGroup) view).getBackground() != null) ((ViewGroup) view).getBackground().setAlpha(alpha);
}
}
else if (view instanceof ImageView)
{
if (((ImageView) view).getDrawable() != null) ((ImageView) view).getDrawable().setAlpha(alpha);
if (((ImageView) view).getBackground() != null) ((ImageView) view).getBackground().setAlpha(alpha);
}
else if (view instanceof TextView)
{
((TextView) view).setTextColor(((TextView) view).getTextColors().withAlpha(alpha));
if (((TextView) view).getBackground() != null) ((TextView) view).getBackground().setAlpha(alpha);
}
else if (view instanceof EditText)
{
((EditText) view).setTextColor(((EditText) view).getTextColors().withAlpha(alpha));
if (((EditText) view).getBackground() != null) ((EditText) view).getBackground().setAlpha(alpha);
}
return true;
}
You can add other kinds of views as you need.
You can extend the views draw() method and use the canvas.saveAlphaLayer()
public void draw(Canvas canvas) {
canvas.saveLayerAlpha(null, alphaValue, ALL_SAVE_FLAG);
super.draw(canvas);
canvas.restore();
}
You can set Alpha to all colors of the view.(such as the text of a button or the child views of a view group). Make them into colors xml and use in all View.
You can read colors from the view recursively and add alpha to them and set them back.
You can create the view as a main view of a new Activity. Then do as in How do I create a transparent Activity on Android?

How to set property "android:drawableTop" of a button at runtime

How to set property "android:drawableTop" of a button at runtime
Use
button.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use 0 if you do not want a Drawable there. The Drawables' bounds will be set to their intrinsic bounds.
If you use
button.setCompoundDrawables(left, top, right, bottom);
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use null if you do not want a Drawable there. The Drawables must already have had setBounds(Rect) called.
Drawable top = getResources().getDrawable(R.drawable.image);
button.setCompoundDrawablesWithIntrinsicBounds(null, top , null, null);
final Drawable drawableTop = getResources().getDrawable(R.drawable.btn_check_buttonless_on);
btnByCust.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
btnByCust.setCompoundDrawablesWithIntrinsicBounds(null, drawableTop , null, null);
}
});
Button button = (Button) findViewById(R.id.button);
button.setCompoundDrawables(left, top, right, bottom);
I use this code for use the "Theme.Holo" button with a "Custom image" at left and change it (the image)with a function that is called from various ways.
protected void app_dibujarLogojuego() {
if(bitmaplogojuego!=null){
bitmaplogojuego.recycle();
bitmaplogojuego=null;
}
Drawable LOGO = null;
if(verjuego.equals("COSA1")){ LOGO = getResources().getDrawable(R.drawable.img_logo_COSA1); }
if(verjuego.equals("COSA2")){ LOGO = getResources().getDrawable(R.drawable.img_logo_COSA2); }
if(verjuego.equals("COSA3")){ LOGO = getResources().getDrawable(R.drawable.img_logo_COSA3); }
if(verjuego.equals("COSA4")){ LOGO = getResources().getDrawable(R.drawable.img_logo_COSA4); }
BUTTON_DECLARED_ID.setCompoundDrawablesWithIntrinsicBounds(LOGO, null , null, null);
}
btn.setBackgroundResource(R.drawable.your_image_name_here);
If you are using Kotlin, you can use extension method to make things look elegant.
fun TextView.setDrawableTop(iconId: Int) {
val icon = this.context?.resources?.getDrawable(iconId)
this.setCompoundDrawablesWithIntrinsicBounds(null, icon, null, null)
}
Then you can use it like this:
// myTextView: TextView
myTextView.setDrawableTop(R.drawable.ic_happy)
Create an extension function like this and set top drawable like this
tvAccepted.setTopDrawable(R.drawable.ic_preparing_order_active)
fun TextView.setTopDrawable(icon: Int) {
this.setCompoundDrawablesRelativeWithIntrinsicBounds(0,icon,0,0)
}
where
setCompoundDrawablesRelativeWithIntrinsicBounds(left/start, top, right/end, bottom)

Changing ImageView source

I have an ImageView with a source image set in the xml using the following syntax:
<ImageView
android:id="#+id/articleImg"
style="#style/articleImgSmall_2"
android:src="#drawable/default_m" />
Now I need to change this image programmatically. What I need to do is delete the old image and add a new one though. What I have done is this:
myImgView.setBackgroundResource(R.drawable.monkey);
It works but I noticed android stacks the new image on top of the old one (dont ask me how I found out it's not relevant for the discussion :). I definitely need to get rid of the old one before setting the new image.
How can I achieve that?
Changing ImageView source:
Using setBackgroundResource() method:
myImgView.setBackgroundResource(R.drawable.monkey);
you are putting that monkey in the background.
I suggest the use of setImageResource() method:
myImgView.setImageResource(R.drawable.monkey);
or with setImageDrawable() method:
myImgView.setImageDrawable(getResources().getDrawable(R.drawable.monkey));
*** With new android API 22 getResources().getDrawable() is now deprecated. This is an example how to use now:
myImgView.setImageDrawable(getResources().getDrawable(R.drawable.monkey, getApplicationContext().getTheme()));
and how to validate for old API versions:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
myImgView.setImageDrawable(getResources().getDrawable(R.drawable.monkey, getApplicationContext().getTheme()));
} else {
myImgView.setImageDrawable(getResources().getDrawable(R.drawable.monkey));
}
You're supposed to use setImageResource instead of setBackgroundResource.
myImgView.setImageResource(R.drawable.monkey);
is used for setting image in the current image view, but if want to delete this image
then you can use this code like:
((ImageView) v.findViewById(R.id.ImageView1)).setImageResource(0);
now this will delete the image from your image view, because it has set the resources value to zero.
get ID of ImageView as
ImageView imgFp = (ImageView) findViewById(R.id.imgFp);
then Use
imgFp.setImageResource(R.drawable.fpscan);
to set source image programatically instead from XML.
Supplemental visual answer
ImageView: setImageResource() (standard method, aspect ratio is kept)
View: setBackgroundResource() (image is stretched)
Both
My fuller answer is here.
Or try this one. For me it's working fine:
imageView.setImageDrawable(ContextCompat.getDrawable(this, image));
If you want to set in imageview an image that is inside the mipmap dirs you can do it like this:
myImageView.setImageDrawable(getResources().getDrawable(R.mipmap.my_picture)
Just write a method for changing imageview
public void setImage(final Context mContext, final ImageView imageView, int picture)
{
if (mContext != null && imageView != null)
{
try
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
imageView.setImageDrawable(mContext.getResources().getDrawable(picture, mContext.getApplicationContext().getTheme()));
} else
{
imageView.setImageDrawable(mContext.getResources().getDrawable(picture));
}
} catch (Exception e)
{
e.printStackTrace();
}
}
}

Categories

Resources