美文网首页support design
Bottom Sheet 及相关知识

Bottom Sheet 及相关知识

作者: lijiankun24 | 来源:发表于2017-06-11 11:24 被阅读132次

本文主要介绍 Bottom Sheet 的相关知识,包括 BottomSheetBehavior、BottomSheetDialog 和 BottomSheetDialogFragment,并简单写了一个工程 BottomSheetPractice,里面包括一些适合用 Bottom Sheet 实现的功能效果。

BottomSheetPractice 效果图如下所示:

bottomsheet.gif

实现上面这种效果除了使用 BottomSheet 以外,还可以使用 PopupWindow 和 DialogFragment,由于 Fragment 一直是 Google 比较推崇的,所以也会讲一下使用 DialogFragment 实现上面效果的代码,使用 PopupWindow 实现上面效果的代码我就不多加介绍,关于 PopupWindow 的资料还是比较多的。

关于 Bottom Sheet

Bottom Sheet 是 Design Support Library 23.2 版本引入的一个控件,主要用于实现从底部弹出一个对话框的效果。Bottom Sheet 中默认的内容是隐藏的(部分隐藏/全部隐藏),可以通过代码设置或者相关的手势操作显示 Bottom Sheet 的全部内容。
在 Google 官方推出 Bottom Sheet 之前,在 Github 上面已经有一些开源的库实现类似的效果:

如果使用 Google 官方的 Bottom Sheet 可以实现项目的功能需求,我还是喜欢使用官方的,不是说上面提到的三个开源库不好,而是本人的一个小习惯而已。

添加依赖

compile 'com.android.support:design:25.3.1'

我使用的是 Design Support Library 版本是 25.3.1,但是只要使用比 23.2.0+ 更新版本的 Design Support Library 都是可以的。

BottomSheetBehavior

首先介绍一下 BottomSheetBehaviorBottomSheetBehavior 在百度地图中使用的比较多,如下图所示的效果就可以使用 BottomSheetBehavior 实现,我简单实现了一个类似的效果。

  • 百度地图
  • 自己实现类似的效果
behavior.gif

布局文件

activity_main.xml 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    </android.support.design.widget.AppBarLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="?attr/actionBarSize"
        android:orientation="vertical">

        <TextView
            style="@style/TextViewStyle"
            android:gravity="start|center_vertical"
            android:text="EditText"
            android:textStyle="bold"/>

        <View style="@style/LineStyle"/>

        ......
    </LinearLayout>

    <include
        layout="@layout/bottom_sheet_behavior"/>

</android.support.design.widget.CoordinatorLayout>

bottom_sheet_behavior.xml 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/ll_sheet_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:behavior_hideable="true"
    app:behavior_peekHeight="48dp"
    app:layout_behavior="@string/bottom_sheet_behavior">

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#C7C7C7"/>

    <TextView
        style="@style/TextViewStyle"
        android:gravity="start|center_vertical"
        android:paddingLeft="25dp"
        android:paddingStart="25dp"
        android:text="Introduction"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:paddingEnd="48dp"
        android:paddingLeft="48dp"
        android:paddingRight="48dp"
        android:paddingStart="48dp"
        android:paddingTop="12dp"
        android:paddingBottom="12dp"
        android:text="This is a demo for me to practice Bottom Sheet and I hope this demo could help you,too. ---- lijiankun24"
        android:textColor="@android:color/black"
        android:background="@android:color/holo_blue_light"
        android:textSize="14sp"/>
</LinearLayout>

正如上面两个布局文件所示,若想使用 BottomSheetBehavior 的效果,顶部的布局需要是 CoordinatorLayoutbottom_sheet_behavior.xml 就是底部 Bottom Sheet 的布局文件,其中可以是自己定义的任意布局效果,但是在 bottom_sheet_behavior.xml 中的根布局需要使用三个以 app 开头的属性。

<LinearLayout
    ...
    app:behavior_hideable="true"
    app:behavior_peekHeight="48dp"
    app:layout_behavior="@string/bottom_sheet_behavior">

    ...
