Wednesday, September 16, 2015

MyNotes: ImageSwitcher


As I mentioned in my last post I added ImageSwitchers to the NoteEditOptionsBar. ImageSwitchers are a type of ViewAnimator.  Where a ViewFlipper animates swapping views, the ImageSwitcher does the same thing between images. 

This allows me to smoothly change the image displayed for some of icons based on the state of the note.  I have two icons that this applies to: save and cancel/delete.   For the save icon, I only want it available when the note has been edited and contains text.  In all other cases I want it gone.

I also want the delete/cancel icon to differ based on note state.  If the note is persisted, then the delete icon shows.  Otherwise, I want the cancel icon to show. 

Both of the problems can be solved in a lot of different ways.  For instance, I could define two independent views for the cancel and delete.  The views would share the same space claim on the UI, but only show one at a time.   I could then apply a ViewFlipper to transition between the views.  That was actually how I first solved the problem, but it seemed like overkill.  I has a nested layout didn’t feel necessary and I didn’t like having two icons that performed the same click behavior, but only differed by the image displayed.

I could also implement it as a single ImageView and then changed the source image as needed.   I wasn’t happy with this because there was an abrupt transition between the images.  I could add animation logic around, but again it felt like overkill and seemed fragile.

Ultimately, that was I why I decided to move to the ImageSwitcher.  It givesme to have the best of both worlds.  I (theoretically) have a single ImageView that smoothly animates between images. 

The theoretically is because the ImageSwitcher uses a factory to create Views as needed.  I think it actually create multiple Views under the covers, but I from my code’s perspective I can treat it as a single View and leave the messy details to Google.

Step 1: Define the ImageSwitcher in XML

   2:     <ImageSwitcher
   3:         android:id="@+id/component_note_edit_options_bar_purge"
   4:         style="@style/EditToolbarOption"
   5:         android:layout_width="wrap_content"
   6:         android:layout_height="wrap_content"
   7:         android:layout_alignParentLeft="true"
   8:         android:inAnimation="@anim/fade_in"
   9:         android:outAnimation="@anim/fade_out" />

In the XML, define the ImageSwitcher like any other View.  It has two special attributes inAnimation and outAnimation to define any special animations around switching the images.   In this case I want to fade out the old image and fade in the new image.

Step 2: Create a View Factory in Code

   2:     /**
   3:      * The view factory to support the {@link ImageSwitcher}s. This will create a basic {@link
   4:      * ImageView} when making the view.
   5:      */
   6:     final ViewSwitcher.ViewFactory mViewFactory = new ViewSwitcher.ViewFactory() {
   8:         public View makeView() {
   9:             // Create a new ImageView set it's properties
  10:             ImageView imageView = new ImageView(getContext());
  11:             imageView.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.MATCH_PARENT,
  12:                                                                      LayoutParams.MATCH_PARENT));
  13:             return imageView;
  14:         }
  15:     };

This is a mandatory bit for the ImageSwitcher.  Use the makeView method to create the views you want to be handled by the ImageSwitcher.   In my case,  I want simple, no frills ImageViews so I create one and return it.

Step 3: Assign the factory

   1: void initialize(Context context) {
   2:     View rootView = LayoutInflater.from(context)
   3:                                   .inflate(R.layout.component_note_edit_options_bar, this,
   4:                                            true);
   5:     ButterKnife.inject(this, rootView);
   6:     mPurgeImageSwitcher.setFactory(mViewFactory);
   7:     mSaveImageSwitcher.setFactory(mViewFactory);
   9: }

Assign the factory created in step 2 to the ImageSwitchers.

Step 4: Swap the images

   1: /**
   2:   * Change the Purge button imagery based on the note. If the note is currently persisted, then
   3:   * show the delete image. Otherwise, show the cancel image.
   4:   *
   5:   * @param note
   6:   *         the note to use for determining the purge button UI.
   7:   */
   8:  private void updatePurgeButtonImagery(@NonNull Note note) {
   9:      if (note.getId() == Note.NOT_STORED) {
  10:          mPurgeImageSwitcher.setImageResource(R.drawable.cancel_background);
  11:      }
  12:      else {
  13:          mPurgeImageSwitcher.setImageResource(R.drawable.delete_note_background);
  14:      }
  15:  }

To change the image shown in the ImageSwitcher call setImageDrawable, setImageResource, or setImageUri.


The commits with these changes can be found on github in and

No comments :

Post a Comment