【问题标题】:ViewPager in DialogFragment - IllegalStateException: Fragment does not have a viewDialogFragment 中的 ViewPager - IllegalStateException:片段没有视图
【发布时间】:2013-11-30 18:15:08
【问题描述】:

我想要达到的目标

  • 来自FragmentActivity 在单击操作栏中的操作按钮时显示对话框
  • DialogFragment - 没有标题的对话框
  • TabHost - 对话框顶部的选项卡
  • ViewPagerFragmentPagerAdapter - 可滑动,哪些内容连接到选项卡
  • 2-3 对话框按钮(对话框的不同子类,不同的按钮) - 不应该在ViewPagerFragment 之一中,这意味着相同的按钮应该保留在对话框的底部,无论如何Fragment ViewPager 显示的内容。


问题

IllegalStateException: Fragment does not have a view


到目前为止我已经尝试/做了什么

  • android.support.v4 包用于必要的类
  • 调用getChildFragmentManager() 而不是getSupportedFragmentManager()
  • 实施了此链接 https://code.google.com/p/android/issues/detail?id=42601 建议的帖子 #10。我将代码直接复制/粘贴到我的两个 Fragment 类中,ViewPager 应该显示,加上 DialogFragment 类。
  • 在我的自定义DialogFragment 中,我首先尝试覆盖onCreateView,然后是onCreateDialog,然后同时覆盖两者。我必须运行所有这些,但结果出乎意料。
    • onCreateView:无法访问AlertDialog.Builder 来创建所需的按钮,除了对话框的结果很棒。
    • onCreateDialog:上面显示的错误消息。我仍然认为这种方法与我想要达到的目标一样接近。
    • onCreateViewonCreateDialog:在onCreateView 中扩充对话框布局,并将对话框按钮添加到onCreateDialog 中的AlertDialog.Builder。这显示了对话框,但 AlertDialog.Builder 中添加的按钮不可见。另外,当点击 EditText 字段时,键盘没有出现。


源代码

大部分来自 Tutorial to implement the use of TabHost in Android 2.2 + ViewPager and FragmentsActivityFragment 的代码在 DialogFragment 中。但是,我将其 ViewPager 替换为来自此答案 https://stackoverflow.com/a/18167273/2375978 的源代码中的修改版本。这是为了能够在高度上wrap_content

我的项目中的错误代码在DialogFragmentonCreateDialog方法中,我相信。

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), AlertDialog.THEME_HOLO_DARK);
    LayoutInflater inflater = getActivity().getLayoutInflater();
    view = inflater.inflate(R.layout.dialog_test, null);
    addActionButtons(builder, view);    
    builder.setView(view);

    mViewPager = (WrapContentHeightViewPager) view.findViewById(R.id.viewpager);

    initialiseTabHost();

    List<Fragment> fragments = getFragments();

    pageAdapter = new DialogPageAdapter(getChildFragmentManager(), fragments);
    mViewPager.setAdapter(pageAdapter);
    mViewPager.setOnPageChangeListener(this);

    Dialog dialog = builder.create();
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    dialog.show();

    return dialog;
}


堆栈跟踪 LogCat 日志

FATAL EXCEPTION: main
java.lang.IllegalStateException: Fragment does not have a view
    at android.support.v4.app.Fragment$1.findViewById(Fragment.java:1425)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:901)
    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1088)
    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1444)
    at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:461)
    at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:1011)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:880)
    at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1374)
    at my.app.package.name.WrapContentHeightViewPager.onMeasure(WrapContentHeightViewPager.java:31)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:617)
    at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:399)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1396)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5059)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2377)
    at android.view.View.measure(View.java:15481)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1982)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1200)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1398)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1118)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4525)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
    at android.view.Choreographer.doFrame(Choreographer.java:525)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4946)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.jav


还有……

  • 我未能成功尝试 https://code.google.com/p/android/issues/detail?id=42601 中提到的其他可能的解决方案,在帖子 #2 和 #13 中提到,因为我不明白如何以及在哪里可以在我的代码中使用它(我想我'我和写#18的人在同一条船上)。

【问题讨论】:

  • 如果你成功了,请分享你的代码

标签: android android-fragments android-viewpager android-dialogfragment


【解决方案1】:

我想我刚刚遇到了同样的问题,并通过查看DialogFragment 的源代码学到了一些东西。

看起来即使重写 onCreateDialog(...) 是创建自定义对话框的有效方法,它也会导致 DialogFragment 具有空视图,就像错误消息中所说的那样。在大多数情况下这很好 - DialogFragment 不需要 View 来显示 Dialog,但如果你想进一步嵌套 Fragment(就像你做的那样),这不会飞。