</LinearLayout>
  • app:layout_behavior="@string/bottom_sheet_behavior": 这个属性相当于告诉 CoordinatorLayout: 这是一个 Bottom Sheet 的布局
  • app:behavior_hideable="true": 表示可以让 Bottom Sheet 完全隐藏,默认为 false
  • app:behavior_peekHeight="60dp": 表示 Bottom Sheet 处于隐藏状态时,显示的高度,默认是 0。

像上面一样写好布局文件以后,Bottom Sheet 的布局文件就已经完成,但是还需要使用 Java 代码控制它。

Java 控制代码

MainActivity 中添加相应的逻辑控制代码,即可完成如 BottomSheetPractice 所示的效果。Bottom Sheet 有 5 种状态:

  • STATE_DRAGGING: 表示用户正在向上或者向下拖动 Bottom Sheet
  • STATE_SETTLING: 视图从脱离手指自由滑动到最终停下的这一小段时间
  • STATE_EXPANDED: 表示视图处于完全展开的状态
  • STATE_COLLAPSED: 表示视图处于默认的折叠状体
  • STATE_HIDDEN: 表示视图被隐藏
private void toggleBottomSheet() {
    // 得到 Bottom Sheet 的视图对象,即被使用 `app:layout_behavior` 的视图
    View view = findViewById(R.id.ll_sheet_root);
    // 得到 Bottom Sheet 的视图对象所对应的 BottomSheetBehavior 对象
    final BottomSheetBehavior behavior = BottomSheetBehavior.from(view);

    findViewById(R.id.btn_bottom_sheet).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
              if (behavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                  behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
              } else {
                  behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
              }
            }
        });
}

如上面代码所示:

  • 可以通过 BottomSheetBehavior 对象的 setState(int) 方法设置 Bottom Sheet 对象处于展开还是折叠的状态
  • 也可以通过getState() 方法得到 Bottom Sheet 对象目前处于什么状态
  • 还可以通过 setBottomSheetCallback(BottomSheetCallback) 方法设置监听 Bottom Sheet 状态的变化情况,因为 Bottom Sheet 对象的状态还可以通过手势改变。

BottomSheetDialog

一个使用 BottomSheetDialog 实现的效果如下所示:

dialog.gif

布局文件

布局文件是fragment_custom_bottom_sheet.xml,里面代码很普通,没有什么特别的。

?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_fragment_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <View
        android:id="@+id/view_blank"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="170dp"
            android:background="#F6F6F6"
            android:orientation="vertical">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="52dp"
                android:layout_marginEnd="11dp"
                android:layout_marginLeft="11dp"
                android:layout_marginRight="11dp"
                android:layout_marginStart="11dp">

                <TextView
                    android:id="@+id/tv_cancel"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentStart="true"
                    android:gravity="center_vertical"
                    android:paddingEnd="11dp"
                    android:paddingLeft="11dp"
                    android:paddingRight="11dp"
                    android:paddingStart="11dp"
                    android:text="取消"
                    android:textColor="#36404A"
                    android:textSize="16sp"/>

                <TextView
                    android:id="@+id/tv_send"
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentRight="true"
                    android:gravity="center_vertical"
                    android:paddingEnd="11dp"
                    android:paddingLeft="11dp"
                    android:paddingRight="11dp"
                    android:paddingStart="11dp"
                    android:text="发送"
                    android:textColor="#C7C7C7"
                    android:textSize="16sp"/>
            </RelativeLayout>

            <EditText
                android:id="@+id/et_input_msg"
                android:layout_width="match_parent"
                android:layout_height="107dp"
                android:layout_marginBottom="11dp"
                android:layout_marginEnd="11dp"
                android:layout_marginLeft="11dp"
                android:layout_marginRight="11dp"
                android:layout_marginStart="11dp"
                android:gravity="top|start"
                android:hint="输入评价"
                android:inputType="textMultiLine"
                android:minLines="3"
                android:padding="12dp"
                android:scrollHorizontally="false"
                android:textColor="#36404A"
                android:textColorHint="#C7C7C7"
                android:textSize="17sp"/>
        </LinearLayout>
    </ScrollView>
