15

TL;DR I have many buttons, and I am swapping the images of them. For some reason, my code only works on certain phones, and not on others.

My app uses the following code to compare images on imagebuttons:

onCreate:

redsquare = getResources().getDrawable(R.drawable.redsquare);
bitred = ((BitmapDrawable) redsquare).getBitmap();

onClick (v is the button clicked)

ClickGround = v.getBackground(); //Get the background of button clicked
//the bitmap background of the button clicked
BitClick = ((BitmapDrawable) ClickGround).getBitmap(); 

Then, later on in the onClick, I check if the user clicked on the redSquare by doing this:

if (BitClick == bitred) { //Make sure it is the red square that user clicked
}

I have tested it on my emulator and HUAWEI phone, and it works fine. When I tested it on my other phone (lg g3), the if statement doesn't go through. Why are the results different? Is the image somehow being messed up in my phone?

Revolver_Ocelot
  • 8,609
  • 3
  • 30
  • 48
Ruchir Baronia
  • 7,406
  • 5
  • 48
  • 83

3 Answers3

11

First of all, Resources.getDrawable(int) is deprecated. It is probably not related to your problem, but you should fix it.

If you compare bitmaps using == you are comparing object identities. If the == test is giving you false, that means you are comparing different objects. There can be no doubt about that.

Your snippets don't give enough context to be sure, but here are some possibilities:

  1. Something in your code is causing a different value to be assigned to bitred.

  2. The bitred identifiers in the two snippets of code do not denote the same Java variable.

  3. Your assumption that the "red" bitmap that is being used as a red background will always be the same object is invalid.

Lets assume that you have already eliminated 1. and 2. above, and focus on 3. How could that happen? Well, we can't see the relevant code (where you randomly swap the button images) but I can think of a couple of possibilities:

  • You could be fetching the bitmap from Resources repeatedly.
  • The method you call to switch the bitmaps could be creating / setting a copy.
  • The method you call to get the clicked button's bitmap could be returning a copy.

And since each of the above operations could depend on API implementations that could behave differently (because the javadoc allows that) the behavior of your app can be platform dependent.


So what is the solution?

Hypothetically, if you can figure out what is causing different bitmap objects to be used, you could potentially work around it. However, while your code still relies on unspecified behavior, there is a risk that it will break on other phones ...

A better solution is to change your app so that DOES NOT rely on using == to compare Bitmap objects. For example:

  • Associate a tag with each button.

     button1.setTag("one");
     button2.setTag("two");
    
  • Create a HashMap that maps from a button's tag to the current color for that button. The HashMap is part of the app's "model" state.

    Map colors = new HashMap();
    ...
    colors.put("one", "red");
    colors.put("two", "blue");
    
  • When you change the image bitmaps for the buttons, make the corresponding update to the map.

    // After swap the images of button1 and button2 ...
    String c1 = colors.get("one");
    String c2 = colors.get("two");
    colors.put("one", c2);
    colors.put("two", c1);
    
  • In the onClick method, use the map to lookup the button's current color rather than attempting to figure it out by comparing Bitmap objects.

    if (colors.get(v.getTag()).equals("red")) {
        // it is red!
    }
    