考虑到您想与 AlertDialog.Builder 进行交互,我认为确实没有完美的解决方案,但您有几个选择:

  1. 在对话框中创建按​​钮作为视图的一部分(不使用 AlertDialog.Builder)。您可以通过覆盖 onCreateView 而不是 onCreateDialog 来做到这一点。您应该能够通过将按钮放在它们自己的片段中来获得您提到的功能。我们在我的演出中做了类似的事情,我非常喜欢这种方法。
  2. 实现您自己的类型,该类型继承自 Fragment 并以各种方式反映 DialogFragment,但允许您需要的内容除外。这不应该太可怕,因为 DialogFragment 只有 ~400 并且被大量评论。可能会很有趣。
  3. 使用常规 PagerAdapter 而不是 FragmentPagerAdapter。这样一来,您的 DialogFragment 没有 View 就无关紧要了。

【讨论】:

    【解决方案2】:

    使用

    onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    

    而不是 onCreateDialog(Bundle savedInstanceState)。不要创建警报对话框,使用方法提供的充气器,并构建您的视图。它对我有用。

    最好的问候!

    【讨论】:

    • 这确实有效。如果您像我一样覆盖 onCreateDialog 来创建对话框,这将节省您。
    【解决方案3】:

    如果你实现onCreateDialog 来使用AlertDialog,你会在访问getChildFragmentManager 或类似的东西时碰到IllegalStateException: Fragment does not have a view

    要解决此问题,请同时实现onCreateDialogonCreateView,其中onCreateView 返回在onCreateDialog 中膨胀的视图。

    class LocationPickerDialog : DialogFragment() {
    
        lateinit var customView: View
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            return customView
        }
    
        override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
            Log.d(TAG, "onCreateDialog")
            // StackOverflowError
            // customView = layoutInflater.inflate(R.layout.dialog_location_picker, null)
            customView = activity!!.layoutInflater.inflate(R.layout.dialog_location_picker, null)
    
            val builder = AlertDialog.Builder(context!!)
                    .setView(customView)
                    .setPositiveButton(android.R.string.ok) { _, _ ->
                        // do something
                    }
                    .setNegativeButton(android.R.string.cancel) { _, _ ->
                        // do something
                    }
            val dialog = builder.create()
    
            return dialog
        }
    
        override fun onActivityCreated(savedInstanceState: Bundle?) {
            // if onCreateView doesn't return a view 
            // java.lang.IllegalStateException: Fragment does not have a view
            mapFragment = childFragmentManager.findFragmentByTag("map") as SupportMapFragment?
        }
    }
    

    https://code.luasoftware.com/tutorials/android/android-alertdialog-in-dialogfragment-fragment-does-not-have-a-view/

    【讨论】:

    【解决方案4】:

    感谢@Tommy Visic 写了一个非常好的描述它的工作。

    发布对我有用的代码。

    我已经从onCreateDialog 方法中删除了Dialog 构建代码,实际上删除了onCreateDialog 方法和我在Dialog's 自定义视图中实现的对话框视图我已将其作为View 包含在onCreateView 方法和所有的东西都开始工作了。

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.login_sigup_screen, null, false);
        bind = ButterKnife.bind(this, view);
        initViewPager();
        return view;
    }
    

    这个实现面临的另一个问题是:

    Activity 具有Toolbar/ActionBar 时,它也会显示在DialogFragment 中以避免出现以下情况: 实现FragmentonViewCreated方法并添加以下代码

    Dialog dialog = getDialog();
    dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    float dimAmount = 0.6f;
    dialog.getWindow().setDimAmount(dimAmount);
    

    这将从DialogFragment 中删除ToolbarActivity 将按原样显示。

    干杯

    谢谢@汤米

    问候 宙斯

    【讨论】:

      【解决方案5】:

      我遇到了同样的问题并实现了以下解决方案:

      class MyDialogFragment: DialogFragment {
      
        private lateinit var dialog: AlertDialog
        private lateinit var dialogView: View
      
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
          // return the view inflated for your dialog fragment
          return dialogView
        }
      
        // this is called before onCreateView
        override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
          dialogView = LayoutInflater
                  .from(ContextThemeWrapper(requireContext(), R.style.MyAlertDialogStyle))
                  .inflate(R.layout.fragment_dialog, null, false)
          dialog = AlertDialog
                  .Builder(requireContext())
                  .setView(dialogView)
                  .create()
      
          dialog.setOnShowListener {
              childFragmentManager
                        .beginTransaction()
                        .replace(R.id.container, MyNestedFragmentInsideTheDialog())
                        .commit()
              // retrieve dialog buttons if any to manage onClickListener yourself
              // dialog.getButton(AlertDialog.BUTTON_POSITIVE)
          }
        }
      
      }
      

      这个想法是自己膨胀对话框内容并保留对它的引用,以便它可以返回到 onCreateView 中,然后由 childfragmentManager 使用;-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-22
        • 1970-01-01
        • 2014-02-13
        • 2018-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-23
        相关资源
        最近更新 更多