</LinearLayout>

Java 控制代码

代码中的注释已经写的比较清楚,相信各位都可以看懂。

 private void showBottomSheetDialog() {
        // 创建 BottomSheetDialog 对象
        BottomSheetDialog mDialog = new BottomSheetDialog(MainActivity.this);
        // 通过 inflate 的方式得到 BottomSheetDialog 的布局 View
        View view = LayoutInflater
                .from(MainActivity.this)
                .inflate(R.layout.fragment_custom_bottom_sheet, null);

        view.findViewById(R.id.tv_cancel).setOnClickListener(this);
        view.findViewById(R.id.tv_send).setOnClickListener(this);
        view.findViewById(R.id.view_blank).setOnClickListener(this);
        mEditText = (EditText) view.findViewById(R.id.et_input_msg);
        final TextView tvSend = (TextView) view.findViewById(R.id.tv_send);

        // 为 BottomSheetDialog 设置 ContentView 并显示 BottomSheetDialog
        mDialog.setContentView(view);
        mDialog.show();

        // 得到 BottomSheetBehavior 对象并设置其显示高度
        View bottomSheet = mDialog.findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        //getHeight(Context) 方法为得到手机屏幕整体的高度,并将其设置给 BottomSheetBehavior 的 peekHeight。
        behavior.setPeekHeight(Utils.getHeight(MainActivity.this));

        //设置整个 Bottom Sheet 背景为透明,否则会是白色的,参考:https://stackoverflow.com/questions/37104960/bottomsheetdialog-with-transparent-background
        ((View) view.getParent()).setBackgroundColor(ContextCompat
                .getColor(MainActivity.this, android.R.color.transparent));

        // 显示软键盘
        Utils.toggleSoftInput(MainActivity.this, mEditText);

        // 为 EditText 设置监听器,修改一些 tvSend 文字的颜色
        mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                if (s.toString().length() != 0) {
                    tvSend.setTextColor(ContextCompat.getColor(MainActivity.this,
                            R.color.colorPrimary));
                } else {
                    tvSend.setTextColor(Color.parseColor("#C7C7C7"));
                }
            }
        });
    }

    // 得到手机屏幕的高度,返回值是像素值
    public static int getHeight(Context context) {
          WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
          Display display = wm.getDefaultDisplay();
          DisplayMetrics metrics = new DisplayMetrics();
          display.getMetrics(metrics);

          return metrics.heightPixels;
    }

    // 显示软键盘
    public static void toggleSoftInput(Context context, final EditText editText) {
        if (context == null || editText == null) {
            return;
        }
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                InputMethodManager inputManager = (InputMethodManager) editText
                        .getContext()
                        .getSystemService(Context.INPUT_METHOD_SERVICE);

                inputManager.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
            }
        }, 250);
    }

BottomSheetDialogFragment

一个使用 BottomSheetDialogFragment 实现的效果如下所示:

dialogfragment.gif

这小节讲一下使用 BottomSheetDialogFragment 实现分享界面的代码。

布局文件

fragment_share.xml,可以看到在顶部布局 LinearLayout 使用的 Bottom Sheet 相关的三个属性:app:behavior_hideableapp:behavior_peekHeightapp:layout_behavior="@string/bottom_sheet_behavior"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/ll_sheet_root"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:behavior_hideable="true"
    app:behavior_peekHeight="0dp"
    app:layout_behavior="@string/bottom_sheet_behavior">

    <View
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="#C7C7C7"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_sheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:paddingTop="24dp"/>
</LinearLayout>

可以看到在 fragment_share.xml 使用了 RecyclerView,它的 Item 的布局文件是
bottom_sheet_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingBottom="24dp">

    <ImageView
        android:id="@+id/iv_sheet_item"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:contentDescription="@null"/>

    <TextView
        android:id="@+id/tv_sheet_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

Java 控制代码

CustomShareBottomSheetDialogFragment 是 BottomSheetDialogFragment 的子类,代码如下所示:

