【Android】OnCheckedChangeListener is called twice

RadioGroup.OnCheckedChangeListener is called twicejava


今天又碰到了,貌似尚未被修復,順便貼出來。android

原android Issue地址:RadioGroup.OnCheckedChangeListener is called twice when the selection is clearedide

具體表現

RadioGroup中包含有若干個RadioButton,當在代碼中調用RadioGroup.check(id)方法動態設置被選中的RadioButton的時候,RadioGroup.OnCheckedChangeListener(RadioGroup group, int checkedId)會被調用屢次。測試

示例

假設當前選擇的是RadioButtonA,調用RadioGroup.check(RadioButtonBid)以後,RadioGroup.OnCheckedChangeListener(RadioGroup group, int checkedId)的調用狀況以下:this

第一次:checkedId爲RadioButtonAIdgoogle

第二次:checkedId爲RadioButtonBIdspa

第三次:checkedId爲RadioButtonBIdcode

測試代碼

public class MainActivity extends Activity implements CompoundButton.OnCheckedChangeListener,
RadioGroup.OnCheckedChangeListener{

	RadioGroup mRadioGroup;
	RadioButton radio_0;
	RadioButton radio_1;
	RadioButton radio_2;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mRadioGroup = (RadioGroup) findViewById(R.id.group);
		radio_0 = (RadioButton) findViewById(R.id.radio_0);
		radio_1 = (RadioButton) findViewById(R.id.radio_1);
		radio_2 = (RadioButton) findViewById(R.id.radio_2);

		mRadioGroup.setOnCheckedChangeListener(this);
		radio_0.setOnCheckedChangeListener(this);
		radio_1.setOnCheckedChangeListener(this);
		radio_2.setOnCheckedChangeListener(this);
	}

	public void doTestClick(View view) {
		switch (view.getId()) {
		case R.id.check_radio_0:
			mRadioGroup.check(R.id.radio_0);
			break;
		case R.id.check_radio_1:
			mRadioGroup.check(R.id.radio_1);
			break;
		case R.id.check_radio_2:
			mRadioGroup.check(R.id.radio_2);
			break;

		default:
			break;
		}
	}

	@Override
	public void onCheckedChanged(RadioGroup group, int checkedId) {
		Log.i("mRadioGroup onCheckedChanged", "checkedId:" + checkedId);
	}
	
	@Override
	public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
		switch (buttonView.getId()) {
		case R.id.radio_0:
			Log.i("radio_0 onCheckedChanged", "isChecked:" + isChecked);
			break;
		case R.id.radio_1:
			Log.i("radio_1 onCheckedChanged", "isChecked:" + isChecked);
			break;
		case R.id.radio_2:
			Log.i("radio_2 onCheckedChanged", "isChecked:" + isChecked);
			break;

		default:
			break;
		}
	}

}

<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <Button
            android:id="@+id/check_radio_0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="doTestClick"
            android:text="check_radio_0" />

        <Button
            android:id="@+id/check_radio_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="doTestClick"
            android:text="check_radio_1" />

        <Button
            android:id="@+id/check_radio_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="doTestClick"
            android:text="check_radio_2" />
    </LinearLayout>

    <RadioGroup
        android:id="@+id/group"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <RadioButton
            android:id="@+id/radio_0"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="0" />

        <RadioButton
            android:id="@+id/radio_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="1" />

        <RadioButton
            android:id="@+id/radio_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="2" />
    </RadioGroup>

</LinearLayout>

產生緣由

RadioGroup check簡化以下:xml

public void check(int newid) {
	setCheckedStateForView(mCheckedId, false); //這裏取消原來選擇的RadioButton
	setCheckedStateForView(newid, true);//設置當前選中的RadioButton爲激活狀態
	setCheckedId(newid);//觸發OnCheckedChangeListener
}

RadioGroup setCheckedStateForView簡化以下:文檔

private void setCheckedStateForView(int viewId, boolean checked) {
	RadioButton childRadioButton = (RadioButton)findViewById(viewId);
	childRadioButton.setChecked(checked);
}

RadioGroup setCheckedId簡化以下:

private void setCheckedId(int id) {
	mCheckedId = id;//保存當前選擇的RadioButton
	mOnCheckedChangeListener.onCheckedChanged(this, mCheckedId);
}


同時RadioGroup內部有一個check狀態跟蹤器,簡化一下以下:

private class CheckedStateTracker implements CompoundButton.OnCheckedChangeListener {
	public void onCheckedChanged(CompoundButton childRadioButton, boolean isChecked) {
	    int childRadioButtonId = childRadioButton.getId();
	    setCheckedId(id);
	}
}

當每個RadioButton被添加到RadioGroup的時候,就給每個RadioButton設置一個checked狀態監聽

childRadioButton.mOnCheckedChangeWidgetListener = mCheckedStateTracker;

因此每當RadioButton的check狀態發生變化,都會觸發check狀態跟蹤器。

RadioButton的setChecked(boolean)簡化以下:

public void setChecked(boolean checked) {
	mOnCheckedChangeListener.onCheckedChanged(this, mChecked); //觸發RadioButton各自的監聽
	mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);//觸發check狀態跟蹤器
}

說明

因此結合開頭的例子,調用RadioGroup.check(RadioButtonBid)以後發生的狀況以下:

1.RadioGroup.setCheckedStateForView(RadioButtonAid, false);
    --> RaidoButton.mOnCheckedChangeWidgetListener.onCheckedChanged(RadioButtonA, false) 
        --> RadioGroup.setCheckedId(RadioButtonAId) 
            --> RadioGroup.mOnCheckedChangeListener.onCheckedChanged(RadioGroup, RadioButtonAId);

2.RadioGroup.setCheckedStateForView(RadioButtonBid, true);
    --> RaidoButton.mOnCheckedChangeWidgetListener.onCheckedChanged(RadioButtonB, true) 
        --> RadioGroup.setCheckedId(RadioButtonBId) 
            --> RadioGroup.mOnCheckedChangeListener.onCheckedChanged(RadioGroup, RadioButtonBId);

3.RadioGroup.setCheckedId(RadioButtonBId) 
    --> RadioGroup.mOnCheckedChangeListener.onCheckedChanged(RadioGroup, RadioButtonBId);


按照Android官方文檔的API說明:

public abstract void onCheckedChanged (RadioGroup group, int checkedId)
Parameters
    group	the group in which the checked radio button has changed
checkedId	the unique identifier of the newly checked radio button


那個checkedId的參數在實現上是名存實亡的。

相關文章
相關標籤/搜索