سؤال

I develops an Android Application and I'm facing some problems.

In my MainActivity I have the following components:

  • Navigation Drawer (instanciates and loads many fragments)
  • ViewPager (only for ArticleFragment which display the detail view of an article)

Normaly, the ViewPager is related to a specific Activity, but I really want to use both Navigation Drawer and ViewPager. The only way is to implement both elements in same activity.

The DrawerLayout is composed by the navigation drawer itself and by the content area. In my content area I have a FrameLayout and the ViewPager. The problem is that the ViewPager is always displayed.

So my idea was to play with the android:visibility property to show/hide alternately the FrameLayout and the ViewPager. If you look at my activity_main.xml you can notice the android:visibility set to invisible on the ViewPager.

/layout/activity_main.xml

    <!-- The main content view -->
    <RelativeLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/container"
            android:paddingTop="?android:attr/actionBarSize"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <!-- ViewPager -->
        <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible" />

    </RelativeLayout>

    <!-- The navigation drawer -->
    <fragment android:id="@+id/navigation_drawer"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:name="NavigationDrawerFragment" />

</android.support.v4.widget.DrawerLayout>

And then:

MainActivity.java

public class MainActivity extends ActionBarActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks {

    private NavigationDrawerFragment mNavigationDrawerFragment;
    private FrameLayout mContainer;

    private ViewPager mPager;
    private PagerAdapter mPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Set up the navigation drawer
        mContainer = (FrameLayout) findViewById(R.id.container);
        mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
        mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));

        // Set up the viewpager
        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                invalidateOptionsMenu();
            }
        });
    }

    //-------------------------------//
    //       NAVIGATION DRAWER       //
    //-------------------------------//

    /**
     * Update the main content by replacing fragments
     */
    @Override
    public void onNavigationDrawerItemSelected(int position) {
        Fragment fragment = null;
        Boolean viewPagerArticles = false;

        switch (position) {
            case 0:
                fragment = new HomeFragment();
                break;
            case 1:
                fragment = new FooFragment();
                break;
            case 2:
                viewPagerArticles = true;

                // Hide navigation drawer container and show the viewpager
                mContainer.setVisibility(FrameLayout.GONE);
                mPager.setVisibility(ViewPager.VISIBLE);

                // NullPointerException with new this line. Why?
                // Fragment already instanciate in ScreenSlidePagerAdapter -> getItem() -> ArticleFragment.create()?
                //fragment = new ArticleFragment();
                break;
            default:
                break;
        }

        // Show the view pager and hide navigation drawer container
        // =================
        // THE BUG IS HERE
        // =================
        if (!viewPagerArticles) {
            mContainer.setVisibility(FrameLayout.VISIBLE);
            mPager.setVisibility(ViewPager.GONE);
        }

        if (fragment != null) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            fragmentManager.beginTransaction()
                    .replace(R.id.container, fragment)
                    .commit();
        }
    }

    ...

    //-------------------------------//
    //           VIEWPAGER           //
    //-------------------------------//

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

        @Override
        public Fragment getItem(int position) {
            return DayFragment.create(position);
        }

        @Override
        public int getCount() { ... }
    }
    ...
}

NavigationDrawerFragment.java

http://pastebin.com/LwKZnNiB

Display ViewPager content

On my HomeFragment I have a button with a onClickListener:

mButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        ((MainActivity)getActivity()).onNavigationDrawerItemSelected(2);
    }
});

When I load MainActivity I have these errors : InflateException and NullPointerException

AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android/com.example.android.ui.MainActivity}: android.view.InflateException: Binary XML file line #34: Error inflating class fragment
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2211)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            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:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: android.view.InflateException: Binary XML file line #34: Error inflating class fragment
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:713)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
            at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:267)
            at android.app.Activity.setContentView(Activity.java:1895)
            at android.support.v7.app.ActionBarActivity.superSetContentView(ActionBarActivity.java:216)
            at android.support.v7.app.ActionBarActivityDelegateICS.setContentView(ActionBarActivityDelegateICS.java:111)
            at android.support.v7.app.ActionBarActivity.setContentView(ActionBarActivity.java:76)
            at com.example.android.ui.MainActivity.onCreate(MainActivity.java:42)
            at android.app.Activity.performCreate(Activity.java:5133)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            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:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
            at com.example.android.ui.MainActivity.onNavigationDrawerItemSelected(MainActivity.java:117)
            at com.example.android.ui.NavigationDrawerFragment.selectItem(NavigationDrawerFragment.java:202)
            at com.example.android.ui.NavigationDrawerFragment.onCreate(NavigationDrawerFragment.java:80)
            at android.support.v4.app.Fragment.performCreate(Fragment.java:1477)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:893)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1082)
            at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1184)
            at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:291)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:685)
            at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
            at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:267)
            at android.app.Activity.setContentView(Activity.java:1895)
            at android.support.v7.app.ActionBarActivity.superSetContentView(ActionBarActivity.java:216)
            at android.support.v7.app.ActionBarActivityDelegateICS.setContentView(ActionBarActivityDelegateICS.java:111)
            at android.support.v7.app.ActionBarActivity.setContentView(ActionBarActivity.java:76)
            at com.example.android.ui.MainActivity.onCreate(MainActivity.java:42)
            at android.app.Activity.performCreate(Activity.java:5133)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
            at android.app.ActivityThread.access$600(ActivityThread.java:141)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:5103)
            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:737)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)

Everything is fine (no errors, activity loaded) without:

    // =================
    // THE BUG IS HERE
    // =================
    if (!viewPagerArticles) {
        mContainer.setVisibility(FrameLayout.VISIBLE);
        mPager.setVisibility(ViewPager.GONE);
    }

...but it does not match my expectations because the Navigation Drawer fragments are always hiding (it's normal because of mContainer.setVisibility(FrameLayout.GONE);. I just want to inverse the mContainer/mPager visibilities.

I hope I was enough clear...!

Any idea?

هل كانت مفيدة؟

المحلول

You get that NullPointerException because the of the way you built the NavigationDrawerFragment fragment. More precisely, in its onCreate() method you call selectItem() which propagates through the Activity(registered as a listener) to the onNavigationDrawerItemSelected() callback. The problem is that the fragment will be inflated at the point when setContentView() is called(in onCreate()) which means you'll enter the onNavigationDrawerItemSelected() callback before the lines:

mContainer = (FrameLayout) findViewById(R.id.container);
mPager = (ViewPager) findViewById(R.id.pager);

run, rendering those references null in onNavigationDrawerItemSelected().

To solve this you could simply replace the current <fragment /> tag from the Activity layout with a basic container(a placeholder empty FrameLayout for example) and add the NavigationDrawerFragment manually in onCreate() after the lines above(where you bind the views). There could be another better solution(or I could make other suggestions) but it's not very clear how you want to handle the visibility. Maybe you can expand on this.

Edit 2:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set up the navigation drawer
    mContainer = (FrameLayout) findViewById(R.id.container);


    // Set up the viewpager
    mPager = (ViewPager) findViewById(R.id.pager);
    mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
    mPager.setAdapter(mPagerAdapter);
    mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            invalidateOptionsMenu();
        }
    });
    if (savedInstanceState == null) {
        NavigationDrawerFragment ndf = new NavigationDrawerFragment();
        ndf.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));
        getSupportFragmentManager().beginTransaction().add(R.id.nav_drawer, ndf).commit();
    }
}

Edit 1:

//...
<!-- ViewPager -->
        <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible" />

    </RelativeLayout>
    <!-- The navigation drawer -->
    <FrameLayout android:id="@+id/nav_drawer"
        android:layout_width="@dimen/navigation_drawer_width"
        android:layout_height="match_parent"
        android:layout_gravity="start"
     />

</android.support.v4.widget.DrawerLayout>

And in onCreate() at the end:

//...
mPager.setAdapter(mPagerAdapter);
        mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                invalidateOptionsMenu();
            }
        });
getSupportFragmentManager().beginTransaction().add(R.id.nav_drawer, new NavigationDrawerFragment()).commit();

If you get an exception with the code above, it's in the same onNavigationDrawerItemSelected() method?

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top