public class CustomShareBottomSheetDialogFragment extends BottomSheetDialogFragment {

    @Override
    public void setupDialog(Dialog dialog, int style) {
        initView(dialog);
    }

    private void initView(Dialog dialog) {
        if (dialog == null || !isAdded()) {
            return;
        }

        View view = LayoutInflater
                .from(getContext())
                .inflate(R.layout.fragment_share, null);
        dialog.setContentView(view);

        View bottomSheet = dialog.findViewById(R.id.design_bottom_sheet);
        BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setPeekHeight(Utils.getHeight(getContext()));

        // 设置 BottomSheet DialogFragment 的背景为透明的,否则为白素,参考: https://stackoverflow.com/questions/37104960/bottomsheetdialog-with-transparent-background
        LinearLayout layout = (LinearLayout) view.findViewById(R.id.ll_sheet_root);
        ((View) layout.getParent())
                .setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));

        int[] mIcons = {R.drawable.ic_share_wechat,
                R.drawable.ic_share_wechat_timeline,
                R.drawable.ic_share_sina_weibo,
                R.drawable.ic_share_tencent_qq,
                R.drawable.ic_share_twitter,
                R.drawable.ic_share_pocket,
                R.drawable.ic_share_evernote,
                R.drawable.ic_share_instapaper};
        String[] mTitles = {"微信好友",
                "朋友圈",
                "新浪微博",
                "QQ",
                "Twitter",
                "Pocket",
                "印象笔记",
                "Instapaper"
        };

        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.rv_sheet);
        CustomSheetAdapter sheetAdapter = new CustomSheetAdapter(mTitles, mIcons, getContext());
        recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3));
        recyclerView.setAdapter(sheetAdapter);
    }


    static class CustomSheetAdapter extends RecyclerView.Adapter<CustomSheetAdapter.SheetViewHolder> {

        private String[] mTitles = null;

        private int[] mIcons = null;

        private Context mContext = null;

        private LayoutInflater mInflater = null;

        CustomSheetAdapter(String[] titles, int[] icons, Context context) {
            mTitles = titles;
            mIcons = icons;
            mContext = context;
            mInflater = LayoutInflater.from(mContext);
        }

        @Override
        public SheetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new SheetViewHolder(mInflater.inflate(R.layout.bottom_sheet_item, parent, false));
        }

        @Override
        public void onBindViewHolder(SheetViewHolder holder, int position) {
            holder.getTVTitle().setText(mTitles[position]);
            holder.getIVIcon().setImageResource(mIcons[position]);
        }

        @Override
        public int getItemCount() {
            return mTitles == null ? 0 : mTitles.length;
        }

        class SheetViewHolder extends RecyclerView.ViewHolder {
            private TextView mTVTitle = null;

            private ImageView mIVIcon = null;


            SheetViewHolder(View itemView) {
                super(itemView);
                mTVTitle = (TextView) itemView.findViewById(R.id.tv_sheet_item);
                mIVIcon = (ImageView) itemView.findViewById(R.id.iv_sheet_item);
            }

            TextView getTVTitle() {
                return mTVTitle;
            }

            ImageView getIVIcon() {
                return mIVIcon;
            }
        }
    }
}      

MainActivity.java 中相关的代码部分,如下所示:

private void showShareBSDialogFragment(){
    FragmentManager fragmentManager = getSupportFragmentManager();
    CustomShareBottomSheetDialogFragment fragment =
            (CustomShareBottomSheetDialogFragment) fragmentManager
                    .findFragmentByTag(TAG_SHARE_BS_DIALOG_FRAGMENT);

    if (fragment == null) {
        fragment = new CustomShareBottomSheetDialogFragment();
    }
    fragment.show(fragmentManager, TAG_SHARE_BS_DIALOG_FRAGMENT);
}

DialogFragment

