Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.widget;
     18 
     19 import com.android.internal.R;
     20 
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Canvas;
     24 import android.graphics.drawable.Drawable;
     25 import android.util.AttributeSet;
     26 import android.view.Gravity;
     27 import android.view.ViewDebug;
     28 import android.view.accessibility.AccessibilityEvent;
     29 import android.view.accessibility.AccessibilityNodeInfo;
     30 
     31 
     32 /**
     33  * An extension to TextView that supports the {@link android.widget.Checkable} interface.
     34  * This is useful when used in a {@link android.widget.ListView ListView} where the it's
     35  * {@link android.widget.ListView#setChoiceMode(int) setChoiceMode} has been set to
     36  * something other than {@link android.widget.ListView#CHOICE_MODE_NONE CHOICE_MODE_NONE}.
     37  *
     38  * @attr ref android.R.styleable#CheckedTextView_checked
     39  * @attr ref android.R.styleable#CheckedTextView_checkMark
     40  */
     41 public class CheckedTextView extends TextView implements Checkable {
     42     private boolean mChecked;
     43     private int mCheckMarkResource;
     44     private Drawable mCheckMarkDrawable;
     45     private int mBasePadding;
     46     private int mCheckMarkWidth;
     47     private boolean mNeedRequestlayout;
     48 
     49     private static final int[] CHECKED_STATE_SET = {
     50         R.attr.state_checked
     51     };
     52 
     53     public CheckedTextView(Context context) {
     54         this(context, null);
     55     }
     56 
     57     public CheckedTextView(Context context, AttributeSet attrs) {
     58         this(context, attrs, R.attr.checkedTextViewStyle);
     59     }
     60 
     61     public CheckedTextView(Context context, AttributeSet attrs, int defStyle) {
     62         super(context, attrs, defStyle);
     63 
     64         TypedArray a = context.obtainStyledAttributes(attrs,
     65                 R.styleable.CheckedTextView, defStyle, 0);
     66 
     67         Drawable d = a.getDrawable(R.styleable.CheckedTextView_checkMark);
     68         if (d != null) {
     69             setCheckMarkDrawable(d);
     70         }
     71 
     72         boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
     73         setChecked(checked);
     74 
     75         a.recycle();
     76     }
     77 
     78     public void toggle() {
     79         setChecked(!mChecked);
     80     }
     81 
     82     @ViewDebug.ExportedProperty
     83     public boolean isChecked() {
     84         return mChecked;
     85     }
     86 
     87     /**
     88      * <p>Changes the checked state of this text view.</p>
     89      *
     90      * @param checked true to check the text, false to uncheck it
     91      */
     92     public void setChecked(boolean checked) {
     93         if (mChecked != checked) {
     94             mChecked = checked;
     95             refreshDrawableState();
     96             notifyAccessibilityStateChanged();
     97         }
     98     }
     99 
    100 
    101     /**
    102      * Set the checkmark to a given Drawable, identified by its resourece id. This will be drawn
    103      * when {@link #isChecked()} is true.
    104      *
    105      * @param resid The Drawable to use for the checkmark.
    106      *
    107      * @see #setCheckMarkDrawable(Drawable)
    108      * @see #getCheckMarkDrawable()
    109      *
    110      * @attr ref android.R.styleable#CheckedTextView_checkMark
    111      */
    112     public void setCheckMarkDrawable(int resid) {
    113         if (resid != 0 && resid == mCheckMarkResource) {
    114             return;
    115         }
    116 
    117         mCheckMarkResource = resid;
    118 
    119         Drawable d = null;
    120         if (mCheckMarkResource != 0) {
    121             d = getResources().getDrawable(mCheckMarkResource);
    122         }
    123         setCheckMarkDrawable(d);
    124     }
    125 
    126     /**
    127      * Set the checkmark to a given Drawable. This will be drawn when {@link #isChecked()} is true.
    128      *
    129      * @param d The Drawable to use for the checkmark.
    130      *
    131      * @see #setCheckMarkDrawable(int)
    132      * @see #getCheckMarkDrawable()
    133      *
    134      * @attr ref android.R.styleable#CheckedTextView_checkMark
    135      */
    136     public void setCheckMarkDrawable(Drawable d) {
    137         if (mCheckMarkDrawable != null) {
    138             mCheckMarkDrawable.setCallback(null);
    139             unscheduleDrawable(mCheckMarkDrawable);
    140         }
    141         mNeedRequestlayout = (d != mCheckMarkDrawable);
    142         if (d != null) {
    143             d.setCallback(this);
    144             d.setVisible(getVisibility() == VISIBLE, false);
    145             d.setState(CHECKED_STATE_SET);
    146             setMinHeight(d.getIntrinsicHeight());
    147 
    148             mCheckMarkWidth = d.getIntrinsicWidth();
    149             d.setState(getDrawableState());
    150         } else {
    151             mCheckMarkWidth = 0;
    152         }
    153         mCheckMarkDrawable = d;
    154         // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed.
    155         resolvePadding();
    156     }
    157 
    158     /**
    159      * Gets the checkmark drawable
    160      *
    161      * @return The drawable use to represent the checkmark, if any.
    162      *
    163      * @see #setCheckMarkDrawable(Drawable)
    164      * @see #setCheckMarkDrawable(int)
    165      *
    166      * @attr ref android.R.styleable#CheckedTextView_checkMark
    167      */
    168     public Drawable getCheckMarkDrawable() {
    169         return mCheckMarkDrawable;
    170     }
    171 
    172     /**
    173      * @hide
    174      */
    175     @Override
    176     protected void internalSetPadding(int left, int top, int right, int bottom) {
    177         super.internalSetPadding(left, top, right, bottom);
    178         setBasePadding(isLayoutRtl());
    179     }
    180 
    181     @Override
    182     public void onRtlPropertiesChanged(int layoutDirection) {
    183         super.onRtlPropertiesChanged(layoutDirection);
    184         updatePadding();
    185     }
    186 
    187     private void updatePadding() {
    188         resetPaddingToInitialValues();
    189         int newPadding = (mCheckMarkDrawable != null) ?
    190                 mCheckMarkWidth + mBasePadding : mBasePadding;
    191         if (isLayoutRtl()) {
    192             mNeedRequestlayout |= (mPaddingLeft != newPadding);
    193             mPaddingLeft = newPadding;
    194         } else {
    195             mNeedRequestlayout |= (mPaddingRight != newPadding);
    196             mPaddingRight = newPadding;
    197         }
    198         if (mNeedRequestlayout) {
    199             requestLayout();
    200             mNeedRequestlayout = false;
    201         }
    202     }
    203 
    204     private void setBasePadding(boolean isLayoutRtl) {
    205         if (isLayoutRtl) {
    206             mBasePadding = mPaddingLeft;
    207         } else {
    208             mBasePadding = mPaddingRight;
    209         }
    210     }
    211 
    212     @Override
    213     protected void onDraw(Canvas canvas) {
    214         super.onDraw(canvas);
    215 
    216         final Drawable checkMarkDrawable = mCheckMarkDrawable;
    217         if (checkMarkDrawable != null) {
    218             final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
    219             final int height = checkMarkDrawable.getIntrinsicHeight();
    220 
    221             int y = 0;
    222 
    223             switch (verticalGravity) {
    224                 case Gravity.BOTTOM:
    225                     y = getHeight() - height;
    226                     break;
    227                 case Gravity.CENTER_VERTICAL:
    228                     y = (getHeight() - height) / 2;
    229                     break;
    230             }
    231 
    232             final boolean isLayoutRtl = isLayoutRtl();
    233             final int width = getWidth();
    234             final int top = y;
    235             final int bottom = top + height;
    236             final int left;
    237             final int right;
    238             if (isLayoutRtl) {
    239                 left = mBasePadding;
    240                 right = left + mCheckMarkWidth;
    241             } else {
    242                 right = width - mBasePadding;
    243                 left = right - mCheckMarkWidth;
    244             }
    245             checkMarkDrawable.setBounds( left, top, right, bottom);
    246             checkMarkDrawable.draw(canvas);
    247         }
    248     }
    249 
    250     @Override
    251     protected int[] onCreateDrawableState(int extraSpace) {
    252         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
    253         if (isChecked()) {
    254             mergeDrawableStates(drawableState, CHECKED_STATE_SET);
    255         }
    256         return drawableState;
    257     }
    258 
    259     @Override
    260     protected void drawableStateChanged() {
    261         super.drawableStateChanged();
    262 
    263         if (mCheckMarkDrawable != null) {
    264             int[] myDrawableState = getDrawableState();
    265 
    266             // Set the state of the Drawable
    267             mCheckMarkDrawable.setState(myDrawableState);
    268 
    269             invalidate();
    270         }
    271     }
    272 
    273     @Override
    274     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
    275         super.onInitializeAccessibilityEvent(event);
    276         event.setClassName(CheckedTextView.class.getName());
    277         event.setChecked(mChecked);
    278     }
    279 
    280     @Override
    281     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
    282         super.onInitializeAccessibilityNodeInfo(info);
    283         info.setClassName(CheckedTextView.class.getName());
    284         info.setCheckable(true);
    285         info.setChecked(mChecked);
    286     }
    287 }
    288