CheckedTextView.java revision aff599b4abb10bad6711ff9348f97a56240e0612
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;
29
30
31/**
32 * An extension to TextView that supports the {@link android.widget.Checkable} interface.
33 * This is useful when used in a {@link android.widget.ListView ListView} where the it's
34 * {@link android.widget.ListView#setChoiceMode(int) setChoiceMode} has been set to
35 * something other than {@link android.widget.ListView#CHOICE_MODE_NONE CHOICE_MODE_NONE}.
36 *
37 */
38public class CheckedTextView extends TextView implements Checkable {
39    private boolean mChecked;
40    private int mCheckMarkResource;
41    private Drawable mCheckMarkDrawable;
42    private int mBasePadding;
43    private int mCheckMarkWidth;
44    private boolean mNeedRequestlayout;
45
46    private static final int[] CHECKED_STATE_SET = {
47        R.attr.state_checked
48    };
49
50    public CheckedTextView(Context context) {
51        this(context, null);
52    }
53
54    public CheckedTextView(Context context, AttributeSet attrs) {
55        this(context, attrs, 0);
56    }
57
58    public CheckedTextView(Context context, AttributeSet attrs, int defStyle) {
59        super(context, attrs, defStyle);
60
61        TypedArray a = context.obtainStyledAttributes(attrs,
62                R.styleable.CheckedTextView, defStyle, 0);
63
64        Drawable d = a.getDrawable(R.styleable.CheckedTextView_checkMark);
65        if (d != null) {
66            setCheckMarkDrawable(d);
67        }
68
69        boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
70        setChecked(checked);
71
72        a.recycle();
73    }
74
75    public void toggle() {
76        setChecked(!mChecked);
77    }
78
79    @ViewDebug.ExportedProperty
80    public boolean isChecked() {
81        return mChecked;
82    }
83
84    /**
85     * <p>Changes the checked state of this text view.</p>
86     *
87     * @param checked true to check the text, false to uncheck it
88     */
89    public void setChecked(boolean checked) {
90        if (mChecked != checked) {
91            mChecked = checked;
92            refreshDrawableState();
93        }
94    }
95
96
97    /**
98     * Set the checkmark to a given Drawable, identified by its resourece id. This will be drawn
99     * when {@link #isChecked()} is true.
100     *
101     * @param resid The Drawable to use for the checkmark.
102     */
103    public void setCheckMarkDrawable(int resid) {
104        if (resid != 0 && resid == mCheckMarkResource) {
105            return;
106        }
107
108        mCheckMarkResource = resid;
109
110        Drawable d = null;
111        if (mCheckMarkResource != 0) {
112            d = getResources().getDrawable(mCheckMarkResource);
113        }
114        setCheckMarkDrawable(d);
115    }
116
117    /**
118     * Set the checkmark to a given Drawable. This will be drawn when {@link #isChecked()} is true.
119     *
120     * @param d The Drawable to use for the checkmark.
121     */
122    public void setCheckMarkDrawable(Drawable d) {
123        if (mCheckMarkDrawable != null) {
124            mCheckMarkDrawable.setCallback(null);
125            unscheduleDrawable(mCheckMarkDrawable);
126        }
127        mNeedRequestlayout = (d != mCheckMarkDrawable);
128        if (d != null) {
129            d.setCallback(this);
130            d.setVisible(getVisibility() == VISIBLE, false);
131            d.setState(CHECKED_STATE_SET);
132            setMinHeight(d.getIntrinsicHeight());
133
134            mCheckMarkWidth = d.getIntrinsicWidth();
135            d.setState(getDrawableState());
136        } else {
137            mCheckMarkWidth = 0;
138        }
139        mCheckMarkDrawable = d;
140        // Do padding resolution. This will call setPadding() and do a requestLayout() if needed.
141        resolvePadding();
142    }
143
144    /**
145     * @hide
146     */
147    @Override
148    protected void resolvePadding() {
149        super.resolvePadding();
150        int newPadding = (mCheckMarkDrawable != null) ?
151                mCheckMarkWidth + mBasePadding : mBasePadding;
152        mNeedRequestlayout |= (mPaddingRight != newPadding);
153        mPaddingRight = newPadding;
154        if (mNeedRequestlayout) {
155            requestLayout();
156            mNeedRequestlayout = false;
157        }
158    }
159
160    @Override
161    public void setPadding(int left, int top, int right, int bottom) {
162        super.setPadding(left, top, right, bottom);
163        mBasePadding = mPaddingRight;
164    }
165
166    @Override
167    protected void onDraw(Canvas canvas) {
168        super.onDraw(canvas);
169
170        final Drawable checkMarkDrawable = mCheckMarkDrawable;
171        if (checkMarkDrawable != null) {
172            final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
173            final int height = checkMarkDrawable.getIntrinsicHeight();
174
175            int y = 0;
176
177            switch (verticalGravity) {
178                case Gravity.BOTTOM:
179                    y = getHeight() - height;
180                    break;
181                case Gravity.CENTER_VERTICAL:
182                    y = (getHeight() - height) / 2;
183                    break;
184            }
185
186            int right = getWidth();
187            checkMarkDrawable.setBounds(
188                    right - mPaddingRight,
189                    y,
190                    right - mPaddingRight + mCheckMarkWidth,
191                    y + height);
192            checkMarkDrawable.draw(canvas);
193        }
194    }
195
196    @Override
197    protected int[] onCreateDrawableState(int extraSpace) {
198        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
199        if (isChecked()) {
200            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
201        }
202        return drawableState;
203    }
204
205    @Override
206    protected void drawableStateChanged() {
207        super.drawableStateChanged();
208
209        if (mCheckMarkDrawable != null) {
210            int[] myDrawableState = getDrawableState();
211
212            // Set the state of the Drawable
213            mCheckMarkDrawable.setState(myDrawableState);
214
215            invalidate();
216        }
217    }
218
219    @Override
220    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
221        super.onInitializeAccessibilityEvent(event);
222        event.setChecked(mChecked);
223    }
224}
225