Android: ViewPager + Fragments: modify fragment view when "onPageScrolled" - android

Well, I think the title is quite self explanatory. I have a ViewPager in my HomeActivity, the ViewPager contains 5 fragments at the moment.
When one of the fragments is visible by calling ViewPager's onPageScrolled I want to modify some views inside the current displayed fragment according to some conditions in the HomeActiviy.
After some research, it seems like I cannot find a good way to communicate in the direction HomeActivity --> Fragments inside ViewPager.
I have easily solved the communication in the direction Fragments in ViewPager --> HomeActivity using an Interfacebut this trick seems to not be working on the other direction.
I can access each time the current displayed fragment using a method on my FragmentStatePagerAdapter
public Fragment getActiveFragment(int position){
return myFragmentsList.get(position);
}
However, by doing that, I would have to cast each Fragment into its class MyFragment1 MyFragment2 MyFragment3....
Any easy clean way to achieve that?. Here is the relevant portion of the code:
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (currentStatus == 1){
Fragment activeFrag = mPagerAdapter.getActiveFragment(mPager.getCurrentItem());
//here I would like to modify one of the 5 fragments
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});

Define an interface,
interface FragmentListenerInterface {
public void onFragmentSelected();
}
Implement this interface in all of your fragments. change the communication between fragment and viewpager. write a method to return fragmentlistenerinterface instead of fragment.
public FragmentListenerInterface getActiveFragment(int position){
return (FragmentListenerInterface) myFragmentsList.get(position);
}
After this just say, in your code
public void onPageSelected(int position) {
mViewPager.getActiveFragment(position).onFragmentSelected();
}
And implement onFragmentSelected() in every fragment.

Related

How to communicate between fragment that holds the viewpager and viewpager's fragments (MVP)?

I'm building a mvp application and i'm looking for a clean and correct way to communicate between main fragment and those viewpager's fragments.
More specific,I have :
JobsFragment : fragment which contains the view pager
NewJobsFragment : one of view pager fragments
RecommendedJobsFragment : 2rd fragment from viewpager
SavedJobsFragment : 3rd fragment from viewpager
All viewpager's fragments contain a RecyclerView.Each part uses a MVP structure ( JobsFragment has it's own JobsPresenter,NewJobsFragments has NewJobsPresenter and so on)
User can filter the jobs in each fragment.Those filters can be selected from JobsFragment and when this is happening I need to 'notify' current fragment from viewpager and send those filters to it.
I read about child-parent relationship between presenters but I don't think it's a proper solution.
I also think about sharing a single presenter for all fragments but it's a lot of code and I guess it won't be readable anymore.
Thanks in advance !
Create one Abstract Fragment that extends Fragment. like this
public abstract BaseChildFragment extends Fragment{
public void onViewPagerPagechange();
}
Make All ChildFragment (JobsFragment , NewJobsFragment etc) extends BaseChildFragment ,
On your Pager Adapter add below code.
LruCache<Integer,BaseChildFragment> cache = new LruCache<>(4);
public BaseChildFragment getBaseChildFragment(int position){
return cache.get(position);
}
#Override
public Fragment getItem(int position) {
if (position == JOB) {
if (Utility.isEmpty(fragmentProfile)) {
jobsFragment = JobsFragment .getInstance()
cache.put(position,fragmentProfile );
}
return jobsFragment ;
} else if (position == NEW_JOB) {
......
return fragment;
}
return null;
}
On your Fragment that has View pager AddpageChangeListener like this
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
// optional
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
// optional
#Override
public void onPageSelected(int position) {
BaseChildFragment fragment = adapter.getBaseChildFragment(position);
fragmeng.onViewPagerPagechange();
//you can invoke presenter logic by calling this method on each child presenter will call your filter (its upto you)
}
// optional
#Override
public void onPageScrollStateChanged(int state) { }
});
This will call your ChildFragment onViewPagerPagechange(); method. where you can load your child fragments. you can also get currently selected fragment. and simplest way is that you can get current fragment and check with getInstance();

Call activity method from async method inside the viewpager fragment

Consider there is an activity with a title. Viewpager fragment is inside the activity. Fragment will be loaded with data from network API.
Now I need to update the activity title after getting the data from API.
Problem:
Since Viewpager is used, it loads the (prev), current, (next) fragments too. So activity is not aware of which title it is for.
Each title is different for each fragment, so I need to update the title only when the user views that fragment.
You need to create object of your Activity, By using this Object you are able to use activity method.
For more detail; visit this link.How to use method from another Class?
Use this with addOnPageChangeListener method of pager, in it you will get your current postion of pager.
In you Activity add the listener on ViewPager:
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener()
{
#Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
}
#Override public void onPageSelected(int position)
{
//You can use switch case too
if (position== 0)
{
//set title
}
else if (position== 1)
{
//set title
}...
}
#Override public void onPageScrollStateChanged(int state)
{
}
});
You can call the Activity method from Fragment by
Make method in Activity public
In fragment use getActivty() -> cast it to corresponding Activity then call this method
If you want to call Activity method only when fragment visible you can try https://stackoverflow.com/a/42019934/5381331
In Activity, you want to get the current visible fragment, you can try https://stackoverflow.com/a/47789367/5381331

