Android中CheckBox与CompoundButton源码解析[通俗易懂]

Android中CheckBox与CompoundButton源码解析[通俗易懂]经历过了前面一系列的讲解,下面我们直接来看看系统里面的CheckBox与CompoundButton类的源码文件。你肯定会发现很多熟悉的地方。结合下面源码,我们对它们进行解析解析,它里面使用的就是自定义drawablestate。我们首先直接看CheckBox的源码publicclassCheckBoxextendsCompoundButton{publicCheckBo

大家好,又见面了,我是你们的朋友全栈君。

经历过了前面一系列的讲解,下面我们直接来看看系统里面的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,我们什么都不做,直接运行代码,就会看到下面的运行界面:
Android中CheckBox与CompoundButton源码解析[通俗易懂]

当我们单击这个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我们可以找到:
Android中CheckBox与CompoundButton源码解析[通俗易懂]
Android中CheckBox与CompoundButton源码解析[通俗易懂]

我们可以看到,其实就是根据不同的状态,为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的这个方法。

@Override
protected 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里面也有这个方法。

@Override
protected 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函数。

@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();
}

接着执行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();
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/133821.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 解决busuanzi_count突然失效的方法(hexo-theme-next)

    解决busuanzi_count突然失效的方法(hexo-theme-next)

  • sqlmap下载安装教程_termux 安装kali

    sqlmap下载安装教程_termux 安装kali第一步:下载python:https://www.python.org/downloads/(这里有python各种版本,但是一般建议安装3和2.7)sqlmap:https://github

  • eXtremeDB微秒级实时数据库简介「建议收藏」

    eXtremeDB微秒级实时数据库简介「建议收藏」eXtremeDB微秒级实时数据库简介 eXtremeDB实时数据库是美国McObject公司于上世纪九十年代末推出的全世界第一款全内存式实时数据库,特别为高性能、低开销、稳定可靠的极速实时数据管理而设计。 eXtremeDB的性能可以达到微秒一级的惊人速度。eXtremeDB能够达到这样惊人的极限速度,是由其对市场的独特理解、长期的行业经验、持续不断的创新精神和革命性的体系结构等…

  • 关于rndc-confgen 在centos 上不能用的解决方法

    关于rndc-confgen 在centos 上不能用的解决方法

  • java三大框架要学多久_新手学习SSH三大框架的几点建议[通俗易懂]

    java三大框架要学多久_新手学习SSH三大框架的几点建议[通俗易懂]说起三大框架,目前人们常用的是SSM,有人会认为SSH框架已经落后被淘汰了,其实这样说也不完全对。它毕竟实现了经典的MVC框架的基本功能,在有些项目还会用到。正因为用得不多,反而会出现很多问题。同时SSH在教学中也会被经常用到,笔者觉得SSH在教学中存在的意义在于能够帮助学习者理解经典MVC框架的原理。下面就简单谈一谈作为新手如何学习SSH。所谓SSH展开来讲包括了struts、spring、hi…

  • 网站的404错误页面制作方法[通俗易懂]

    网站的404错误页面制作方法[通俗易懂]网站的404错误页面怎么做?  网站的404错误页面怎么做呢?很多人学习完seo就对404错误页面的理解有所偏差,认为404错误页面只是为了SEO而存在,实际上,404错误页面不单只是为了seo而存在,它一共提出以下几点理念:  一、提供简明的问题描述,消除访客的挫败感。这个很好理解,告诉用户访问页面不存在,而不会是系统默认的错误页面。  二、提供合理的解决方案,辅助访客完成访问目标。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号