【问题标题】:How can I get (or emulate) a ViewPager inside a DialogFragment?如何在 DialogFragment 中获取(或模拟)ViewPager?
【发布时间】:2016-03-09 14:53:45
【问题描述】:

我想要一个有几页的dialogFragment,以便为我的应用程序输入一些设置。当我使用AlertDialog.Builder 时,我得到一个IllegalStateException 说“片段没有视图”,这让我感到困惑(而且堆栈也毫无用处)。

我可以使用其中的ViewPager 弹出一个弹出窗口,完全按照我的意愿进行操作,但它没有我想要的按钮。我真的很喜欢默认AlertDialog 的外观,所以如果可能的话,我最好使用它(出于一致性原因)。如果没有,我可以制作一个看起来完全相同的自定义对话框。

那么,我怎样才能让ViewPagerAlertDialog / dialogFragment(或类似的)中工作?

编辑: 澄清一下,它不一定是AlertDialog,但我想要设置标题、(永久)消息和按钮的选项。

EDIT2: 所以在FragmentFirst newInstance() 中返回f 时出现“片段没有视图”错误。这应该将片段返回到adapter 中的getItem() 方法,该方法应该返回与正确位置关联的片段。我希望这可以帮助任何人查明问题所在。

EDIT3: 在搞砸了一些东西后,我现在得到了这个错误:No view found for id 0x7f0c00b7 (com.example.tim.timapp:id/pager) for fragment FragmentDefault{47febfa #1 id=0x7f0c00b7 android:switcher:2131493047:0},基本上是在告诉我一些事情是试图在错误的片段中找到寻呼机视图?不太确定如何解决。注意。 FragmentDefaultFragmentFirst 完全一样,除了它的名字和一些id。

EDIT4:(2016 年 3 月 11 日)一个问题:当使用 newInstance 方法时(就像我在片段中所做的那样),返回的片段是否应该有视图?当我调用if (f.getView() == null) 时,它返回true,这让我想知道f 是否应该有一个视图。如果是这样,为什么不呢?我在网上看到的每个newInstance 方法的布局都与我的完全相同,所以看起来我并没有遗漏什么。
此外,由于某种原因,片段(FragmentFirst、FragmentSecond 等)中的onCreateView 永远无法到达。我在onCreateView 之后有一个Log.d,它永远不会显示在logcat 中。有什么想法吗?

代码:
调用 dialogFragment

TestActivityFragment newFragment = new TestActivityFragment();  
newFragment.show(getChildFragmentManager(), "dialog");  

TestActivityFragment:

package com.example.fragments.MainFragments.Test;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.tim.timapp.R;

public class TestActivityFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        final View view = inflater.inflate(R.layout.test_dialog, null);

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setView(view)
                .setPositiveButton("POS", null)
                .setNegativeButton("NEG", null)
                .setTitle("TEST_TITLE")
                .setMessage("TEST_MESSAGE");

        ViewPager mPager = (ViewPager) view.findViewById(R.id.pager);
        FragmentStatePagerAdapter mPagerAdapter = new TestAdapter(getChildFragmentManager());
        mPager.setAdapter(mPagerAdapter);

        return builder.create();
    }

    private class TestAdapter extends FragmentStatePagerAdapter {
        public TestAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch(position) {
                case 0: return FragmentFirst.newInstance("FragmentFirst, Instance 1");
                case 1: return FragmentSecond.newInstance("SecondFragment, Instance 1");
                case 2: return FragmentThird.newInstance("ThirdFragment, Instance 1");
                case 3: return FragmentThird.newInstance("ThirdFragment, Instance 2");
                default: return FragmentDefault.newInstance("DefaultFragment");
            }
        }

        @Override
        public int getCount() {
            return 5;
        }
    }
}

其中一个片段

package com.example.fragments.MainFragments.Test;

import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.tim.timapp.R;

public class FragmentFirst extends Fragment{

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.frag_first, container, false);

        TextView tv = (TextView) v.findViewById(R.id.tvFragFirst);
        tv.setText(getArguments().getString("msg"));

        return v;
    }

    public static FragmentFirst newInstance(String text) {

        FragmentFirst f = new FragmentFirst();
        Bundle b = new Bundle();
        b.putString("msg", text);

        f.setArguments(b);

        return f;
    }
}

XML 的

test_dialog.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="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

(我也有 viewPager 作为父布局,里面没有任何东西,没有改变任何东西)

frag_first.xml

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/tvFragFirst"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:text="TextView"
        android:textAppearance="?android:textAppearanceMedium"/>

</RelativeLayout>

日志

