Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why FragmentStateAdapter not work in ViewPager2? [duplicate]

Android Studio 3.6

I want in my activity to use 2 fragments inside ViewPager2.

Here my activity:

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
public class QRBluetoothSwipeActivity extends AppCompatActivity {
    private ViewPager2 myViewPager2;
    private ViewPagerFragmentAdapter myAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // Make sure this is before calling super.onCreate
        setTheme(R.style.AppTheme); // show splash screen
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qr_bluetooth_swipe_activity);
        init();
    }

    private void init() {
        myAdapter = new ViewPagerFragmentAdapter(getSupportFragmentManager());
        myAdapter.addFragment(new BluetoothPageFragment());
        myAdapter.addFragment(new QrPageFragment());
        myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
        myViewPager2.setAdapter(myAdapter);
    }

}

here ViewPagerFragmentAdapter

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.ArrayList;

public class ViewPagerFragmentAdapter  extends FragmentStateAdapter {

    private ArrayList<Fragment> arrayList = new ArrayList<>();

    public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return arrayList.get(position);
    }

    public void addFragment(Fragment fragment) {
        arrayList.add(fragment);
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }
}

but I get compile error:

ViewPagerFragmentAdapter is not abstract and does not override abstract method createFragment(int) in FragmentStateAdapter

How I can fix this and use FRAGMENTS in ViewPager2 ?

Thanks

like image 655
Alexei Avatar asked Oct 23 '19 16:10

Alexei


People also ask

How do I refresh FragmentStateAdapter?

You can use startActivityForResult(or broadcast/receiver) to get the result from the activity. Then use adapter. notifyDataSetChanged to refresh the fragment.

What is the difference between ViewPager and ViewPager2?

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager . If your app already uses ViewPager , read this page to learn more about migrating to ViewPager2 .

How do I get fragments from FragmentStateAdapter?

If you have fragment tag though, you can easily retrieve reference to it from the FragmentManager by calling findFragmentByTag() .

What is the difference between FragmentPagerAdapter vs FragmentStatePagerAdapter?

There is another PagerAdapter type that you can use called FragmentPagerAdapter . FragmentPagerAdapter is used exactly like FragmentStatePagerAdapter . It only differs in how it unloads your fragments when they are no longer needed (Figure 11.4). With FragmentStatePagerAdapter , your unneeded fragment is destroyed.


1 Answers

FragmentStateAdapter in ViewPager2 is a little bit different than in ViewPager as follows:

  • Instead of implementing getCount(), implement getItemCount()

  • Instead of implementing getItem(int position), implement createFragment(int position)

  • Constructor signature is different by adding Lifecycle argument which should be included in super as well

So replace your ViewPagerFragmentAdapter with below

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.ArrayList;


public class ViewPagerFragmentAdapter extends FragmentStateAdapter {

    private ArrayList<Fragment> arrayList = new ArrayList<>();

    public ViewPagerFragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
        super(fragmentManager, lifecycle);
    }


    public void addFragment(Fragment fragment) {
        arrayList.add(fragment);
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        // return your fragment that corresponds to this 'position'
        return arrayList.get(position);
    }

}

Also make sure to import/extend the right adapter of ViewPager2

import androidx.viewpager2.adapter.FragmentStateAdapter;

UPDATE 2021

This answered the original issue of the question; However creating the FragmentStateAdapter this way is not that right generally speaking; because of:

  1. To have a high performant ViewPager, it should keep only a few instantiated off-screen page fragments.

  2. Keeping all the page fragments into a list is not good in terms of wasting device resources.

  3. createFragment(), as its name implies, is intended to create a new fragment for a particular page and return it; not to return a fragment from a list that has already instantiated fragments.

  4. Above all as per documentation createFragment()::

Provide a new Fragment associated with the specified position.

The adapter will be responsible for the Fragment lifecycle:

  • The Fragment will be used to display an item.
  • The Fragment will be destroyed when it gets too far from the viewport, and its state will be saved. When the item is close to the viewport again, a new Fragment will be requested, and a previously saved state will be used to initialize it.

So, I'd suggest to change this design to include that into consideration:

Assuming your page fragment is PageFragment; then you can have single instance pattern by having static PageFragment newInstance(int position) {} method in the PageFragment, and then the createFragment() should be then:

@NonNull
@Override
public Fragment createFragment(int position) {
    // return a brand new fragment that corresponds to this 'position'
    return PageFragment.newInstance(position); // Create a new instance of the page fragment
}
like image 134
Zain Avatar answered Nov 09 '22 15:11

Zain