朋友让帮忙实现一个单选的ListDialog,原生的DialogSingleChoice很符合要求,但是不需要右边的RadioButton,于是就研究了一下DialogSingleChoice,之后通过源码找到了修改DialogSingleChoice布局的方法。经测试2.3.3 4.1 4.4.2系统版本下都是同样的效果。
首页看一下效果,默认SingleChoiceDialog样式
通过代码反射字段修改后的样式
接下来上代码,首先来点初始化数据
private String[] items;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
items= new String[] { "小张", "小杉", "小艳", "小马","小钟" };
showDialog();
}
接下来上通过反射修改dialog样式的部分
public void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setSingleChoiceItems(items, 0,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Toast.makeText(MainActivity.this, items[which], 0)
.show();
}
});
builder.setCancelable(false);
try {
//得到AlertDialog私有变量P
Field fieldP = builder.getClass().getDeclaredField("P");
//设置私有变量P为可以访问
fieldP.setAccessible(true);
//得到P的对象
Object p = fieldP.get(builder);
//得到P中的mIsSingleChoice字段
Field declaredField = p.getClass().getDeclaredField(
"mIsSingleChoice");
declaredField.setAccessible(true);
//将P身上的mIsSingleChoice字段设置为false
declaredField.set(p, false);
} catch (Exception e) {
e.printStackTrace();
}
builder.create().show();
}
注释写得很详细我就不赘述了。
接下来说说我是如何找源码的。
首先进入到dialog的setSingleChoiceItems源码中,我们注意到有一个变量P.mIsSingleChoice = true,这个P变量记录了dialog是一个单选dialog,接着我们去找找AlertController.AlertParams P的源码,eclipse不能直接跳入,直接用工具把这个类搜出来。
private final AlertController.AlertParams P;
public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
P.mItems = items;
P.mOnClickListener = listener;
P.mCheckedItem = checkedItem;
P.mIsSingleChoice = true;
return this;
}
打开AlertController.AlertParams直接搜索mIsSingleChoice在哪里用到了,搜到如下代码
int layout = mIsSingleChoice
? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
在找到定义这两个资源ID的代码
mSingleChoiceItemLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
com.android.internal.R.layout.select_dialog_singlechoice);
mListItemLayout = a.getResourceId(
com.android.internal.R.styleable.AlertDialog_listItemLayout,
com.android.internal.R.layout.select_dialog_item);
首先看一下默认mIsSingleChoicetrue为true的布局select_dialog_singlechoice,这是一个带有CheckBox的自定义TextView
<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/primary_text_light_disable_only"
android:gravity="center_vertical" android:paddingLeft="12dip"
android:paddingRight="7dip"
android:checkMark="@android:drawable/btn_radio"
android:ellipsize="marquee"/>
mIsSingleChoice为false的布局select_dialog_singlechoice,就是一个TextView
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:gravity="center_vertical"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:ellipsize="marquee"
/>
源代码看完了,然后就是通过开头反射那部分代码,去修改mIsSingleChoice属性为false,从而让dialog item去加载textview的这个布局,达到我们想要的效果。
才发现,其实使用builder.setItems(items, listener)方法就可以达到上述效果,不记得这个API了,- =不过也可以作为查看源码和反射的学习。