Android Pitfall - 扒一扒RadioGroup 和 RadioButton

今天工作时,需要以编程方式对RadioGroup进行操作(包括清空、选中),结果却遇到了一个非常意外的结果—— radioButton.setChecked(true); 后,该按钮竟不会变为选中状态!在Android源码中一阵翻腾后(开源万岁啊),于是便有了这篇博文。


首先,RadioButton 继承自 CompoundButton,大部分的功能都是由CompoundButton完成的。其中,也包括我们熟悉的 setChecked(booleanchecked)


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;            
    }
}

点击按钮或调用setChecked()方法后,CompoundButton更新选中状态后,会依次调用 

mOnCheckedChangeListener.onCheckedChanged() 和 
mOnCheckedChangeWidgetListener.onCheckedChanged(),前者是用户程序调用 button.setOnclickListener() 方法所设置的回调函数,而后者,则得从 RadioGroup 说起。
RadioGroup 构造器中,会调用如下这么一个函数:
private void init() {
    mChildOnCheckedChangeListener = new CheckedStateTracker();
    mPassThroughListener = new PassThroughHierarchyChangeListener();
    super.setOnHierarchyChangeListener(mPassThroughListener);
}

在这里,RadioGroup 设置了 OnHierarchyChangeListener 这么一个Callback,而在这个Callback —— 
PassThroughHierarchyChangeListener 中,
public void onChildViewAdded(View parent, View child) {
    if (parent == RadioGroup.this && child instanceof RadioButton) {
        int id = child.getId();
        // generates an id if it's missing
        if (id == View.NO_ID) {
            id = View.generateViewId();
            child.setId(id);
        }
        ((RadioButton) child).setOnCheckedChangeWidgetListener(
                mChildOnCheckedChangeListener);
    }

    if (mOnHierarchyChangeListener != null) {
        mOnHierarchyChangeListener.onChildViewAdded(parent, child);
    }
}

对于每个 instanceof RadioButton 的 child view,都会调用 setOnCheckedChangeWidgetListener(),这就是我们调用radioButton.setChecked()的第二个回调了,也是RadioGroup能够保持只有一个按钮选中的秘密所在。
RadioGroup 的回调函数就长这样:
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    // prevents from infinite recursion
    if (mProtectFromCheckedChange) {
        return;
    }

    mProtectFromCheckedChange = true;
    if (mCheckedId != -1) {
        setCheckedStateForView(mCheckedId, false);
    }
    mProtectFromCheckedChange = false;

    int id = buttonView.getId();
    setCheckedId(id);
}

首先,将先前选中的按钮设置为“未选择”(setCheckedStateForView(mCheckedId, false)),然后更新内部状态(setCheckedId(id))。
在我的代码中,有这么一段:
for (RadioButton button : radioButtons) {
    button.setChecked(false);
}

把RadioGroup中所有的按钮都设置为false(记这个操作为“清空”),确实,如我所愿,所有按钮均显示“未选中”。但是,但我再次点击“清空”操作前那个被选中的按钮时,任凭我怎么点击,他始终都无动于衷。
至于原因,上面已经说明了:
在setChecked()里,由于未选中的按钮状态没有改变,所有,setChecked() 什么都不做,而对于那个已经选中的按钮,他的状态将变为未选中,而后更新drawable (虽然我们看不到变化,但他确实调用了refreshDrawableState()更新其状态 )。
至于问题,就出在后面的 mOnCheckedChangeWedgitListener.onCheckedChanged(this, mChecked)。

RadioGroup中,他会将先前已选中的按钮变为“未选中”,而后更新内部的mChekedId ,于是,便把前面 refreshDrawableState() 所更新的drawable状态又变回了 unchecked,也就是我们所看到的,“无法再次选中”。



解决方案:直接调用 radioGroup.check(-1) 或 clearCheck();

/**
 * <p>Sets the selection to the radio button whose identifier is passed in
 * parameter. Using -1 as the selection identifier clears the selection;
 * such an operation is equivalent to invoking {@link #clearCheck()}.</p>
 *
 * @param id the unique id of the radio button to select in this group
 *
 * @see #getCheckedRadioButtonId()
 * @see #clearCheck()
 */
public void check(int id) {
    // don't even bother
    if (id != -1 && (id == mCheckedId)) {
        return;
    }

    if (mCheckedId != -1) {
        setCheckedStateForView(mCheckedId, false);
    }

    if (id != -1) {
        setCheckedStateForView(id, true);
    }

    setCheckedId(id);
}

这里的第二个if语句会将之前选择的按钮设置为“未选中”。至此,问题圆满解决。



PS:点击后切换StateListDrawable是在 performClick() 里实现的

@Override
public boolean performClick() {
    toggle();

    final boolean handled = super.performClick();
    if (!handled) {
        // View only makes a sound effect if the onClickListener was
        // called, so we'll need to make one here instead.
        playSoundEffect(SoundEffectConstants.CLICK);
    }

    return handled;
}

public void toggle() {
    setChecked(!mChecked);
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Simultaneous, on-chip FPGA delay measurement is a useful technique for characterizing the delay of digital circuits. However, there are several pitfalls and tradeoffs that should be considered when using this technique. One common pitfall is the assumption that the delay of a circuit is constant across different input patterns. In reality, the delay can vary depending on the input pattern. Therefore, it is important to measure the delay across a range of input patterns to obtain an accurate characterization. Another pitfall is the assumption that the delay of a circuit is symmetric for rising and falling edges. This is not always the case, especially for circuits with non-linear input/output characteristics. Asymmetry can result in errors in the delay measurement, so it is important to measure the delay for both rising and falling edges. Tradeoffs that must be considered include the tradeoff between measurement accuracy and measurement time. To obtain a more accurate measurement, it may be necessary to measure the delay multiple times and average the results. However, this can increase the measurement time, which may not be feasible in some applications. Another tradeoff is between measurement accuracy and the complexity of the measurement circuitry. More complex measurement circuitry can provide more accurate measurements, but it can also increase the cost and complexity of the overall system. In summary, simultaneous, on-chip FPGA delay measurement is a useful technique for characterizing digital circuits, but it is important to consider the pitfalls and tradeoffs when using this technique.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值