Fragment 和 FragmentStatePagerAdapter 中带有 ViewPager 的片段导致异常(带有完整示例) [英] Fragment with ViewPager inside Fragment and FragmentStatePagerAdapter results in Exception (with complete example)

本文介绍了Fragment 和 FragmentStatePagerAdapter 中带有 ViewPager 的片段导致异常(带有完整示例)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有 ViewPager 的简单片段.

我正在使用最新的支持库 v4 rev18!

如果我第一次显示子片段,一切正常,如果我返回并再次显示,应用程序崩溃并出现以下异常:

我有一个完整的示例,它显示了何时发生以下异常:

java.lang.NullPointerException在 android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:569)在 android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:211)在 android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1281)在 android.view.View.dispatchRestoreInstanceState(View.java:12043)在 android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2688)在 android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2694)在 android.view.View.restoreHierarchyState(View.java:12021)在 android.support.v4.app.Fragment.restoreViewState(Fragment.java:425)在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:949)在 android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)在 android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)在 android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)在 android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)在 android.os.Handler.handleCallback(Handler.java:615)在 android.os.Handler.dispatchMessage(Handler.java:92)在 android.os.Looper.loop(Looper.java:137)在 android.app.ActivityThread.main(ActivityThread.java:4800)在 java.lang.reflect.Method.invokeNative(Native Method)在 java.lang.reflect.Method.invoke(Method.java:511)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:798)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:565)在 dalvik.system.NativeStart.main(本机方法)

我能够以其他方式在子片段中使用 ViewPager,但如果我手动添加/删除子片段并在子片段中使用 FragmentStatePagerAdapter,我将无法使其正常工作...

下面的例子应该可以,但它没有......我已经添加了一些解决一些问题的代码,但它并不能解决所有问题......

