Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

state_checked doesn't toggle imageview on and off

Tags:

android

Android Studio 2.0 preview

Hello,

I have this selector that is connected to an imageview. I want to toggle the imageview on and off. So off will display a green color and on will display a red color.

However, when I click the imageview nothing happens. I have tried different combinations of state_pressed and state_checked. And now its getting too confusing. What am I missing here.

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

    <item android:state_checked="true"
          android:drawable="@drawable/bottom_left_border_pressed">
    </item>
</selector>

Many thanks for any suggestions,

like image 297
ant2009 Avatar asked Nov 26 '15 15:11

ant2009


4 Answers

However, when I click the imageview nothing happens.

So, as @Zielony said, the reason is simple: ImageView not supported checked states.

Each class extended from View supports different states: pressed, selected, focused, checkable (note that is not checked state) etc. But checked is special state. And only few Views support it: ToggleButton, Switch, RadioButton, CheckBox, CheckedTextView. They implemented Checkable interface.

You have variants how to solve your case, but it depends what exactly you need:

  1. If you really want this simple thing

    So off will display a green color and on will display a red color.

    you can use CheckBox or CheckedTextView for example. Just create selector:

    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="<red_color>" android:state_checked="true" />
        <item android:drawable="<green_color>" />
    </selector>
    

    and use it

    <CheckBox
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:text=""
        android:button="@null"
        android:clickable="true"
        android:background="<your_selector>"/>
    
  2. Use other state. You can use state_activated (or state_selected but, be careful, because selected is transient property)

    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:drawable="<red_color>" android:state_activated="true" />
        <item android:drawable="<green_color>" />
    </selector>
    

    and toggle this state by code

    <your_image_view>.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            <your_image_view>.setActivated(!<your_image_view>.isActivated());
        }
    });
    
  3. Write your own checkable class. You can see from other people how to do it:

    1. Official android implementation from old Contacts application

      public class CheckableImageView extends ImageView implements Checkable {
          private boolean mChecked;
      
          private static final int[] CHECKED_STATE_SET = {
              android.R.attr.state_checked
          };
      
          public CheckableImageView(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          @Override
          public int[] onCreateDrawableState(int extraSpace) {
              final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
              if (isChecked()) {
                  mergeDrawableStates(drawableState, CHECKED_STATE_SET);
              }
              return drawableState;
          }
      
          public void toggle() {
              setChecked(!mChecked);
          }
      
          public boolean isChecked() {
              return mChecked;
          }
      
          public void setChecked(boolean checked) {
              if (mChecked != checked) {
                  mChecked = checked;
                  refreshDrawableState();
              }
          }
      }
      
    2. Other implementation in MultiChoiceAdapter

    3. Implementation with OnCheckedChangeListener and saved state, like CheckBox class and also this is the best implementation I've ever seen

      public class CheckableImageView extends ImageView implements Checkable {
      
          private static final int[] checkedStateSet = { android.R.attr.state_checked };
      
          private boolean mChecked = false;
          private OnCheckedChangeListener mOnCheckedChangeListener;
      
          private boolean mBroadcasting;
      
          public CheckableImageView(Context context) {
              super(context);
          }
      
          public CheckableImageView(Context context, AttributeSet attrs) {
              super(context, attrs);
          }
      
          public CheckableImageView(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
          }
      
          @Override
          public boolean isChecked() {
              return mChecked;
          }
      
          @Override
          public boolean performClick() {
              toggle();
              return super.performClick();
          }
      
          @Override
          public void toggle() {
              setChecked(!mChecked);
          }
      
          @Override
          public int[] onCreateDrawableState(int extraSpace) {
              final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
              if (isChecked()) {
                  mergeDrawableStates(drawableState, checkedStateSet);
              }
              return drawableState;
          }
      
          @Override
          public void setChecked(boolean checked) {
              if (mChecked != checked) {
                  mChecked = checked;
                  refreshDrawableState();
      
                  // Avoid infinite recursions if setChecked() is called from a listener
                  if (mBroadcasting) {
                      return;
                  }
      
                  mBroadcasting = true;
                  if (mOnCheckedChangeListener != null) {
                      mOnCheckedChangeListener.onCheckedChanged(this, mChecked);
                  }
      
                  mBroadcasting = false;
              }
          }
      
          /**
           * Register a callback to be invoked when the checked state of this button
           * changes.
           *
           * @param listener the callback to call on checked state change
           */
          public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
              mOnCheckedChangeListener = listener;
          }
      
          /**
           * Interface definition for a callback to be invoked when the checked state
           * of a compound button changed.
           */
          public static interface OnCheckedChangeListener {
              /**
               * Called when the checked state of a compound button has changed.
               *
               * @param buttonView The compound button view whose state has changed.
               * @param isChecked  The new checked state of buttonView.
               */
              void onCheckedChanged(CheckableImageView buttonView, boolean isChecked);
          }
      
          static class SavedState extends BaseSavedState {
              boolean checked;
      
              /**
               * Constructor called from {@link CompoundButton#onSaveInstanceState()}
               */
              SavedState(Parcelable superState) {
                  super(superState);
              }
      
              /**
               * Constructor called from {@link #CREATOR}
               */
              private SavedState(Parcel in) {
                  super(in);
                  checked = (Boolean) in.readValue(null);
              }
      
              @Override
              public void writeToParcel(Parcel out, int flags) {
                  super.writeToParcel(out, flags);
                  out.writeValue(checked);
              }
      
              @Override
              public String toString() {
                  return "CheckableImageView.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked + "}";
              }
      
              public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
                  @Override
                  public SavedState createFromParcel(Parcel in) {
                      return new SavedState(in);
                  }
      
                  @Override
                  public SavedState[] newArray(int size) {
                      return new SavedState[size];
                  }
              };
          }
      
          @Override
          public Parcelable onSaveInstanceState() {
              Parcelable superState = super.onSaveInstanceState();
              SavedState ss = new SavedState(superState);
              ss.checked = isChecked();
              return ss;
          }
      
          @Override
          public void onRestoreInstanceState(Parcelable state) {
              SavedState ss = (SavedState) state;
      
              super.onRestoreInstanceState(ss.getSuperState());
              setChecked(ss.checked);
              requestLayout();
          }
      }
      

