4

I've got a tab view set up that that has custom fragments for each tab using a viewpager. This is my code:

Holding Fragment

public class FragInboxMainView extends Fragment implements CGFragment {
    private CGController controller;
    private CGFragment thisFragment;

    @Bind(R.id.inboxViewPager)ViewPager inboxViewPager;
    @Bind(R.id.inboxTabs)TabLayout inboxTabLayout;
    @Bind(R.id.inbox_progress_wheel)ProgressWheel inboxProgressWheel;

    public FragInboxMainView(){}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_inbox_mainview, container, false);
        ButterKnife.bind(this, rootView);
        thisFragment = this;
        Globals g = Globals.getInstance();
        /** Show loading spinner */
        this.inboxProgressWheel.setBarColor(ContextCompat.getColor(controller.getContext(), g.getUserObject().getUserThemeColor()));
        this.inboxProgressWheel.setVisibility(View.VISIBLE);
        /** Display the profile information based off the ID */
        controller.displayInbox(thisFragment);

        return rootView;
    }


    public void hideProgressSpinner() {
        this.inboxProgressWheel.setVisibility(View.GONE);
    }


    public ViewPager getInboxViewPager() {
        return this.inboxViewPager;
    }

    public TabLayout getInboxTabLayout() {
        return this.inboxTabLayout;
    }
}

Its layout file

<?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"
              xmlns:wheel="http://schemas.android.com/apk/res-auto"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
    <android.support.design.widget.TabLayout
        android:id="@+id/inboxTabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="scrollable" />
    <com.pnikosis.materialishprogress.ProgressWheel
        android:id="@+id/inbox_progress_wheel"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:layout_gravity="center"
        wheel:matProg_barColor="#5588FF"
        wheel:matProg_progressIndeterminate="true"
        android:visibility="gone"/>


    <android.support.v4.view.ViewPager
        android:id="@+id/inboxViewPager"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1"
        android:background="@android:color/white" />
</LinearLayout>

Tab fragment and its inflation file

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

    <com.baoyz.widget.PullRefreshLayout
        android:id="@+id/tabPullRefresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <view
        android:id="@+id/tabRecyclerHolder"
        class="android.support.v7.widget.RecyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="8dp"
        android:paddingBottom="8dp"
        android:clipToPadding="false"
        android:layout_centerInParent="true"/>

    </com.baoyz.widget.PullRefreshLayout>
    <com.melnykov.fab.FloatingActionButton
        android:id="@+id/tabFab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:layout_margin="16dp"
        android:src="@mipmap/ic_add_white"/>
</android.support.design.widget.CoordinatorLayout>

public class TabRecyclerHolder extends Fragment {

    @Bind(R.id.tabRecyclerHolder) RecyclerView tabRecyclerHolder;
    @Bind(R.id.tabPullRefresh) PullRefreshLayout tabPullRefresh;
    @Bind(R.id.tabFab) FloatingActionButton recyclerFab;

    private String tabTitle = "Title";


    public TabRecyclerHolder(){}

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.tab_recycler_holder, container, false);

        ButterKnife.bind(this, rootView);

        recyclerFab.hide(false);

        tabPullRefresh.setRefreshStyle(PullRefreshLayout.STYLE_MATERIAL);

        return rootView;
    }

    public RecyclerView getTabRecyclerHolder() {
        return this.tabRecyclerHolder;
    }

    public FloatingActionButton getRecyclerFab() {
        return this.recyclerFab;
    }

    public String getTabTitle() {
        return this.tabTitle;
    }

    public void setTabTitle(String title) {
        this.tabTitle = title;
    }

    public PullRefreshLayout getTabPullRefresh() {
        return this.tabPullRefresh;
    }
}

My tab adapter

public class TabPagerAdapter extends FragmentStatePagerAdapter {

    private CGController controller;
    private List<Object> items;

    public TabPagerAdapter(FragmentManager fm, CGController controller, List<Object> items) {
        super(fm);
        this.controller = controller;
        this.items = items;
    }

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

    @Override
    public Fragment getItem(int num) {
        return (TabRecyclerHolder)items.get(num);
    }

    @Override
    public String getPageTitle(int num){
        return ((TabRecyclerHolder)items.get(num)).getTabTitle();
    }
}

The processing code

