setImageDrawable not having effect without existing content - android

I have an ImageView define in XML like as follows:
<ImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
I'm attempting to set the image contents dynamically in code, based on some user action (they select an image from their library). I set the image using the following code:
ImageView bgView = (ImageView)root.findViewById(R.id.background);
Drawable d = Drawable.createFromPath(wallpaper);
bgView.setImageDrawable(d);
Calling this code has no immediate effect on the ImageView instance. However, if I put some sort of image into the XML definition, something like:
<ImageView
android:id="#+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="#drawable/test_image"/>
It works fine. Does anyone know what the cause of the issue might be? I've verified that the image setting code is running on the UI thread, and that the Drawable created is valid. The problem feels like it might be related to the layout not being performed correctly to reflect the added image?

Try:
ImageView bgView = (ImageView)root.findViewById(R.id.background);
Drawable d = Drawable.createFromPath(wallpaper);
// check d != null
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
bgView.setImageDrawable(d);

So I figured out what the issue was, after a bit more digging. In the case where an empty image is being set (that is, the default state when no custom background is found in the user settings), my code does .setImageDrawable(null) when the view is loaded. Removing this seemed to fix the issue.

Related

Java/Android get.Background() setting alpha to all instances of drawable? [duplicate]

I've recently updated my phone to Android Marshmallow and ran my existing app on it, but noticed a difference in color behavior: When applying changes to the background of a view (drawable), all views that share the same background (reference) will also the same changes applied. While previously, this was not the case.
Example
In this example, I have a two views with the same background color, and I want to change the alpha level of one of both views.
First we define the views in the layout:
<LinearLayout
android:id="#+id/test1"
android:orientation="horizontal"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/testColor2">
</LinearLayout>
<LinearLayout
android:id="#+id/test2"
android:orientation="horizontal"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/testColor1"
android:layout_marginLeft="5dp">
</LinearLayout>
Both views share the same background color or drawable:
<color name="testColor1">#3F51B5</color>
<color name="testColor2">#3F51B5</color>
The result looks like this:
Now we are going to change one of the two background, like this:
LinearLayout test1 = (LinearLayout) findViewById(R.id.test1);
LinearLayout test2 = (LinearLayout) findViewById(R.id.test2);
test1.getBackground().setAlpha(80);
Which results in this:
However, the desired and expected result is obviously this:
Download the sample project here.
A few thoughs:
When setting the Alpha level trough XML, this behavior does not apply.
It does not matter if both views refer to a different color definition in colors.xml (like in the example), refer to the same color definition of both have the same color (hex) directly in the view's xml file.
Question
How can I make changes to a view's background without this affecting other views that share the same background. Preferably while still being able to use a background that directly refers to a color defined in the color's xml file
Most likely the class of each view's background and constantstate are
the same object. It seems as if the two color resources have been
"merged" somewhere -- meaning they have shared ConstantState. Maybe in
the Resources class' caching? I would've expected them to stay
separate since they're different resources (albeit with the same color
value), but apparently not.
– Snild Dolkow
The ColorDrawable's state stores alpha, so any changes to one will change the others. To prevent this, you can first call mutate() on the drawable, separating the two drawables (by making a copy of the state).
In the example, this would result in using test1.getBackground().mutate().setAlpha(80); instead of directly applying the alpha.

Color/drawable changes are applied to all views with the same background (color) [Marshmallow]

