0

I am trying to replace the current active fragment inside ViewPager when item inside fragment's recycelerview is clicked. So far, I manage to pop up the Toast on the second fragment when the item on the first fragment is clicked, but the all the view of the second fragment is blank.

Here is my code:

MainActivity.class

public class MainActivity extends AppCompatActivity {
    
    private ViewPager viewPager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        viewPager = findViewById(R.id.view_pager);
        FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager());
        
        viewPager.setAdapter(adapter);
        viewPager.setOffscreenPageLimit(adapter.getCount() - 1);
    
    }
    
    // ..........
    
    private static class FragmentAdapter extends FragmentStatePagerAdapter {

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

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return AllGenreFragment.newInstance();
                case 1:
                    return DashboardFragment.newInstance();
                case 2:
                    return NotificationFragment.newInstance();
            }
            return null;
        }

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

AllGenreFragment.class

public class AllGenreFragment extends Fragment {

    RecyclerView genreRV;
    private AllGenreAdapter allGenreAdapter;
    private List<Genre> genreList;
    
    public static AllGenreFragment newInstance() {
        return new AllGenreFragment();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setHasOptionsMenu(true);
    }

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

        View v = inflater.inflate(R.layout.fragment_genre, container, false);

        genreRV = v.findViewById(R.id.rv);
        genreRV.setHasFixedSize(true);
        GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 2);
        genreRV.setLayoutManager(layoutManager);
        genreList = new ArrayList<>();
        allGenreAdapter = new AllGenreAdapter(genreList, getContext());
        genreRV.setAdapter(allGenreAdapter);

        return v;
    }

}

AllGenreAdapter.class

public class AllGenreAdapter extends RecyclerView.Adapter<AllGenreAdapter.ViewHolder> {

    // .......
    
    class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {

        public ViewHolder(final View itemView) {
            super(itemView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                
                    Toast.makeText(context, "Genre: " + genre_title.getText().toString(), Toast.LENGTH_SHORT).show();

                    AppCompatActivity activity = (AppCompatActivity) itemView.getContext();

                    FragmentManager fragmentManager = activity.getSupportFragmentManager();
                    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

                    Fragment allAnimeFragment = new AllAnimeFragment();

                    Bundle bundle = new Bundle();
                    bundle.putString("genreid", genre_id.getText().toString().trim());
                    allAnimeFragment.setArguments(bundle);

                    fragmentTransaction.replace(R.id.view_pager, allAnimeFragment);
                    fragmentTransaction.addToBackStack(null);
                    fragmentTransaction.commit();

                }
            });

        }

    }

    // .......

}

AllAnimeFragment.class

public class AllAnimeFragment extends Fragment {

    RecyclerView animeRV;
    private AllAnimeAdapter allAnimeAdapter;
    private List<Anime> animeList;
    String genreid;

    public static AllAnimeFragment newInstance() {
        return new AllAnimeFragment();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setHasOptionsMenu(true);
    }

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

        View v = inflater.inflate(R.layout.fragment_anime, container, false);

        Bundle arguments = getArguments();
        genreid = arguments.getString("genreid");
        
        Toast.makeText(getContext(), genreid, Toast.LENGTH_SHORT).show();
        
        // .....

        return v;
    }
}
Papa Joe
  • 11
  • 3

2 Answers2

0

you should add a context in the constructor of your adapter

public class AllGenreAdapter(Context context) extends RecyclerView.Adapter<AllGenreAdapter.ViewHolder> {
// ....... 
}

then pass your activity to your adapter, use it to replace your fragment

  • yes, context is already included in adapter, and I don't think I need to add context constructor, am I right? CMIIW. public class AllGenreAdapter extends RecyclerView.Adapter { private Context context; public AllGenreAdapter(Context context) { this.context = context; } } – Papa Joe Dec 18 '18 at 03:16
  • Well, you need your MainActivity to make that transaction, not AppCompatActivity – Vũ Hồng Sơn Dec 18 '18 at 03:20
  • Here's what I've tried: replacing AppCompatActivity activity = (AppCompatActivity) itemView.getContext(); with MainActivity activity = (MainActivity) itemView.getContext(); and seems like it's not working, the blank screen still appear after I clicked the item. cheers. – Papa Joe Dec 18 '18 at 03:26
  • maybe you should try this https://stackoverflow.com/questions/7723964/replace-fragment-inside-a-viewpager – Vũ Hồng Sơn Dec 18 '18 at 03:33
0

