I have a seek bar and I am setting the thumb drawable in code. When the activity is starting I can see the changed drawable for the thumb but if I start a new activity from the seekbar activity and come back, the seekbar's thumb gets invisible (only if I set it's drawable again). This is happening only if I come back from other activity to the seekbar activity.
I need to change the drawable of the thumb in onRestart() because the other activityes may change the color or shape of the thumb and I need to refresh it's drawable.
I tried invalidate() on the seekbar but no use...
EDIT:
I tried to make 3 static Drawable objects and load the images in onCreate() and I noticed that after coming back on the SeekBar activity, if I set the thumb drawable to the one that is already set, the thumb is visible but if I change the drawable, the thumb becomes invisible.
EDIT 2:
In this case I set the loaded drawables to the thumb:
String gender = getGender();
if (gender.equals(Profile.GENDER_1)) {
mSeekBar.setThumb(mDrawable1);
} else if (gender.equals(Profile.GENDER_2)) {
mSeekBar.setThumb(mDrawable2);
} else {
mSeekBar.setThumb(mDrawable3);
}
And this is if I try to get the drawables from the resources
String gender = getGender();
if (gender.equals(Profile.GENDER_1)) {
mDrawable = mSeekBar.getContext().getResources().getDrawable(R.drawable.slider_thumb_1);
} else if (gender.equals(Profile.GENDER_2)) {
mDrawable = mSeekBar.getContext().getResources().getDrawable(R.drawable.slider_thumb_2);
} else {
mDrawable = mSeekBar.getContext().getResources().getDrawable(R.drawable.slider_thumb_3);
}
mSeekBar.setThumb(mDrawable);
In both cases the thumb is getting invisible..
What can be the problem? does somebody know the answer?
Thank you!
You are not setting the drawable's bounds before using it.
Try adding this line before the setThumb() call:
mDrawable.setBounds(0,0,
mDrawable.getIntrinsicWidth(),
mDrawable.getIntrinsicHeight()
);
Related
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.
I use pallete class to set random background color to textview, and some images cannot generate color and return color like gray. I find that some image file work with getMutedColor and some other work with getVibrantColor and others.
Here is my layout. I use ImageView to show bitmap and a TextView below it to show name with random background.
How can we detect whether image file work with `getMutedColor` or `getVibrantColor` or others?
Here is my code I try so far:
public void generateColor(Bitmap mypic){
mImageView.setImageBitmap(result);
Palette p = Palette.from(result).generate();
mTextView.setBackgroundColor(p.getVibrantColor(default_color));
}
I am appreciate for any help.
I'd simply check for null.
public int getSwatch(Bitmap b){
Palette p = Palette.from(b).generate();
Palette.Swatch swatch;
if((swatch = p.getVibrantSwatch()) != null){
return swatch.getRgb();
}
if((swatch = p.getLightMutedSwatch()) != null){
return swatch.getRgb();
}
.
.
.
return Color.WHITE;
}
Color.WHITE at the end is the fallback color. Should Palette be unable to find a color, you will still get WHITE as a result. You can swap it with any color you wish.
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.
From my understanding, the ImageButton within LibGDX is a frame containing an image. Is it possible to set the background of the frame?
For example, I would like to use a Button background, and apply an Icon on top of that image.
Current Code
Skin with the background:
"com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle": {
"default": {
"imageDown": "button-down",
"imageUp": "button-up"
}
}
Creating the ImageButton:
// Getting imageButtonStyle with "default" style, as it just has the background.
ImageButton.ImageButtonStyle imageButtonStyle = skin.get( "default", ImageButton.ImageButtonStyle.class );
ImageButton button = new ImageButton( imageButtonStyle );
// Set the image on the button to something.
Background image.
Background image with icon overlayed.
Thank you for your help.
The trick is using the 2 different Styles available. ImageButtonStyle to set the icon attributes, but since ImageButtonStyle extends ButtonStyle, the background attributes are set in ButtonStyle. Something like:
ImageButtonStyle style = new ImageButtonStyle();
style.up = background;
style.down = background;
style.checked = background;
style.imageUp = icon_up;
style.imageDown = icon_down;
style.imageChecked = icon_checked;
style.unpressedOffsetY = -20; // to "not" center the icon
style.unpressedOffsetX = -30; // to "not" center the icon
ImageButton heartButton = new ImageButton(style);
ImageButton envelopeButton = new ImageButton(envelopeStyle);
Something like that (for me, backround, icon_* are Drawable). But that's the basics and worked like a charm for me.
You could implement your own button class that extends ImageButton.
Then, if you overload the constructor, you can pass either Drawables or Textures for imageUp, imageDown and background to it:
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.SpriteDrawable;
public class myButton extends ImageButton
{
public myButton(Texture texture_up, Texture texture_down, Texture background)
{
super(new SpriteDrawable(new Sprite(texture_up)),
new SpriteDrawable(new Sprite(texture_down)));
this.setBackground(new SpriteDrawable(new Sprite(background));
}
}
Now, you can use your own Button class and instantiate it as follows:
Texture textureUp = new Texture(Gdx.files.internal("data/image_up.png"));
Texture textureDown = new Texture(Gdx.files.internal("data/image_down.png"));
Texture background = new Texture(Gdx.files.internal("data/background.png"));
MyButton myButton = new MyButton(textureUp, textureDown, background);
Maybe play around with it a little and you'll find out what else you can do with it. Just make sure that you have the images in the correct resolution. Background doesn't have to be an image.
A button by definition will have three states:
imageDown - When the mouse is clicked on the button
imageUp - When the mouse is released on the button
imageChecked - When the mouse is hovering on the button
In the scene2d api there isn't a way yet of manually setting the imageButton's background image but if you do want something like that its best to have an array of images and an index as to what image you want, and render using a sprite batch e.g:
Texture[] images;
int currentImage;
I've a requirement in which an ImageView's enabled state is changed.
I have 2 images: one for the enabled state & another for the disabled state.
Without using a selector, how can I change the Image src when the setEnabled(true/false) is changed?
That is:
imageView.setEnabled(true);
imageView - use image imgEnabled.png
imageView.setEnabled(false);
imageView - use image imgDisabled.png
Thank You
Try this
if(imageView.isEnabled())
{
imageView.setBackgroundResource(R.drawable.imgEnabled);
}
else
{
imageView.setBackgroundResource(R.drawable.imgDisabled);
}
You can do it by setting the image yourself using setImageResource.
But this may not be appropriate, since your view's state may be changed from your code anywhere.
imageView.setEnabled(true);
imageView.setImageResource(R.drawable.imgEnabled);
imageView.setEnabled(false);
imageView.setImageResource(R.drawable.imgDisabled);
You can also write your own class that extends ImageView
and override the following method
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if(isEnabled())
{
//set drawable for enabled state here
}
else
{
//set drawable for disabled state here
}
}
you can use this to change image resource-
imageView.setBackgroundResource(R.drawable.image);