Android中CheckBox与CompoundButton源码解析
来源:互联网 发布:网络词 仙女是什么意思 编辑:程序博客网 时间:2024/06/17 06:19
经历过了前面一系列的讲解,下面我们直接来看看系统里面的CheckBox与CompoundButton类的源码文件。你肯定会发现很多熟悉的地方。
结合下面源码,我们对它们进行解析解析,它里面使用的就是自定义drawable state。
我们首先直接看CheckBox的源码
public class CheckBox extends CompoundButton { public CheckBox(Context context) { this(context, null); } public CheckBox(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.checkboxStyle); } public CheckBox(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(CheckBox.class.getName()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CheckBox.class.getName()); }}
我们可以看到,它里面并没有做什么,因为它的操作都是继承自CompoundButton.
我们先来看看下面一个普通的布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CheckBox"/></RelativeLayout>
在上面的这个相对布局中,就写了一个CheckBox,我们什么都不做,直接运行代码,就会看到下面的运行界面:
当我们单击这个CheckBox,我们发现他会被选中,那么它内部到底是怎么实现的呢?
其实使用过drawable state的人应该对这个并不陌生,我们经常这样做:
1、在res/drawable文件下创建selector.xml,示例代码如下:
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="false" android:drawable="@drawable/title_button_back"> </item> <item android:state_pressed="true" android:drawable="@drawable/title_button_back_h"> </item> <item android:state_window_focused="false" android:drawable="@drawable/title_button_back"> </item></selector>
2、编写布局文件,为布局文件中的ImageButton设置selector,示例代码如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="fill_parent"> <ImageButton android:id="@+id/title_IB" android:layout_height="wrap_content" android:layout_width="wrap_content" android:background="#00000000" android:layout_marginRight="4dp" android:layout_centerVertical="true" android:src="@drawable/selector"> </ImageButton> </RelativeLayout>
当我们单击这个图片按钮的时候,图片按钮的图片就会发生变化,其实CheckBox也是这样做的,只是这些系统为我们做了。
下面我们来看看系统实现源码:
上面在布局文件中直接写了一个CheckBox,布局文件被解析后就会实例化这个CheckBox对象,就会执行CheckBox的构造函数:
public CheckBox(Context context, AttributeSet attrs) { this(context, attrs, com.android.internal.R.attr.checkboxStyle);}public CheckBox(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}
从上面我们可以知道两点:
1、CheckBox的默认样式是com.android.internal.R.attr.checkboxStyle
2、最终执行的时候CompoundButton的构造函数
在frameworks/base/core/res/res/values/themes.xml文件中,已经初始化了checkBoxStyle样式:
<item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox</item>
在frameworks/base/core/res/res/values/styles.xml文件,我们来看看它的样式是什么:
<style name="Widget.CompoundButton.CheckBox"> <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item></style>
在frameworks/base/core/res/res/values/themes.xml文件:
<item name="listChoiceIndicatorMultiple">@android:drawable/btn_check</item>
可以看到,CheckBox的默认样式就是给它的button属性赋值了一个btn_check,我们来看看btn_check文件里面的具体内容。
在frameworks/base/core/res/res/drawable/btn_check.xml
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Enabled states --> <item android:state_checked="true" android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/btn_check_on" /> <item android:state_checked="false" android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/btn_check_off" /> <item android:state_checked="true" android:state_pressed="true" android:state_enabled="true" android:drawable="@drawable/btn_check_on_pressed" /> <item android:state_checked="false" android:state_pressed="true" android:state_enabled="true" android:drawable="@drawable/btn_check_off_pressed" /> <item android:state_checked="true" android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_check_on_selected" /> <item android:state_checked="false" android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_check_off_selected" /> <item android:state_checked="false" android:state_enabled="true" android:drawable="@drawable/btn_check_off" /> <item android:state_checked="true" android:state_enabled="true" android:drawable="@drawable/btn_check_on" /> <!-- Disabled states --> <item android:state_checked="true" android:state_window_focused="false" android:drawable="@drawable/btn_check_on_disable" /> <item android:state_checked="false" android:state_window_focused="false" android:drawable="@drawable/btn_check_off_disable" /> <item android:state_checked="true" android:state_focused="true" android:drawable="@drawable/btn_check_on_disable_focused" /> <item android:state_checked="false" android:state_focused="true" android:drawable="@drawable/btn_check_off_disable_focused" /> <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable" /> <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable" /></selector>
我们看到,state_checked为true的时候,drawable=”@drawable/btn_check_on”,state_checked为false的时候,drawable=”@drawable/btn_check_off”.
@drawable/btn_check_on图片和@drawable/btn_check_off图片,在frameworks/base/core/res/res/drawable我们可以找到:
我们可以看到,其实就是根据不同的状态,为button属性赋值了不同的图片资源,这就是我们看到的效果。
state_checked这个状态的定义,在下面进行了定义:
<declare-styleable name="DrawableStates"> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a view has input focus. --> <attr name="state_focused" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a view's window has input focus. --> <attr name="state_window_focused" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a view is enabled. --> <attr name="state_enabled" format="boolean" /> <!-- State identifier indicating that the object <var>may</var> display a check mark. See {@link R.attr#state_checked} for the identifier that indicates whether it is actually checked. --> <attr name="state_checkable" format="boolean"/> <!-- State identifier indicating that the object is currently checked. See {@link R.attr#state_checkable} for an additional identifier that can indicate if any object may ever display a check, regardless of whether state_checked is currently set. --> <attr name="state_checked" format="boolean"/> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a view (or one of its parents) is currently selected. --> <attr name="state_selected" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when the user is pressing down in a view. --> <attr name="state_pressed" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a view or its parent has been "activated" meaning the user has currently marked it as being of interest. This is an alternative representation of state_checked for when the state should be propagated down the view hierarchy. --> <attr name="state_activated" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_active" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_single" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_first" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_middle" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}.--> <attr name="state_last" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, indicating that the Drawable is in a view that is hardware accelerated. This means that the device can at least render a full-screen scaled bitmap with one layer of text and bitmaps composited on top of it at 60fps. When this is set, the colorBackgroundCacheHint will be ignored even if it specifies a solid color, since that optimization is not needed. --> <attr name="state_accelerated" format="boolean" /> <!-- State value for {@link android.graphics.drawable.StateListDrawable StateListDrawable}, set when a pointer is hovering over the view. --> <attr name="state_hovered" format="boolean" /> <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable} indicating that the Drawable is in a view that is capable of accepting a drop of the content currently being manipulated in a drag-and-drop operation. --> <attr name="state_drag_can_accept" format="boolean" /> <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable} indicating that a drag operation (for which the Drawable's view is a valid recipient) is currently positioned over the Drawable. --> <attr name="state_drag_hovered" format="boolean" /> <!-- State for {@link android.graphics.drawable.StateListDrawable StateListDrawable} indicating that a View has accessibility focus. --> <attr name="state_accessibility_focused" format="boolean" /></declare-styleable>
我们可以看到<attr name="state_checked" format="boolean"/>
下面我们看看CompoundButton的构造函数,因为CheckBox最终执行的是它的构造函数。
public CompoundButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0); Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); if (d != null) { setButtonDrawable(d); } boolean checked = a .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false); setChecked(checked); a.recycle();}
我们可以知道CompoundButton自定义了两个属性,进入frameworks/base/core/res/res/values/attrs.xml文件:
<declare-styleable name="CompoundButton"> <!-- Indicates the initial checked state of this button. --> <attr name="checked" format="boolean" /> <!-- Drawable used for the button graphic (e.g. checkbox, radio button, etc). --> <attr name="button" format="reference"/></declare-styleable>
我们可以看到它定义了两个属性:checked,button,一个存放的是一个boolean类型的值,表示是否被选中,一个存放的是引用类型,对应的是一个图片。
在构造函数中,我们获取到了这两个属性值。上面就是对button这个属性进行了默认赋值,然后我们这里就可以获取到上面的btn_check这个xml文件的drawable。
也就是说Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button)中的d的到的就是btn_check这个xml文件的drawable。
然后分别调用了setButtonDrawable(d)和setChecked(checked)来对我们自定义的控件进行了设置。
我们来看看setButtonDrawable(d)函数:
public void setButtonDrawable(Drawable d) { if (d != null) { if (mButtonDrawable != null) { mButtonDrawable.setCallback(null); unscheduleDrawable(mButtonDrawable); } //这里设置Callback的原因就是refreshDrawableState刷新的时候可以回调到invalidateDrawable //这样就可以重绘,这个我们在#详解refreshDrawableList()的执行流程#这篇文件讲过 d.setCallback(this); d.setVisible(getVisibility() == VISIBLE, false); mButtonDrawable = d; setMinHeight(mButtonDrawable.getIntrinsicHeight()); } refreshDrawableState();}
最后调用了refreshDrawableState这个方法,这个执行过程我们也分析过,整个执行思路都是一样,不过不同的是,里面的很多方法都被覆盖了。
这里我们再来把整个思路走一走。
refreshDrawableState执行的还是View里面的这个方法,直接看源码。
public void refreshDrawableState() { //首先把标志设置为PFLAG_DRAWABLE_STATE_DIRTY mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY; drawableStateChanged(); ViewParent parent = mParent; if (parent != null) { parent.childDrawableStateChanged(this); }}
drawableStateChanged这个方法不在执行View里面的这个方法,因为CompoundButton里面有这个方法,我们看看CompoundButton的这个方法。
@Overrideprotected void drawableStateChanged() { super.drawableStateChanged(); if (mButtonDrawable != null) { int[] myDrawableState = getDrawableState(); // Set the state of the Drawable mButtonDrawable.setState(myDrawableState); invalidate(); }}
getDrawableState这个函数还是调用的View的这个函数。
public final int[] getDrawableState() { //PFLAG_DRAWABLE_STATE_DIRTY是前面refreshDrawableState设置的 //如果mDrawableState不为空,并且不需要刷新状态,则直接返回,否则重新进行获取 if ((mDrawableState != null) && ((mPrivateFlags & PFLAG_DRAWABLE_STATE_DIRTY) == 0)) { return mDrawableState; } else { //这里就是重新得到视图状态,并将其返回 mDrawableState = onCreateDrawableState(0); //清除前面refreshDrawableState设置的标识 mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY; return mDrawableState; }}
下面调用到了onCreateDrawableState这个方法,这个方法也不在是执行View里面的这个方法,因为CompoundButton里面也有这个方法。
@Overrideprotected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState;}
结合之前我们详解refreshDrawableList()的执行流程的分析。这个方法就是在之前的基础上加入了一个判断,如果isChekced为真,就把我们自定义的这个状态加进去,这样当前状态里面就有我们的自定义的状态,后面在状态二维数组中查询的时候,就可以找到对应的drawable图片进行设置了。
这个函数执行完了就会回到上面的getDrawableState函数,然后把当前的状态返回到drawableStateChanged函数中,接着看里面的代码。
接着执行mButtonDrawable.setState(myDrawableState),把得到的当前状态数组设置进去。里面的执行过去前面详解refreshDrawableList()的执行流程的分析也说过,是完全一样的,我们就省略,不明白就再看看,最后会执行invalidateSelf函数。
这个函数就会得到前面我们设置的Callback回调,调用它的invalidateDrawable方法。
public void invalidateSelf() { final Callback callback = getCallback(); if (callback != null) { callback.invalidateDrawable(this); }}
跟前面一样,因为View实现了这个回调,所以它执行的是View里面的实现方法。在这个方法里面执行了invalidate函数,所以会执行onDraw方法.
在CompoundButton里面实现了这个方法,它不再直接执行View里面的这个方法了。
protected void onDraw(Canvas canvas) { super.onDraw(canvas); final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; final int drawableHeight = buttonDrawable.getIntrinsicHeight(); final int drawableWidth = buttonDrawable.getIntrinsicWidth(); int top = 0; switch (verticalGravity) { case Gravity.BOTTOM: top = getHeight() - drawableHeight; break; case Gravity.CENTER_VERTICAL: top = (getHeight() - drawableHeight) / 2; break; } int bottom = top + drawableHeight; int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; int right = isLayoutRtl() ? getWidth() : drawableWidth; buttonDrawable.setBounds(left, top, right, bottom); buttonDrawable.draw(canvas); }}
这样这个图片就绘制出来了,从btn_check里面我们可以看到最开始显示的图片是btn_check_off,当我们单击这个CheckBox的时候,会执行performClick函数。
@Overridepublic boolean performClick() { /* * XXX: These are tiny, need some surrounding 'expanded touch area', * which will need to be implemented in Button if we only override * performClick() */ /* When clicked, toggle the state */ toggle(); return super.performClick();}
接着执行toggle函数。
public void toggle() { setChecked(!mChecked);}
这里面就把当前的状态设置为与之前相反的状态,刚开始为false,这个时候就为true.
接着我们看看setChecked函数。
public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting) { return; } mBroadcasting = true; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } mBroadcasting = false; }}
在这个里面又执行了refreshDrawableState函数,同样把上面的refreshDrawableState过程又执行了一遍,执行在到onCreateDrawableState就不一样了。
我们在来看看这个函数:
protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState;}
第一次执行的时候isChecked为false,自定义的这个状态没有合进去,这次就把自定义的这个状态合进去,这样就可以查询到我们的状态,所以就可以找到定义的drawable,所以图片发生改变为btn_check_on。
最后把完整的CompoundButton源码贴出来,可以对照上面将的,然后再根据前面讲的详解refreshDrawableList()的执行流程这个过程理解理解。
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package android.widget;import com.android.internal.R;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.drawable.Drawable;import android.os.Parcel;import android.os.Parcelable;import android.util.AttributeSet;import android.view.Gravity;import android.view.ViewDebug;import android.view.accessibility.AccessibilityEvent;import android.view.accessibility.AccessibilityNodeInfo;/** * <p> * A button with two states, checked and unchecked. When the button is pressed * or clicked, the state changes automatically. * </p> * * <p><strong>XML attributes</strong></p> * <p> * See {@link android.R.styleable#CompoundButton * CompoundButton Attributes}, {@link android.R.styleable#Button Button * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link * android.R.styleable#View View Attributes} * </p> */public abstract class CompoundButton extends Button implements Checkable { private boolean mChecked; private int mButtonResource; private boolean mBroadcasting; private Drawable mButtonDrawable; private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener; private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; public CompoundButton(Context context) { this(context, null); } public CompoundButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CompoundButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0); Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); if (d != null) { setButtonDrawable(d); } boolean checked = a .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false); setChecked(checked); a.recycle(); } public void toggle() { setChecked(!mChecked); } @Override public boolean performClick() { /* * XXX: These are tiny, need some surrounding 'expanded touch area', * which will need to be implemented in Button if we only override * performClick() */ /* When clicked, toggle the state */ toggle(); return super.performClick(); } @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } /** * <p>Changes the checked state of this button.</p> * * @param checked true to check the button, false to uncheck it */ public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); notifyViewAccessibilityStateChangedIfNeeded( AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting) { return; } mBroadcasting = true; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } mBroadcasting = false; } } /** * Register a callback to be invoked when the checked state of this button * changes. * * @param listener the callback to call on checked state change */ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mOnCheckedChangeListener = listener; } /** * Register a callback to be invoked when the checked state of this button * changes. This callback is used for internal purpose only. * * @param listener the callback to call on checked state change * @hide */ void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { mOnCheckedChangeWidgetListener = listener; } /** * Interface definition for a callback to be invoked when the checked state * of a compound button changed. */ public static interface OnCheckedChangeListener { /** * Called when the checked state of a compound button has changed. * * @param buttonView The compound button view whose state has changed. * @param isChecked The new checked state of buttonView. */ void onCheckedChanged(CompoundButton buttonView, boolean isChecked); } /** * Set the background to a given Drawable, identified by its resource id. * * @param resid the resource id of the drawable to use as the background */ public void setButtonDrawable(int resid) { if (resid != 0 && resid == mButtonResource) { return; } mButtonResource = resid; Drawable d = null; if (mButtonResource != 0) { d = getResources().getDrawable(mButtonResource); } setButtonDrawable(d); } /** * Set the background to a given Drawable * * @param d The Drawable to use as the background */ public void setButtonDrawable(Drawable d) { if (d != null) { if (mButtonDrawable != null) { mButtonDrawable.setCallback(null); unscheduleDrawable(mButtonDrawable); } d.setCallback(this); d.setVisible(getVisibility() == VISIBLE, false); mButtonDrawable = d; setMinHeight(mButtonDrawable.getIntrinsicHeight()); } refreshDrawableState(); } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(CompoundButton.class.getName()); event.setChecked(mChecked); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(CompoundButton.class.getName()); info.setCheckable(true); info.setChecked(mChecked); } @Override public int getCompoundPaddingLeft() { int padding = super.getCompoundPaddingLeft(); if (!isLayoutRtl()) { final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { padding += buttonDrawable.getIntrinsicWidth(); } } return padding; } @Override public int getCompoundPaddingRight() { int padding = super.getCompoundPaddingRight(); if (isLayoutRtl()) { final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { padding += buttonDrawable.getIntrinsicWidth(); } } return padding; } /** * @hide */ @Override public int getHorizontalOffsetForDrawables() { final Drawable buttonDrawable = mButtonDrawable; return (buttonDrawable != null) ? buttonDrawable.getIntrinsicWidth() : 0; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); final Drawable buttonDrawable = mButtonDrawable; if (buttonDrawable != null) { final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; final int drawableHeight = buttonDrawable.getIntrinsicHeight(); final int drawableWidth = buttonDrawable.getIntrinsicWidth(); int top = 0; switch (verticalGravity) { case Gravity.BOTTOM: top = getHeight() - drawableHeight; break; case Gravity.CENTER_VERTICAL: top = (getHeight() - drawableHeight) / 2; break; } int bottom = top + drawableHeight; int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; int right = isLayoutRtl() ? getWidth() : drawableWidth; buttonDrawable.setBounds(left, top, right, bottom); buttonDrawable.draw(canvas); } } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mButtonDrawable != null) { int[] myDrawableState = getDrawableState(); // Set the state of the Drawable mButtonDrawable.setState(myDrawableState); invalidate(); } } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == mButtonDrawable; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState(); } static class SavedState extends BaseSavedState { boolean checked; /** * Constructor called from {@link CompoundButton#onSaveInstanceState()} */ SavedState(Parcelable superState) { super(superState); } /** * Constructor called from {@link #CREATOR} */ private SavedState(Parcel in) { super(in); checked = (Boolean)in.readValue(null); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeValue(checked); } @Override public String toString() { return "CompoundButton.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked + "}"; } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state setFreezesText(true); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.checked = isChecked(); return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss.checked); requestLayout(); }}
- Android中CheckBox与CompoundButton源码解析
- Android控件 - TextView、Button、EditText、CompoundButton、CheckBox简介
- Android widget之CompoundButton
- CompoundButton之RadioButton、CheckBox、ToggleButton、Switch
- CheckBox多选按钮实现CompoundButton.OnCheckedChangeListener
- CompoundButton
- android中RadioButton与CheckBox小结
- Android中ListView与CheckBox结合-…
- Android中ListView与CheckBox结合-…
- android中ZoomControls 源码解析
- Android中AsyncTask源码解析
- Android中Dialog源码解析
- Android中AsyncTask源码解析
- Android中RecyclerView源码解析
- android中AsyncTask源码解析
- CompoundButton.OnCheckedChangeListener与RadioGroup.OnCheckedChangeListener冲突
- Android中ListView与CheckBox结合----多选与记录
- Android中ListView与CheckBox结合----多选与记录
- Thinkpad E450个性化设置:F1~F12恢复正常按键;Fn与Ctrl按键互换
- iOS UIView设置背景图片
- HDU 5414 矩阵的性质
- Lua中的基本函数库
- JQuery的扩展与noConflict
- Android中CheckBox与CompoundButton源码解析
- Spring MapFactoryBean example
- 从UIImage的矩阵变换看矩阵运算的原理
- Docker初探
- poi 中获得行数 getLastRowNum() 和 getPhysicalNumberOfRows()
- Hadoop 1.x中fsimage和edits合并实现
- 解决:System.Data.SqlClient.SqlError: FILESTREAM 功能被禁用
- c查找算法——插值查找
- 问答Swift2.0-基础部分