Your approach is not really good, try below way:

  • Create a container fragment, called ContainerFragment, example, you will be using this fragment for your ViewPager.

  • The ContainerFragment should contain a container view, and add AllGenreFragment in the first them it is invoked.

  • In AllGenreFragment, create a callback, when an item in the RecyclerView be clicked, then in ContainerFragment will replace (or add) current fragment (AllGenreFragment) to AllAnimeFragment

First, create ContainerFragment,

public class ContainerFragment extends Fragment implements AllGenreFragment.OnAllGenreFragmentListener {

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

    @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        initialLoad();
    }

    private void initialLoad() {
        getChildFragmentManager()
                .beginTransaction()
                .add(R.id.container, AllGenreFragment.newInstance())
                .commit();
    }

    @Override public void onItemClicked() {
        getChildFragmentManager()
                .beginTransaction()
                .replace(R.id.container, AllAnimeFragment.newInstance())
                .commit();
    }
}

This Fragment should implement a callback from AllGenreFragment for handling fragment transaction.

also, the xml layout

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

Next step, add above fragment into ViewPager, we using ContainerFragment instead of AllGenreFragment.

  @Override
            public Fragment getItem(int position) {
                switch (position) {
                    case 0:
                        return ContainerFragment().newInstance();
                    case 1:
                        return DashboardFragment.newInstance();
                    case 2:
                        return NotificationFragment.newInstance();
                }
                return null;
            }

Ok, so now come back to AllGenreFragment, we should create a CallBack that we implement in ContainerFragment

public interface AllGenreFragmentListener {
     onItemClicked();
}

and then,

private AllGenreFragmentListener  listener;



@Override public void onAttach(Context context) {
        super.onAttach(context);
        if (getParentFragment() instanceof AllGenreFragmentListener) {
            listener = (AllGenreFragmentListener) getParentFragment();
        }
    }

so we have an instance of AllGenreFragmentListener, from now, whenever listener call onItemClicked, the method onItemClicked in ContainerFragment will be invoked.

Ok, let do it,

create below variable and method in your AllGenreAdapter:

private AllGenreFragmentListener listener;

public void setListener(AllGenreFragmentListener listener) {
   this.listener = listener;
}

and update your ViewHodler

itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                
                    Toast.makeText(context, "Genre: " + genre_title.getText().toString(), Toast.LENGTH_SHORT).show();
                   if (listener != null)
                      listener.onItemClicked()
            });

        }

Don't forget call setListener method in your AllGenreFragment, so please add

@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (allGenreAdapter != null) allGenreAdapter.setListener(listener);
    }

That all,

I type this code by hand, not in my IDE so maybe it not compile, so please fix it if you face any compile error.

Note

If you want to keep the behavior of the BACK button, override onBackPressed in your MainActivity

@Override public void onBackPressed() {
        Fragment f = getCurrentFragment();
        if (f != null) {
            if (f.getChildFragmentManager().getBackStackEntryCount() > 1) {
                f.getChildFragmentManager().popBackStack();
            } else {
                super.onBackPressed();
            }

        } else {
            super.onBackPressed();
        }

    }

    public Fragment getCurrentFragment() {
        return (Fragment) viewPager.getAdapter()
                .instantiateItem(viewPager, viewPager.getCurrentItem());
    }

Also, add fragment into backStack whenever we add them to the container view (in your ContainerFragment)

private void initialLoad() {
        getChildFragmentManager()
                .beginTransaction()
                .add(R.id.container, AllGenreFragment.newInstance())
                .addToBackStack(null)
                .commit();
    }

    @Override public void onItemClicked() {
        // change `replace` to  `add` and add more `.addToBackStack(null)`
        getChildFragmentManager()
                .beginTransaction()
                .add(R.id.container, AllAnimeFragment.newInstance())
                .addToBackStack(null)
                .commit();
    }
DamirH
  • 13
  • 3
GianhTran
  • 3,443
  • 2
  • 22
  • 42
  • could you provide the code? because I don't understand how to implements your method. cheers. – Papa Joe Dec 18 '18 at 03:13
  • @PapaJoe I update my answer with a little bit of code, please take a look – GianhTran Dec 18 '18 at 03:43
  • Thanks for the code. I'm now trying to implement your code. – Papa Joe Dec 18 '18 at 04:12
  • IT WORKED !! one more question if you don't mind, when the back button is pressed, the activity get finished. How do I prevent this? when the back button is pressed, go back to AllGenreFragment. cheers – Papa Joe Dec 18 '18 at 04:33
  • @PapaJoe sure, I have updated my answer again, and please accept this answer if it helps, nice to help you! – GianhTran Dec 18 '18 at 04:56