03-09 15:30:55.910 23343-23343/? I/art: Not late-enabling -Xcheck:jni (already on)
03-09 15:30:55.940 23343-23349/? E/art: Failed sending reply to debugger: Broken pipe
03-09 15:30:55.940 23343-23349/? I/art: Debugger is no longer active
03-09 15:30:55.950 23343-23343/? W/System: ClassLoader referenced unknown path: /data/app/com.example.tim.timapp-2/lib/x86_64
03-09 15:30:56.060 23343-23356/? D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
03-09 15:30:56.150 23343-23356/? I/OpenGLRenderer: Initialized EGL, version 1.4
03-09 15:30:56.210 23343-23356/? W/EGL_emulation: eglSurfaceAttrib not implemented
03-09 15:30:56.210 23343-23356/? W/OpenGLRenderer: Failed to set EGL_SWAP_BEHAVIOR on surface 0x7f5f5597db40, error=EGL_SUCCESS
03-09 15:30:58.870 23343-23343/com.example.tim.timapp D/GSF - getChildCount: 0
03-09 15:30:58.910 23343-23343/com.example.tim.timapp D/AndroidRuntime: Shutting down VM
03-09 15:30:58.910 23343-23343/com.example.tim.timapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.tim.timapp, PID: 23343
    java.lang.IllegalStateException: Fragment does not have a view
        at android.app.Fragment$1.onFindViewById(Fragment.java:2181)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:963)
        at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
        at android.app.BackStackRecord.run(BackStackRecord.java:793)
        at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1535)
        at android.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:562)
        at android.support.v13.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:168)
        at android.support.v4.view.ViewPager.populate(ViewPager.java:1177)
        at android.support.v4.view.ViewPager.populate(ViewPager.java:1025)
        at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1545)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
        at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1112)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:632)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
        at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
        at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
        at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
        at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
        at android.view.View.measure(View.java:18788)
        at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
        at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1191)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
        at android.view.Choreographer.doCallbacks(Choreographer.java:670)
        at android.view.Choreographer.doFrame(Choreographer.java:606)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:148)
        at android.app.ActivityThread.main(ActivityThread.java:5417)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
03-09 15:35:59.030 23343-23343/? I/Process: Sending signal. PID: 23343 SIG: 9

附言。如果我忘记添加一段代码,请告诉我,以便我添加。

【问题讨论】:

  • stacktrace 不是没用的,它告诉你缺少一个视图。那是因为您没有在 DialogFragment 中实现 onCreateView()。见这里android-developers.blogspot.de/2012/05/…
  • 我已经完成了一些(随机)测试,将 onCreateView() 方法添加到 dialogFragment。由于视图在 onCreateDialog() 方法中被夸大了,我认为它是可消耗的。晚饭后我会更深入地查看您的链接。
  • 刚刚使用 onCreateView() 进行了测试。我应该如何准确地使用它?无论什么顺序,onCreateDialog() 都会在 onCreateView 之前被调用,如果您在 onCreateView 中设置视图,则会导致视图出现 NPE,除此之外,我不知道我需要 onCreateView() 来做什么。
  • 是的,我想我有点草率了。我自己测试了一下,当使用 onCreateDialog 时,我无法让它与 viewPager 一起工作,如果你使用像 textView 这样的普通视图,它可以工作,但这对你的情况没有帮助。使用 onCreateView 时,viewPager 可以工作,但是您会丢失所有对话框属性,例如按钮。 Frak 碎片。

标签: java android android-fragments android-viewpager


【解决方案1】:

所以,我终于设法得到了我想要的东西。不过,有些东西已经改变了。


onCreateDialog() 方法没有改变,除了将return builder.create(); 替换为return builder.show();,以及一些外观上的变化。


名称为TestAdapter 的正确(咳嗽)现在是PagerAdapter,而不是FragmentStatePagerAdapter。它可能不漂亮,但它确实有效。

private class TestAdapter extends PagerAdapter {

    private int mCurrentPosition = -1;

    @Override
    public int getCount() {
        return pageAmount;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        int layoutThingy;

        switch(position) {
            case 0:
                layoutThingy = R.layout.fragment_generalsettingsinputdialog1;
                break;
            case 1:
                layoutThingy = R.layout.fragment_generalsettingsinputdialog2;
                break;
            case 2:
                layoutThingy = R.layout.fragment_generalsettingsinputdialog3;
                break;
            default:
                layoutThingy = R.layout.fragment_viewpagererror;
        }

        View view = inflater.inflate(layoutThingy, null);
        container.addView(view);
        view.requestFocus();
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == ((View) object);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        super.setPrimaryItem(container, position, object);
        if (position != mCurrentPosition) {
            View view = (View) object;
            CustomPager pager = (CustomPager) container;
            if (view != null) {
                mCurrentPosition = position;
                pager.measureCurrentView(view);
            }
        }
    }
}

