CheckedTextView.java revision 343e11345ed496003f605e1b3bba5850d3e6cf0e
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 17package android.widget; 18 19import com.android.internal.R; 20 21import android.content.Context; 22import android.content.res.TypedArray; 23import android.graphics.Canvas; 24import android.graphics.drawable.Drawable; 25import android.util.AttributeSet; 26import android.view.Gravity; 27import android.view.ViewDebug; 28import android.view.accessibility.AccessibilityEvent; 29import 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 */ 41public 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 int initialPaddingLeft = -1; 50 private int initialPaddingRight = -1; 51 52 private static final int[] CHECKED_STATE_SET = { 53 R.attr.state_checked 54 }; 55 56 public CheckedTextView(Context context) { 57 this(context, null); 58 } 59 60 public CheckedTextView(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 64 public CheckedTextView(Context context, AttributeSet attrs, int defStyle) { 65 super(context, attrs, defStyle); 66 67 TypedArray a = context.obtainStyledAttributes(attrs, 68 R.styleable.CheckedTextView, defStyle, 0); 69 70 Drawable d = a.getDrawable(R.styleable.CheckedTextView_checkMark); 71 if (d != null) { 72 setCheckMarkDrawable(d); 73 } 74 75 boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false); 76 setChecked(checked); 77 78 a.recycle(); 79 } 80 81 public void toggle() { 82 setChecked(!mChecked); 83 } 84 85 @ViewDebug.ExportedProperty 86 public boolean isChecked() { 87 return mChecked; 88 } 89 90 /** 91 * <p>Changes the checked state of this text view.</p> 92 * 93 * @param checked true to check the text, false to uncheck it 94 */ 95 public void setChecked(boolean checked) { 96 if (mChecked != checked) { 97 mChecked = checked; 98 refreshDrawableState(); 99 notifyAccessibilityStateChanged(); 100 } 101 } 102 103 104 /** 105 * Set the checkmark to a given Drawable, identified by its resourece id. This will be drawn 106 * when {@link #isChecked()} is true. 107 * 108 * @param resid The Drawable to use for the checkmark. 109 * 110 * @see #setCheckMarkDrawable(Drawable) 111 * @see #getCheckMarkDrawable() 112 * 113 * @attr ref android.R.styleable#CheckedTextView_checkMark 114 */ 115 public void setCheckMarkDrawable(int resid) { 116 if (resid != 0 && resid == mCheckMarkResource) { 117 return; 118 } 119 120 mCheckMarkResource = resid; 121 122 Drawable d = null; 123 if (mCheckMarkResource != 0) { 124 d = getResources().getDrawable(mCheckMarkResource); 125 } 126 setCheckMarkDrawable(d); 127 } 128 129 /** 130 * Set the checkmark to a given Drawable. This will be drawn when {@link #isChecked()} is true. 131 * 132 * @param d The Drawable to use for the checkmark. 133 * 134 * @see #setCheckMarkDrawable(int) 135 * @see #getCheckMarkDrawable() 136 * 137 * @attr ref android.R.styleable#CheckedTextView_checkMark 138 */ 139 public void setCheckMarkDrawable(Drawable d) { 140 if (mCheckMarkDrawable != null) { 141 mCheckMarkDrawable.setCallback(null); 142 unscheduleDrawable(mCheckMarkDrawable); 143 } 144 mNeedRequestlayout = (d != mCheckMarkDrawable); 145 if (d != null) { 146 d.setCallback(this); 147 d.setVisible(getVisibility() == VISIBLE, false); 148 d.setState(CHECKED_STATE_SET); 149 setMinHeight(d.getIntrinsicHeight()); 150 151 mCheckMarkWidth = d.getIntrinsicWidth(); 152 d.setState(getDrawableState()); 153 } else { 154 mCheckMarkWidth = 0; 155 } 156 mCheckMarkDrawable = d; 157 // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed. 158 resolvePadding(); 159 } 160 161 /** 162 * Gets the checkmark drawable 163 * 164 * @return The drawable use to represent the checkmark, if any. 165 * 166 * @see #setCheckMarkDrawable(Drawable) 167 * @see #setCheckMarkDrawable(int) 168 * 169 * @attr ref android.R.styleable#CheckedTextView_checkMark 170 */ 171 public Drawable getCheckMarkDrawable() { 172 return mCheckMarkDrawable; 173 } 174 175 /** 176 * @hide 177 */ 178 @Override 179 protected void internalSetPadding(int left, int top, int right, int bottom) { 180 super.internalSetPadding(left, top, right, bottom); 181 setBasePadding(isLayoutRtl()); 182 initialPaddingLeft = mPaddingLeft; 183 initialPaddingRight = mPaddingRight; 184 } 185 186 @Override 187 public void onRtlPropertiesChanged(int layoutDirection) { 188 super.onRtlPropertiesChanged(layoutDirection); 189 updatePadding(); 190 } 191 192 private void updatePadding() { 193 int newPadding = (mCheckMarkDrawable != null) ? 194 mCheckMarkWidth + mBasePadding : mBasePadding; 195 mNeedRequestlayout |= (mPaddingRight != newPadding); 196 if (isLayoutRtl()) { 197 mPaddingLeft = newPadding; 198 } else { 199 mPaddingRight = newPadding; 200 } 201 if (mNeedRequestlayout) { 202 requestLayout(); 203 mNeedRequestlayout = false; 204 } 205 } 206 207 @Override 208 public void setPadding(int left, int top, int right, int bottom) { 209 super.setPadding(left, top, right, bottom); 210 setBasePadding(isLayoutRtl()); 211 } 212 213 @Override 214 public void setPaddingRelative(int start, int top, int end, int bottom) { 215 super.setPaddingRelative(start, top, end, bottom); 216 setBasePadding(isLayoutRtl()); 217 } 218 219 private void setBasePadding(boolean isLayoutRtl) { 220 if (isLayoutRtl) { 221 mBasePadding = mPaddingLeft; 222 } else { 223 mBasePadding = mPaddingRight; 224 } 225 } 226 227 @Override 228 protected void onDraw(Canvas canvas) { 229 super.onDraw(canvas); 230 231 final Drawable checkMarkDrawable = mCheckMarkDrawable; 232 if (checkMarkDrawable != null) { 233 final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; 234 final int height = checkMarkDrawable.getIntrinsicHeight(); 235 236 int y = 0; 237 238 switch (verticalGravity) { 239 case Gravity.BOTTOM: 240 y = getHeight() - height; 241 break; 242 case Gravity.CENTER_VERTICAL: 243 y = (getHeight() - height) / 2; 244 break; 245 } 246 247 final boolean isLayoutRtl = isLayoutRtl(); 248 final int width = getWidth(); 249 final int top = y; 250 final int bottom = top + height; 251 final int left; 252 final int right; 253 if (isLayoutRtl) { 254 left = mBasePadding; 255 right = left + mCheckMarkWidth; 256 } else { 257 right = width - mBasePadding; 258 left = right - mCheckMarkWidth; 259 } 260 checkMarkDrawable.setBounds( left, top, right, bottom); 261 checkMarkDrawable.draw(canvas); 262 } 263 } 264 265 @Override 266 protected int[] onCreateDrawableState(int extraSpace) { 267 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 268 if (isChecked()) { 269 mergeDrawableStates(drawableState, CHECKED_STATE_SET); 270 } 271 return drawableState; 272 } 273 274 @Override 275 protected void drawableStateChanged() { 276 super.drawableStateChanged(); 277 278 if (mCheckMarkDrawable != null) { 279 int[] myDrawableState = getDrawableState(); 280 281 // Set the state of the Drawable 282 mCheckMarkDrawable.setState(myDrawableState); 283 284 invalidate(); 285 } 286 } 287 288 @Override 289 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 290 super.onInitializeAccessibilityEvent(event); 291 event.setClassName(CheckedTextView.class.getName()); 292 event.setChecked(mChecked); 293 } 294 295 @Override 296 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 297 super.onInitializeAccessibilityNodeInfo(info); 298 info.setClassName(CheckedTextView.class.getName()); 299 info.setCheckable(true); 300 info.setChecked(mChecked); 301 } 302} 303