import java.lang.reflect.Field;导入android.os.Bundle;导入android.support.v4.app.Fragment;导入android.support.v4.app.FragmentActivity;导入 android.support.v4.app.FragmentManager;导入 android.support.v4.app.FragmentStatePagerAdapter;导入 android.support.v4.app.FragmentTransaction;导入 android.support.v4.view.ViewPager;导入 android.view.LayoutInflater;导入android.view.View;导入 android.view.View.OnClickListener;导入android.view.ViewGroup;导入 android.widget.TextView;公共类 TestActivity 扩展 FragmentActivity 实现 OnClickListener{私有片段片段1;私有片段片段2;public void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.test_main);fragment1 = getSupportFragmentManager().findFragmentByTag("fragment1");fragment2 = getSupportFragmentManager().findFragmentByTag("fragment2");}@覆盖public void onClick(查看 v){片段 nextFragment = null;字符串 nextFragmentTag = null;if (v.getId() == R.id.button1){如果(片段1 ==空)fragment1 = ContainerFragment.newInstance(1);下一个片段 = 片段 1;nextFragmentTag = "片段1";}否则 if (v.getId() == R.id.button2){如果(片段2 == null)fragment2 = ContainerFragment.newInstance(2);下一个片段 = 片段 2;nextFragmentTag = "片段2";}FragmentTransaction 事务 = getSupportFragmentManager().beginTransaction();transaction.replace(R.id.main, nextFragment, nextFragmentTag);transaction.addToBackStack(null);事务.commit();}公共静态类 MainPagerAdapter 扩展 FragmentStatePagerAdapter{公共 int 按钮;公共 MainPagerAdapter(FragmentManager fm){超级(调频);}@覆盖公共片段getItem(int i){返回 SubFragment.newInstance(button, i);}@覆盖公共 int getCount(){返回 10;}}公共静态类 ContainerFragment 扩展 Fragment{静态 ContainerFragment newInstance(int pos){ContainerFragment f = new ContainerFragment();捆绑参数 = 新捆绑();args.putInt("pos", pos);f.setArguments(args);返回 f;}@覆盖public View onCreateView(LayoutInflater inflater, ViewGroup 容器,捆绑保存的InstanceState){查看 rootView = inflater.inflate(R.layout.view_pager, container, false);ViewPager pager = (ViewPager) rootView.findViewById(R.id.pager);MainPagerAdapter 适配器 = new MainPagerAdapter(getChildFragmentManager());adapter.button = getArguments().getInt("pos");pager.setAdapter(适配器);返回根视图;}//---------------------------------------------------------------//HACK FIX für java.lang.IllegalStateException: 无活动//---------------------------------------------------------------私有静态最终字段 sChildFragmentManagerField;静止的{字段 f = null;尝试{f = android.support.v4.app.Fragment.class.getDeclaredField("mChildFragmentManager");f.setAccessible(true);}捕捉(NoSuchFieldException e){}sChildFragmentManagerField = f;}@覆盖公共无效 onDetach(){super.onDetach();如果(sChildFragmentManagerField != null){尝试{sChildFragmentManagerField.set(this, null);}捕获(异常 e){}}}}公共静态类 SubFragment 扩展 Fragment{静态 SubFragment newInstance(int button, int pos){子片段 f = 新的子片段();捆绑参数 = 新捆绑();args.putString("key", "Button" + button + "Fragment" + pos);f.setArguments(args);返回 f;}@覆盖public View onCreateView(LayoutInflater inflater, ViewGroup 容器,捆绑保存的InstanceState){查看 rootView = inflater.inflate(R.layout.test_subfragment, container, false);((TextView) rootView.findViewById(R.id.tv1)).setText(getArguments().getString("key"));返回根视图;}}}

为了完整起见,我也添加了 xml 文件:

test_main.xml

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent" ><线性布局android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="垂直"><按钮android:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onClick"android:text="Button1"/><按钮android:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onClick"android:text="Button2"/></线性布局></框架布局>

test_subfragment.xml

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/tv1"android:layout_width="match_parent"android:layout_height="match_parent"机器人:重力=中心"android:textSize="50sp"/>

view_pager.xml

<?xml version="1.0" encoding="utf-8"?><线性布局 xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/llContainer"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="垂直">

解决方案

在主 Activity 的布局中添加额外的 FrameLayout.这是放置 Fragment 的位置.

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent" ><线性布局android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="垂直"><按钮android:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onClick"android:text="Button1"/><按钮android:id="@+id/button2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onClick"android:text="Button2"/><!-- 添加这个--><框架布局android:id="@+id/fragment_frame"android:layout_width="match_parent"android:layout_height="match_parent" ></框架布局></线性布局></框架布局>

由于您将 Fragments 添加到 BackStack无需保存它们的实例.这就是您的应用程序崩溃的原因.调整你的 onClick() 方法如下:

 @Overridepublic void onClick(查看 v){片段 nextFragment = null;字符串 nextFragmentTag = null;if (v.getId() == R.id.button1){nextFragment = ContainerFragment.newInstance(1);nextFragmentTag = "片段1";}否则 if (v.getId() == R.id.button2){nextFragment = ContainerFragment.newInstance(2);nextFragmentTag = "片段2";}FragmentTransaction 事务 = getSupportFragmentManager().beginTransaction();transaction.replace(R.id.fragment_frame, nextFragment, nextFragmentTag);transaction.addToBackStack(null);事务.commit();}

<块引用>

我测试了代码.使用按钮更改片段有效,并且保留 ViewPager 返回时的状态.

I have a simple Fragment with a ViewPager.

I'm using the up to date support library, v4 rev18!

If I show the sub fragment the first time, everything works fine, if I go back and show it again, the app crashes with the following exception:

I have a complete example which shows, WHEN the following exception is occuring:

java.lang.NullPointerException
at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:569)
at android.support.v4.app.FragmentStatePagerAdapter.restoreState(FragmentStatePagerAdapter.java:211)
at android.support.v4.view.ViewPager.onRestoreInstanceState(ViewPager.java:1281)
at android.view.View.dispatchRestoreInstanceState(View.java:12043)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2688)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2694)
at android.view.View.restoreHierarchyState(View.java:12021)
at android.support.v4.app.Fragment.restoreViewState(Fragment.java:425)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:949)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
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:4800)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:798)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:565)
at dalvik.system.NativeStart.main(Native Method)

I am able to use the ViewPager in a child fragment in every other way, but I can't get it working if I add/remove the sub fragments manually and use a FragmentStatePagerAdapter in the sub fragments...

Following example should work, but it doesn't... i already added some code which solves some problems, but it does not solve all problems...

import java.lang.reflect.Field;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;

