Assert ImageView was loaded with specific drawable resource ID - android

I'm writing a Robolectric unit test and I need to make an assertion that an ImageView had setImageResource(int) called on it with a certain resource ID. I'm using fest-android for assertions but it doesn't appear to contain this assertion.
I also tried to get the ShadowImageView from Robolectric for the ImageView because I know it used to give you access to this, but it's now gone.
Lastly, I tried to call setImageDrawable in my code instead of setImageResource, then in my test assert like this:
assertThat(imageView).hasDrawable(resources.getDrawable(R.drawable.some_drawable));
but this also fails, even though the failure message clearly shows it's the same Drawable being loaded.

For Background
ImageView imageView = (ImageView) activity.findViewById(R.id.imageview);
assertEquals(R.drawable.expected, Robolectric.shadowOf(imageView.getBackground()).getCreatedFromResId());
For Drawable
ImageView imageView = (ImageView) activity.findViewById(R.id.imageview);
assertEquals(R.drawable.expected, Robolectric.shadowOf(imageView.getDrawable()).getCreatedFromResId());

From Roboelectric 3.0+
This is how you can do:
int drawableResId = Shadows.shadowOf(errorImageView.getDrawable()).getCreatedFromResId();
assertThat("error image drawable", R.drawable.ic_sentiment_dissatisfied_white_144dp, is(equalTo(drawableResId)));

I ended up extending fest-android to solve this:
public class CustomImageViewAssert extends ImageViewAssert {
protected CustomImageViewAssert(ImageView actual) {
super(actual);
}
public CustomImageViewAssert hasDrawableWithId(int resId) {
boolean hasDrawable = hasDrawableResourceId(actual.getDrawable(), resId);
String errorMessage = String.format("Expected ImageView to have drawable with id <%d>", resId);
Assertions.assertThat(hasDrawable).overridingErrorMessage(errorMessage).isTrue();
return this;
}
private static boolean hasDrawableResourceId(Drawable drawable, int expectedResId) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
Bitmap bitmap = bitmapDrawable.getBitmap();
ShadowBitmap shadowBitmap = (ShadowBitmap) shadowOf(bitmap);
int loadedFromResourceId = shadowBitmap.getCreatedFromResId();
return expectedResId == loadedFromResourceId;
}
}
The magic sauce is:
ShadowBitmap shadowBitmap = (ShadowBitmap) shadowOf(bitmap);
int loadedFromResourceId = shadowBitmap.getCreatedFromResId();
which is Robolectric specific, so I can't submit a pull request to fest-android with this.

With kotlin extensions:
fun ImageView.hasDrawable(resId: Int): Boolean =
Shadows.shadowOf(this.drawable).createdFromResId == resId
fun ImageView.hasBackground(resId: Int): Boolean =
Shadows.shadowOf(this.background).createdFromResId == resId
Usage:
assertTrue(image.hasDrawable(R.drawable.image_resource))

Related

How to change background color at image in widget android

