• 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             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      */
setCheckMarkDrawable(int resid)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      */
setCheckMarkDrawable(Drawable d)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      */
getCheckMarkDrawable()168     public Drawable getCheckMarkDrawable() {
169         return mCheckMarkDrawable;
170     }
171 
172     /**
173      * @hide
174      */
175     @Override
internalSetPadding(int left, int top, int right, int bottom)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
onRtlPropertiesChanged(int layoutDirection)182     public void onRtlPropertiesChanged(int layoutDirection) {
183         super.onRtlPropertiesChanged(layoutDirection);
184         updatePadding();
185     }
186 
updatePadding()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 
setBasePadding(boolean isLayoutRtl)204     private void setBasePadding(boolean isLayoutRtl) {
205         if (isLayoutRtl) {
206             mBasePadding = mPaddingLeft;
207         } else {
208             mBasePadding = mPaddingRight;
209         }
210     }
211 
212     @Override
onDraw(Canvas canvas)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
onCreateDrawableState(int extraSpace)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
drawableStateChanged()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
onInitializeAccessibilityEvent(AccessibilityEvent event)274     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
275         super.onInitializeAccessibilityEvent(event);
276         event.setClassName(CheckedTextView.class.getName());
277         event.setChecked(mChecked);
278     }
279 
280     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)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