(Note that this is close to what Biju Parvathy suggests, but he hasn't said explicitly how to deal with the button colors / images changing.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I am still confused about why we cannot rely on the `Bitmap object` comparisons. Also, I am only swapping the images, and not the actual buttons, so how do I attach the `HashMap` or `Tag` to a drawable? Thanks so much Stephen! – Ruchir Baronia Jan 10 '16 at 06:19
  • You haven't show us the relevant code. However, it is pretty clear that something in the code you haven't shown us is making assumptions that it should not do. For example, does the javadoc **guarantee** that when you set a button image and then fetch it, you will get the same `Bitmap` object? – Stephen C Jan 10 '16 at 06:23
  • Okay, so if we do assume that the `Bitmap` object is somehow not being accurate, how do I create a `Hashmap` for each image or a `tag` for each button. I like the last option you gave me to look at the map to the buttons color. how do I do that? I am not actually switching the buttons though... – Ruchir Baronia Jan 10 '16 at 06:26
  • *"So how do I attach the HashMap or Tag to a drawable?"* - You don't. The Hashmap is part of the app state (the model). The tag is associated with the button, not to the Drawable. – Stephen C Jan 10 '16 at 06:26
  • You don't create a HashMap for each image. Please read my answer again. – Stephen C Jan 10 '16 at 06:27
  • But I'm not randomly swapping buttons, I'm randomly swapping the `image buttons` images. – Ruchir Baronia Jan 10 '16 at 06:27
  • Okay, so I'm unclear on how to do the `HashMap` option and update the `tag` on switch. Can you show me an example? – Ruchir Baronia Jan 10 '16 at 06:30
  • Thanks for the long detailed answer. One answer says to just change my `==` to `.sameAs`...Would that work? What are the problems with that? Thanks so much! – Ruchir Baronia Jan 16 '16 at 21:44
  • It is probably OK, but if the different places where the bitmap is loaded could result in it being loaded with different dimensions, config or pixels, then a test using sameAs could fail. – Stephen C Jan 16 '16 at 23:31
  • How would I know what to switch the map into? Because the image swap is random... – Ruchir Baronia Jan 17 '16 at 19:07
  • I'm not getting this. When your program (or the user) decides (randomly or not) to switch some images, your program knows what images are about to be switched. It has to know this, otherwise it can't switch them! – Stephen C Jan 17 '16 at 22:40
  • yes, but how what code will I add that will switch the maps? I know what colors are being switched, but the code you have will work only when switching the images of button one and two. Now the maps are switched correctly, but what if another two buttons switch, what code will I run then? Do I need a switch statement to determine what mappings to switch? – Ruchir Baronia Jan 17 '16 at 23:34
  • Will I need 16 switch cases then? – Ruchir Baronia Jan 17 '16 at 23:36
  • No. You don't need a `switch` statement at all. I am using the word "switch" with it's normal English meaning ... just like you are doing when you say "now the maps are switched correctly". Please treat my code (above) as a general example, not a specific solution to cut-and-paste. (Obviously, I can't give you a specific solution because you never showed us the context. And besides, you shouldn't need one!) – Stephen C Jan 18 '16 at 03:32
  • I know, but how will I know when to go through which code that switched the map? I will need to switch the mappings differently...how? – Ruchir Baronia Jan 18 '16 at 03:56
  • Can you provide an example? Thanks – Ruchir Baronia Jan 18 '16 at 03:57
7

You Can, I'm making a small change in your comparison code to make it work with all devices.

Bitmap BitRed = ((BitmapDrawable)getResources().getDrawable(R.drawable.redsquare)).getBitmap();

Bitmap BitClick = ((BitmapDrawable) v.getBackground()).getBitmap();

if (BitClick.sameAs(BitRed)) 
{ 
    //Your Button with Red Square Clicked
}

Please refer SameAs function for more details. Hope this help you.

Let'sRefactor
  • 3,303
  • 4
  • 27
  • 43
  • How is that different from what I have been doing – Ruchir Baronia Jan 11 '16 at 04:59
  • `==` operator only check reference, won't compare bitmap data – Let'sRefactor Jan 11 '16 at 06:38
  • so you are just saying to change == into =? – Ruchir Baronia Jan 15 '16 at 15:23
  • My advice is to use `sameAs()` function to compare instead of using `==` :) – Let'sRefactor Jan 15 '16 at 15:39
  • Would that work everywhere? Why dont the other answers say to use that? – Ruchir Baronia Jan 16 '16 at 21:43
  • 1
    As we already know, `SameAs` will compare data where as `==` compares reference. What I suspect is some device vendors might have implemented bitmap caching under the hood, so all time when we call `getBitmap()` it's giving us back the same reference to it, hence `==` returns true for those devices. Thanks @RuchirBaronia, to give me an interesting matter to research. I'll edit and update the answer once i get a solid information from an official/trusted source. – Let'sRefactor Feb 09 '16 at 07:14
  • Okay I did :) I am still confused on why == didn't work though. If you have any more info. please let me know! :) – Ruchir Baronia Feb 15 '16 at 16:52
2

First of all Resources.getDrawable(int) is deprecated , so you will surely get this kind of issues in some newer devices. Better approach is to use tags for your button for each drawable you gonna use for that specific button. For example, Consider you have 4 buttons i.e. button1, button2, button3 and button4. Now suppose initials they all have some defualt background.

Lets say,

button1.setBackgroundResource(R.drawable.default1);
button1.setTag(R.drawable.default1); // set the tag for button same as the drawable id

Do the same for all of your 4 buttons

button4.setBackgroundResource(R.drawable.default4);
button4.setTag(R.drawable.default4); // set the tag for button same as the drawable id

Now everytime when you change background/images for your buttons, you need to update the tag as well, something like this

button1.setBackgroundResource(R.drawable.redimage);
button1.setTag(R.drawable.redimage); // set the tag for button same as the drawable id

and on your buttons onClick(), you have to simply make use of a switch case to differentiate between different tags

@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.button1:
        switch(button1.getTag()){
            case R.drawable.default1:
            // do whatever action you want to perform
            break;
            case R.drawable.redimage:
            // do whatever action you want to perform
            break;
        }
        break;
        // similarly you can do the same for rest of the buttons.
    }
}

Hope this helps.

Let'sRefactor
  • 3,303
  • 4
  • 27
  • 43
Mukesh Rana
  • 4,051
  • 3
  • 27
  • 39
  • But the buttons arent moving its the images – Ruchir Baronia Jan 16 '16 at 21:44
  • I understand and i didn't say that anywhere, I only said whenever you change your images, you need to set that image drawable as a tag to your button and use that tag as the basis of distinction between different images while in onClick() – Mukesh Rana Jan 16 '16 at 21:50
  • I think you still didn't understand, tag is nothing but some unique value that needs to be attached to the button for each image. Like in other answers, they have used "one" and "two" static values, that can cause error if you gave same tag/value for two different images, since image drawable is gonna be unique and different for each image you gonna set to your button, that's why I have used that drawable value as a tag for each image to be set to button. Let me know if you still don't understand. – Mukesh Rana Jan 17 '16 at 08:03