I have a problem with change background color at image in widget.
Right now i'm using like this:
try {
Class c = Class.forName("android.widget.RemoteViews");
Method m = c.getMethod("setDrawableParameters", int.class, boolean.class, int.class, int.class, PorterDuff.Mode.class, int.class);
m.invoke(remoteViews, R.id.myImage, true, -1, Color.HSVToColor(color), PorterDuff.Mode.SRC_OVER, -1);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
Everything works perfect, until i tested on android 9, here i receive an error:
java.lang.NoSuchMethodException: setDrawableParameters [int, boolean,
int, int, class android.graphics.PorterDuff$Mode, int]
Do you have any idea how can i make it work on android 9 also?
Thanks.
Another solution that worked for me is to have the image source in the xml layout android:src="#mipmap/myImage" and in code remoteViews.setInt(R.id.myImageView,"setColorFilter", myColor);
setDrawableParameters(..) is called setDrawableTint(..) in Android 9.0.
Here is the source code of the new method setDrawableTint(..) available in Android 9.0, I guess it does the same job as setDrawableParameters(..) in previous version of Android but you have to try it out in your project.
/**
* Equivalent to calling
* {#link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
* on the {#link Drawable} of a given view.
* <p>
* The operation will be performed on the {#link Drawable} returned by the
* target {#link View#getBackground()} by default. If targetBackground is false,
* we assume the target is an {#link ImageView} and try applying the operations
* to {#link ImageView#getDrawable()}.
* <p>
*/
private class SetDrawableTint extends Action {
SetDrawableTint(int id, boolean targetBackground,
int colorFilter, #NonNull PorterDuff.Mode mode) {
this.viewId = id;
this.targetBackground = targetBackground;
this.colorFilter = colorFilter;
this.filterMode = mode;
}
SetDrawableTint(Parcel parcel) {
viewId = parcel.readInt();
targetBackground = parcel.readInt() != 0;
colorFilter = parcel.readInt();
filterMode = PorterDuff.intToMode(parcel.readInt());
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
dest.writeInt(targetBackground ? 1 : 0);
dest.writeInt(colorFilter);
dest.writeInt(PorterDuff.modeToInt(filterMode));
}
#Override
public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
final View target = root.findViewById(viewId);
if (target == null) return;
// Pick the correct drawable to modify for this view
Drawable targetDrawable = null;
if (targetBackground) {
targetDrawable = target.getBackground();
} else if (target instanceof ImageView) {
ImageView imageView = (ImageView) target;
targetDrawable = imageView.getDrawable();
}
if (targetDrawable != null) {
targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
}
}
#Override
public int getActionTag() {
return SET_DRAWABLE_TINT_TAG;
}
boolean targetBackground;
int colorFilter;
PorterDuff.Mode filterMode;
}
You could check how the new method works and what parameters needs and then have in your project something like:
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.P){
// Your actual implementation with "setDrawableParameters"
changeImageBackgroundColorBeforeAndroidPIE()
} else{
// Your new implementation with "setDrawableTint"
changeImageBackgroundColorAndroidPIEAndLater()
}
Another solution could be:
As I see the method setDrawableParameters(..) is hidden, so it is not meant to be used in this way, I guess it won't be the optimal solution to solve your problem. It looks like a hacky solution, but it can actually get worst once they change it or once it won't be supported in the way you thought it works.
Let's start from the beginning, you want to change the background color of an image in a widget, in a RemoteView more precisely, probably you could convert the drawable into a bitmap and then call RemoteViews.setImageBitmap()
Here is how to Convert a drawable into a bitmap
UPDATE: Another solution that the author just found was
Another solution that worked for me is to have the image source in the
xml layout android:src="#mipmap/myImage" and in code
remoteViews.setInt(R.id.myImageView,"setColorFilter", myColor);
Here the author answer: https://stackoverflow.com/a/55123126/3564632

How to check android drawable type?

Can I check the type of drawable at runtime whether it is an xml shape/selector/layer-list or jpg/png/gif file?
You can get type of drawable with this:
public static String getTypeOfDrawable(int drawableId,Context context) {
Drawable resImg = context.getResources().getDrawable(drawableId);
return resImg.getClass().toString().replace("class android.graphics.drawable.","");
}
You will get result like:
BitmapDrawable
StateListDrawable
Then you if it is BitmapDrawable its not that hard to get format of file

ion java.lang.ClassCastException