DialogFragment 和 Bottom Sheet 其实是没有多大关系的,如果非要扯点关系的话,那只能是 BottomSheetDialogFragment 是 DialogFragment 的子类,但是这里我还是想介绍一下 DialogFragment 的相关知识。用 DialogFragment 也可以实现和 BottomSheetDialogFragment 一样的效果。如下所示:

dialogfragment1.gif

如上图所示,使用 DialogFragment 和 BottomSheetDialogFragment 实现 输入评价 的功能,从效果上来看其实并没有多大的区别。

布局文件

布局文件使用的还是 fragment_custom_bottom_sheet.xml,这里我就不重复粘贴了。

Java控制代码

CustomDialogFragment 是 DialogFragment 的子类,实现输入评价的功能。

public class CustomDialogFragment extends DialogFragment implements View.OnClickListener {

    private OnSendCommentClickListener mCommentClickListener = null;

    private EditText mEditText = null;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnSendCommentClickListener) {
            mCommentClickListener = (OnSendCommentClickListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnSendCommentClickListener");
        }
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

      // 使用不带Theme的构造器, 获得的dialog边框距离屏幕仍有几毫米的缝隙。
         Dialog mDialog = new Dialog(getContext(), R.style.BottomDialog);
         // 在 setContentView 方法前设置
         mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);              
         LayoutInflater mInflater = LayoutInflater.from(getContext());
         View view = mInflater.inflate(R.layout.fragment_dialog_custom, null);
         initView(view);
         mDialog.setContentView(view);
         // 外部点击取消
         mDialog.setCanceledOnTouchOutside(true);

         // 设置宽度为屏宽, 靠近屏幕底部
         Window window = mDialog.getWindow();                                
         if (window != null) {
             WindowManager.LayoutParams lp = window.getAttributes();
             // 紧贴底部
             lp.gravity = Gravity.BOTTOM;
             // 宽度为 MATCH_PARENT
             lp.width = WindowManager.LayoutParams.MATCH_PARENT;
             // 高度为 WRAP_CONTENT
             lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
             // 背景为透明的
             window.setBackgroundDrawableResource(android.R.color.transparent);
             window.setAttributes(lp);
             // 弹出软键盘
             window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
         }
         return mDialog;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCommentClickListener = null;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.view_blank:
            case R.id.tv_cancel:
                dismiss();
                break;
            case R.id.tv_send:
                sendComment();
                break;
        }
    }

    private void sendComment() {
        String comment = mEditText.getText().toString();
        if (!isAdded() || mCommentClickListener == null || TextUtils.isEmpty(comment)) {
            return;
        }
        mCommentClickListener.onClick(comment);
        dismiss();
    }

    private void initView(View view) {
        view.findViewById(R.id.view_blank).setOnClickListener(this);
        view.findViewById(R.id.tv_cancel).setOnClickListener(this);
        view.findViewById(R.id.tv_send).setOnClickListener(this);

        final TextView textView = (TextView) view.findViewById(R.id.tv_send);
        mEditText = (EditText) view.findViewById(R.id.et_input_msg);
        mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                if (s.toString().length() != 0) {
                    textView.setTextColor(ContextCompat.getColor(getContext(),
                            R.color.colorPrimary));
                } else {
                    textView.setTextColor(Color.parseColor("#C7C7C7"));
                }
            }
        });
    }
}

MainActivity.java 中的关键代码如下所示:

    private void showDialogFragment() {
       FragmentManager fragmentManager = getSupportFragmentManager();
       CustomDialogFragment dialogFragment = (CustomDialogFragment) fragmentManager
               .findFragmentByTag(TAG_DIALOG_FRAGMENT);
       if (dialogFragment == null) {
           dialogFragment = new CustomDialogFragment();
       }

       Dialog dialog = dialogFragment.getDialog();
       if (dialog == null || !dialog.isShowing()) {
           dialogFragment.show(fragmentManager, TAG_DIALOG_FRAGMENT);
       }
   }

需要注意的是:MainActivity 需要实现自定的 OnSendCommentClickListener 接口,用于 CustomDialogFragmentMainActivity 传递数据。

引申

