javaandroidandroid-fragmentsonitemclicklistenerandroid-viewpager2

ViewPager2 not working properly with Fragments and click events


I just wanna know if I'm doing something wrong since I'm kinda new to all this.
If there is anything else that you'll like me to add just let me know.
This is the repo branch where I'm trying to implement the ViewPager if you wanna see all the code.

Context

So I have 4 Categories represented with Fragments, each of this categories holds an ArrayList of items that each has a onItemClickListener that should reproduce some audio.
I'm trying to display the Fragments with a ViewPager but the problem is that when I scroll from a Fragment to another, then come back to the already created Fragment, it doesnt register the touch event, nothing happens, not even an error nor exception.
If I go to a newly created Fragment the touch works just fine.
Also, after switching back to an already created Fragment if I scroll even just a little bit to another Fragment and comeback or through the ArrayList of that Fragment for some reason it starts to recognize the touch in the ArrayList items again.

Similar questions that didn't really help

What I've tried

Code Snippets

word_list.xml:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/tan_background" />

activity_main.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"/>

</FrameLayout>

This is one of the Fragments, the other three are basically the same, just the items in the arrayList change and some other minor things:

// ...Skipped some irrelevant code...

public class NumbersFragment extends Fragment {
    private ArrayList<Word> mWords;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.word_list, container, false);

        mWords = new ArrayList<>();
        // ...Add all the items to the list...

        // Make the adapter for the word items
        WordAdapter adapter = new WordAdapter(getActivity(), mWords, R.color.category_numbers);
        // Find the root view of the list
        ListView listView = rootView.findViewById(R.id.root_list_view);
        // Add adapter to the root list view
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Log.d("NumbersFragment", "CLICKED");
                }
            }

        });

        return rootView;
    }

    @Override
    public void onPause() {
        super.onPause();

        Log.d("NumbersFragment", "Fragment paused");
    }

}

This is the Category adapter, it manages the fragments:

public class CategoryAdapter extends FragmentStateAdapter {
    private static final int NUM_CATEGORIES = 4;

    // Required public constructor
    public CategoryAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        // Depending on which page the user is in,
        // create a fragment of the corresponding category
        switch (position) {
            case 0:
                return new NumbersFragment();
            case 1:
                return new FamilyFragment();
            case 2:
                return new ColorsFragment();
            default:
                return new PhrasesFragment();
        }
    }

    @Override
    public int getItemCount() {
        return NUM_CATEGORIES;
    }
}

And this is my MainActivity:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set the content of the activity to use the activity_main.xml layout file
        setContentView(R.layout.activity_main);

        // Find the view pager that will allow the user to swipe between fragments
        ViewPager2 viewPager = findViewById(R.id.viewpager);

        // Create an adapter that knows which fragment should be shown on each page
        CategoryAdapter adapter = new CategoryAdapter(this);
        //or CategoryAdapter adapter = new CategoryAdapter(getSupportFragmentManager(), getLifecycle());

        // Set the adapter into the view pager
        viewPager.setAdapter(adapter);
    }
}

Solution

  • add this in your MainActivity viewPager.setOffscreenPageLimit(3); after creating viewpager

    It’s because the ViewPager has a default offscreen limit of 1 ,and ViewPager2 has a default offscreen limit of 0.

    In ViewPager2 when you switch tabs the previous tab will be automatically refreshed.

    in ViewPager if you have 3 tabs or more when you switch to 3rd tab automatically first one will be destroyed and when you goes to 1st tab it will be recreated.

    viewPager.setOffscreenPageLimit(3); from this line when you switch to a tab,the previous 3 tabs will be preloaded and next 3 tabs will be preloaded so nothing will be refreshed.