I'm using ION (https://github.com/koush/ion) to load images from the web into a ListView of ImageView. Currently I want to get the bitmap from the ImageView, but I'm getting one exception that is force closing my app:
java.lang.ClassCastException: com.koushikdutta.ion.IonDrawable cannot be cast to android.graphics.drawable.BitmapDrawable
These are the lines that I'm using:
final ImageView itemImageView = (ImageView)view.findViewById(R.id.photoImage);
final Bitmap itemDrawable = ((BitmapDrawable) itemImageView.getDrawable()).getBitmap();
How can I solve this? Thanks in advance!
Ion sets a custom drawable to manage animated GIFs, fade transitions, etc, so it can't be cast to a BitmapDrawable.
Use:
Ion.with(imageView).getBitmap()
to get the bitmap out
If you set a callback when making the Ion request and have it return an ImageViewBitmapInfo you can retrieve bitmaps that way. For e.g.
Ion.with(imageView)
.placeholder(R.drawable.default_avatar)
.load(userImageURL)
.withBitmapInfo()
.setCallback(new FutureCallback<ImageViewBitmapInfo>() {
#Override
public void onCompleted(Exception e, ImageViewBitmapInfo result) {
// Check for exceptions
// Check bitmaps first
Bitmap b = result.getBitmapInfo().bitmaps[0];
...
};
It may be worth looking into:
nostra13's "Universal Image Loader"
It's an awesome library with tons of features to display images from URLs, cast to bitmaps, etc.
if(imageView.getDrawable() instanceof BitmapDrawable)
bm = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
else { /***to check if it is ION drawable, use ION*/
bm = Ion.with(imageView).getBitmap();
}

I want change my image in android

i want to change my image, if i have this code:
ImageView img = new ImageView(this);
img.setImageResource(R.drawable.my_image);
that code works, but i want save R.drawable.my_image in one variable
let's say listofimage , it because i must change the name of image file
let's say i have "my_image", "my_image1", "my_image2"
i try to change the code like this
ImageView img = new ImageView(this);
string listofimage="";
if(result1.equals("my_image")){
listofimage="R.drawable.my_image";
} else if (result1.equals("my_image1")) {
listofimage="R.drawable.my_image1";
}else if (result1.equals("my_image2")) {
listofimage="R.drawable.my_image2";
}
img.setImageResource(listofimage);
but that code didn't work. can anyone help me?
With your code you could do like this:
ImageView img = new ImageView(this);
if(result1.equals("my_image")){
img.setImageResource(R.drawable.my_image);
} else if (result1.equals("my_image1")) {
img.setImageResource(R.drawable.my_image1);
}else if (result1.equals("my_image2")) {
img.setImageResource(R.drawable.my_image2);
}
You code contains three compilation errors:
String with lowercase "s"
R.drawable.my_image is of type int
setImageResource expects int as its parameter
Solution is to replace "string" with "int" in your code to look like this:
ImageView img = new ImageView(this);
int listofimage=R.drawable.my_image;
img.setImageResource(listofimage);

How do I iterate over a large number of resources with similar names?

I'm currently trying to start a thread which fetches a drawable from its resources and posts it after a set amount of time. Then fetches the next drawable from resources and does the same thing all over.
final ImageView zImageView = (ImageView) findViewById(R.id.zzz_animation_view);
new Thread(new Runnable() {
public void run() {
for(int i = 1; i<=510; i++)
{
final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.z_animation0001); // how do I increment this recourse to z_animation0002, 0003, ..., 0510 ?
zImageView.postDelayed(new Runnable() {
public void run() {
zImageView.setImageBitmap(bitmap);
}
},200);
}
}
}).start();
Firstly. Is this a proper way of approaching the problem? The drawables are too large for me to use animation-list and as such my goal is to load the images one at a time to ensure that I have enough memory. Secondly how do I solve the problem of iterating over my resources?
You can get resource id by its name using Resources.getIdentifier :
final int current = 5;
final String pattern = "z_animation%04d";
final String name = String.format(pattern, current);
final int id = getResources().getIdentifier(name, "drawable", getPackageName());
final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);
Also, here you can find my answer for the same question with code sample that does exactly what you want.
I would put the images in the assets folder and load them from there as described here: https://xjaphx.wordpress.com/2011/10/02/store-and-use-files-in-assets/
In the assets folder you can just add a number to the file name.

Categories

Resources