BottomSheetDialog/BottomSheetDialogFragment——该使用哪个,如何使用?

33
我正在开发一个Material Design应用程序。我想要实现的一个功能是一些形式的投票。当用户单击列表中的某个元素时,将显示一个类似于这样的持久底部表单对话框:

Persistent bottom sheet dialog with question and three buttons.

然后,当用户单击任何一个按钮时,该对话框应该消失,并且模态底部表单对话框应该显示,为用户提供有关最初单击的列表项的更多信息。 它看起来像这样:

A modal bottom sheet dialog which shows up after one of the buttons is clicked

我找不到关于BottomSheetDialog和BottomSheetDialogFragment的清晰解释,以及如何正确使用它们的方法,即使在阅读了一些关于AppCompat对话框的信息之后也是如此。 因此,我的问题是:

  1. 它们有什么不同,每种情况下我应该使用哪一个?
  2. 如何在活动中获取有关对话框中按下的按钮的数据?
  3. 有没有关于使用它们的实现代码或教程的链接?
2个回答

20

最终,我找到了解决方案并且它起作用了。如果我做错了什么,请告诉我。它基本上像这个指南中的DialogFragment一样工作,但是我做了一些不同的事情。

1)它们的区别与DialogFragment和Dialog的区别相同,它们都是模态的。如果您需要持久对话框,请使用BottomSheetBehaviour(我发现在我的应用程序中两个对话框都必须是模态的)。

2)我必须先用一些代码回答第三个问题,然后回答第二个问题就容易了。

3)创建一个新的public class,它extends BottomSheetDialogFragment,我称其为FragmentRandomEventPoll。这里必须实现两件事。

  • Override method onCreateView. It is nearly the same as onCreate method in Activities, except for that it returns the View it should inflate:

    // We will need it later
    private static String headerItem;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                 Bundle savedInstanceState) {
    
    View v = inflater.inflate(R.layout.fragment_random_event_poll, container, false);
        header = (TextView) v.findViewById(R.id.uRnd_fragment_bottom_sheet_poll_header);
        skip = (Button) v.findViewById(R.id.uRnd_fragment_bottom_sheet_button_skip);
        header.setText(...);
    
        // I implemented View.OnClickListener interface in my class
        skip.setOnClickListener(this);
        return v;
    }
    
  • Static method which you can pass necessary data to and get new instance of this class (Probably I could have just used a regular constructor, I'll have to experiment with it a bit more). URandomEventListItem is the data model class.

    public static FragmentRandomEventPoll newInstance(URandomEventListItem item) {
        FragmentRandomEventPoll fragment = new FragmentRandomEventPoll();
        headerItem = item.getHeader();
        return fragment;
    } 
    

2) 要在activity或其他任何位置获取输入事件,请定义一个带有必要方法的接口并创建其实例的setter方法:

private PollButtonClickListener listener;

public void setListener(PollButtonClickListener listener) {
    this.listener = listener;
}

public interface PollButtonClickListener {
    void onAnyButtonClick(Object data)
}

在您想获取数据的位置(布局中指定了“dialog_event_poll”标签):

FragmentRandomEventPoll poll = FragmentRandomEventPoll.newInstance(events.get(id));
poll.setListener(new FragmentRandomEventPoll.PollButtonClickListener() {
        @Override
        public void onAnyButtonClick(Object data) {
            // Do what you want with your data
        }
    });
    poll.show(getSupportFragmentManager(), "dialog_event_poll");
}

如果有任何不清楚的地方,我的项目文件可以在 Github 上找到:Github

1
不要像这样设置监听器(如果活动重新创建,我认为它将被取消设置),你应该使用回调模式(更多信息请参见此Android Developers页面),这样可以避免此问题。其他方面都很棒。它帮助我实现了底部对话框。谢谢 :) - Sufian

2

关于处理来自DialogFragment/BottomSheetDialogFragment的事件。

对于拥有许多活动的应用程序,这种方法非常好:

context as MyDialogFragmentListener

但是我遇到了一个单个活动和多个片段的应用程序问题。由于可能有很多片段,将所有事件通过主要活动传递到必要的片段似乎是一个非常糟糕的选择。因此,我决定采取以下方法:

  private inline fun <reified T> findListeners(): ArrayList<T> {
    val listeners = ArrayList<T>()
    context?.let {
        if (it is T) listeners.add(it)
        if (it is AppCompatActivity) {
            it.supportFragmentManager.fragments.forEach { fragment ->
                if (fragment is T) listeners.add(fragment)
                fragment.childFragmentManager.fragments.forEach { childFragment ->
                    if (childFragment is T) listeners.add(childFragment)
                }
            }
        }
    }
    return listeners
}

在DialogFragment中的代码:

  private val listeners by lazy { findListeners<MyDialogFragmentListener>() }

当然,片段可以包含任意多的其他片段,并且可能需要通过递归进行检查,但在我的情况下这是多余的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接