0

I've created activity from TabbedActivity template (in Android Studio): enter image description here
This template uses Fragment and FragmentPagerAdapter to handle tabs.

This is code of two classes, PlaceholderFragment extends Fragment, and SectionsPagerAdapter extends FragmentPagerAdapter:

public static class PlaceholderFragment extends Fragment {
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";


    public PlaceholderFragment() {
    }

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

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

        int scnum = getArguments().getInt(ARG_SECTION_NUMBER);

        final ListView listView = (ListView) rootView.findViewById(R.id.products_list);


        // here refresh my listView trough internet

        return rootView;
    }
}


/**
 * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
 * one of the sections/tabs/pages.
 */
public class SectionsPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class below).
        return PlaceholderFragment.newInstance(position + 1);
    }

    @Override
    public int getCount() {
        // Show 3 total pages.
        return 3;
    }
}

In onCreateView method I refresh my ListView. This method is called when i switch tabs.
But I need to refresh ListView not when user switch tabs, but:
1. every 10 seconds
2. when user comes back to this activity (onRestart() method is called)

I have no idea how to access my ListView outside onCreateView method.
I've already tried many solutions from the internet to call onCreateView method from onRestart method, and just to access my ListView from onRestart(). None of them worked.

Globally in Activity class I have an instance of SectionsPagerAdapter and ViewPager:

private SectionsPagerAdapter mSectionsPagerAdapter;

private ViewPager mViewPager;

They are initalized in onCreate by this way:

    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

    // Set up the ViewPager with the sections adapter.
    mViewPager = (ViewPager) findViewById(R.id.container);
    mViewPager.setAdapter(mSectionsPagerAdapter);

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);

    mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
    tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));

I think, there are 2 possible solutions, access my Fragment's view or just recall onCreateView.

I've already tried to do:

  1. This one just doesn't work:

    @Override
    protected void onRestart() {
        super.onRestart();
    
        mSectionsPagerAdapter.notifyDataSetChanged();
    
    }
    
  2. This one crashes app because of NullPointerException:

    @Override
    protected void onRestart() {
        super.onRestart();
    
        Fragment f = mSectionsPagerAdapter.getItem(0); // tab with my list view is first tab. ( getItem(1) also does not work )
        View v = f.getView(); // v is null here, however f isn't.
    
        final ListView listView = v.findViewById(R.id.products_list); // v is null, NullPointerException is thrown.
    
        // here refresh my listView trough internet
    
    }
    

EDIT:
Since I started using Kotlin, answer based on that language will be also accepted. However, I am new to Kotlin, so more detailed answer would be needed then.

J K
  • 632
  • 7
  • 24

1 Answers1

3

So you have a list in a Fragment that is inside a ViewPager. Ther are a few ways to solve your problem, I'd suggest you use the first one, because it is the simplest.

  1. Fragment have lifecycle callbacks that are triggered together with lifecycle callbacks of Activity that contains that fragment. It covers most of the methods, but unfortunately not the onRestart that you are looking for. But that's not a big problem. Actually, you almost never need onRestart because you have onStart method, that method is accessible from both Fragments and Activities. It is called every time activity is restarted + the very first time activity is started. And as we can see it is exactly what you need. So to have your list updated every time, just remove the update code from the onCreate method and put it into onStart method of the Fragment.

    public static class PlaceholderFragment extends Fragment {
    
        private static final String ARG_SECTION_NUMBER = "section_number";
        private ListView listView = null;
    
        public PlaceholderFragment() {
        }
    
        public static PlaceholderFragment newInstance(int sectionNumber) {
            PlaceholderFragment fragment = new PlaceholderFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_list_view, container, false);
    
            int scnum = getArguments().getInt(ARG_SECTION_NUMBER);
    
            listView = (ListView) rootView.findViewById(R.id.products_list);
    
            return rootView;
        }
    
        @Override
        public void onStart() {
            // here refresh your listView trough internet using the listView class field
        }
    }
    
  2. Another option would be to get a reference to your fragment inside the activity. The solution I usually use is to retain a reference to the fragment inside the adapter. It is similar to the second option in your question. The only problem with your solution is that mSectionsPagerAdapter.getItem(0); always create a new fragment, while you need to get a reference to already existing fragment.

    public class SectionsPagerAdapter extends FragmentPagerAdapter {
    
        private PlaceholderFragment fragmentZero = null;
    
        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm);
        }
    
        @Override
        public Fragment getItem(int position) {
            PlaceholderFragment tabFragment = PlaceholderFragment.newInstance(position + 1);                
    
            if (position == 0) {
                fragmentZero = tabFragment;
            }
    
            return tabFragment;
        }
    
        @Override
        public int getCount() {
            return 3;
        }
    
        public PlaceholderFragment getFragmentZero() {
            return fragmentZero;
        }
    }
    

    You also need to move your list update logic to a separate method in the PlaceholderFragment:

    private ListView listView = null;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_list_view, container, false);
    
        int scnum = getArguments().getInt(ARG_SECTION_NUMBER);
    
        listView = (ListView) rootView.findViewById(R.id.products_list);
    
        refreshListView();
    
        return rootView;
    }
    
    public void refreshListView() {
        // here refresh your listView trough internet
    }
    

    At this point you can do:

    @Override
    protected void onRestart() {
        super.onRestart();
    
        Fragment listFragment = mSectionsPagerAdapter.getFragmentZero(); 
    
        listFragment.refreshListView();
    }
    
  3. Whenever you have a fragment inside an activity, in a ViewPager or not, you can get a reference to the fragment using FragmentManager. Check this for details regarding the ViewPager option.

All the code above is rather straightforward and would not require any special Kotlin idioms if you'd prefer to implement it in Kotlin. You can start with converting your classes in the android studio. To do it:

  • Navigate to your java class
  • Press Control + Shift + A on Windows or Command + Shift + A on Mac
  • Search for Convert Java File to Kotlin File
  • Apply

And you are good to go with the Kotlin code of given classes. The only thing that might be quite different from java is handling ARG_SECTION_NUMBER since Kotlin does not have static fields and use Companion Objects instead.

TpoM6oH
  • 8,385
  • 3
  • 40
  • 72
  • Thank you for very detailed answer, this is what I was excepting when starting bounty. I used 2nd option, because it was easiest to synchronize it with rest of my code. – J K Mar 06 '18 at 17:06