public void viewInbox() {
    /** Set up the views */
    receivedHolder = new TabRecyclerHolder();
    receivedHolder.setTabTitle(Constants.TAB_INBOX_RECEIVED);
    sentHolder = new TabRecyclerHolder();
    sentHolder.setTabTitle(Constants.TAB_INBOX_SENT);

    tabs.add(receivedHolder);
    tabs.add(sentHolder);

    /** Set up the tabs */
    final ViewPager inboxViewPager = inboxFragment.getInboxViewPager();
    TabLayout inboxTabLayout = inboxFragment.getInboxTabLayout();

    /** Set the adapter for the view pager */
    inboxViewPager.setAdapter(new TabPagerAdapter(inboxFragment.getChildFragmentManager(), controller, tabs));
    /** set up the tab look and feel */
    inboxTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
    inboxTabLayout.setTabMode(TabLayout.MODE_FIXED);

    inboxViewPager.setOffscreenPageLimit(3);
    inboxViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(inboxTabLayout));
    inboxTabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            inboxViewPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

    /** And, display! */
    inboxTabLayout.setupWithViewPager(inboxViewPager);

    receivedAdapter = new RecyclerListAdapter(controller, items);
    final RecyclerView receivedList = receivedHolder.getTabRecyclerHolder();
    receivedList.setLayoutManager(new LinearLayoutManager(controller.getContext()));
    receivedList.setAdapter(receivedAdapter);
}

There is some code i've missed out but its not pertanent to the question. The code works perfectly when initially viewing the fragment. However since my application contains a single activity and just replaces a content view for each fragment navigated to, each fragment is added to the back stack and then popped when the back button is pressed. My issue is that when navigating back to this fragment the view inside the tab isn't being inflated, which means that no elements can be accessed (and therefore the app crashes while trying to display the data in the recyclerview etc).

I have had a look at this question: TabLayout ViewPager Not Loading When Using Backstack and implemented its suggestion (using getChildFragmentManager() when setting up the pager adapter) however that has not fixed my issue.

Help would be appreciated!

Community
  • 1
  • 1
Matt Davis
  • 470
  • 5
  • 16
  • java.lang.NullPointerException when trying to access an element that is supposed to be inflated inside the tab. If i remove any code attempting to access the inner fragment and just let it display without crashing it shows nothing inside the tabs at all. – Matt Davis Nov 14 '15 at 09:17
  • http://pastebin.com/ECKF8VKg The fragment displays and shows the loading spinner but when the app has downloaded all the data and tries to tell the spinner to hide it can't find it. – Matt Davis Nov 16 '15 at 08:50
  • I've deleted my comments to not clutter your question. Try to log the viewInbox() method and make sure it gets called when the fragment is recreated. – user Nov 18 '15 at 05:39
  • Yeah it does. The part of the code that i've missed out does a HTTP JSON request. On back pressed, it processes everything in viewInbox() all the way down to trying to set the pull refresh from TabViewHolder(). It's almost as if its not inflating the view inside the new instance of TabViewHolder and therefore gets the nullpointerexception. Is there a way of forcing it to inflate the fragment when the instance gets created? – Matt Davis Nov 19 '15 at 00:51
  • I don't know what is happening in your code. Your best bet is to set a breakpoint at the start of viewInbox() and then step through the code with the debugger to make sure that the data and the fragments are ok. – user Nov 19 '15 at 07:54
  • Mucking around with the debugger - when I initially view the fragment and it attempts to get the data from TabRecyclerHolder this is what the debugger gives me about the object: `TabRecyclerHolder{ef0cb89 #0 id=0x7f0d00bd}`. It also shows that the pullrefresh has an object instance too However when navigating elsewhere and then back through the fragment stack it gives me this: `TabRecyclerHolder{1a8a8e64}`, and any UI variables inside the object are null. So it looks like it's not inflating the view again.. – Matt Davis Nov 26 '15 at 23:02
  • @Luksprog any more ideas? I've had more of a play and confirmed that it's not creating a view (doesn't seem to be calling `onCreateView` or `onViewCreated` on the backstack at all). I've tried using the `childfragmentmanager` of all fragments available, and tried getting the view directly rather than through butterknife but to no avail. – Matt Davis Dec 23 '15 at 07:34

2 Answers2

0

Change this public View onCreateView(LayoutInflater inflater,... to public void onViewCreated (View view, Bundle savedInstanceState)

so you are going to have something like this

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {        
    return inflater.inflate(R.layout.tab_recycler_holder, container, false);
}

then

@Override
public void onViewCreated (View view, Bundle savedInstanceState){
     ButterKnife.bind(this, view);
    recyclerFab.hide(false);
    tabPullRefresh.setRefreshStyle(PullRefreshLayout.STYLE_MATERIAL);
    ...

see if it helps

Elltz
  • 10,730
  • 4
  • 31
  • 59
0

Extend FragmentStatePagerAdapter in TabPagerAdapter as

FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)
Divya
  • 145
  • 10