片段与片段ViewPager和FragmentStatePagerAdapter导致异常内(含完整示例) [英] Fragment with ViewPager inside Fragment and FragmentStatePagerAdapter results in Exception (with complete example)

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

问题描述

我有一个简单的片段与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.运行(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(本机方法)
在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在子片段...

下面的例子应该工作,但它不...我已经添加了一些code它解决了一些问题,但它并不能解决所有的问题...

 进口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;
    私人片段fragment2;

    公共无效的onCreate(包savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        的setContentView(R.layout.test_main);

        片段1 = getSupportFragmentManager()findFragmentByTag(片段1)。
        fragment2 = getSupportFragmentManager()findFragmentByTag(fragment2);
    }

    @覆盖
    公共无效的onClick(视图v)
    {
        片段nextFragment = NULL;
        字符串nextFragmentTag = NULL;
        如果(v.getId()== R.id.button1)
        {
            如果(片段1 == NULL)
                片段1 = ContainerFragment.newInstance(1);
            nextFragment =片段1;
            nextFragmentTag =片段1;
        }
        否则,如果(v.getId()== R.id.button2)
        {
            如果(fragment2 == NULL)
                fragment2 = ContainerFragment.newInstance(2);
            nextFragment = fragment2;
            nextFragmentTag =fragment2;
        }

        FragmentTransaction交易= getSupportFragmentManager()的BeginTransaction()。
        transaction.replace(R.id.main,nextFragment,nextFragmentTag);
        transaction.addToBackStack(空);
        器transaction.commit();
    }

    公共静态类MainPagerAdapter扩展FragmentStatePagerAdapter
    {
        公众诠释按钮;

        公共MainPagerAdapter(FragmentManager FM)
        {
            超(FM);
        }

        @覆盖
        公共片段的getItem(int i)以
        {
            返回SubFragment.newInstance(按钮,I);
        }

        @覆盖
        公众诠释getCount将()
        {
            返回10;
        }
    }

    公共静态类ContainerFragment扩展片段
    {

        静态ContainerFragment的newInstance(INT POS)
        {
            ContainerFragment F =新ContainerFragment();
            捆绑的args =新包();
            args.putInt(POS,POS);
            f.setArguments(参数);
            返回F;
        }

        @覆盖
        公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,
                捆绑savedInstanceState)
        {
            查看rootView = inflater.inflate(R.layout.view_pager,集装箱,假);
            ViewPager寻呼机=(ViewPager)rootView.findViewById(R.id.pager);
            MainPagerAdapter适配器=新MainPagerAdapter(getChildFragmentManager());
            adapter.button = getArguments()调用getInt(POS)。
            pager.setAdapter(适配器);
            返回rootView;
        }

        // ------------------------------------------------ ---------------
        // HACK FIX献给java.lang.IllegalStateException:没有活动
        // ------------------------------------------------ ---------------

        私有静态最后场sChildFragmentManagerField;
        静止
        {
            域F = NULL;
            尝试
            {
                F = android.support.v4.app.Fragment.class.getDeclaredField(mChildFragmentManager);
                f.setAccessible(真正的);
            }
            赶上(NoSuchFieldException E)
            {
            }
            sChildFragmentManagerField = F;
        }

        @覆盖
        公共无效onDetach()
        {
            super.onDetach();

            如果(sChildFragmentManagerField!= NULL)
            {
                尝试
                {
                    sChildFragmentManagerField.set(本,NULL);
                }
                赶上(例外五)
                {
                }
            }
        }
    }

    公共静态类亚片段扩展分片
    {
        静态的newInstance亚片段(INT按钮,INT POS)
        {
            亚片段F =新亚片段();
            捆绑的args =新包();
            args.putString(钥匙,按钮+按键+碎片+ POS);
            f.setArguments(参数);
            返回F;
        }

        @覆盖
        公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,
                捆绑savedInstanceState)
        {
            查看rootView = inflater.inflate(R.layout.test_subfragment,集装箱,假);
            ((TextView中)rootView.findViewById(R.id.tv1))的setText(getArguments()的getString(密钥));
            返回rootView;
        }
    }
}
 

为了完整起见,我添加XML文件,以及:

test_main.xml

 < XML版本=1.0编码=UTF-8&GT?;
<的FrameLayout的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:ID =@ + ID /主
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent>

    <的LinearLayout
        机器人:layout_width =match_parent
        机器人:layout_height =match_parent
        机器人:方向=垂直>

        <按钮
            机器人:ID =@ + ID /按钮1
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:的onClick =的onClick
            机器人:文本=Button1的/>

        <按钮
            机器人:ID =@ + ID /按钮2
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:的onClick =的onClick
            机器人:文本=Button2的/>
    < / LinearLayout中>

< /的FrameLayout>
 

test_subfragment.xml

 < XML版本=1.0编码=UTF-8&GT?;
< TextView中的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:ID =@ + ID / TV1
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent
    机器人:重力=中心
    机器人:TEXTSIZE =50SP/>
 

view_pager.xml

 < XML版本=1.0编码=UTF-8&GT?;
< LinearLayout中的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    的xmlns:程序=htt​​p://schemas.android.com/apk/res-auto
    机器人:ID =@ + ID / llContainer
    机器人:layout_width =match_parent
    机器人:layout_height =WRAP_CONTENT
    机器人:方向=垂直>

    < android.support.v4.view.ViewPager
        机器人:ID =@ + ID /寻呼机
        机器人:layout_width =match_parent
        机器人:layout_height =WRAP_CONTENT/>

< / LinearLayout中>
 

解决方案

添加额外的FrameLayout到您的布局的主要活动。这是你把片段。

 < XML版本=1.0编码=UTF-8&GT?;
<的FrameLayout的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:ID =@ + ID /主
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent>

    <的LinearLayout
        机器人:layout_width =match_parent
        机器人:layout_height =WRAP_CONTENT
        机器人:方向=垂直>

        <按钮
            机器人:ID =@ + ID /按钮1
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:的onClick =的onClick
            机器人:文本=Button1的/>

        <按钮
            机器人:ID =@ + ID /按钮2
            机器人:layout_width =WRAP_CONTENT
            机器人:layout_height =WRAP_CONTENT
            机器人:的onClick =的onClick
            机器人:文本=Button2的/>
 <! - 添加此 - >
        <的FrameLayout
            机器人:ID =@ + ID / fragment_frame
            机器人:layout_width =match_parent
            机器人:layout_height =match_parent>

        < /的FrameLayout>

    < / LinearLayout中>

< /的FrameLayout>
 

由于您将您的片段到 BackStack 没有必要保存它们的一个实例。这就是为什么你的应用程序崩溃的原因。调整你的的onClick()的方法如下:

  @覆盖
    公共无效的onClick(视图v)
    {
        片段nextFragment = NULL;
        字符串nextFragmentTag = NULL;
        如果(v.getId()== R.id.button1)
        {
            nextFragment = ContainerFragment.newInstance(1);
            nextFragmentTag =片段1;
        }
        否则,如果(v.getId()== R.id.button2)
        {
            nextFragment = ContainerFragment.newInstance(2);
            nextFragmentTag =fragment2;
        }

        FragmentTransaction交易= getSupportFragmentManager()的BeginTransaction()。
        transaction.replace(R.id.fragment_frame,nextFragment,nextFragmentTag);
        transaction.addToBackStack(空);
        器transaction.commit();
    }
 

  

我测试了code。改变片段与按钮的作品,和    returing到它时,ViewPager了剩下的状态是preserved

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.

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

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