This options that come to mind firstly and have a good implementation. But you can also come up with your own versions, or use simple variables to save state and toggle manualy like @Chirag-Savsani said, but in that case you would have to abandon to use of selectors.

like image 83
Igor Tyulkanov Avatar answered Oct 15 '22 22:10

Igor Tyulkanov


The reason is very simple - ImageView doesn't check for state_checked at all. Comment by @frank-n-stein is the closest to an answer for this question.

You have two options:

  • use a view with support for state_checked (for example a CheckBox)
  • add state_checked to ImageView

To add state_checked support you have to implement Checkable interface. Like this:

public class CheckableImageView extends ImageView implements Checkable {
    private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};

    private boolean mChecked;

    ... constructors

    @Override
    public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            refreshDrawableState();
        }
    }

    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void toggle() {
        setChecked(!mChecked);
    }

    @Override
    public boolean performClick() {
        toggle();
        return super.performClick();
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }

        return drawableState;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        SavedState result = new SavedState(super.onSaveInstanceState());
        result.checked = mChecked;
        return result;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        setChecked(ss.checked);
    }

    protected static class SavedState extends BaseSavedState {
        protected boolean checked;

        protected SavedState(Parcelable superState) {
            super(superState);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(checked ? 1 : 0);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };

        private SavedState(Parcel in) {
            super(in);
            checked = in.readInt() == 1;
        }
    }
}

The code is from here: https://github.com/shomeser/AndroidLayoutSelector/blob/master/LayoutSelector/src/main/java/com/example/layoutselector/CheckableLinearLayout.java

like image 35
Zielony Avatar answered Oct 15 '22 22:10

Zielony


Hello I also use this scenario in my current application.

1) Using CheckBox

Use CheckBox with android:button="@null" property, This property will remove border of CheckBoxand display only your drawable images.

state_checked property will work with CheckBox

<CheckBox
    android:id="@+id/imgDisplayCheckimg"
    android:layout_width="wrap_contenrt"
    android:layout_height="wrap_contenrt"
    android:background="@drawable/display_checkbox"
    android:button="@null"
    android:checked="false"
    android:clickable="true" />

This is Drawable file display_checkbox.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- When selected, use red-->
    <item android:drawable="@drawable/red_color" android:state_checked="true"/>
    <!-- When not selected, use green-->
    <item android:drawable="@drawable/green_color" android:state_checked="false"/>
</selector>

Replace red_color and green_color with your drawable name.

2) Using ImageView

Declare this variable Globally

boolean switchStatus = false;

Find your ImageView and add below Click Listener.

switchImageView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(switchStatus == true) {
            anonymousImage.setImageResource(R.drawable.red);
            switchStatus = false;
        } else {
            anonymousImage.setImageResource(R.drawable.green);
            switchStatus = true;
        }
    }
});

ImageView in layout file.

<ImageView
    android:id="@+id/switchImageView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/green" />

Change name at green and red with your drawable name.

like image 26
Chirag Savsani Avatar answered Oct 15 '22 20:10

Chirag Savsani


You can try:

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

    <item android:state_selected ="true"
          android:drawable="@drawable/bottom_left_border_pressed">
    </item>
</selector>

And java code:

imageview.setImageDrawable(getBaseContext().getResources().getDrawable(R.drawable....));

//set the click listener

    imageview.setOnClickListener(new OnClickListener() {

        public void onClick(View v) {
            v.setSelected(!v.isSelected());
            //other code if need
        }

    });
like image 32
mdtuyen Avatar answered Oct 15 '22 21:10

mdtuyen