11

I need to change the image of a toggle button every time it is clicked.

Is it efficient to do this?

public void onClickToggleButton(View v) {
    if(_on) {
        _on=false;
        myImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_off));
    } else {
        _on=true;
        myImageView.setImageDrawable(getResources().getDrawable(R.drawable.btn_on));
    }
}

Or does it mean the Drawable will be decoded from the PNG file every time?

In which case calling getDrawable() only twice (in onCreate()) and keeping my own references to the 2 Drawables would be better.

Sébastien
  • 13,831
  • 10
  • 55
  • 70
  • 2
    See the source http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/content/res/Resources.java#Resources.getDrawable%28int%29 (if you follow the method calls, you'll end up in the caching part and eventually in BitmapState) (short answer is there is a WeakRef cache to avoid decoding more than once as long as the memory allows) – njzk2 May 06 '14 at 20:51
  • on a side note, you should really not be a/ adding your own state when android ToggleButton already does that and b/ changing the drawable every time you click on it. instead, use a StateListDrawable to define the drawable to use for each state (on, off, pressed, focused, disabled ...) – njzk2 May 06 '14 at 20:54
  • 1
    Why do you bother create the Drawable yourself ? Call setImageResource would be better. – Stephane Mathis May 06 '14 at 20:56
  • @StephaneMathis The documentation contains a warning about calling `setImageResource()` from the UI thread. Besides I don't think the efficiency of this method is different from the one I proposed. – Sébastien May 06 '14 at 21:37

2 Answers2

1

As of the Android API 28 source code, when you call Resources#getDrawable, ResourceImpl#loadDrawable is called, which has this snippet at its start:

// If the drawable's XML lives in our current density qualifier,
// it's okay to use a scaled version from the cache. Otherwise, we
// need to actually load the drawable from XML.
final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;

There is also a field that's manipulated by the Android Zygote process called mIsPreloading in the ResourceImpl class. If useCache is true and ResourcesImpl is not preloading, then a cached version of the drawable is used. There's also some further logic around caching ColorDrawables and themed Drawables.

If you're like me and you need a deep copy of a Drawable and this caching behavior is getting in your way, check out this related answer: Deep copy of a Drawable

Cogentleman
  • 321
  • 2
  • 5
0

This don't response your question if is efficient or not to call every time this method. But as @njzk2 noted, you could use a State Selector on your toggle button.

I copy to you an example of a working one (i'm using). Just change the name of the drawable by yours drawables.

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/toggle_on" android:state_checked="true" />
  <item android:drawable="@drawable/toggle_off" android:state_checked="false" />
</selector>

On your xml where you define your toogle Button set the background as:

android:background="@drawable/toogle_selector"

Where "toogle_selector" is the name of the file i copied before.

With this you can forget the efficiency of loading the drawable everytime.

Hope this helps.

Community
  • 1
  • 1
Sulfkain
  • 5,082
  • 1
  • 20
  • 38