项目中经常会遇到底部弹窗,例如分享弹窗等,今天就来把底部弹窗实现方案总结一下。
底部表单样式的对话框基类。依赖于Behavior机制。
dependencies {
implementation 'com.google.android.material:material:1.4.0'
}
public abstract class BaseBottomSheetDialog extends BottomSheetDialog {
public BaseBottomSheetDialog(@NonNull Context context) {
super(context, R.style.Theme_BottomSheetDialog_Base);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
initView();
}
@Override
protected void onStart() {
super.onStart();
changePeekHeight();
Window window = getWindow();
if (window != null) {
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, getPeekHeight());
window.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
}
}
private void changePeekHeight() {
final int peekHeight = getPeekHeight();
if (peekHeight > 0) {
View bottomSheet = findViewById(com.google.android.material.R.id.design_bottom_sheet);
BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheet);
// 设置是否可拖拽
behavior.setDraggable(isDraggable());
behavior.setPeekHeight(peekHeight);
// 解决平板上宽度无法填满问题
behavior.setMaxWidth(Resources.getSystem().getDisplayMetrics().widthPixels);
ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
if (isFixHeight()) {
layoutParams.height = peekHeight;
}
bottomSheet.setLayoutParams(layoutParams);
}
}
protected boolean isDraggable() {
return true;
}
protected boolean isFixHeight() {
return true;
}
protected abstract int getLayoutResId();
protected abstract void initView();
protected int getPeekHeight() {
return WindowManager.LayoutParams.WRAP_CONTENT;
}
}
为了去掉BottomSheetDialog默认的背景,需要修改主题样式:
<style name="Theme.BottomSheetDialog.Base" parent="Theme.Design.Light.BottomSheetDialog">
- "bottomSheetStyle"
>@style/BottomSheetDialogStyle
style>
<style name="BottomSheetDialogStyle" parent="Widget.Design.BottomSheet.Modal">
- "android:background"
>@android:color/transparent
style>
除此之外,如果需要对生命周期进行感知或者自动恢复,还可以使用BottomSheetDialogFragment作为继承基类。
小技巧:如果设置了PeekHeight以后需要设置最大可展开高度,只需要在根布局设置android:maxHeight属性即可
继承Dialog实现自定义弹窗。
public abstract class BaseBottomDialog extends AppCompatDialog {
public BaseBottomDialog(Context context) {
super(context, R.style.Theme_BottomDialog_Base);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResId());
initView();
}
@Override
protected void onStart() {
super.onStart();
Window window = getWindow();
if (window != null) {
window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, getPeekHeight());
window.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
}
}
protected abstract int getLayoutResId();
protected abstract void initView();
protected int getPeekHeight() {
return WindowManager.LayoutParams.WRAP_CONTENT;
}
}
为了去掉弹窗自带的背景,需要自定义主题样式:
<style name="Theme.BottomDialog.Base" parent="android:style/Theme.Dialog">
- "android:windowBackground"
>@android:color/transparent
- "android:windowNoTitle"
>true
- "android:windowIsFloating">true
- "android:windowContentOverlay">@null
- "android:windowAnimationStyle">@style/Animation.BottomDialog
style>
<style name="Animation.BottomDialog" parent="Animation.AppCompat.Dialog">
- "android:windowEnterAnimation"
>@anim/design_bottom_sheet_slide_in
- "android:windowExitAnimation"
>@anim/design_bottom_sheet_slide_out
style>
其中的弹窗动画样式为:
design_bottom_sheet_slide_in
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/bottom_sheet_slide_duration"
android:interpolator="@android:anim/accelerate_decelerate_interpolator">
<translate
android:fromYDelta="20%p"
android:toYDelta="0"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"/>
set>
design_bottom_sheet_slide_out
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@integer/bottom_sheet_slide_duration"
android:interpolator="@android:anim/accelerate_interpolator">
<translate
android:fromYDelta="0"
android:toYDelta="20%p"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"/>
set>
通过WindowManager添加视图到窗口的方式。
public abstract class BaseBottomPanel {
protected Context context;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mParams;
private boolean mShowing = false;
private View mView;
public BaseBottomPanel(Context context) {
this.context = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
public void show() {
if (!mShowing) {
inflate();
try {
mWindowManager.addView(mView, mParams);
mShowing = true;
} catch (Exception e) {
mShowing = false;
return;
}
}
initView();
}
public void dismiss() {
if (mShowing) {
try {
mWindowManager.removeView(mView);
mShowing = false;
} catch (Exception e) {
mShowing = true;
}
}
}
private void inflate() {
mView = LayoutInflater.from(context).inflate(getLayoutResId(), null);
mParams = new WindowManager.LayoutParams();
mParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
mParams.format = PixelFormat.TRANSLUCENT;
mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mParams.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
mParams.windowAnimations = R.style.Animation_BottomDialog;
mParams.width = WindowManager.LayoutParams.MATCH_PARENT;
mParams.height = getPeekHeight();
mParams.dimAmount = 0.6f;
}
public Context getContext() {
return context;
}
protected <T extends View> T findViewById(@IdRes int id) {
if (mView == null) {
return null;
}
return mView.findViewById(id);
}
protected abstract int getLayoutResId();
protected abstract void initView();
protected int getPeekHeight() {
return WindowManager.LayoutParams.WRAP_CONTENT;
}
}
窗口动画为:
<style name="Animation.BottomDialog" parent="Animation.AppCompat.Dialog">
- "android:windowEnterAnimation"
>@anim/design_bottom_sheet_slide_in
- "android:windowExitAnimation"
>@anim/design_bottom_sheet_slide_out
style>
通过PopWindow指定Gravity为Bottom来实现底部弹窗、
PopupWindow popupWindow = new PopupWindow();
View view = LayoutInflater.from(this).inflate(R.layout.dialog_share, null);
popupWindow.setContentView(view);
popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popupWindow.showAtLocation(findViewById(android.R.id.content), Gravity.BOTTOM, 0, 0);
以上只是个人总结的部分基类封装或实现,有需要的可以自行拷贝并进行修改以契合个人需求,同时也希望能给个赞,谢谢。
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!