片段(原帖中提到的One of the fragments 已经消失了。它们不再需要了。


test_dialog.xml 文件也已更改。它现在包含一个自定义 viewpager(和一些页面指示器,但这不是让我感到困扰的部分)。

<?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:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true">

    <com.example.tim.timapp.CustomStuff.CustomPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:overScrollMode="never"/>

    <com.viewpagerindicator.CirclePageIndicator
        android:id="@+id/circlePageIndicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:fillColor="@color/colorPrimary"
        app:strokeColor="@color/lightGrey"/>

</LinearLayout>

要继续,CustomPager 如下所示:

package com.example.tim.timapp.CustomStuff;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;

public class CustomPager extends ViewPager {

    private View mCurrentView;

    public CustomPager(Context context) {
       super(context);
    }

    public CustomPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mCurrentView == null) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        int height = 0;
        mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        int h = mCurrentView.getMeasuredHeight();
        if (h > height) height = h;
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void measureCurrentView(View currentView) {
        mCurrentView = currentView;
        requestLayout();
    }

    public int measureFragment(View view) {
        if (view == null)
            return 0;

        view.measure(0, 0);
        return view.getMeasuredHeight();
    }
}

它可能看起来令人生畏,但它并没有比将 viewpager 调整为其当前显示页面的大小做更多的事情。各种动画看起来都不漂亮,而且速度很慢,但每次都能完成。


最后是frag_first.xml XML。这些可以是您想要的任何类型的布局 XML,只要它适合 AlertDialog。


只是为了(证明)证明它有效,这里有一张图片。
如果您需要更多信息,请随时询问。我已经为此苦苦挣扎了很长时间,所以我很乐意提供帮助。


(点击查看大图)

【讨论】:

  • 嘿,感谢这个扩展的答案,我没有实现自定义 viewpager,但你的代码解决了我的一些问题!无论如何,我在单击编辑文本时无法打开键盘。你也经历过吗?如果有,你是怎么解决的?
  • 我确实做到了,你可以在这里找到解决方案:stackoverflow.com/a/36264266/5721694
  • 你真的是我的英雄。我只是希望我能投票两次:)
  • 只是来帮忙。您总是可以投票给其他对您有帮助的答案;)
【解决方案2】:

我做了类似的事情,但我在视图寻呼机中添加了自定义视图。

        <android.support.v4.view.ViewPager
            android:id="@+id/view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <package.CustomViewOne
                android:id="@+id/custom_view_one"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <package.CustomViewTwo
                android:id="@+id/custom_view_two"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <package.CustomViewThree
                android:id="@+id/custom_view_three"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </android.support.v4.view.ViewPager>

我的适配器是这样的:

class SlidePagerAdapter extends PagerAdapter {

    private ViewPager viewPager;

    public SlidePagerAdapter(ViewPager viewPager) {
        this.viewPager = viewPager;
    }

    public View instantiateItem(ViewGroup collection, int position) {
        return viewPager.getChildAt(position);
    }

    @Override
    public int getCount() {
        return viewPager.getChildCount();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == (object);
    }

    @Override
    public int getItemPosition(Object object) {

        for (int i = 0; i < getCount(); i++) {
            if (object == viewPager.getChildAt(i)) {
                return i;
            }
        }

        return POSITION_NONE;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        viewPager.removeView((View) object);
    }
}

示例自定义视图。 (CustomViewOne.Java)

public class CustomViewOne extends LinearLayout {

    public CustomViewOne(Context context) {
        super(context);
        initLayout();
    }

    public CustomViewOne(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLayout();
    }

    public CustomViewOne(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initLayout();
    }

    public void initLayout() {
        LayoutInflater.from(getContext()).inflate(R.layout.custom_view_one_layout, this);
    }
}

【讨论】:

  • 我对如何使用它有点困惑。我需要创建新视图吗?这些包在 xml 中是如何工作的?
  • 您必须将包替换为视图类所在的包名。是的,您需要创建自定义视图并在自定义视图中扩展您的布局。
  • 所以在你的例子中包是&lt;android.support.v4.view.ViewPager.CustomViewOne...?
  • 我在上面的答案中添加了一个自定义视图示例。所以当你创建一个java类CustomViewOne.java。在类的第一行中,您将看到包名称。所以取包名并在其旁边添加类名。
  • 如果我可能会问(为了更好地理解正在发生的事情),为什么 CustomViewOne 需要三个构造函数(我相信这就是它的名称)?最后一个似乎和前两个做的一样,并添加了一些东西。
猜你喜欢
  • 1970-01-01
  • 2015-05-21
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多