I've recently updated my phone to Android Marshmallow and ran my existing app on it, but noticed a difference in color behavior: When applying changes to the background of a view (drawable), all views that share the same background (reference) will also the same changes applied. While previously, this was not the case.
Example
In this example, I have a two views with the same background color, and I want to change the alpha level of one of both views.
First we define the views in the layout:
<LinearLayout
android:id="#+id/test1"
android:orientation="horizontal"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/testColor2">
</LinearLayout>
<LinearLayout
android:id="#+id/test2"
android:orientation="horizontal"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#color/testColor1"
android:layout_marginLeft="5dp">
</LinearLayout>
Both views share the same background color or drawable:
<color name="testColor1">#3F51B5</color>
<color name="testColor2">#3F51B5</color>
The result looks like this:
Now we are going to change one of the two background, like this:
LinearLayout test1 = (LinearLayout) findViewById(R.id.test1);
LinearLayout test2 = (LinearLayout) findViewById(R.id.test2);
test1.getBackground().setAlpha(80);
Which results in this:
However, the desired and expected result is obviously this:
Download the sample project here.
A few thoughs:
When setting the Alpha level trough XML, this behavior does not apply.
It does not matter if both views refer to a different color definition in colors.xml (like in the example), refer to the same color definition of both have the same color (hex) directly in the view's xml file.
Question
How can I make changes to a view's background without this affecting other views that share the same background. Preferably while still being able to use a background that directly refers to a color defined in the color's xml file
Most likely the class of each view's background and constantstate are
the same object. It seems as if the two color resources have been
"merged" somewhere -- meaning they have shared ConstantState. Maybe in
the Resources class' caching? I would've expected them to stay
separate since they're different resources (albeit with the same color
value), but apparently not.
– Snild Dolkow
The ColorDrawable's state stores alpha, so any changes to one will change the others. To prevent this, you can first call mutate() on the drawable, separating the two drawables (by making a copy of the state).
In the example, this would result in using test1.getBackground().mutate().setAlpha(80); instead of directly applying the alpha.

Changing AppWidget's background dynamically with a 9 patch drawable

There is no setBackground() method in the RemoteViews class, so I've used the following workaround:
Created a FrameLayout for my app widget with an ImageView as the background view.
Changed the ImageView image using setImageViewResource() method.
Unfortunately, when it comes to 9-patch drawables this method does not work. Also when an ImageView's android:src attribute points to 9-patch - it doesn't work too. Is there any way to change the AppWidget's background image programatically using a 9-patch drawable? Thanks in advance.
EDIT
Settings the 9-patch as initial background in the XML:
<ImageView
android:id="#+id/small_widget_layout_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/background_lime" />
When I use android:src="#drawable/background_lime" it doesn't stretch the image properly, this code works fine. And the code to change the background from the AppWidgetProvider onUpdate method:
views.setImageViewResource(R.id.small_widget_layout_bg,
R.drawable.backgroung_lime);
This does not stretch the image as a 9-patch.
This answer was diagnosed in the above comments...
RemoteView doesn't allow access to View.setBackground(), so your workaround of using the android:src property of an ImageView is good, providing that the android:scaleType property is set to fitXY.
ImageView won't stretch it's foreground image unless you tell it to.
Please ignore if u find this trivial or irrelevant, but canT you try (assuming you are dealing with widgets):
Declaring different layouts (xml)for your widget.
Change the remoteView's source (layout.id) instead of trying to make alterations to the selected layout.
AFAIK, this is the most common approach to solving such problems. This is not perfect for two simple things I could note myself:
What do you do if you have n different "states" / "views" in your widget?
But as long as your 9-patch files are also static resources, n is painful but still theoretically manageable.
It s tedious to keep track of the changes in these parallel files.
I'd also love to find an easy way for this one...
This approach may not be an option for you also because it is basically the hard way. But it s an option nonetheless.
Suggestion #2
Have you tried using the method?
public void setInt (int viewId, String methodName, int value)
remoteView.setInt(R.id.viewid, "setBackgroundResource", R.drawable.backgroung_lime);
From another question: Change remoteView ImageView background

Android button setColorFilter behaviour [duplicate]

