• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CheckedTextView(Context context)53     public CheckedTextView(Context context) {
54         this(context, null);
55     }
56 
CheckedTextView(Context context, AttributeSet attrs)57     public CheckedTextView(Context context, AttributeSet attrs) {
58         this(context, attrs, R.attr.checkedTextViewStyle);
59     }
60 
CheckedTextView(Context context, AttributeSet attrs, int defStyle)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 
toggle()78     public void toggle() {
79         setChecked(!mChecked);
80     }
81 
82     @ViewDebug.ExportedProperty
isChecked()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      */
setChecked(boolean checked)92     public void setChecked(boolean checked) {
93         if (mChecked != checked) {
94             mChecked = checked;
95             refreshDrawableState();
96             notifyViewAccessibilityStateChangedIfNeeded(
97                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
98         }
99     }
100 
101 
102     /**
103      * Set the checkmark to a given Drawable, identified by its resourece id. This will be drawn
104      * when {@link #isChecked()} is true.
105      *
106      * @param resid The Drawable to use for the checkmark.
107      *
108      * @see #setCheckMarkDrawable(Drawable)
109      * @see #getCheckMarkDrawable()
110      *
111      * @attr ref android.R.styleable#CheckedTextView_checkMark
112      */
setCheckMarkDrawable(int resid)113     public void setCheckMarkDrawable(int resid) {
114         if (resid != 0 && resid == mCheckMarkResource) {
115             return;
116         }
117 
118         mCheckMarkResource = resid;
119 
120         Drawable d = null;
121         if (mCheckMarkResource != 0) {
122             d = getResources().getDrawable(mCheckMarkResource);
123         }
124         setCheckMarkDrawable(d);
125     }
126 
127     /**
128      * Set the checkmark to a given Drawable. This will be drawn when {@link #isChecked()} is true.
129      *
130      * @param d The Drawable to use for the checkmark.
131      *
132      * @see #setCheckMarkDrawable(int)
133      * @see #getCheckMarkDrawable()
134      *
135      * @attr ref android.R.styleable#CheckedTextView_checkMark
136      */
setCheckMarkDrawable(Drawable d)137     public void setCheckMarkDrawable(Drawable d) {
138         if (mCheckMarkDrawable != null) {
139             mCheckMarkDrawable.setCallback(null);
140             unscheduleDrawable(mCheckMarkDrawable);
141         }
142         mNeedRequestlayout = (d != mCheckMarkDrawable);
143         if (d != null) {
144             d.setCallback(this);
145             d.setVisible(getVisibility() == VISIBLE, false);
146             d.setState(CHECKED_STATE_SET);
147             setMinHeight(d.getIntrinsicHeight());
148 
149             mCheckMarkWidth = d.getIntrinsicWidth();
150             d.setState(getDrawableState());
151         } else {
152             mCheckMarkWidth = 0;
153         }
154         mCheckMarkDrawable = d;
155         // Do padding resolution. This will call internalSetPadding() and do a requestLayout() if needed.
156         resolvePadding();
157     }
158 
159     /**
160      * Gets the checkmark drawable
161      *
162      * @return The drawable use to represent the checkmark, if any.
163      *
164      * @see #setCheckMarkDrawable(Drawable)
165      * @see #setCheckMarkDrawable(int)
166      *
167      * @attr ref android.R.styleable#CheckedTextView_checkMark
168      */
getCheckMarkDrawable()169     public Drawable getCheckMarkDrawable() {
170         return mCheckMarkDrawable;
171     }
172 
173     /**
174      * @hide
175      */
176     @Override
internalSetPadding(int left, int top, int right, int bottom)177     protected void internalSetPadding(int left, int top, int right, int bottom) {
178         super.internalSetPadding(left, top, right, bottom);
179         setBasePadding(isLayoutRtl());
180     }
181 
182     @Override
onRtlPropertiesChanged(int layoutDirection)183     public void onRtlPropertiesChanged(int layoutDirection) {
184         super.onRtlPropertiesChanged(layoutDirection);
185         updatePadding();
186     }
187 
updatePadding()188     private void updatePadding() {
189         resetPaddingToInitialValues();
190         int newPadding = (mCheckMarkDrawable != null) ?
191                 mCheckMarkWidth + mBasePadding : mBasePadding;
192         if (isLayoutRtl()) {
193             mNeedRequestlayout |= (mPaddingLeft != newPadding);
194             mPaddingLeft = newPadding;
195         } else {
196             mNeedRequestlayout |= (mPaddingRight != newPadding);
197             mPaddingRight = newPadding;
198         }
199         if (mNeedRequestlayout) {
200             requestLayout();
201             mNeedRequestlayout = false;
202         }
203     }
204 
setBasePadding(boolean isLayoutRtl)205     private void setBasePadding(boolean isLayoutRtl) {
206         if (isLayoutRtl) {
207             mBasePadding = mPaddingLeft;
208         } else {
209             mBasePadding = mPaddingRight;
210         }
211     }
212 
213     @Override
onDraw(Canvas canvas)214     protected void onDraw(Canvas canvas) {
215         super.onDraw(canvas);
216 
217         final Drawable checkMarkDrawable = mCheckMarkDrawable;
218         if (checkMarkDrawable != null) {
219             final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
220             final int height = checkMarkDrawable.getIntrinsicHeight();
221 
222             int y = 0;
223 
224             switch (verticalGravity) {
225                 case Gravity.BOTTOM:
226                     y = getHeight() - height;
227                     break;
228                 case Gravity.CENTER_VERTICAL:
229                     y = (getHeight() - height) / 2;
230                     break;
231             }
232 
233             final boolean isLayoutRtl = isLayoutRtl();
234             final int width = getWidth();
235             final int top = y;
236             final int bottom = top + height;
237             final int left;
238             final int right;
239             if (isLayoutRtl) {
240                 left = mBasePadding;
241                 right = left + mCheckMarkWidth;
242             } else {
243                 right = width - mBasePadding;
244                 left = right - mCheckMarkWidth;
245             }
246             checkMarkDrawable.setBounds(mScrollX + left, top, mScrollX + right, bottom);
247             checkMarkDrawable.draw(canvas);
248         }
249     }
250 
251     @Override
onCreateDrawableState(int extraSpace)252     protected int[] onCreateDrawableState(int extraSpace) {
253         final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
254         if (isChecked()) {
255             mergeDrawableStates(drawableState, CHECKED_STATE_SET);
256         }
257         return drawableState;
258     }
259 
260     @Override
drawableStateChanged()261     protected void drawableStateChanged() {
262         super.drawableStateChanged();
263 
264         if (mCheckMarkDrawable != null) {
265             int[] myDrawableState = getDrawableState();
266 
267             // Set the state of the Drawable
268             mCheckMarkDrawable.setState(myDrawableState);
269 
270             invalidate();
271         }
272     }
273 
274     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)275     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
276         super.onInitializeAccessibilityEvent(event);
277         event.setClassName(CheckedTextView.class.getName());
278         event.setChecked(mChecked);
279     }
280 
281     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)282     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
283         super.onInitializeAccessibilityNodeInfo(info);
284         info.setClassName(CheckedTextView.class.getName());
285         info.setCheckable(true);
286         info.setChecked(mChecked);
287     }
288 }
289