自定义条目数量的底部弹框

前言

什么都不说了,先上效果图

底部弹框效果图

知识点

该功能的实现主要依赖于以下两点

  • 载体:DialogFragment(android.support.v4.app)
  • Item:代码动态添加

以前写提示框大多是用Dialog,又或者是用PopupWindow。现在换个花样,用DialogFragment来实现。

这里用的是v4包内的DialogFragment,可以支持Android3.0之前的版本,而且android.app.DialogFragment 在API28被弃用了。

DialogFragment extends Fragment,其父类是Fragment,所以显示此布局的Activity必须是FragmentActivity的子类。

完整代码

Java

BottomPopUpDialog.java

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
226
227
228
229
230
231
232
233
234
235
236
public class BottomPopUpDialog extends DialogFragment {


private TextView mCancel;

private LinearLayout mContentLayout;

private Builder mBuilder;

private static BottomPopUpDialog getInstance(Builder builder) {
BottomPopUpDialog dialog = new BottomPopUpDialog();
dialog.mBuilder = builder;
return dialog;

}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (getDialog().getWindow() != null) {
getDialog().getWindow().setBackgroundDrawableResource(mBuilder.mBackgroundShadowColor);
}
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Holo_Light_NoActionBar);
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@SuppressLint("InflateParams") View view = inflater.inflate(R.layout.bottom_popup_dialog, null);
initView(view);
registerListener(view);
setCancelable(true);
return view;
}


private void initView(View view) {
mContentLayout = view.findViewById(R.id.pop_dialog_content_layout);
mCancel = view.findViewById(R.id.cancel);
initItemView();
}


private void registerListener(View view) {

view.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
dismiss();
}
return false;
}
});

mCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBuilder.mListener.onCancleClick();
dismiss();
}
});
}


@Override
public void show(FragmentManager manager, String tag) {
try {
super.show(manager, tag);
} catch (Exception e) {
e.printStackTrace();
}
}


private void initItemView() {
//循环添加item
for (int i = 0; i < mBuilder.mDataArray.length; i++) {
final PopupDialogItem dialogItem = new PopupDialogItem(getContext());
dialogItem.refreshData(mBuilder.mDataArray[i]);

//最后一项隐藏分割线
if (i == mBuilder.mDataArray.length - 1) {
dialogItem.hideLine();
}

//设置字体颜色
if (mBuilder.mColorArray.size() != 0 && mBuilder.mColorArray.get(i) != 0) {
dialogItem.setTextColor(mBuilder.mColorArray.get(i));
}

//设置分割线颜色
if (mBuilder.mLineColor != 0) {
dialogItem.setLineColor(mBuilder.mLineColor);
}

//设置首项的字体大小,这里我写死为12sp
if (i == 0) {
dialogItem.setTextSize(12f);
}

mContentLayout.addView(dialogItem);

//设置首项是否添加点击回调
if (i == 0 && !mBuilder.firstItemClickable) {
continue;
}

//设置点击回调
dialogItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBuilder.mListener.onDialogClick(dialogItem.getItemContent());
if (mBuilder.mIsCallBackDismiss) {
dismiss();
}
}
});
}
}


public static class Builder {

private String[] mDataArray;

private SparseIntArray mColorArray = new SparseIntArray();

private BottomPopDialogOnClickListener mListener;

private int mLineColor;

private boolean mIsCallBackDismiss = false;

private boolean firstItemClickable = false;

private int mBackgroundShadowColor = R.color.transparent_70;


/**
* 设置item数据
*/
public Builder setDialogData(String[] dataArray) {
mDataArray = dataArray;
return this;
}

/**
* 设置监听item监听器
*/
public Builder setItemOnListener(BottomPopDialogOnClickListener listener) {
mListener = listener;
return this;
}


/**
* 设置字体颜色
*
* @param index item的索引
* @param color res color
*/
public Builder setItemTextColor(int index, int color) {
mColorArray.put(index, color);
return this;
}

/**
* 设置item分隔线颜色
*/
public Builder setItemLineColor(int color) {
mLineColor = color;
return this;
}

/**
* 设置是否点击回调取消dialog
*/
public Builder setCallBackDismiss(boolean dismiss) {
mIsCallBackDismiss = dismiss;
return this;
}

/**
* 设置首项是否可点击
* @param firstItemClickable
*/
public void setFirstItemClickable(boolean firstItemClickable) {
this.firstItemClickable = firstItemClickable;
}

/**
* 设置dialog背景阴影颜色
*/
public Builder setBackgroundShadowColor(int color) {
mBackgroundShadowColor = color;
return this;
}


public BottomPopUpDialog create() {
return BottomPopUpDialog.getInstance(this);
}


public BottomPopUpDialog show(FragmentManager manager, String tag) {
BottomPopUpDialog dialog = create();
dialog.show(manager, tag);
return dialog;
}


}


public interface BottomPopDialogOnClickListener {
/**
* item点击事件回调
*
* @param tag item字符串 用于识别item
*/
void onDialogClick(String tag);

/**
* 点击取消
*/
void onCancleClick();
}

}

BottomPopUpDialog.java 主要是一个 Builder内部类,用于获取配置参数;