若是在 Activity 中新建 DialogFragment 并显示的话,可以通过 interface 的方法,从 DialogFragment 给 Activity 传递数据;那么如果是在一个 Fragment 中新建 DialogFragment 并显示的话,通过什么方法可以从 DialogFragment 向 Fragment 传递数据呢?可以通过如下的方式从 DialogFragment 向 Fragment 传递数据:

public class ParentFragment extends Fragment {

    private static final int MSG_DIALOG_REQUEST_CODE = 101;

    public ParentFragment() {
    }

    public static ParentFragment newInstance() {
        ParentFragment fragment = new ParentFragment();
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    public void showChildeDialogFragment() {

        FragmentManager fragmentManager = getChildFragmentManager();
        ChildeDialogFragment dialogFragment = (ChildeDialogFragment) fragmentManager.findFragmentByTag(TAG_ET);
        if (dialogFragment == null) {
            dialogFragment = ChildeDialogFragment
                  .newInstance(ParentFragment.this, MSG_DIALOG_REQUEST_CODE);
        }

        Dialog dialog = dialogFragment.getDialog();
        if (dialog == null || !dialog.isShowing()) {
            dialogFragment.show(fragmentManager, TAG_ET);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case MSG_DIALOG_REQUEST_CODE:
                    String msg = data.getStringExtra(ChildeDialogFragment.EXTRA_MSG);
                    // msg 即是从 DialogFragment 传递到 Fragment 中的参数
                    break;
            }
        }
    }

}
public class ChildeDialogFragment extends DialogFragment {

    public static final String EXTRA_MSG = "extra";

    public static EditTextDialogFragment newInstance(Fragment target, int requestCode) {
        EditTextDialogFragment fragment = new EditTextDialogFragment();
        fragment.setTargetFragment(target, requestCode);
        return fragment;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // 使用不带Theme的构造器, 获得的dialog边框距离屏幕仍有几毫米的缝隙。
        Dialog mDialog = new Dialog(getContext(), R.style.BottomDialog);
        // 在 setContentView 方法前设置
        mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);              
        LayoutInflater mInflater = LayoutInflater.from(getContext());
        View view = mInflater.inflate(R.layout.fragment_dialog_custom, null);
        initView(view);
        mDialog.setContentView(view);

        return mDialog;
    }

    @Override
    protected void initView(View view) {
        if (view == null) {
            return;
        }
        // 一些相关的初始化 View 的操作。
        ....
    }

    private void send(String msg) {
        Fragment target = getTargetFragment();
        if (target == null) {
            return;
        }

        Intent data = new Intent();
        data.putExtra(EXTRA_MSG, msg);
        target.onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, data);
        dismiss();
    }
}

如上面代码所示,从 DialogFragment 向 Fragment 传递数据,需要使用 Intent 和 Fragment 的 onActivityResult(int requestCode, int resultCode, Intent data) 方法,在 Fragment 中重写 onActivityResult(int requestCode, int resultCode, Intent data) 方法,并通过 requestCoderesultCodeIntent 即可获取到 DialogFragment 传递多来的数据了。

关于 Bottom Sheet 的 Material Design 设计

官方的关于 Bottom Sheet 的 Material Design 的设计原则:Components– Bottom sheets(需要科学上网)

详细的设计原则在官网上已经有非常详细的说明,但是国内的 APP 设计的五花八门,很少有 APP 的设计遵从 Material Design 设计原则,Material Design 设计的图标和 UI 还是非常美观的,就贴两个关于 Bottom Sheet 的 Material Design 设计效果图吧

BottomSheet1.png BottomSheet2.png

至此,关于 Bottom Sheet 的文章到此结束,如果有什么问题欢迎指出。我的工作邮箱:jiankunli24@gmail.com


参考资料:

使用Bottom Sheet实现底部菜单 -- 猿圆猿

Android 原生BottomSheet 介绍及坑 -- Jinwong

Bottom Sheet使用教程

相关文章

网友评论

    本文标题:Bottom Sheet 及相关知识

    本文链接:https://www.haomeiwen.com/subject/lhdhqxtx.html