概述
日常app开发中,对话框无疑是交互非常方便的载体,无论是app更新提示对话框还是评论列表对话框,可以减少不必要的页面跳转。今天我们的主题是Design Support Library中新增的控件BottomSheetDialog,顾名思义,就是底部弹出,下滑关闭的对话框。
没图没真香,我们先来看看效果图:
效果图
网易云效果
demo效果(丑了点,见谅)
基本使用
1、导入依赖
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
2、编写代码
BottomSheetDialog需要导入support:design包,我们创建TestBottomSheetDialogFragment.class继承DialogFragment
public class TestBottomSheetDialogFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
//返回BottomSheetDialog的实例
return new BottomSheetDialog(this.getContext());
}
@Override
public void onStart() {
super.onStart();
BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
//注意控件id别搞错
FrameLayout bottomSheet =
dialog.getDelegate().findViewById(android.support.design.R.id.design_bottom_sheet);
if (bottomSheet != null) {
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) bottomSheet.getLayoutParams();
//设置弹窗最大高度
layoutParams.height = getPeekHeight();
bottomSheet.setLayoutParams(layoutParams);
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
//peekHeight即弹窗的最大高度
behavior.setPeekHeight(getPeekHeight());
// 初始为展开状态
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
/**
* 弹窗高度,默认为屏幕高度的四分之三
* 子类可重写该方法返回peekHeight
*
* @return height
*/
protected int getPeekHeight() {
int peekHeight = getResources().getDisplayMetrics().heightPixels;
//设置弹窗高度为屏幕高度的3/4
return peekHeight - peekHeight / 3;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_bottom_sheet,container,false);
RecyclerView recyclerView = view.findViewById(R.id.rv);
RvAdapter adapter = new RvAdapter(getData());
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new DividerItemDecoration(getContext()
,LinearLayoutManager.VERTICAL));
return view;
}
private List<String> getData(){
List<String> list = new ArrayList<>();
for (int i=0;i<20;i++){
list.add("this is item in "+i);
}
return list;
}
}
对应的dialog_bottom_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<TextView
android:id="@+id/tv_comment_num"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:gravity="center"
android:text="20首歌"
android:textColor="#333333"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<View
android:id="@+id/view_line"
android:layout_width="0dp"
android:layout_height="1dp"
android:background="@color/design_default_color_primary_dark"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_comment_num"/>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:src="@android:drawable/ic_menu_close_clear_cancel"
app:layout_constraintBottom_toBottomOf="@+id/tv_comment_num"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_comment_num"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_line"/>
</android.support.constraint.ConstraintLayout>
列表adapter
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.RvViewHolder> {
private List<String> mList;
public RvAdapter(List<String> list) {
mList = list;
}
@NonNull
@Override
public RvViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
View itemView = LayoutInflater.from(viewGroup.getContext())
.inflate(android.R.layout.simple_list_item_activated_1,viewGroup,false);
return new RvViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull RvViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList.size();
}
class RvViewHolder extends RecyclerView.ViewHolder{
TextView mTextView;
RvViewHolder(@NonNull View itemView) {
super(itemView);
mTextView = itemView.findViewById(android.R.id.text1);
}
}
}
抽取共同代码
上面的TestBottomSheetDialogFragment代码量还是有点多的,我们可以新建BaseBottomSheetDialogFragment,抽取共同的代码
public abstract class BaseBottomSheetDialogFragment extends DialogFragment {
protected View mRootView;
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
return new BottomSheetDialog(this.getContext());
}
@Override
public void onStart() {
super.onStart();
BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
FrameLayout bottomSheet =
dialog.getDelegate().findViewById(android.support.design.R.id.design_bottom_sheet);
if (bottomSheet != null) {
CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) bottomSheet.getLayoutParams();
layoutParams.height = getPeekHeight();
bottomSheet.setLayoutParams(layoutParams);
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setPeekHeight(getPeekHeight());
// 初始为展开状态
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
}
/**
* 弹窗高度,默认为屏幕高度的四分之三
* 子类可重写该方法返回peekHeight
*
* @return height
*/
protected int getPeekHeight() {
int peekHeight = getResources().getDisplayMetrics().heightPixels;
return peekHeight - peekHeight / 3;
}
/**
* 返回布局 resId
*
* @return layoutId
*/
protected abstract int getLayoutRes();
/**
* 初始化数据
*
* @param savedInstanceState bundle
*/
protected abstract void initData(Bundle savedInstanceState);
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(getLayoutRes(), container, false);
return mRootView;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initData(savedInstanceState);
}
}
然后TestBottomSheetDialogFragment继承BaseBottomSheetDialogFragment
public class TestBottomSheetDialogFragment extends BaseBottomSheetDialogFragment {
@Override
protected int getLayoutRes() {
return R.layout.dialog_bottom_sheet;
}
@Override
protected void initData(Bundle savedInstanceState) {
RecyclerView recyclerView = mRootView.findViewById(R.id.rv);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
LinearLayoutManager.VERTICAL));
RvAdapter adapter = new RvAdapter(getData());
recyclerView.setAdapter(adapter);
}
private List<String> getData(){
List<String> list = new ArrayList<>();
for (int i=0;i<20;i++){
list.add("this is item in "+i);
}
return list;
}
}
子类代码量是不是瞬间少了很多,我们来运行下看看效果
OK,至此实现了网易云的弹窗效果!
总结
1、BottomSheetDialog默认弹窗高度是覆盖整个屏幕的,我们在behavior.setPeekHeight(getPeekHeight());中指定弹窗高度为getPeekHeight(),具体看参考BottomSheetBehavior源码;
2、Google官网建议开发者使用DialogFragment,我们直接继承DialogFragment,然后返回BottomSheetDialog的实例,再指定弹窗的最大高度就OK。