initItemView()方法,将 Builder内的mDataArray转换成PopupDialogItem显示。

setStyle(),设置显示样式。两个参数setStyle(@DialogStyle int style, @StyleRes int theme)
style:DialogFragment提供了4种,STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT。

theme:自定义样式,指定该dialog样式。当该值为0时,通过style确定具体样式。

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
/**
* Style for {@link #setStyle(int, int)}: a basic,
* normal dialog.
* 普通的dialog样式
*/
public static final int STYLE_NORMAL = 0;

/**
* Style for {@link #setStyle(int, int)}: don't include
* a title area.
* 没有标题
*/
public static final int STYLE_NO_TITLE = 1;

/**
* Style for {@link #setStyle(int, int)}: don't draw
* any frame at all; the view hierarchy returned by {@link #onCreateView}
* is entirely responsible for drawing the dialog.
* 直接将onCreateView()中返回的View显示
*/
public static final int STYLE_NO_FRAME = 2;

/**
* Style for {@link #setStyle(int, int)}: like
* {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
* The user can not touch it, and its window will not receive input focus.
* 在STYLE_NO_FRAME的基础上,禁用输入
*/
public static final int STYLE_NO_INPUT = 3;


public void setStyle(@DialogStyle int style, @StyleRes int theme) {
mStyle = style;
if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
mTheme = android.R.style.Theme_Panel;
}
if (theme != 0) {
mTheme = theme;
}
}

PopupDialogItem.java

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
public class PopupDialogItem extends LinearLayout {


private Context mContext;

private TextView mContentView;

private View mLineView;

private String mContent;

public PopupDialogItem(Context context) {
super(context);
mContext = context;
initView();
}

public PopupDialogItem(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initView();
}

private void initView() {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
assert inflater != null;
View view = inflater.inflate(R.layout.item_bottom_popup_dialog, this);
mContentView = view.findViewById(R.id.popup_dialog_item);
mLineView = view.findViewById(R.id.popup_dialog_line);
}

public void refreshData(String text) {
mContentView.setText(text);
mContent = text;
}

public void setLineColor(int color) {
mLineView.setBackgroundResource(color);
}

public void hideLine() {
mLineView.setVisibility(GONE);
}

public String getItemContent() {
return mContent;
}

public void setTextColor(int textColor) {
mContentView.setTextColor(ContextCompat.getColor(mContext, textColor));
}

public void setTextSize(float size) {
mContentView.setTextSize(SP, size);
}

}

MaxHeightScrollView.java

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
public class MaxHeightScrollView extends ScrollView {


public MaxHeightScrollView(Context context) {
super(context);

}

public MaxHeightScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Context context = getContext();
if (null != context) {
int screenHeight = getScreenHeight(context);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(screenHeight * 2 / 3, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

/**
* 获取屏幕高度
*/
private int getScreenHeight(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.heightPixels;
}


}

重写onMeasure方法,将高度设置为屏幕高度的 2 / 3。

res

bottom_popup_dialog.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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/transparent"
android:gravity="bottom">

<com.nhtzj.learn.widget.bottomsheet.MaxHeightScrollView
android:id="@+id/sl_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@drawable/bg_dialog"
android:padding="2dp">

<LinearLayout
android:id="@+id/pop_dialog_content_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_dialog"
android:orientation="vertical">

</LinearLayout>
</com.nhtzj.learn.widget.bottomsheet.MaxHeightScrollView>

<TextView
android:id="@+id/cancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/sl_root"
android:layout_marginBottom="17dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="14dp"
android:background="@drawable/bg_dialog"
android:gravity="center"
android:minHeight="55dp"
android:padding="10dp"
android:text="@string/cancel"
android:textColor="@color/text_green"
android:textSize="18sp" />


</RelativeLayout>

####item_bottom_popup_dialog.xml(item布局)

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
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:orientation="vertical">


<TextView
android:id="@+id/popup_dialog_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="56dp"
android:textColor="@color/text_green"
android:textSize="18sp"/>

<View
android:id="@+id/popup_dialog_line"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@color/line_color" />

</LinearLayout>

bg_dialog.xml (背景)

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="@android:color/white" />

</shape>

colors.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="text_green">#1FC88B</color>
<color name="text_black">#333333</color>
<color name="text_gray">#999999</color>

<color name="line_color">#EDEDED</color>
<color name="transparent_70">#70000000</color>
</resources>

arrays.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="array_transfer_manager">
<item></item>
<item>设为管理员</item>
<item>删除</item>
</string-array>
</resources>

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new BottomPopUpDialog.Builder()
.setDialogData(getResources().getStringArray(R.array.array_transfer_manager))
.setCallBackDismiss(true)
.setItemTextColor(0, R.color.text_gray)
.setItemOnListener(new BottomPopUpDialog.BottomPopDialogOnClickListener() {
@Override
public void onDialogClick(String tag) {
//TODO 按钮点击的回调,通过tag(按钮文本)区分
}

@Override
public void onCancleClick() {
//TODO 取消按钮点击的回调
}
})
.show(getSupportFragmentManager(), BottomPopUpDialog.class.getSimpleName());

参考

坚持原创技术分享,您的支持是对我最大的鼓励!