为过滤动态加入单选按钮

项目上做的一个过滤筛选的,先看看效果图
1

这里的数据都是动态写入的,弹出的的是popupwindow,原本是侧滑的抽屉布局,领导要改成这样子,只好改了。

1.Android Radiogroup的坑

Android原生的Radiogroup是不支持换行的,也就是要么一直竖着要么一直横着,如果用两组radiogroup还需要自己实现互斥算法,比较麻烦。
我这里使用自定义radiogroup,继承系统的RadioGroup来实现,也可以继承LinearLayout,不过显然第一种比较简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.RadioGroup;
/**
* 重新对RadioGroup进行布局,可以折行
* 默认水平开始排布
*/
public class RadioGroupEx extends RadioGroup {
private static final String TAG = "RadioGroupEx";
public RadioGroupEx(Context context) {
super(context);
}
public RadioGroupEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//调用ViewGroup的方法,测量子view
measureChildren(widthMeasureSpec, heightMeasureSpec);
//最大的宽
int maxWidth = 0;
//累计的高
int totalHeight = 0;
//当前这一行的累计行宽
int lineWidth = 0;
//当前这行的最大行高
int maxLineHeight = 0;
//用于记录换行前的行宽和行高
int oldHeight;
int oldWidth;
int count = getChildCount();
//假设 widthMode和heightMode都是AT_MOST
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
//得到这一行的最高
oldHeight = maxLineHeight;
//当前最大宽度
oldWidth = maxWidth;
int deltaX = child.getMeasuredWidth() + params.leftMargin + params.rightMargin;
if (lineWidth + deltaX + getPaddingLeft() + getPaddingRight() > widthSize) {//如果折行,height增加
//和目前最大的宽度比较,得到最宽。不能加上当前的child的宽,所以用的是oldWidth
maxWidth = Math.max(lineWidth, oldWidth);
//重置宽度
lineWidth = deltaX;
//累加高度
totalHeight += oldHeight;
//重置行高,当前这个View,属于下一行,因此当前最大行高为这个child的高度加上margin
maxLineHeight = child.getMeasuredHeight() + params.topMargin + params.bottomMargin;
Log.v(TAG, "maxHeight:" + totalHeight + "---" + "maxWidth:" + maxWidth);
} else {
//不换行,累加宽度
lineWidth += deltaX;
//不换行,计算行最高
int deltaY = child.getMeasuredHeight() + params.topMargin + params.bottomMargin;
maxLineHeight = Math.max(maxLineHeight, deltaY);
}
if (i == count - 1) {
//前面没有加上下一行的搞,如果是最后一行,还要再叠加上最后一行的最高的值
totalHeight += maxLineHeight;
//计算最后一行和前面的最宽的一行比较
maxWidth = Math.max(lineWidth, oldWidth);
}
}
//加上当前容器的padding值
maxWidth += getPaddingLeft() + getPaddingRight();
totalHeight += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxWidth,
heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
//pre为前面所有的child的相加后的位置
int preLeft = getPaddingLeft();
int preTop = getPaddingTop();
//记录每一行的最高值
int maxHeight = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
//r-l为当前容器的宽度。如果子view的累积宽度大于容器宽度,就换行。
if (preLeft + params.leftMargin + child.getMeasuredWidth() + params.rightMargin + getPaddingRight() > (r - l)) {
//重置
preLeft = getPaddingLeft();
//要选择child的height最大的作为设置
preTop = preTop + maxHeight;
maxHeight = getChildAt(i).getMeasuredHeight() + params.topMargin + params.bottomMargin;
} else { //不换行,计算最大高度
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + params.topMargin + params.bottomMargin);
}
//left坐标
int left = preLeft + params.leftMargin;
//top坐标
int top = preTop + params.topMargin;
int right = left + child.getMeasuredWidth();
int bottom = top + child.getMeasuredHeight();
//为子view布局
child.layout(left, top, right, bottom);
//计算布局结束后,preLeft的值
preLeft += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
}
} }