Where to put "ViewPager.setCurrentItem()" Method

I have Nested Fragments [1 main Fragment called "MainFrag" and many child Fragments]
the App can show MainFrag or can show other fragments.
what I want to do is to setCurrentItem of MainFrag's ViewPager every time the user selects to show that MainFrag so I tried to put it at it's onCreateView but this only works for the first time the User Shows MainFrag as if user Selects another fragment and then re-selects MainFrag the seCurrentItem is not working
I also tried to put it inside MainFrag's onResume, It worked as required but it also causes a problem that if the user moved the App to background and then reOpen it viewPager will Scroll and i don't want this
So where Exactly Can I put setCurrentItem ?
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
Edit: I am using Navigation Menu to Move between 3 main fragments where MainFrag is one of them.
After your mViewPager.setAdapter(adapter); method you can put
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
After setting adapter to viewpager add setCurrentItem method
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(mCurrentWeekFragmentItemNumber);
Try this:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
viewPager.setCurrentItem(position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
}

SupportFragmentManager's findFragmentById always returns a null

I am trying to get a Fragment reference using the findFragmentById() but it always returns a null object. I have used FragmentPagerAdapter and attached a OnPageChangeListener So that whenever I scroll to a specific Fragment, say FragmentB, a method of FragmentB should be fired.
I have the following code:
mPager.addOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == 1) {
FragmentB fb = (FragmentB) getSupportFragmentManager().findFragmentById(R.id.fragment_b);
fb.refreshList();
//fb is always null
Toast.makeText(MainActivity.this,"Scrolled", Toast.LENGTH_SHORT).show();
//Toast is working fine
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2){}
#Override
public void onPageScrollStateChanged(int arg0){}
});
Just do this instead:
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
PagerAdapter pagerAdapter = (PagerAdapter) mPager.getAdapter();
if (position == 1) {
FragmentB fragmentB = (FragmentB)pagerAdapter.getItem(position);
fragmentB.refreshList();
}
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});
The problem with findFragmentById is not intended to be used in this way:
Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction. This first searches through fragments that are currently added to the manager's activity; if no such fragment is found, then all fragments currently on the back stack associated with this ID are searched.
You should call it to get which fragment is in your fragment's container (for example, if you're using Navigation Drawer and you want to know which of the fragment is in the viewport), while you're using ViewPager, which hosts multiple fragments.
I hope, it helps.
findFragmentById() will always return null if you're not declaring your fragment in your activity's content view(it's .xml file) because you can't set a fragment's ID programmatically!
If you're interested in that here is what your code might look like inside an activity's xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="#+id/my_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.mypackage.MyFragment"/>
</LinearLayout>
And this way, looking up a fragment in the FragmentManager with findFragmentById() would return you the proper instance of MyFragment.
findFragmentById() is one way of looking up fragments in the FragmentManager but there is also another way through findFragmentByTag().
In your case you most probably set up those fragments in an instance of a PagerAdapter through the following method:
#Override
public Fragment getItem(int position) {}
Now there are a lot of examples and answers encouraging you to use some reflection to get a fragment from the FragmentManager. Android automatically generates a tag for each fragment and you can get this name with the following method:
private static String getFragmentName(int viewId, int id) {
return "android:switcher:" + viewId + ":" + id;
}
And then you could build a getter method to get a fragment like this:
public Fragment getFragmentForPosition(int position) {
Fragment fragment = getSupportFragmentManager().findFragmentByTag(getFragmentName(mViewPager.getId(), getItemId(position)));
return fragment; // May be null if the fragment is not found. Handle that case too wherever you call this method.
}
Where viewId is the ID from the xml file of the ViewPager and id can be obtained in the adapter anywhere with getItemId(position).
This will work but it is a bit of an ugly solution because of the reflection used here.
This is a really easy but ugly solution which should work if the Android team doesn't change their logic for generating tags :)
Please refer to the this SO response for a list of other possible solutions.

Fragment-based app using ViewPager (Android Studio Template) creates off-screen fragments

I created a new project in Android Studio that uses fragments and tabs to select a fragment.
So Android Studio created a project for me that uses a ViewPager to select the different fragments.
Unfortunately, ViewPager will create off-screen fragments and even trigger onResume() in the off-screen fragment !
This is a problem, since code in onResume() in fragment 2 depends on what a user does in fragment 1.
I've tried setting mViewPager.setOffscreenPageLimit(0); but this doesn't seem to have any effect, the neighboring fragment is still called with onResume() before it's actually shown.
QUESTION
What callback is triggered in a Fragment when it's actually shown?
Otherwise, how can I set it up so that I can adjust the UI in fragment 2 depending on what the user did in Fragment 1 ?
you can use setOnPageChangeListener() like this
mViewPager.setOffscreenPageLimit(1);
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {}
#Override
public void onPageSelected(int position) {
// TO DO
}
#Override
public void onPageScrollStateChanged(int state) {}
});

Categories

Resources