John Smith
John Smith

Reputation: 752

Fragment Crash On Orientation Change

My App crashes upon orientation change. I have taken care of following the default advice when this happens, namely that each fragment has a parameter-free constructor and, since I have implemented the Fragments inside my MainActivity, they are declared static. Yet I still face the same Exception as if I hadn't done so.

public class MainActivity extends FragmentActivity {

    public static final String      URL = "url";
    public static final String      ID  = "id";

    private SettingsPageAdapter     pageAdapter;
    private ViewPager               pager;
    private FrameLayout             layoutTabBar;

    public static class SettingsPageAdapter extends FragmentPagerAdapter {


        private final List<Fragment>    fragments;

        public SettingsPageAdapter(FragmentManager fm) {
            super(fm);
            fragments = new ArrayList<Fragment>();
        }

        public void addWebViewItem(String url, String id) {
            final Fragment settingsFragment = new SettingsWebView();
            final Bundle bundle = new Bundle();
            bundle.putString(URL, url);
            bundle.putString(ID, id);
            settingsFragment.setArguments(bundle);
            this.fragments.add(settingsFragment);
        }

        public void addItem(Fragment f, String id) {
            final Bundle bundle = new Bundle();
            bundle.putString(ID, id);
            f.setArguments(bundle);
            this.fragments.add(f);
        }

        @Override
        public Fragment getItem(int position) {
            return this.fragments.get(position);
        }

        @Override
        public int getCount() {
            return this.fragments.size();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {

            getSupportFragmentManager().beginTransaction()
                .add(R.id.bar, new BarFragment()).commit();
        }

        pageAdapter = new SettingsPageAdapter(getSupportFragmentManager());

        pageAdapter.addWebViewItem("https://www.google.com", "WebView 1");
        pageAdapter.addWebViewItem("https://m.bbc.co.uk", "WebView 2");
        pageAdapter.addWebViewItem("http://thorbek.net", "WebView 3");
        pageAdapter.addItem(new ContainerFragment(), "container");

        pager = (ViewPager) findViewById(R.id.container);
        pager.setOffscreenPageLimit(pageAdapter.getCount() - 1);
        pager.setAdapter(pageAdapter);

        pager.setOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int arg0) {
                Log.d("PAGER", "onPageSelected");

            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
                Log.d("PAGER", "onPageScrolled");

            }

            @Override
            public void onPageScrollStateChanged(int arg0) {
                Log.d("PAGER", "onPageScrollStateChanged");
                final int currentItem = pager.getCurrentItem();
                final BarFragment barFragment = (BarFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.bar);
                barFragment.setCurrentItem(currentItem);

            }
        });

        layoutTabBar = (FrameLayout) findViewById(R.id.bar);
        initAnimation();
    }

    private void initAnimation() {
         ...
    }

    public static class BarFragment extends Fragment {

        private final List<Button>  buttons         = new ArrayList<Button>();
        private int                 currentIndex    = -1;


        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            final View rootView = inflater.inflate(R.layout.fragment_buttonbar,
                container, false);

            initButtons(rootView);

            return rootView;
        }

        @SuppressWarnings("deprecation")
        public void setCurrentItem(int index) {

            if (currentIndex != index) {

                resetButtons();
                buttons.get(index).setBackgroundDrawable(
                    buttons.get(index).getContext().getResources()
                            .getDrawable(R.drawable.button_settings_pressed));
                currentIndex = index;
            }
        }

        @SuppressWarnings("deprecation")
        private void initButtons(final View rootView) {

            ...
        }

        @SuppressWarnings("deprecation")
        private void resetButtons() {
            ...
        }
    }

Java.lang.RuntimeException: Unable to start activity      ComponentInfo{com.example.test/com.example.test.MainActivity}:     android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.test.MainActivity$BarFragment: make sure class name exists, is public, and has an empty constructor that is public
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2351)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2403)
at android.app.ActivityThread.access$700(ActivityThread.java:165)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1379)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5391)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.support.v4.app.Fragment$InstantiationException: Unable to instantiate fragment com.example.test.MainActivity$BarFragment: make sure class name exists, is public, and has an empty constructor that is public
at android.support.v4.app.Fragment.instantiate(Fragment.java:435)
at android.support.v4.app.FragmentState.instantiate(Fragment.java:101)
at android.support.v4.app.FragmentManagerImpl.restoreAllState(FragmentManager.java:1823)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:264)
at com.example.test.MainActivity.onCreate(MainActivity.java:79)
at android.app.Activity.performCreate(Activity.java:5122)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1150)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2315)
Caused by: java.lang.IllegalAccessException: access to class not allowed
at java.lang.Class.newInstanceImpl(Native Method)
at java.lang.Class.newInstance(Class.java:1319)
at android.support.v4.app.Fragment.instantiate(Fragment.java:420)

MainActivity.java:79 is at super.onCreate(savedInstanceState);

So it is being thrown immediately in onCreate of MainActivity.

Upvotes: 2

Views: 4987

Answers (4)

Virginia Woolf
Virginia Woolf

Reputation: 1268

The exception is quite clear.

make sure class name exists, is public, and has an empty constructor that is public

Your BarFragment class has to be public

public static class BarFragment extends Fragment

Alternatively you can avoid fragment re-creation by doing the following when creating the fragment

fragment.setRetainInstance(true)

Upvotes: 3

shanta rao
shanta rao

Reputation: 11

When the orientation changes the activity is recreating again you have to use savedInstanceState maintain the fragmented state.

if(savedInstanceState == null) 
{
    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}

Upvotes: 0

Android_chirag
Android_chirag

Reputation: 11

setRetainInstance(true);

I have just put this line and it solved my problem.

Example of my code :

public  class PlaceholderFragment extends Fragment{
    String str;
    public PlaceholderFragment (String strDesc1){
        this.str=strDesc1;
        setRetainInstance(true);  //Hear put this line
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState){
        View rootView = inflater.inflate(R.layout.quiz_play_descrption_frg_one_, null,false);
        TextView txtdisc=(TextView) rootView.findViewById(R.id.txt_Discription_quizPlayFragment);
        txtdisc.setText(str);

        return rootView;
    }
}

Upvotes: 0

DejanRistic
DejanRistic

Reputation: 2039

Looks like your BarFragment is not public. Try changing it to

 public static class BarFragment extends Fragment

Also, you should not add your own empty constructor to the fragment. The default constructor will be setup automatically and should correctly handle restoring state. If you need to pass arguments to the fragment try using a static newInstance method and assigning a bundle to it.

See these docs for further explanation:

http://developer.android.com/reference/android/app/Fragment.html#Fragment%28%29

Basically remove the constructor you added.

Good Luck

Upvotes: 1

Related Questions