在布局中用这个radiogroup替代原生的既可,使用方法不变

2.在activity中加入popup以及动态添加radiobutton的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/**
* 过滤的popup
*/
private final static String primaryUrl = "www/js/primary_class_phase.json";//小学
private final static String juniorUrl = "www/js/junior_class_phase.json";//初中
private final static String seniorUrl = "www/js/senior_class_phase.json";//高中
private RadioGroupEx phaseRadio;//学段
private RadioGroupEx gradeRadio;//年级
private RadioGroupEx subjectRadio;//科目
private String[] phaseCode={"2","3","4"};
private String[] gradeCode={"1","2","3","5","7","9"};
private List<String> subjectCode=new ArrayList<>();
private void showPopupWindowFilter(View view) {
// 一个自定义的布局,作为显示的内容
View contentView = LayoutInflater.from(this).inflate(R.layout.openclass_filter_popup, null);
phaseRadio = (RadioGroupEx) contentView.findViewById(R.id.openclass_radio_phase);
gradeRadio = (RadioGroupEx) contentView.findViewById(R.id.openclass_radio_grade);
subjectRadio = (RadioGroupEx) contentView.findViewById(R.id.openclass_radio_subject);
phaseRadio.setOrientation(LinearLayout.HORIZONTAL);
gradeRadio.setOrientation(LinearLayout.HORIZONTAL);
subjectRadio.setOrientation(LinearLayout.HORIZONTAL);
RadioGroup.OnCheckedChangeListener popupRadioCheckListener = new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (group.getId()){
case R.id.openclass_radio_phase://学段
phaseId = (String)group.findViewById(checkedId).getTag();
mGradeId = "";
subjectId = "";
int pid = Integer.parseInt(phaseId);
createSubjectRadio(pid,subjectRadio);
createGradeRadio(pid,gradeRadio);
break;
case R.id.openclass_radio_grade://年级
mGradeId = (String)group.findViewById(checkedId).getTag();
break;
case R.id.openclass_radio_subject://科目
subjectId = (String)group.findViewById(checkedId).getTag();
break;
}
Log.d(TAG,"phaseId:"+phaseId+"mGradeId:"+mGradeId+"subjectId:"+subjectId);
if(!StringUtils.isEmpty(phaseId)&&!StringUtils.isEmpty(mGradeId)&&!StringUtils.isEmpty(subjectId)){
int currentItem = mViewPager.getCurrentItem();
Fragment fragment = adapter.getPage(currentItem);
if (fragment instanceof OpenclassUpdateFilter) {
OpenclassUpdateFilter openclassUpdateFilter = (OpenclassUpdateFilter)fragment ;
openclassUpdateFilter.updateData(phaseId,mGradeId,subjectId);
}
}
}
};
phaseRadio.setOnCheckedChangeListener(popupRadioCheckListener);
gradeRadio.setOnCheckedChangeListener(popupRadioCheckListener);
subjectRadio.setOnCheckedChangeListener(popupRadioCheckListener);
createPhaseRadio(phaseRadio);
createGradeRadio(2,gradeRadio);//创建年级单选,根据学段,默认小学
createSubjectRadio(2,subjectRadio);//创建科目单选,根据学段,默认小学
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
int width = (int) ((wm.getDefaultDisplay().getWidth()) * 0.35);//获取屏幕宽度的0.35倍赋给该popupWindow
final PopupWindow popupWindow = new PopupWindow(contentView,
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT, true);
popupWindow.setAnimationStyle(R.style.popwin_anim_style);
popupWindow.setTouchable(true);
popupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("mengdd", "onTouch : ");
return false;
// 这里如果返回true的话,touch事件将被拦截
// 拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss
}
});
// 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框
// 我觉得这里是API的一个bug
popupWindow.setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.transparent)));
// 设置好参数之后再show
popupWindow.showAsDropDown(view);
// popupWindow.showAtLocation(view, Gravity.RIGHT | Gravity.TOP, ScreenUtil.dip2px(this, 8), ScreenUtil.dip2px(this, 70));
}
private void createPhaseRadio(RadioGroup radioGroup){
RadioButton tempButton1 = getBaseRadioButton();
tempButton1.setText("小学");
tempButton1.setTag(phaseCode[0]);
radioGroup.addView(tempButton1, getbaseLayoutParams());
RadioButton tempButton2 = getBaseRadioButton();
tempButton2.setText("初中");
tempButton2.setTag(phaseCode[1]);
radioGroup.addView(tempButton2, getbaseLayoutParams());
RadioButton tempButton3 = getBaseRadioButton();
tempButton3.setText("高中");
tempButton3.setTag(phaseCode[2]);
radioGroup.addView(tempButton3, getbaseLayoutParams());
tempButton1.setChecked(true);//默认小学
phaseId = "2";
}
private void createGradeRadio(int phase,RadioGroup radioGroup){
radioGroup.removeAllViews();
switch (phase){
case 2:
RadioButton tempButton1 = getBaseRadioButton();
tempButton1.setText("一年级");
tempButton1.setTag(gradeCode[0]);
radioGroup.addView(tempButton1, getbaseLayoutParams());
RadioButton tempButton2 = getBaseRadioButton();
tempButton2.setText("二年级");
tempButton2.setTag(gradeCode[1]);
radioGroup.addView(tempButton2, getbaseLayoutParams());
RadioButton tempButton3 = getBaseRadioButton();
tempButton3.setText("三年级");
tempButton3.setTag(gradeCode[2]);
radioGroup.addView(tempButton3, getbaseLayoutParams());
RadioButton tempButton4 = getBaseRadioButton();
tempButton4.setText("四年级");
tempButton4.setTag(gradeCode[3]);
radioGroup.addView(tempButton4, getbaseLayoutParams());
RadioButton tempButton5 = getBaseRadioButton();
tempButton5.setText("五年级");
tempButton5.setTag(gradeCode[4]);
radioGroup.addView(tempButton5, getbaseLayoutParams());
RadioButton tempButton6 = getBaseRadioButton();
tempButton6.setText("六年级");
tempButton6.setTag(gradeCode[5]);
radioGroup.addView(tempButton6, getbaseLayoutParams());
break;
case 3:
case 4:
RadioButton tempButton7 = getBaseRadioButton();
tempButton7.setText("一年级");
tempButton7.setTag(gradeCode[0]);
radioGroup.addView(tempButton7, getbaseLayoutParams());
RadioButton tempButton8 = getBaseRadioButton();
tempButton8.setText("二年级");
tempButton8.setTag(gradeCode[1]);
radioGroup.addView(tempButton8, getbaseLayoutParams());
RadioButton tempButton9 = getBaseRadioButton();
tempButton9.setText("三年级");
tempButton9.setTag(gradeCode[2]);
radioGroup.addView(tempButton9, getbaseLayoutParams());
break;
}
}
private void createSubjectRadio(final int phase, final RadioGroup radioGroup){
dialogHelper.showProgressDialog("加载中...");
new Thread(new Runnable() {
@Override
public void run() {
String jsonStr="";
switch (phase){
case 2:
jsonStr = AppJsonFileReader.getJson(OpenClassActivity.this,primaryUrl);//小学
break;
case 3:
jsonStr = AppJsonFileReader.getJson(OpenClassActivity.this,juniorUrl);//初中
break;
case 4:
jsonStr = AppJsonFileReader.getJson(OpenClassActivity.this,seniorUrl);//高中
break;
}
final List<Map<String, String>> data = AppJsonFileReader.setListData(jsonStr);
// list.addAll(mCources);
runOnUiThread(new Runnable() {
@Override
public void run() {
radioGroup.removeAllViews();
subjectCode.clear();
radioGroup.setOrientation(LinearLayout.HORIZONTAL);
for(int i = 0;i<data.size();i++){
RadioButton tempButton = getBaseRadioButton();
tempButton.setText(data.get(i).get("Name"));
tempButton.setTag(data.get(i).get("Code"));
radioGroup.addView(tempButton, getbaseLayoutParams());
subjectCode.add(data.get(i).get("Code"));
}
dialogHelper.dismissProgressDialog();
}
});
}
}).start();
}
private RadioButton getBaseRadioButton(){
RadioButton tempButton = new RadioButton(this);
// tempButton.setTextSize(ScreenUtil.dip2px(context, 5));
tempButton.setTextSize(TypedValue.COMPLEX_UNIT_SP,16);
tempButton.setTextColor(getResources().getColorStateList(R.color.filter_phase_text_selecter));
tempButton.setButtonDrawable(getResources().getDrawable(android.R.color.transparent));
// tempButton.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
tempButton.setBackgroundResource(R.drawable.filter_phase_selecter);
// tempButton.setCompoundDrawablePadding(ScreenUtil.dip2px(getActivity(), 10));
// tempButton.setPadding(20, 20, 20, 20); // 设置文字距离按钮四周的距离
// layoutParams.weight =0.3f;
// tempButton.setLayoutParams(layoutParams);
return tempButton;
}
private RadioGroup.LayoutParams getbaseLayoutParams(){
RadioGroup.LayoutParams layoutParams = new RadioGroup.LayoutParams(RadioGroup.LayoutParams.WRAP_CONTENT, RadioGroup.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(15,15,15,15);
return layoutParams;
}

动态创建radiobutton的地方纠结了好久,主要是设置margin不生效的问题,这里要注意要用RadioGroup.LayoutParams,在stackoverflow上找到的解决办法。

服务端的过滤数据写的是配置,不会变的的学段和年级就直接写死了,科目写的json配置文件,动态读取。

popup的xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:student="http://schemas.android.com/apk/res-auto"
android:id="@id/openclass_filter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp">
<TextView
android:id="@id/openclass_condition_phase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="@string/openclass_screening_phase"
android:textSize="20sp" />
<com.tyky.edu.teacher.main.ui.RadioGroupEx
android:id="@+id/openclass_radio_phase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/openclass_condition_phase"></com.tyky.edu.teacher.main.ui.RadioGroupEx>
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="@color/filter_radio_line" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp">
<TextView
android:id="@id/openclass_condition_grade"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="@string/openclass_screening_grade"
android:textSize="20sp" />
<com.tyky.edu.teacher.main.ui.RadioGroupEx
android:id="@+id/openclass_radio_grade"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/openclass_condition_grade"></com.tyky.edu.teacher.main.ui.RadioGroupEx>
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="@color/filter_radio_line" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp">
<TextView
android:id="@id/openclass_condition_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="@string/openclass_screening_subject"
android:textSize="20sp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/openclass_condition_subject"
android:fillViewport="true">
<com.tyky.edu.teacher.main.ui.RadioGroupEx
android:id="@+id/openclass_radio_subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
></com.tyky.edu.teacher.main.ui.RadioGroupEx>
</ScrollView>
</RelativeLayout>
</LinearLayout>

3.直接在点击事件出调用showPopupWindowFilter既可

这里popup弹出用到了一个折叠动画

下面附上代码,以后有时间再详细写写各种动画

style:

1
2
3
4
<style name="popwin_anim_style">
<item name="android:windowEnterAnimation">@anim/popup_show_anim</item>
<item name="android:windowExitAnimation">@anim/popup_hide_anim</item>
</style>

popup_show_anim:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="0.0"
android:toYScale="1.0"
android:pivotX="100%"
android:pivotY="0"
android:duration="300" />
</set>

popup_hide_anim:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale="1.0"
android:fromYScale="1.0"
android:toYScale="0.0"
android:pivotX="100%"
android:pivotY="0"
android:duration="300" />
</set>