public class TestActivity extends FragmentActivity implements OnClickListener
{
    private Fragment fragment1;
    private Fragment fragment2;

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_main);

        fragment1 = getSupportFragmentManager().findFragmentByTag("fragment1");
        fragment2 = getSupportFragmentManager().findFragmentByTag("fragment2");
    }

    @Override
    public void onClick(View v)
    {
        Fragment nextFragment = null;
        String nextFragmentTag = null;
        if (v.getId() == R.id.button1)
        {
            if (fragment1 == null)
                fragment1 = ContainerFragment.newInstance(1);
            nextFragment = fragment1;
            nextFragmentTag = "fragment1";
        }
        else if (v.getId() == R.id.button2)
        {
            if (fragment2 == null)
                fragment2 = ContainerFragment.newInstance(2);
            nextFragment = fragment2;
            nextFragmentTag = "fragment2";
        }

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.main, nextFragment, nextFragmentTag);
        transaction.addToBackStack(null);
        transaction.commit();
    }

    public static class MainPagerAdapter extends FragmentStatePagerAdapter
    {
        public int button;

        public MainPagerAdapter(FragmentManager fm)
        {
            super(fm);
        }

        @Override
        public Fragment getItem(int i)
        {
            return SubFragment.newInstance(button, i);
        }

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

    public static class ContainerFragment extends Fragment
    {

        static ContainerFragment newInstance(int pos)
        {
            ContainerFragment f = new ContainerFragment();
            Bundle args = new Bundle();
            args.putInt("pos", pos);
            f.setArguments(args);
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            View rootView = inflater.inflate(R.layout.view_pager, container, false);
            ViewPager pager = (ViewPager) rootView.findViewById(R.id.pager);
            MainPagerAdapter adapter = new MainPagerAdapter(getChildFragmentManager());
            adapter.button = getArguments().getInt("pos");
            pager.setAdapter(adapter);
            return rootView;
        }

        // ---------------------------------------------------------------
        // HACK FIX für java.lang.IllegalStateException: No activity
        // ---------------------------------------------------------------

        private static final Field sChildFragmentManagerField;
        static
        {
            Field f = null;
            try
            {
                f = android.support.v4.app.Fragment.class.getDeclaredField("mChildFragmentManager");
                f.setAccessible(true);
            }
            catch (NoSuchFieldException e)
            {
            }
            sChildFragmentManagerField = f;
        }

        @Override
        public void onDetach()
        {
            super.onDetach();

            if (sChildFragmentManagerField != null)
            {
                try
                {
                    sChildFragmentManagerField.set(this, null);
                }
                catch (Exception e)
                {
                }
            }
        }
    }

    public static class SubFragment extends Fragment
    {
        static SubFragment newInstance(int button, int pos)
        {
            SubFragment f = new SubFragment();
            Bundle args = new Bundle();
            args.putString("key", "Button " + button + "Fragment " + pos);
            f.setArguments(args);
            return f;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            View rootView = inflater.inflate(R.layout.test_subfragment, container, false);
            ((TextView) rootView.findViewById(R.id.tv1)).setText(getArguments().getString("key"));
            return rootView;
        }
    }
}

for the sake of completeness, I add the xml files as well:

test_main.xml

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Button1" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Button2" />
    </LinearLayout>

</FrameLayout>

test_subfragment.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tv1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:textSize="50sp" />

view_pager.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/llContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

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

</LinearLayout>

解决方案

Add an additional FrameLayout to your layout of the main activity. This is where you put the Fragment.

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Button1" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="onClick"
            android:text="Button2" />
 <!-- add this -->
        <FrameLayout
            android:id="@+id/fragment_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >

        </FrameLayout>

    </LinearLayout>

</FrameLayout>

Since you are adding your Fragments to the BackStack, there is no need for saving an instance of them. This is the reason why you app crashes. Adjust your onClick() method as follows:

    @Override
    public void onClick(View v)
    {
        Fragment nextFragment = null;
        String nextFragmentTag = null;
        if (v.getId() == R.id.button1)
        {
            nextFragment = ContainerFragment.newInstance(1);
            nextFragmentTag = "fragment1";
        }
        else if (v.getId() == R.id.button2)
        {
            nextFragment = ContainerFragment.newInstance(2);
            nextFragmentTag = "fragment2";
        }

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(R.id.fragment_frame, nextFragment, nextFragmentTag);
        transaction.addToBackStack(null);
        transaction.commit();
    }

I tested the code. Changing the Fragments with the buttons works, and the state the ViewPager was left when returing to it is preserved.

这篇关于Fragment 和 FragmentStatePagerAdapter 中带有 ViewPager 的片段导致异常(带有完整示例)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