(I changed the question a bit, because the problem is a bit clearer now)
I have 4 buttons on my application, and when a user clickes certain button
I change that button color.
when button 3 is clicked I want to change his color to green, otherwise I want remove his green filter (when button1/2/4 are clicked).
If I click on button 3 It does get the green filter. If then I click button 4 it removes the green filter, but if I click button 1 or 2, nothing happens.
When I switched the position of the buttons in the XML, and put button3 first, It doesnt happen, ideas?
The relevant part of the layout xml is:
<Button
android:id="#+id/ans1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/ans2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans3"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans4"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
The code is:
if (answer.equals("3"))
{
question.setText("In if");
d.setColorFilter(filter);
}
else
{
question.setText("else");
d.setColorFilter(null);
}
I seem to remember having issues when creating too many ColorFilters before. It doesn't sound for certain like that's what's at fault here, since it's happening right away. Still, what you might try is having the filter as a class variable, and then using it within the if/else block. Also, as Trev mentioned, since you're just wanting to remove the green filter, you can just pass null to setColorFilter and avoid making the transparent filter, so you'd end up with something like this:
//in main class
PorterDuffColorFilter greenFilter =
new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP);
//in CheckAnswer()
Drawable d = findViewById(R.id.ans2).getBackground();
if(answer.equals("1") d.setColorFilter(greenFilter)
else d.setColorFilter(null);
The default behavior when calling setColorFilter(ColorFilter) on a Drawable does not automatically invalidate the Drawable, meaning it will not redraw itself solely as a result of the method call.
Try calling d.invalidateSelf() after setting the ColorFilter.
Yesterday I posted a suggestion to a very similar problem that you asked here:
Android button setColorFilter behaviour
It appears that you have edited the code you originally posted there in order to incorporate the suggestions you were given (without acknowledging the answers) and then posted exactly the same code in this question.
The Drawable documentation regarding setColorFilter(ColorFilter cf) states that 'null' may be passed to remove any existing filters. So, perhaps it could be that once the TRANSPARENT filter has been applied, then your subsequent GREEN filter can't be seen? I don't know enough about .setColorFilter and PorterDuff to know for sure, but it's worth a shot. Perhaps try:
d.setColorFilter(null);
d.setColorFilter(filter);
Also you could instead use this method:
setColorFilter(int color, PorterDuff.Mode mode)
You just need to mutate each drawable before setColorFilter.
Drawable d = findViewById(R.id.ans2).getBackground();
d = d.mutate();
d.setColorFilter

Android button setColorFilter behaviour

(I changed the question a bit, because the problem is a bit clearer now)
I have 4 buttons on my application, and when a user clickes certain button
I change that button color.
when button 3 is clicked I want to change his color to green, otherwise I want remove his green filter (when button1/2/4 are clicked).
If I click on button 3 It does get the green filter. If then I click button 4 it removes the green filter, but if I click button 1 or 2, nothing happens.
When I switched the position of the buttons in the XML, and put button3 first, It doesnt happen, ideas?
The relevant part of the layout xml is:
<Button
android:id="#+id/ans1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/ans2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans3"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/ans4"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
The code is:
if (answer.equals("3"))
{
question.setText("In if");
d.setColorFilter(filter);
}
else
{
question.setText("else");
d.setColorFilter(null);
}
I seem to remember having issues when creating too many ColorFilters before. It doesn't sound for certain like that's what's at fault here, since it's happening right away. Still, what you might try is having the filter as a class variable, and then using it within the if/else block. Also, as Trev mentioned, since you're just wanting to remove the green filter, you can just pass null to setColorFilter and avoid making the transparent filter, so you'd end up with something like this:
//in main class
PorterDuffColorFilter greenFilter =
new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP);
//in CheckAnswer()
Drawable d = findViewById(R.id.ans2).getBackground();
if(answer.equals("1") d.setColorFilter(greenFilter)
else d.setColorFilter(null);
The default behavior when calling setColorFilter(ColorFilter) on a Drawable does not automatically invalidate the Drawable, meaning it will not redraw itself solely as a result of the method call.
Try calling d.invalidateSelf() after setting the ColorFilter.
Yesterday I posted a suggestion to a very similar problem that you asked here:
Android button setColorFilter behaviour
It appears that you have edited the code you originally posted there in order to incorporate the suggestions you were given (without acknowledging the answers) and then posted exactly the same code in this question.
The Drawable documentation regarding setColorFilter(ColorFilter cf) states that 'null' may be passed to remove any existing filters. So, perhaps it could be that once the TRANSPARENT filter has been applied, then your subsequent GREEN filter can't be seen? I don't know enough about .setColorFilter and PorterDuff to know for sure, but it's worth a shot. Perhaps try:
d.setColorFilter(null);
d.setColorFilter(filter);
Also you could instead use this method:
setColorFilter(int color, PorterDuff.Mode mode)
You just need to mutate each drawable before setColorFilter.
Drawable d = findViewById(R.id.ans2).getBackground();
d = d.mutate();
d.setColorFilter

Categories

Resources