**
前言
**
在Android中,我们一般通过RadioGroup来管理一组RadioButton 来达到 单选按钮的互斥效果。但是,有些需求中,需要完成N行N列这样的RadioButton组成的矩阵,但是我们的RadioGroup是一个耿直的LinearLayout,无法完成网格布局╮(╯▽╰)╭。所以,下面我就像大家来介绍一种实现网格布局的RadioButton的思路。
无图无真相, 先上一下效果图~
**
思路
**
提到网格布局,最简单的就是使用系统的GridView来实现,我们需要做的就是 将RadioButton加入GridView中,然后自己来实现RadioButton之间的互斥。
1. 自定义RadioButton:
自定义一个RadioButton ,命名为IconRadioButton
/**
* Created by jk on 2016/1/20.
* 自定义 RadioButton
*/
public class IconRadioButton extends LinearLayout {
private Context mContext;
private Resources mResources;
private boolean mIsCheck;
private View mContentView;
private ImageView mRadio;
private TextView mTextView;
private ColorStateList mTextColor;
public IconRadioButton(Context context) {
this(context, null);
}
public IconRadioButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IconRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mContext = context;
mResources = context.getResources();
mContentView = LayoutInflater.from(context).inflate(R.layout.btn_icon_radio, this);
mRadio = (ImageView) mContentView.findViewById(R.id.radio);
mTextView = (TextView) mContentView.findViewById(R.id.text);
}
public void setChecked(boolean isChecked) {
mIsCheck = isChecked;
changeUIByChecked(isChecked);
}
public boolean isChecked() {
return mIsCheck;
}
private void changeUIByChecked(boolean isChecked) {
if (mRadio.getVisibility() != GONE) {
mRadio.setImageResource(isChecked ? R.drawable.radio_selected
: R.drawable.radio_unselected);
}
mTextView.setEnabled(isChecked);
}
public void setTextColor(ColorStateList color){
mTextColor = color;
mTextView.setTextColor(color);
}
public void setText(CharSequence text) {
mTextView.setText(text);
}
public void setText(int resId) {
mTextView.setText(resId);
}
public void hiddenRadio(boolean isHidden) {
mRadio.setVisibility(isHidden ? GONE : VISIBLE);
}
}
这个IconRadioButton很简单 就是一个LinearLayout,内部封装了根据是否选中来改变文字和按钮样式的方法。
btn_icon_radio.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="left"
android:paddingBottom="5dp"
android:paddingTop="5dp">
<ImageView
android:id="@+id/radio"
android:layout_width="42dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center"
android:src="@drawable/radio_selected"
android:textColor="#FF1CBFA6"
/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center"
android:textColor="#FF999999"
android:textSize="14dp" />
</LinearLayout>
布局文件很简单 就是一个LinearLayout里面放了一个ImageView和一个TextView
2 . 定义数据模型
/**
* Created by admin on 2016/1/20.
*/
public class RadioItemModel {
public String text; //RadioButton的文字
public boolean isChecked;//是否选中
public boolean hiddenRadio;//是否需要隐藏radio
public ColorStateList textColor;//文字的不同状态的颜色
public RadioItemModel(String text, boolean isChecked, boolean hiddenRadio, ColorStateList textColor) {
this.text = text;
this.isChecked = isChecked;
this.hiddenRadio = hiddenRadio;
this.textColor = textColor;
}
public RadioItemModel(String text) {
this.isChecked = false;
this.hiddenRadio = false;
this.textColor = null;
this.text = text;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public boolean isChecked() {
return isChecked;
}
}
我们定义了一个数据模型来存放RadioButton的各种属性。其中ColorStateList是用来控制TextView在不同状态下的颜色的 比如:state_pressed等。
3. 定义Adapter:
既然是使用GridView 那就需要定义我们的Adapter,我们的互斥逻辑也是在Adapter中实现的。
/**
* Created by jk on 2016/1/20.
* function: 单选矩阵的适配器
* purpose: 系统的RadioGroup是个耿直的LinearLayout╮(╯▽╰)╭ 只能线性排列,无法完成
* N行N列的布局。So 为了实现单选按钮的网格布局 ,重定义
*/
public class GridRadioAdapter extends BaseAdapter {
private List<RadioItemModel> mList;
private Context mContext;
private Resources mResources;
private OnItemCheckedListener mListener;
//RadioButton选中时的回调函数
public interface OnItemCheckedListener {
void onItemChecked(RadioItemModel model, int position);
}
public void setOnItemCheckedListener(OnItemCheckedListener listener) {
this.mListener = listener;
}
public GridRadioAdapter(Context context, List<RadioItemModel> list) {
mContext = context;
mList = list;
check();
}
private void check() {
if (mList.size()>=0 && mList!=null) {
checkDefaultChecked();
checkMutex();
}
}
/**
* 检查互斥事件 默认只能选中一个 如果有多个选择,就选中最后一个
*/
private void checkMutex() {
int checkCount = 0;
for (RadioItemModel item : mList) {
if (item.isChecked) {
checkCount++;
}
}
if (checkCount >= 2) {
setOtherUnChecked(checkCount - 1);
}
}
private void setOtherUnChecked(int position) {
for (int i = 0; i < mList.size(); i++) {
if (i != position) {
RadioItemModel item = mList.get(i);
item.isChecked = false;
} else {
mList.get(position).isChecked = true;
}
}
}
public RadioItemModel getItemChecked() {
for (int i = 0; i < mList.size(); i++) {
RadioItemModel item = mList.get(i);
if (item.isChecked)
return item;
}
return null;
}
/**
* 检查是否有默认选中的按钮 如果没有 默认第一个选中
*/
private void checkDefaultChecked() {
for (RadioItemModel item : mList) {
if (item.isChecked)
return;
}
mList.get(0).isChecked = true;
}
@Override
public int getCount() {
return mList == null ? 0 : mList.size();
}
@Override
public Object getItem(int position) {
if (mList != null && mList.size() > position)
return mList.get(position);
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
IconRadioButton iconBean = null;
iconBean = new IconRadioButton(mContext);
final RadioItemModel model = mList.get(position);
if (!TextUtils.isEmpty(model.text)) {
iconBean.setText(model.text);
}
iconBean.setChecked(model.isChecked);
if (model.textColor != null) {
iconBean.setTextColor(model.textColor);
}
iconBean.hiddenRadio(model.hiddenRadio);
final IconRadioButton finalIconBean = iconBean;
iconBean.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!finalIconBean.isChecked()) {
setOtherUnChecked(position);
if (mListener != null) {
mListener.onItemChecked(model, position);
}
notifyDataSetChanged();
}
}
});
return iconBean;
}
public void addAll(List<RadioItemModel> list){
mList.clear();
if(list.size()>=0 && list!=null){
mList.addAll(list);
check();
notifyDataSetChanged();
}
}
}
在构造函数中,我们调用了check方法来实现默认选中检查和 互斥检查。
(1)checkDefaultChecked函数用来实现默认选中功能,如果传递给我们的数据中没有按钮是默认选中的 ,那我们就让第一个按钮默认选中。
(2)checkMutex函数是用来检查互斥事件的,如果有多个按钮同时被选中,我们就设置被选中的最后一个按钮为选中状态,其他置为非选中状态。比如 按钮1,3,5同时被选中,那么我们就让5置为选中状态,1和3为非选中状态。
(3)OnItemCheckedListener是当按钮被选中时的回调方法。
4.使用方式
使用方式就非常简单粗暴了~
public class MainActivity extends Activity {
private GridView mGridLayout;
private GridRadioAdapter mAdapter;
private List<RadioItemModel> mItemList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private void initData() {
mItemList = new ArrayList<RadioItemModel>();
ColorStateList csl = getResources().getColorStateList(R.color.ddt_color_tab_text);
mItemList.add(new RadioItemModel("全部", false, true, csl));
mItemList.add(new RadioItemModel("早餐前", false, true, csl));
mItemList.add(new RadioItemModel("午餐前", false, true, csl));
mItemList.add(new RadioItemModel("晚餐前", false, true, csl));
mItemList.add(new RadioItemModel("早餐后", false, true, csl));
mItemList.add(new RadioItemModel("午餐后", false, true, csl));
mItemList.add(new RadioItemModel("晚餐后", false, true, csl));
mItemList.add(new RadioItemModel("睡前", false, true, csl));
mAdapter.notifyDataSetChanged();
}
}
PS: 如果设置hiddenRadio这个属性为true,我们就可以实现下面这种效果了。所以这个东西也是可以用来做切换Fragment的选项卡的哦~~~ O(∩_∩)O
以上。